Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Joseph/ta error #979

Draft
wants to merge 19 commits into
base: main
Choose a base branch
from
Draft

Joseph/ta error #979

wants to merge 19 commits into from

Conversation

joseph-sentry
Copy link
Contributor

we want to start generating UploadError objects when we have trouble parsing a JUnit XML so we can show them to the user in the error comment and the regular TA comment.

these 2 new tasks are meant to replace the test results processor and
test results finisher tasks

the difference between the tasks is that the new tasks:
- use the new parse_raw_upload function provided by the test results
  parser library
- instead of writing to the database in the processor, the finisher
  takes care of writing to the database
- the processor writes the results of its parsing and the finisher pulls
  from that
the upload task will check the new ta tasks feature flag to determine
whether to use the newly introduced ta processor and ta finisher tasks

we also call the new ta processor and ta finisher tasks via a chord
since we removed the concurrent db writes from the processors
also update test results parser version
we don't need to chunk them anymore and each upload can get its own
processing task
i want to introduce the new finished state in the test results upload
pipeline

to do so safely i'm adding the new v2_processed and v2_finished states

the reason for this is that the meaning of processed and v2_processed
are different

processed means that test instances are in the db and persisted
but in the new pipeline v2_finished has that meaning and v2_processed
just means that the intermediate results are in redis for now
Copy link

This PR includes changes to shared. Please review them here: https://github.com/codecov/shared/compare/2674ae99811767e63151590906691aed4c5ce1f9...

Copy link

codecov bot commented Dec 23, 2024

❌ 2 Tests Failed:

Tests completed Failed Passed Skipped
1783 2 1781 1
View the top 2 failed tests by shortest run time
tasks/tests/unit/test_ta_finisher_task.py::::test_test_analytics_error_comment
Stack Traces | 0.136s run time
self = <sqlalchemy.engine.base.Connection object at 0x7f7d9c9d1a90>
dialect = <sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0x7f7db97f7cb0>
constructor = <bound method DefaultExecutionContext._init_compiled of <class 'sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2'>>
statement = 'SELECT owners.plan AS owners_plan \nFROM repos JOIN owners ON owners.ownerid = repos.ownerid \nWHERE repos.repoid = %(repoid_1)s \n LIMIT %(param_1)s'
parameters = {'param_1': 1, 'repoid_1': 1026}
args = (<sqlalchemy.dialects.postgresql.psycopg2.PGCompiler_psycopg2 object at 0x7f7d8118a690>, [immutabledict({})])
conn = <sqlalchemy.pool.base._ConnectionFairy object at 0x7f7d819b7a50>
context = <sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0x7f7d8069b350>

    def _execute_context(
        self, dialect, constructor, statement, parameters, *args
    ):
        """Create an :class:`.ExecutionContext` and execute, returning
        a :class:`_engine.ResultProxy`.
    
        """
    
        try:
            try:
                conn = self.__connection
            except AttributeError:
                # escape "except AttributeError" before revalidating
                # to prevent misleading stacktraces in Py3K
                conn = None
            if conn is None:
                conn = self._revalidate_connection()
    
            context = constructor(dialect, self, conn, *args)
        except BaseException as e:
            self._handle_dbapi_exception(
                e, util.text_type(statement), parameters, None, None
            )
    
        if context.compiled:
            context.pre_exec()
    
        cursor, statement, parameters = (
            context.cursor,
            context.statement,
            context.parameters,
        )
    
        if not context.executemany:
            parameters = parameters[0]
    
        if self._has_events or self.engine._has_events:
            for fn in self.dispatch.before_cursor_execute:
                statement, parameters = fn(
                    self,
                    cursor,
                    statement,
                    parameters,
                    context,
                    context.executemany,
                )
    
        if self._echo:
            self.engine.logger.info(statement)
            if not self.engine.hide_parameters:
                self.engine.logger.info(
                    "%r",
                    sql_util._repr_params(
                        parameters, batches=10, ismulti=context.executemany
                    ),
                )
            else:
                self.engine.logger.info(
                    "[SQL parameters hidden due to hide_parameters=True]"
                )
    
        evt_handled = False
        try:
            if context.executemany:
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_executemany:
                        if fn(cursor, statement, parameters, context):
                            evt_handled = True
                            break
                if not evt_handled:
                    self.dialect.do_executemany(
                        cursor, statement, parameters, context
                    )
            elif not parameters and context.no_parameters:
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_execute_no_params:
                        if fn(cursor, statement, context):
                            evt_handled = True
                            break
                if not evt_handled:
                    self.dialect.do_execute_no_params(
                        cursor, statement, context
                    )
            else:
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_execute:
                        if fn(cursor, statement, parameters, context):
                            evt_handled = True
                            break
                if not evt_handled:
>                   self.dialect.do_execute(
                        cursor, statement, parameters, context
                    )

.../local/lib/python3.13.../sqlalchemy/engine/base.py:1276: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0x7f7db97f7cb0>
cursor = <cursor object at 0x7f7da4a01d50; closed: -1>
statement = 'SELECT owners.plan AS owners_plan \nFROM repos JOIN owners ON owners.ownerid = repos.ownerid \nWHERE repos.repoid = %(repoid_1)s \n LIMIT %(param_1)s'
parameters = {'param_1': 1, 'repoid_1': 1026}
context = <sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0x7f7d8069b350>

    def do_execute(self, cursor, statement, parameters, context=None):
>       cursor.execute(statement, parameters)
E       psycopg2.errors.UndefinedTable: relation "repos" does not exist
E       LINE 2: FROM repos JOIN owners ON owners.ownerid = repos.ownerid 
E                    ^

.../local/lib/python3.13.../sqlalchemy/engine/default.py:608: UndefinedTable

The above exception was the direct cause of the following exception:

dbsession = <sqlalchemy.orm.session.Session object at 0x7f7da497dd60>
mocker = <pytest_mock.plugin.MockFixture object at 0x7f7d9c9d1550>
mock_storage = <shared.storage.memory.MemoryStorageService object at 0x7f7da482a2d0>
celery_app = <Celery celery.tests at 0x7f7da65d82d0>
mock_repo_provider_service = <AsyncMock id='140178002626608'>
mock_pull_request_information = <AsyncMock id='140177002625552'>
generate_junit = <function generate_junit.<locals>._generate_junit at 0x7f7d942aa980>

    def test_test_analytics_error_comment(
        dbsession,
        mocker,
        mock_storage,
        celery_app,
        mock_repo_provider_service,
        mock_pull_request_information,
        generate_junit,
    ):
        wrong_xml = """<?xml version="1.0" encoding="utf-8"?>
    <testsuites>
        <testsuite>
            <testcase time="0.001">
                <failure message="hello world"/>
            </testcase>
        </testsuite>
    </testsuites>
    """
        upload = generate_junit(raw=wrong_xml)
    
        argument = {"url": upload.storage_path, "upload_id": upload.id_}
    
        mocker.patch.object(TAProcessorTask, "app", celery_app)
        mocker.patch.object(TAFinisherTask, "app", celery_app)
    
        result = TAProcessorTask().run_impl(
            dbsession,
            repoid=upload.report.commit.repoid,
            commitid=upload.report.commit.commitid,
            commit_yaml={"codecov": {"max_report_age": False}},
            argument=argument,
        )
    
        assert result is False
    
>       result = TAFinisherTask().run_impl(
            dbsession,
            chord_result=[result],
            repoid=upload.report.commit.repoid,
            commitid=upload.report.commit.commitid,
            commit_yaml={"codecov": {"max_report_age": False}},
        )

.../tests/unit/test_ta_finisher_task.py:372: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
tasks/ta_finisher.py:233: in run_impl
    notify_task_sig.apply_async()
.../local/lib/python3.13.../site-packages/celery/canvas.py:400: in apply_async
    return _apply(args, kwargs, **options)
.../local/lib/python3.13.../site-packages/sentry_sdk/tracing_utils.py:673: in func_with_tracing
    return func(*args, **kwargs)
tasks/base.py:150: in apply_async
    user_plan = _get_user_plan_from_task(db_session, self.name, kwargs)
celery_task_router.py:144: in _get_user_plan_from_task
    return func_to_use(dbsession, **task_kwargs)
celery_task_router.py:24: in _get_user_plan_from_repoid
    .first()
.../local/lib/python3.13.../sqlalchemy/orm/query.py:3429: in first
    ret = list(self[0:1])
.../local/lib/python3.13.../sqlalchemy/orm/query.py:3203: in __getitem__
    return list(res)
.../local/lib/python3.13.../sqlalchemy/orm/query.py:3535: in __iter__
    return self._execute_and_instances(context)
.../local/lib/python3.13.../sqlalchemy/orm/query.py:3560: in _execute_and_instances
    result = conn.execute(querycontext.statement, self._params)
.../local/lib/python3.13.../sqlalchemy/engine/base.py:1011: in execute
    return meth(self, multiparams, params)
.../local/lib/python3.13.../sqlalchemy/sql/elements.py:298: in _execute_on_connection
    return connection._execute_clauseelement(self, multiparams, params)
.../local/lib/python3.13.../sqlalchemy/engine/base.py:1124: in _execute_clauseelement
    ret = self._execute_context(
.../local/lib/python3.13.../sqlalchemy/engine/base.py:1316: in _execute_context
    self._handle_dbapi_exception(
.../local/lib/python3.13.../sqlalchemy/engine/base.py:1510: in _handle_dbapi_exception
    util.raise_(
.../local/lib/python3.13.../sqlalchemy/util/compat.py:182: in raise_
    raise exception
.../local/lib/python3.13.../sqlalchemy/engine/base.py:1276: in _execute_context
    self.dialect.do_execute(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0x7f7db97f7cb0>
cursor = <cursor object at 0x7f7da4a01d50; closed: -1>
statement = 'SELECT owners.plan AS owners_plan \nFROM repos JOIN owners ON owners.ownerid = repos.ownerid \nWHERE repos.repoid = %(repoid_1)s \n LIMIT %(param_1)s'
parameters = {'param_1': 1, 'repoid_1': 1026}
context = <sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0x7f7d8069b350>

    def do_execute(self, cursor, statement, parameters, context=None):
>       cursor.execute(statement, parameters)
E       sqlalchemy.exc.ProgrammingError: (psycopg2.errors.UndefinedTable) relation "repos" does not exist
E       LINE 2: FROM repos JOIN owners ON owners.ownerid = repos.ownerid 
E                    ^
E       
E       [SQL: SELECT owners.plan AS owners_plan 
E       FROM repos JOIN owners ON owners.ownerid = repos.ownerid 
E       WHERE repos.repoid = %(repoid_1)s 
E        LIMIT %(param_1)s]
E       [parameters: {'repoid_1': 1026, 'param_1': 1}]
E       (Background on this error at: http://sqlalche..../e/13/f405)

.../local/lib/python3.13.../sqlalchemy/engine/default.py:608: ProgrammingError
tasks/tests/unit/test_ta_finisher_task.py::::test_test_analytics_regular_comment_with_error
Stack Traces | 0.158s run time
dbsession = <sqlalchemy.orm.session.Session object at 0x7f7da497f110>
mocker = <pytest_mock.plugin.MockFixture object at 0x7f7da4a193d0>
mock_storage = <shared.storage.memory.MemoryStorageService object at 0x7f7d8089d190>
celery_app = <Celery celery.tests at 0x7f7dbc129950>
mock_repo_provider_service = <AsyncMock id='140177737206816'>
mock_pull_request_information = <AsyncMock id='140177606934944'>
generate_junit = <function generate_junit.<locals>._generate_junit at 0x7f7d805128e0>

    def test_test_analytics_regular_comment_with_error(
        dbsession,
        mocker,
        mock_storage,
        celery_app,
        mock_repo_provider_service,
        mock_pull_request_information,
        generate_junit,
    ):
        mocker.patch.object(TAProcessorTask, "app", celery_app)
        mocker.patch.object(TAFinisherTask, "app", celery_app)
    
        commit = CommitFactory.create()
    
        wrong_xml = """<?xml version="1.0" encoding="utf-8"?>
    <testsuites>
        <testsuite>
            <testcase time="0.001">
                <failure message="hello world"/>
            </testcase>
        </testsuite>
    </testsuites>
    """
    
        upload = generate_junit(raw=wrong_xml, commit=commit)
        argument = {"url": upload.storage_path, "upload_id": upload.id_}
        first_result = TAProcessorTask().run_impl(
            dbsession,
            repoid=upload.report.commit.repoid,
            commitid=upload.report.commit.commitid,
            commit_yaml={"codecov": {"max_report_age": False}},
            argument=argument,
        )
        assert first_result is False
    
        testruns = [
            {
                "name": "test_divide",
                "outcome": "fail",
                "duration_seconds": 0.001,
                "failure_message": "hello world",
            },
        ]
    
        upload = generate_junit(testruns, commit=commit)
        argument = {"url": upload.storage_path, "upload_id": upload.id_}
        second_result = TAProcessorTask().run_impl(
            dbsession,
            repoid=upload.report.commit.repoid,
            commitid=upload.report.commit.commitid,
            commit_yaml={"codecov": {"max_report_age": False}},
            argument=argument,
        )
        assert second_result is True
    
        result = TAFinisherTask().run_impl(
            dbsession,
            chord_result=[first_result, second_result],
            repoid=upload.report.commit.repoid,
            commitid=upload.report.commit.commitid,
            commit_yaml={"codecov": {"max_report_age": False}},
        )
    
        assert result["notify_attempted"] is True
        assert result["notify_succeeded"] is True
        assert result["queue_notify"] is False
    
        short_form_service_name = services_short_dict.get(
            upload.report.commit.repository.owner.service
        )
    
        mock_repo_provider_service.edit_comment.assert_called_once()
>       mock_repo_provider_service.edit_comment.assert_called_once_with(
            mock_pull_request_information.database_pull.pullid,
            mock_pull_request_information.database_pull.commentid,
            f"""### :x: Unsupported file format
    
    > Upload processing failed due to unsupported file format. Please review the parser error message:
    > `Error parsing JUnit XML in hello_world.junit.xml at 4:32: ParserError: No name found`
    > For more help, visit our [troubleshooting guide](https://docs.codecov.com/docs/test-analytics#troubleshooting).
    
    ---
    ### :x: 1 Tests Failed:
    | Tests completed | Failed | Passed | Skipped |
    |---|---|---|---|
    | 1 | 1 | 0 | 0 |
    <details><summary>View the top 1 failed tests by shortest run time</summary>
    
    >
    > ```python
    > tests.test_parsers.TestParsers test_divide
    > ```
    >
    > <details><summary>Stack Traces | 0.001s run time</summary>
    >
    > >
    > > ```python
    > > hello world
    > > ```
    >
    > </details>
    
    </details>
    
    To view more test analytics, go to the [Test Analytics Dashboard](https://app.codecov.io/{short_form_service_name}/{upload.report.commit.repository.owner.username}/{upload.report.commit.repository.name}/tests/{upload.report.commit.branch})
    :loudspeaker:  Thoughts on this report? [Let us know!](https://github..../feedback/issues/304)""",
        )
E       AssertionError: expected call not found.
E       Expected: edit_comment(<AsyncMock name='mock.database_pull.pullid' id='140177610139232'>, <AsyncMock name='mock.database_pull.commentid' id='140177610141920'>, '### :x: Unsupported file format\n\n> Upload processing failed due to unsupported file format. Please review the parser error message:\n> `Error parsing JUnit XML in hello_world.junit.xml at 4:32: ParserError: No name found`\n> For more help, visit our [troubleshooting guide](https://docs.codecov.com/docs/test-analytics#troubleshooting).\n\n---\n### :x: 1 Tests Failed:\n| Tests completed | Failed | Passed | Skipped |\n|---|---|---|---|\n| 1 | 1 | 0 | 0 |\n<details><summary>View the top 1 failed tests by shortest run time</summary>\n\n> \n> ```python\n> tests.test_parsers.TestParsers test_divide\n> ```\n> \n> <details><summary>Stack Traces | 0.001s run time</summary>\n> \n> > \n> > ```python\n> > hello world\n> > ```\n> \n> </details>\n\n</details>\n\nTo view more test analytics, go to the [Test Analytics Dashboard](https://app.codecov..../general-spring-same/tests/main)\n:loudspeaker:  Thoughts on this report? [Let us know!](https://github..../feedback/issues/304)')
E         Actual: edit_comment(<AsyncMock name='mock.database_pull.pullid' id='140177610139232'>, <AsyncMock name='mock.database_pull.commentid' id='140177610141920'>, '### :x: Unsupported file format\n\n> Upload processing failed due to unsupported file format. Please review the parser error message:\n> `Error parsing JUnit XML at 4:32: ParserError: No name found`\n> For more help, visit our [troubleshooting guide](https://docs.codecov.com/docs/test-analytics#troubleshooting).\n\n---\n### :x: 1 Tests Failed:\n| Tests completed | Failed | Passed | Skipped |\n|---|---|---|---|\n| 1 | 1 | 0 | 0 |\n<details><summary>View the top 1 failed tests by shortest run time</summary>\n\n> \n> ```python\n> tests.test_parsers.TestParsers test_divide\n> ```\n> \n> <details><summary>Stack Traces | 0.001s run time</summary>\n> \n> > \n> > ```python\n> > hello world\n> > ```\n> \n> </details>\n\n</details>\n\nTo view more test analytics, go to the [Test Analytics Dashboard](https://app.codecov..../general-spring-same/tests/main)\n:loudspeaker:  Thoughts on this report? [Let us know!](https://github..../feedback/issues/304)')
E       
E       pytest introspection follows:
E       
E       Args:
E       assert (<AsyncMock n.../issues/304)') == (<AsyncMock n.../issues/304)')
E         
E         At index 2 diff: '### :x: Unsupported file format\n\n> Upload processing failed due to unsupported file format. Please review the parser error message:\n> `Error parsing JUnit XML at 4:32: ParserError: No name found`\n> For more help, visit our [troubleshooting guide](https://docs.codecov.com/docs/test-analytics#troubleshooting).\n\n---\n### :x: 1 Tests Failed:\n| Tests completed | Failed | Passed | Skipped |\n|---|---|---|---|\n| 1 | 1 | 0 | 0 |\n<details><summary>View the top 1 failed tests by shortest run time</summary>\n\n> \n> ```python\n> tests.test_parsers.TestPa...
E         
E         ...Full output truncated (2 lines hidden), use '-vv' to show

.../tests/unit/test_ta_finisher_task.py:478: AssertionError

To view more test analytics, go to the Test Analytics Dashboard
📢 Thoughts on this report? Let us know!

@codecov-staging
Copy link

codecov-staging bot commented Dec 23, 2024

❌ 2 Tests Failed:

Tests completed Failed Passed Skipped
1783 2 1781 1
View the top 2 failed tests by shortest run time
tasks/tests/unit/test_ta_finisher_task.py::::test_test_analytics_error_comment
Stack Traces | 0.136s run time
self = <sqlalchemy.engine.base.Connection object at 0x7f7d9c9d1a90>
dialect = <sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0x7f7db97f7cb0>
constructor = <bound method DefaultExecutionContext._init_compiled of <class 'sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2'>>
statement = 'SELECT owners.plan AS owners_plan \nFROM repos JOIN owners ON owners.ownerid = repos.ownerid \nWHERE repos.repoid = %(repoid_1)s \n LIMIT %(param_1)s'
parameters = {'param_1': 1, 'repoid_1': 1026}
args = (<sqlalchemy.dialects.postgresql.psycopg2.PGCompiler_psycopg2 object at 0x7f7d8118a690>, [immutabledict({})])
conn = <sqlalchemy.pool.base._ConnectionFairy object at 0x7f7d819b7a50>
context = <sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0x7f7d8069b350>

    def _execute_context(
        self, dialect, constructor, statement, parameters, *args
    ):
        """Create an :class:`.ExecutionContext` and execute, returning
        a :class:`_engine.ResultProxy`.
    
        """
    
        try:
            try:
                conn = self.__connection
            except AttributeError:
                # escape "except AttributeError" before revalidating
                # to prevent misleading stacktraces in Py3K
                conn = None
            if conn is None:
                conn = self._revalidate_connection()
    
            context = constructor(dialect, self, conn, *args)
        except BaseException as e:
            self._handle_dbapi_exception(
                e, util.text_type(statement), parameters, None, None
            )
    
        if context.compiled:
            context.pre_exec()
    
        cursor, statement, parameters = (
            context.cursor,
            context.statement,
            context.parameters,
        )
    
        if not context.executemany:
            parameters = parameters[0]
    
        if self._has_events or self.engine._has_events:
            for fn in self.dispatch.before_cursor_execute:
                statement, parameters = fn(
                    self,
                    cursor,
                    statement,
                    parameters,
                    context,
                    context.executemany,
                )
    
        if self._echo:
            self.engine.logger.info(statement)
            if not self.engine.hide_parameters:
                self.engine.logger.info(
                    "%r",
                    sql_util._repr_params(
                        parameters, batches=10, ismulti=context.executemany
                    ),
                )
            else:
                self.engine.logger.info(
                    "[SQL parameters hidden due to hide_parameters=True]"
                )
    
        evt_handled = False
        try:
            if context.executemany:
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_executemany:
                        if fn(cursor, statement, parameters, context):
                            evt_handled = True
                            break
                if not evt_handled:
                    self.dialect.do_executemany(
                        cursor, statement, parameters, context
                    )
            elif not parameters and context.no_parameters:
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_execute_no_params:
                        if fn(cursor, statement, context):
                            evt_handled = True
                            break
                if not evt_handled:
                    self.dialect.do_execute_no_params(
                        cursor, statement, context
                    )
            else:
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_execute:
                        if fn(cursor, statement, parameters, context):
                            evt_handled = True
                            break
                if not evt_handled:
>                   self.dialect.do_execute(
                        cursor, statement, parameters, context
                    )

.../local/lib/python3.13.../sqlalchemy/engine/base.py:1276: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0x7f7db97f7cb0>
cursor = <cursor object at 0x7f7da4a01d50; closed: -1>
statement = 'SELECT owners.plan AS owners_plan \nFROM repos JOIN owners ON owners.ownerid = repos.ownerid \nWHERE repos.repoid = %(repoid_1)s \n LIMIT %(param_1)s'
parameters = {'param_1': 1, 'repoid_1': 1026}
context = <sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0x7f7d8069b350>

    def do_execute(self, cursor, statement, parameters, context=None):
>       cursor.execute(statement, parameters)
E       psycopg2.errors.UndefinedTable: relation "repos" does not exist
E       LINE 2: FROM repos JOIN owners ON owners.ownerid = repos.ownerid 
E                    ^

.../local/lib/python3.13.../sqlalchemy/engine/default.py:608: UndefinedTable

The above exception was the direct cause of the following exception:

dbsession = <sqlalchemy.orm.session.Session object at 0x7f7da497dd60>
mocker = <pytest_mock.plugin.MockFixture object at 0x7f7d9c9d1550>
mock_storage = <shared.storage.memory.MemoryStorageService object at 0x7f7da482a2d0>
celery_app = <Celery celery.tests at 0x7f7da65d82d0>
mock_repo_provider_service = <AsyncMock id='140178002626608'>
mock_pull_request_information = <AsyncMock id='140177002625552'>
generate_junit = <function generate_junit.<locals>._generate_junit at 0x7f7d942aa980>

    def test_test_analytics_error_comment(
        dbsession,
        mocker,
        mock_storage,
        celery_app,
        mock_repo_provider_service,
        mock_pull_request_information,
        generate_junit,
    ):
        wrong_xml = """<?xml version="1.0" encoding="utf-8"?>
    <testsuites>
        <testsuite>
            <testcase time="0.001">
                <failure message="hello world"/>
            </testcase>
        </testsuite>
    </testsuites>
    """
        upload = generate_junit(raw=wrong_xml)
    
        argument = {"url": upload.storage_path, "upload_id": upload.id_}
    
        mocker.patch.object(TAProcessorTask, "app", celery_app)
        mocker.patch.object(TAFinisherTask, "app", celery_app)
    
        result = TAProcessorTask().run_impl(
            dbsession,
            repoid=upload.report.commit.repoid,
            commitid=upload.report.commit.commitid,
            commit_yaml={"codecov": {"max_report_age": False}},
            argument=argument,
        )
    
        assert result is False
    
>       result = TAFinisherTask().run_impl(
            dbsession,
            chord_result=[result],
            repoid=upload.report.commit.repoid,
            commitid=upload.report.commit.commitid,
            commit_yaml={"codecov": {"max_report_age": False}},
        )

.../tests/unit/test_ta_finisher_task.py:372: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
tasks/ta_finisher.py:233: in run_impl
    notify_task_sig.apply_async()
.../local/lib/python3.13.../site-packages/celery/canvas.py:400: in apply_async
    return _apply(args, kwargs, **options)
.../local/lib/python3.13.../site-packages/sentry_sdk/tracing_utils.py:673: in func_with_tracing
    return func(*args, **kwargs)
tasks/base.py:150: in apply_async
    user_plan = _get_user_plan_from_task(db_session, self.name, kwargs)
celery_task_router.py:144: in _get_user_plan_from_task
    return func_to_use(dbsession, **task_kwargs)
celery_task_router.py:24: in _get_user_plan_from_repoid
    .first()
.../local/lib/python3.13.../sqlalchemy/orm/query.py:3429: in first
    ret = list(self[0:1])
.../local/lib/python3.13.../sqlalchemy/orm/query.py:3203: in __getitem__
    return list(res)
.../local/lib/python3.13.../sqlalchemy/orm/query.py:3535: in __iter__
    return self._execute_and_instances(context)
.../local/lib/python3.13.../sqlalchemy/orm/query.py:3560: in _execute_and_instances
    result = conn.execute(querycontext.statement, self._params)
.../local/lib/python3.13.../sqlalchemy/engine/base.py:1011: in execute
    return meth(self, multiparams, params)
.../local/lib/python3.13.../sqlalchemy/sql/elements.py:298: in _execute_on_connection
    return connection._execute_clauseelement(self, multiparams, params)
.../local/lib/python3.13.../sqlalchemy/engine/base.py:1124: in _execute_clauseelement
    ret = self._execute_context(
.../local/lib/python3.13.../sqlalchemy/engine/base.py:1316: in _execute_context
    self._handle_dbapi_exception(
.../local/lib/python3.13.../sqlalchemy/engine/base.py:1510: in _handle_dbapi_exception
    util.raise_(
.../local/lib/python3.13.../sqlalchemy/util/compat.py:182: in raise_
    raise exception
.../local/lib/python3.13.../sqlalchemy/engine/base.py:1276: in _execute_context
    self.dialect.do_execute(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0x7f7db97f7cb0>
cursor = <cursor object at 0x7f7da4a01d50; closed: -1>
statement = 'SELECT owners.plan AS owners_plan \nFROM repos JOIN owners ON owners.ownerid = repos.ownerid \nWHERE repos.repoid = %(repoid_1)s \n LIMIT %(param_1)s'
parameters = {'param_1': 1, 'repoid_1': 1026}
context = <sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0x7f7d8069b350>

    def do_execute(self, cursor, statement, parameters, context=None):
>       cursor.execute(statement, parameters)
E       sqlalchemy.exc.ProgrammingError: (psycopg2.errors.UndefinedTable) relation "repos" does not exist
E       LINE 2: FROM repos JOIN owners ON owners.ownerid = repos.ownerid 
E                    ^
E       
E       [SQL: SELECT owners.plan AS owners_plan 
E       FROM repos JOIN owners ON owners.ownerid = repos.ownerid 
E       WHERE repos.repoid = %(repoid_1)s 
E        LIMIT %(param_1)s]
E       [parameters: {'repoid_1': 1026, 'param_1': 1}]
E       (Background on this error at: http://sqlalche..../e/13/f405)

.../local/lib/python3.13.../sqlalchemy/engine/default.py:608: ProgrammingError
tasks/tests/unit/test_ta_finisher_task.py::::test_test_analytics_regular_comment_with_error
Stack Traces | 0.158s run time
dbsession = <sqlalchemy.orm.session.Session object at 0x7f7da497f110>
mocker = <pytest_mock.plugin.MockFixture object at 0x7f7da4a193d0>
mock_storage = <shared.storage.memory.MemoryStorageService object at 0x7f7d8089d190>
celery_app = <Celery celery.tests at 0x7f7dbc129950>
mock_repo_provider_service = <AsyncMock id='140177737206816'>
mock_pull_request_information = <AsyncMock id='140177606934944'>
generate_junit = <function generate_junit.<locals>._generate_junit at 0x7f7d805128e0>

    def test_test_analytics_regular_comment_with_error(
        dbsession,
        mocker,
        mock_storage,
        celery_app,
        mock_repo_provider_service,
        mock_pull_request_information,
        generate_junit,
    ):
        mocker.patch.object(TAProcessorTask, "app", celery_app)
        mocker.patch.object(TAFinisherTask, "app", celery_app)
    
        commit = CommitFactory.create()
    
        wrong_xml = """<?xml version="1.0" encoding="utf-8"?>
    <testsuites>
        <testsuite>
            <testcase time="0.001">
                <failure message="hello world"/>
            </testcase>
        </testsuite>
    </testsuites>
    """
    
        upload = generate_junit(raw=wrong_xml, commit=commit)
        argument = {"url": upload.storage_path, "upload_id": upload.id_}
        first_result = TAProcessorTask().run_impl(
            dbsession,
            repoid=upload.report.commit.repoid,
            commitid=upload.report.commit.commitid,
            commit_yaml={"codecov": {"max_report_age": False}},
            argument=argument,
        )
        assert first_result is False
    
        testruns = [
            {
                "name": "test_divide",
                "outcome": "fail",
                "duration_seconds": 0.001,
                "failure_message": "hello world",
            },
        ]
    
        upload = generate_junit(testruns, commit=commit)
        argument = {"url": upload.storage_path, "upload_id": upload.id_}
        second_result = TAProcessorTask().run_impl(
            dbsession,
            repoid=upload.report.commit.repoid,
            commitid=upload.report.commit.commitid,
            commit_yaml={"codecov": {"max_report_age": False}},
            argument=argument,
        )
        assert second_result is True
    
        result = TAFinisherTask().run_impl(
            dbsession,
            chord_result=[first_result, second_result],
            repoid=upload.report.commit.repoid,
            commitid=upload.report.commit.commitid,
            commit_yaml={"codecov": {"max_report_age": False}},
        )
    
        assert result["notify_attempted"] is True
        assert result["notify_succeeded"] is True
        assert result["queue_notify"] is False
    
        short_form_service_name = services_short_dict.get(
            upload.report.commit.repository.owner.service
        )
    
        mock_repo_provider_service.edit_comment.assert_called_once()
>       mock_repo_provider_service.edit_comment.assert_called_once_with(
            mock_pull_request_information.database_pull.pullid,
            mock_pull_request_information.database_pull.commentid,
            f"""### :x: Unsupported file format
    
    > Upload processing failed due to unsupported file format. Please review the parser error message:
    > `Error parsing JUnit XML in hello_world.junit.xml at 4:32: ParserError: No name found`
    > For more help, visit our [troubleshooting guide](https://docs.codecov.com/docs/test-analytics#troubleshooting).
    
    ---
    ### :x: 1 Tests Failed:
    | Tests completed | Failed | Passed | Skipped |
    |---|---|---|---|
    | 1 | 1 | 0 | 0 |
    <details><summary>View the top 1 failed tests by shortest run time</summary>
    
    >
    > ```python
    > tests.test_parsers.TestParsers test_divide
    > ```
    >
    > <details><summary>Stack Traces | 0.001s run time</summary>
    >
    > >
    > > ```python
    > > hello world
    > > ```
    >
    > </details>
    
    </details>
    
    To view more test analytics, go to the [Test Analytics Dashboard](https://app.codecov.io/{short_form_service_name}/{upload.report.commit.repository.owner.username}/{upload.report.commit.repository.name}/tests/{upload.report.commit.branch})
    :loudspeaker:  Thoughts on this report? [Let us know!](https://github..../feedback/issues/304)""",
        )
E       AssertionError: expected call not found.
E       Expected: edit_comment(<AsyncMock name='mock.database_pull.pullid' id='140177610139232'>, <AsyncMock name='mock.database_pull.commentid' id='140177610141920'>, '### :x: Unsupported file format\n\n> Upload processing failed due to unsupported file format. Please review the parser error message:\n> `Error parsing JUnit XML in hello_world.junit.xml at 4:32: ParserError: No name found`\n> For more help, visit our [troubleshooting guide](https://docs.codecov.com/docs/test-analytics#troubleshooting).\n\n---\n### :x: 1 Tests Failed:\n| Tests completed | Failed | Passed | Skipped |\n|---|---|---|---|\n| 1 | 1 | 0 | 0 |\n<details><summary>View the top 1 failed tests by shortest run time</summary>\n\n> \n> ```python\n> tests.test_parsers.TestParsers test_divide\n> ```\n> \n> <details><summary>Stack Traces | 0.001s run time</summary>\n> \n> > \n> > ```python\n> > hello world\n> > ```\n> \n> </details>\n\n</details>\n\nTo view more test analytics, go to the [Test Analytics Dashboard](https://app.codecov..../general-spring-same/tests/main)\n:loudspeaker:  Thoughts on this report? [Let us know!](https://github..../feedback/issues/304)')
E         Actual: edit_comment(<AsyncMock name='mock.database_pull.pullid' id='140177610139232'>, <AsyncMock name='mock.database_pull.commentid' id='140177610141920'>, '### :x: Unsupported file format\n\n> Upload processing failed due to unsupported file format. Please review the parser error message:\n> `Error parsing JUnit XML at 4:32: ParserError: No name found`\n> For more help, visit our [troubleshooting guide](https://docs.codecov.com/docs/test-analytics#troubleshooting).\n\n---\n### :x: 1 Tests Failed:\n| Tests completed | Failed | Passed | Skipped |\n|---|---|---|---|\n| 1 | 1 | 0 | 0 |\n<details><summary>View the top 1 failed tests by shortest run time</summary>\n\n> \n> ```python\n> tests.test_parsers.TestParsers test_divide\n> ```\n> \n> <details><summary>Stack Traces | 0.001s run time</summary>\n> \n> > \n> > ```python\n> > hello world\n> > ```\n> \n> </details>\n\n</details>\n\nTo view more test analytics, go to the [Test Analytics Dashboard](https://app.codecov..../general-spring-same/tests/main)\n:loudspeaker:  Thoughts on this report? [Let us know!](https://github..../feedback/issues/304)')
E       
E       pytest introspection follows:
E       
E       Args:
E       assert (<AsyncMock n.../issues/304)') == (<AsyncMock n.../issues/304)')
E         
E         At index 2 diff: '### :x: Unsupported file format\n\n> Upload processing failed due to unsupported file format. Please review the parser error message:\n> `Error parsing JUnit XML at 4:32: ParserError: No name found`\n> For more help, visit our [troubleshooting guide](https://docs.codecov.com/docs/test-analytics#troubleshooting).\n\n---\n### :x: 1 Tests Failed:\n| Tests completed | Failed | Passed | Skipped |\n|---|---|---|---|\n| 1 | 1 | 0 | 0 |\n<details><summary>View the top 1 failed tests by shortest run time</summary>\n\n> \n> ```python\n> tests.test_parsers.TestPa...
E         
E         ...Full output truncated (2 lines hidden), use '-vv' to show

.../tests/unit/test_ta_finisher_task.py:478: AssertionError

To view more test analytics, go to the Test Analytics Dashboard
📢 Thoughts on this report? Let us know!

@codecov-qa
Copy link

codecov-qa bot commented Dec 23, 2024

❌ 2 Tests Failed:

Tests completed Failed Passed Skipped
1783 2 1781 1
View the top 2 failed tests by shortest run time
tasks/tests/unit/test_ta_finisher_task.py::::test_test_analytics_error_comment
Stack Traces | 0.136s run time
self = <sqlalchemy.engine.base.Connection object at 0x7f7d9c9d1a90>
dialect = <sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0x7f7db97f7cb0>
constructor = <bound method DefaultExecutionContext._init_compiled of <class 'sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2'>>
statement = 'SELECT owners.plan AS owners_plan \nFROM repos JOIN owners ON owners.ownerid = repos.ownerid \nWHERE repos.repoid = %(repoid_1)s \n LIMIT %(param_1)s'
parameters = {'param_1': 1, 'repoid_1': 1026}
args = (<sqlalchemy.dialects.postgresql.psycopg2.PGCompiler_psycopg2 object at 0x7f7d8118a690>, [immutabledict({})])
conn = <sqlalchemy.pool.base._ConnectionFairy object at 0x7f7d819b7a50>
context = <sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0x7f7d8069b350>

    def _execute_context(
        self, dialect, constructor, statement, parameters, *args
    ):
        """Create an :class:`.ExecutionContext` and execute, returning
        a :class:`_engine.ResultProxy`.
    
        """
    
        try:
            try:
                conn = self.__connection
            except AttributeError:
                # escape "except AttributeError" before revalidating
                # to prevent misleading stacktraces in Py3K
                conn = None
            if conn is None:
                conn = self._revalidate_connection()
    
            context = constructor(dialect, self, conn, *args)
        except BaseException as e:
            self._handle_dbapi_exception(
                e, util.text_type(statement), parameters, None, None
            )
    
        if context.compiled:
            context.pre_exec()
    
        cursor, statement, parameters = (
            context.cursor,
            context.statement,
            context.parameters,
        )
    
        if not context.executemany:
            parameters = parameters[0]
    
        if self._has_events or self.engine._has_events:
            for fn in self.dispatch.before_cursor_execute:
                statement, parameters = fn(
                    self,
                    cursor,
                    statement,
                    parameters,
                    context,
                    context.executemany,
                )
    
        if self._echo:
            self.engine.logger.info(statement)
            if not self.engine.hide_parameters:
                self.engine.logger.info(
                    "%r",
                    sql_util._repr_params(
                        parameters, batches=10, ismulti=context.executemany
                    ),
                )
            else:
                self.engine.logger.info(
                    "[SQL parameters hidden due to hide_parameters=True]"
                )
    
        evt_handled = False
        try:
            if context.executemany:
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_executemany:
                        if fn(cursor, statement, parameters, context):
                            evt_handled = True
                            break
                if not evt_handled:
                    self.dialect.do_executemany(
                        cursor, statement, parameters, context
                    )
            elif not parameters and context.no_parameters:
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_execute_no_params:
                        if fn(cursor, statement, context):
                            evt_handled = True
                            break
                if not evt_handled:
                    self.dialect.do_execute_no_params(
                        cursor, statement, context
                    )
            else:
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_execute:
                        if fn(cursor, statement, parameters, context):
                            evt_handled = True
                            break
                if not evt_handled:
>                   self.dialect.do_execute(
                        cursor, statement, parameters, context
                    )

.../local/lib/python3.13.../sqlalchemy/engine/base.py:1276: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0x7f7db97f7cb0>
cursor = <cursor object at 0x7f7da4a01d50; closed: -1>
statement = 'SELECT owners.plan AS owners_plan \nFROM repos JOIN owners ON owners.ownerid = repos.ownerid \nWHERE repos.repoid = %(repoid_1)s \n LIMIT %(param_1)s'
parameters = {'param_1': 1, 'repoid_1': 1026}
context = <sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0x7f7d8069b350>

    def do_execute(self, cursor, statement, parameters, context=None):
>       cursor.execute(statement, parameters)
E       psycopg2.errors.UndefinedTable: relation "repos" does not exist
E       LINE 2: FROM repos JOIN owners ON owners.ownerid = repos.ownerid 
E                    ^

.../local/lib/python3.13.../sqlalchemy/engine/default.py:608: UndefinedTable

The above exception was the direct cause of the following exception:

dbsession = <sqlalchemy.orm.session.Session object at 0x7f7da497dd60>
mocker = <pytest_mock.plugin.MockFixture object at 0x7f7d9c9d1550>
mock_storage = <shared.storage.memory.MemoryStorageService object at 0x7f7da482a2d0>
celery_app = <Celery celery.tests at 0x7f7da65d82d0>
mock_repo_provider_service = <AsyncMock id='140178002626608'>
mock_pull_request_information = <AsyncMock id='140177002625552'>
generate_junit = <function generate_junit.<locals>._generate_junit at 0x7f7d942aa980>

    def test_test_analytics_error_comment(
        dbsession,
        mocker,
        mock_storage,
        celery_app,
        mock_repo_provider_service,
        mock_pull_request_information,
        generate_junit,
    ):
        wrong_xml = """<?xml version="1.0" encoding="utf-8"?>
    <testsuites>
        <testsuite>
            <testcase time="0.001">
                <failure message="hello world"/>
            </testcase>
        </testsuite>
    </testsuites>
    """
        upload = generate_junit(raw=wrong_xml)
    
        argument = {"url": upload.storage_path, "upload_id": upload.id_}
    
        mocker.patch.object(TAProcessorTask, "app", celery_app)
        mocker.patch.object(TAFinisherTask, "app", celery_app)
    
        result = TAProcessorTask().run_impl(
            dbsession,
            repoid=upload.report.commit.repoid,
            commitid=upload.report.commit.commitid,
            commit_yaml={"codecov": {"max_report_age": False}},
            argument=argument,
        )
    
        assert result is False
    
>       result = TAFinisherTask().run_impl(
            dbsession,
            chord_result=[result],
            repoid=upload.report.commit.repoid,
            commitid=upload.report.commit.commitid,
            commit_yaml={"codecov": {"max_report_age": False}},
        )

.../tests/unit/test_ta_finisher_task.py:372: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
tasks/ta_finisher.py:233: in run_impl
    notify_task_sig.apply_async()
.../local/lib/python3.13.../site-packages/celery/canvas.py:400: in apply_async
    return _apply(args, kwargs, **options)
.../local/lib/python3.13.../site-packages/sentry_sdk/tracing_utils.py:673: in func_with_tracing
    return func(*args, **kwargs)
tasks/base.py:150: in apply_async
    user_plan = _get_user_plan_from_task(db_session, self.name, kwargs)
celery_task_router.py:144: in _get_user_plan_from_task
    return func_to_use(dbsession, **task_kwargs)
celery_task_router.py:24: in _get_user_plan_from_repoid
    .first()
.../local/lib/python3.13.../sqlalchemy/orm/query.py:3429: in first
    ret = list(self[0:1])
.../local/lib/python3.13.../sqlalchemy/orm/query.py:3203: in __getitem__
    return list(res)
.../local/lib/python3.13.../sqlalchemy/orm/query.py:3535: in __iter__
    return self._execute_and_instances(context)
.../local/lib/python3.13.../sqlalchemy/orm/query.py:3560: in _execute_and_instances
    result = conn.execute(querycontext.statement, self._params)
.../local/lib/python3.13.../sqlalchemy/engine/base.py:1011: in execute
    return meth(self, multiparams, params)
.../local/lib/python3.13.../sqlalchemy/sql/elements.py:298: in _execute_on_connection
    return connection._execute_clauseelement(self, multiparams, params)
.../local/lib/python3.13.../sqlalchemy/engine/base.py:1124: in _execute_clauseelement
    ret = self._execute_context(
.../local/lib/python3.13.../sqlalchemy/engine/base.py:1316: in _execute_context
    self._handle_dbapi_exception(
.../local/lib/python3.13.../sqlalchemy/engine/base.py:1510: in _handle_dbapi_exception
    util.raise_(
.../local/lib/python3.13.../sqlalchemy/util/compat.py:182: in raise_
    raise exception
.../local/lib/python3.13.../sqlalchemy/engine/base.py:1276: in _execute_context
    self.dialect.do_execute(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0x7f7db97f7cb0>
cursor = <cursor object at 0x7f7da4a01d50; closed: -1>
statement = 'SELECT owners.plan AS owners_plan \nFROM repos JOIN owners ON owners.ownerid = repos.ownerid \nWHERE repos.repoid = %(repoid_1)s \n LIMIT %(param_1)s'
parameters = {'param_1': 1, 'repoid_1': 1026}
context = <sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0x7f7d8069b350>

    def do_execute(self, cursor, statement, parameters, context=None):
>       cursor.execute(statement, parameters)
E       sqlalchemy.exc.ProgrammingError: (psycopg2.errors.UndefinedTable) relation "repos" does not exist
E       LINE 2: FROM repos JOIN owners ON owners.ownerid = repos.ownerid 
E                    ^
E       
E       [SQL: SELECT owners.plan AS owners_plan 
E       FROM repos JOIN owners ON owners.ownerid = repos.ownerid 
E       WHERE repos.repoid = %(repoid_1)s 
E        LIMIT %(param_1)s]
E       [parameters: {'repoid_1': 1026, 'param_1': 1}]
E       (Background on this error at: http://sqlalche..../e/13/f405)

.../local/lib/python3.13.../sqlalchemy/engine/default.py:608: ProgrammingError
tasks/tests/unit/test_ta_finisher_task.py::::test_test_analytics_regular_comment_with_error
Stack Traces | 0.158s run time
dbsession = <sqlalchemy.orm.session.Session object at 0x7f7da497f110>
mocker = <pytest_mock.plugin.MockFixture object at 0x7f7da4a193d0>
mock_storage = <shared.storage.memory.MemoryStorageService object at 0x7f7d8089d190>
celery_app = <Celery celery.tests at 0x7f7dbc129950>
mock_repo_provider_service = <AsyncMock id='140177737206816'>
mock_pull_request_information = <AsyncMock id='140177606934944'>
generate_junit = <function generate_junit.<locals>._generate_junit at 0x7f7d805128e0>

    def test_test_analytics_regular_comment_with_error(
        dbsession,
        mocker,
        mock_storage,
        celery_app,
        mock_repo_provider_service,
        mock_pull_request_information,
        generate_junit,
    ):
        mocker.patch.object(TAProcessorTask, "app", celery_app)
        mocker.patch.object(TAFinisherTask, "app", celery_app)
    
        commit = CommitFactory.create()
    
        wrong_xml = """<?xml version="1.0" encoding="utf-8"?>
    <testsuites>
        <testsuite>
            <testcase time="0.001">
                <failure message="hello world"/>
            </testcase>
        </testsuite>
    </testsuites>
    """
    
        upload = generate_junit(raw=wrong_xml, commit=commit)
        argument = {"url": upload.storage_path, "upload_id": upload.id_}
        first_result = TAProcessorTask().run_impl(
            dbsession,
            repoid=upload.report.commit.repoid,
            commitid=upload.report.commit.commitid,
            commit_yaml={"codecov": {"max_report_age": False}},
            argument=argument,
        )
        assert first_result is False
    
        testruns = [
            {
                "name": "test_divide",
                "outcome": "fail",
                "duration_seconds": 0.001,
                "failure_message": "hello world",
            },
        ]
    
        upload = generate_junit(testruns, commit=commit)
        argument = {"url": upload.storage_path, "upload_id": upload.id_}
        second_result = TAProcessorTask().run_impl(
            dbsession,
            repoid=upload.report.commit.repoid,
            commitid=upload.report.commit.commitid,
            commit_yaml={"codecov": {"max_report_age": False}},
            argument=argument,
        )
        assert second_result is True
    
        result = TAFinisherTask().run_impl(
            dbsession,
            chord_result=[first_result, second_result],
            repoid=upload.report.commit.repoid,
            commitid=upload.report.commit.commitid,
            commit_yaml={"codecov": {"max_report_age": False}},
        )
    
        assert result["notify_attempted"] is True
        assert result["notify_succeeded"] is True
        assert result["queue_notify"] is False
    
        short_form_service_name = services_short_dict.get(
            upload.report.commit.repository.owner.service
        )
    
        mock_repo_provider_service.edit_comment.assert_called_once()
>       mock_repo_provider_service.edit_comment.assert_called_once_with(
            mock_pull_request_information.database_pull.pullid,
            mock_pull_request_information.database_pull.commentid,
            f"""### :x: Unsupported file format
    
    > Upload processing failed due to unsupported file format. Please review the parser error message:
    > `Error parsing JUnit XML in hello_world.junit.xml at 4:32: ParserError: No name found`
    > For more help, visit our [troubleshooting guide](https://docs.codecov.com/docs/test-analytics#troubleshooting).
    
    ---
    ### :x: 1 Tests Failed:
    | Tests completed | Failed | Passed | Skipped |
    |---|---|---|---|
    | 1 | 1 | 0 | 0 |
    <details><summary>View the top 1 failed tests by shortest run time</summary>
    
    >
    > ```python
    > tests.test_parsers.TestParsers test_divide
    > ```
    >
    > <details><summary>Stack Traces | 0.001s run time</summary>
    >
    > >
    > > ```python
    > > hello world
    > > ```
    >
    > </details>
    
    </details>
    
    To view more test analytics, go to the [Test Analytics Dashboard](https://app.codecov.io/{short_form_service_name}/{upload.report.commit.repository.owner.username}/{upload.report.commit.repository.name}/tests/{upload.report.commit.branch})
    :loudspeaker:  Thoughts on this report? [Let us know!](https://github..../feedback/issues/304)""",
        )
E       AssertionError: expected call not found.
E       Expected: edit_comment(<AsyncMock name='mock.database_pull.pullid' id='140177610139232'>, <AsyncMock name='mock.database_pull.commentid' id='140177610141920'>, '### :x: Unsupported file format\n\n> Upload processing failed due to unsupported file format. Please review the parser error message:\n> `Error parsing JUnit XML in hello_world.junit.xml at 4:32: ParserError: No name found`\n> For more help, visit our [troubleshooting guide](https://docs.codecov.com/docs/test-analytics#troubleshooting).\n\n---\n### :x: 1 Tests Failed:\n| Tests completed | Failed | Passed | Skipped |\n|---|---|---|---|\n| 1 | 1 | 0 | 0 |\n<details><summary>View the top 1 failed tests by shortest run time</summary>\n\n> \n> ```python\n> tests.test_parsers.TestParsers test_divide\n> ```\n> \n> <details><summary>Stack Traces | 0.001s run time</summary>\n> \n> > \n> > ```python\n> > hello world\n> > ```\n> \n> </details>\n\n</details>\n\nTo view more test analytics, go to the [Test Analytics Dashboard](https://app.codecov..../general-spring-same/tests/main)\n:loudspeaker:  Thoughts on this report? [Let us know!](https://github..../feedback/issues/304)')
E         Actual: edit_comment(<AsyncMock name='mock.database_pull.pullid' id='140177610139232'>, <AsyncMock name='mock.database_pull.commentid' id='140177610141920'>, '### :x: Unsupported file format\n\n> Upload processing failed due to unsupported file format. Please review the parser error message:\n> `Error parsing JUnit XML at 4:32: ParserError: No name found`\n> For more help, visit our [troubleshooting guide](https://docs.codecov.com/docs/test-analytics#troubleshooting).\n\n---\n### :x: 1 Tests Failed:\n| Tests completed | Failed | Passed | Skipped |\n|---|---|---|---|\n| 1 | 1 | 0 | 0 |\n<details><summary>View the top 1 failed tests by shortest run time</summary>\n\n> \n> ```python\n> tests.test_parsers.TestParsers test_divide\n> ```\n> \n> <details><summary>Stack Traces | 0.001s run time</summary>\n> \n> > \n> > ```python\n> > hello world\n> > ```\n> \n> </details>\n\n</details>\n\nTo view more test analytics, go to the [Test Analytics Dashboard](https://app.codecov..../general-spring-same/tests/main)\n:loudspeaker:  Thoughts on this report? [Let us know!](https://github..../feedback/issues/304)')
E       
E       pytest introspection follows:
E       
E       Args:
E       assert (<AsyncMock n.../issues/304)') == (<AsyncMock n.../issues/304)')
E         
E         At index 2 diff: '### :x: Unsupported file format\n\n> Upload processing failed due to unsupported file format. Please review the parser error message:\n> `Error parsing JUnit XML at 4:32: ParserError: No name found`\n> For more help, visit our [troubleshooting guide](https://docs.codecov.com/docs/test-analytics#troubleshooting).\n\n---\n### :x: 1 Tests Failed:\n| Tests completed | Failed | Passed | Skipped |\n|---|---|---|---|\n| 1 | 1 | 0 | 0 |\n<details><summary>View the top 1 failed tests by shortest run time</summary>\n\n> \n> ```python\n> tests.test_parsers.TestPa...
E         
E         ...Full output truncated (2 lines hidden), use '-vv' to show

.../tests/unit/test_ta_finisher_task.py:478: AssertionError

To view more test analytics, go to the Test Analytics Dashboard
📢 Thoughts on this report? Let us know!

Copy link

codecov-public-qa bot commented Dec 23, 2024

❌ 2 Tests Failed:

Tests completed Failed Passed Skipped
1783 2 1781 1
View the top 2 failed tests by shortest run time
tasks/tests/unit/test_ta_finisher_task.py::::test_test_analytics_error_comment
Stack Traces | 0.136s run time
self = <sqlalchemy.engine.base.Connection object at 0x7f7d9c9d1a90>
dialect = <sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0x7f7db97f7cb0>
constructor = <bound method DefaultExecutionContext._init_compiled of <class 'sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2'>>
statement = 'SELECT owners.plan AS owners_plan \nFROM repos JOIN owners ON owners.ownerid = repos.ownerid \nWHERE repos.repoid = %(repoid_1)s \n LIMIT %(param_1)s'
parameters = {'param_1': 1, 'repoid_1': 1026}
args = (<sqlalchemy.dialects.postgresql.psycopg2.PGCompiler_psycopg2 object at 0x7f7d8118a690>, [immutabledict({})])
conn = <sqlalchemy.pool.base._ConnectionFairy object at 0x7f7d819b7a50>
context = <sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0x7f7d8069b350>

    def _execute_context(
        self, dialect, constructor, statement, parameters, *args
    ):
        """Create an :class:`.ExecutionContext` and execute, returning
        a :class:`_engine.ResultProxy`.
    
        """
    
        try:
            try:
                conn = self.__connection
            except AttributeError:
                # escape "except AttributeError" before revalidating
                # to prevent misleading stacktraces in Py3K
                conn = None
            if conn is None:
                conn = self._revalidate_connection()
    
            context = constructor(dialect, self, conn, *args)
        except BaseException as e:
            self._handle_dbapi_exception(
                e, util.text_type(statement), parameters, None, None
            )
    
        if context.compiled:
            context.pre_exec()
    
        cursor, statement, parameters = (
            context.cursor,
            context.statement,
            context.parameters,
        )
    
        if not context.executemany:
            parameters = parameters[0]
    
        if self._has_events or self.engine._has_events:
            for fn in self.dispatch.before_cursor_execute:
                statement, parameters = fn(
                    self,
                    cursor,
                    statement,
                    parameters,
                    context,
                    context.executemany,
                )
    
        if self._echo:
            self.engine.logger.info(statement)
            if not self.engine.hide_parameters:
                self.engine.logger.info(
                    "%r",
                    sql_util._repr_params(
                        parameters, batches=10, ismulti=context.executemany
                    ),
                )
            else:
                self.engine.logger.info(
                    "[SQL parameters hidden due to hide_parameters=True]"
                )
    
        evt_handled = False
        try:
            if context.executemany:
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_executemany:
                        if fn(cursor, statement, parameters, context):
                            evt_handled = True
                            break
                if not evt_handled:
                    self.dialect.do_executemany(
                        cursor, statement, parameters, context
                    )
            elif not parameters and context.no_parameters:
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_execute_no_params:
                        if fn(cursor, statement, context):
                            evt_handled = True
                            break
                if not evt_handled:
                    self.dialect.do_execute_no_params(
                        cursor, statement, context
                    )
            else:
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_execute:
                        if fn(cursor, statement, parameters, context):
                            evt_handled = True
                            break
                if not evt_handled:
>                   self.dialect.do_execute(
                        cursor, statement, parameters, context
                    )

.../local/lib/python3.13.../sqlalchemy/engine/base.py:1276: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0x7f7db97f7cb0>
cursor = <cursor object at 0x7f7da4a01d50; closed: -1>
statement = 'SELECT owners.plan AS owners_plan \nFROM repos JOIN owners ON owners.ownerid = repos.ownerid \nWHERE repos.repoid = %(repoid_1)s \n LIMIT %(param_1)s'
parameters = {'param_1': 1, 'repoid_1': 1026}
context = <sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0x7f7d8069b350>

    def do_execute(self, cursor, statement, parameters, context=None):
>       cursor.execute(statement, parameters)
E       psycopg2.errors.UndefinedTable: relation "repos" does not exist
E       LINE 2: FROM repos JOIN owners ON owners.ownerid = repos.ownerid 
E                    ^

.../local/lib/python3.13.../sqlalchemy/engine/default.py:608: UndefinedTable

The above exception was the direct cause of the following exception:

dbsession = <sqlalchemy.orm.session.Session object at 0x7f7da497dd60>
mocker = <pytest_mock.plugin.MockFixture object at 0x7f7d9c9d1550>
mock_storage = <shared.storage.memory.MemoryStorageService object at 0x7f7da482a2d0>
celery_app = <Celery celery.tests at 0x7f7da65d82d0>
mock_repo_provider_service = <AsyncMock id='140178002626608'>
mock_pull_request_information = <AsyncMock id='140177002625552'>
generate_junit = <function generate_junit.<locals>._generate_junit at 0x7f7d942aa980>

    def test_test_analytics_error_comment(
        dbsession,
        mocker,
        mock_storage,
        celery_app,
        mock_repo_provider_service,
        mock_pull_request_information,
        generate_junit,
    ):
        wrong_xml = """<?xml version="1.0" encoding="utf-8"?>
    <testsuites>
        <testsuite>
            <testcase time="0.001">
                <failure message="hello world"/>
            </testcase>
        </testsuite>
    </testsuites>
    """
        upload = generate_junit(raw=wrong_xml)
    
        argument = {"url": upload.storage_path, "upload_id": upload.id_}
    
        mocker.patch.object(TAProcessorTask, "app", celery_app)
        mocker.patch.object(TAFinisherTask, "app", celery_app)
    
        result = TAProcessorTask().run_impl(
            dbsession,
            repoid=upload.report.commit.repoid,
            commitid=upload.report.commit.commitid,
            commit_yaml={"codecov": {"max_report_age": False}},
            argument=argument,
        )
    
        assert result is False
    
>       result = TAFinisherTask().run_impl(
            dbsession,
            chord_result=[result],
            repoid=upload.report.commit.repoid,
            commitid=upload.report.commit.commitid,
            commit_yaml={"codecov": {"max_report_age": False}},
        )

.../tests/unit/test_ta_finisher_task.py:372: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
tasks/ta_finisher.py:233: in run_impl
    notify_task_sig.apply_async()
.../local/lib/python3.13.../site-packages/celery/canvas.py:400: in apply_async
    return _apply(args, kwargs, **options)
.../local/lib/python3.13.../site-packages/sentry_sdk/tracing_utils.py:673: in func_with_tracing
    return func(*args, **kwargs)
tasks/base.py:150: in apply_async
    user_plan = _get_user_plan_from_task(db_session, self.name, kwargs)
celery_task_router.py:144: in _get_user_plan_from_task
    return func_to_use(dbsession, **task_kwargs)
celery_task_router.py:24: in _get_user_plan_from_repoid
    .first()
.../local/lib/python3.13.../sqlalchemy/orm/query.py:3429: in first
    ret = list(self[0:1])
.../local/lib/python3.13.../sqlalchemy/orm/query.py:3203: in __getitem__
    return list(res)
.../local/lib/python3.13.../sqlalchemy/orm/query.py:3535: in __iter__
    return self._execute_and_instances(context)
.../local/lib/python3.13.../sqlalchemy/orm/query.py:3560: in _execute_and_instances
    result = conn.execute(querycontext.statement, self._params)
.../local/lib/python3.13.../sqlalchemy/engine/base.py:1011: in execute
    return meth(self, multiparams, params)
.../local/lib/python3.13.../sqlalchemy/sql/elements.py:298: in _execute_on_connection
    return connection._execute_clauseelement(self, multiparams, params)
.../local/lib/python3.13.../sqlalchemy/engine/base.py:1124: in _execute_clauseelement
    ret = self._execute_context(
.../local/lib/python3.13.../sqlalchemy/engine/base.py:1316: in _execute_context
    self._handle_dbapi_exception(
.../local/lib/python3.13.../sqlalchemy/engine/base.py:1510: in _handle_dbapi_exception
    util.raise_(
.../local/lib/python3.13.../sqlalchemy/util/compat.py:182: in raise_
    raise exception
.../local/lib/python3.13.../sqlalchemy/engine/base.py:1276: in _execute_context
    self.dialect.do_execute(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0x7f7db97f7cb0>
cursor = <cursor object at 0x7f7da4a01d50; closed: -1>
statement = 'SELECT owners.plan AS owners_plan \nFROM repos JOIN owners ON owners.ownerid = repos.ownerid \nWHERE repos.repoid = %(repoid_1)s \n LIMIT %(param_1)s'
parameters = {'param_1': 1, 'repoid_1': 1026}
context = <sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0x7f7d8069b350>

    def do_execute(self, cursor, statement, parameters, context=None):
>       cursor.execute(statement, parameters)
E       sqlalchemy.exc.ProgrammingError: (psycopg2.errors.UndefinedTable) relation "repos" does not exist
E       LINE 2: FROM repos JOIN owners ON owners.ownerid = repos.ownerid 
E                    ^
E       
E       [SQL: SELECT owners.plan AS owners_plan 
E       FROM repos JOIN owners ON owners.ownerid = repos.ownerid 
E       WHERE repos.repoid = %(repoid_1)s 
E        LIMIT %(param_1)s]
E       [parameters: {'repoid_1': 1026, 'param_1': 1}]
E       (Background on this error at: http://sqlalche..../e/13/f405)

.../local/lib/python3.13.../sqlalchemy/engine/default.py:608: ProgrammingError
tasks/tests/unit/test_ta_finisher_task.py::::test_test_analytics_regular_comment_with_error
Stack Traces | 0.158s run time
dbsession = <sqlalchemy.orm.session.Session object at 0x7f7da497f110>
mocker = <pytest_mock.plugin.MockFixture object at 0x7f7da4a193d0>
mock_storage = <shared.storage.memory.MemoryStorageService object at 0x7f7d8089d190>
celery_app = <Celery celery.tests at 0x7f7dbc129950>
mock_repo_provider_service = <AsyncMock id='140177737206816'>
mock_pull_request_information = <AsyncMock id='140177606934944'>
generate_junit = <function generate_junit.<locals>._generate_junit at 0x7f7d805128e0>

    def test_test_analytics_regular_comment_with_error(
        dbsession,
        mocker,
        mock_storage,
        celery_app,
        mock_repo_provider_service,
        mock_pull_request_information,
        generate_junit,
    ):
        mocker.patch.object(TAProcessorTask, "app", celery_app)
        mocker.patch.object(TAFinisherTask, "app", celery_app)
    
        commit = CommitFactory.create()
    
        wrong_xml = """<?xml version="1.0" encoding="utf-8"?>
    <testsuites>
        <testsuite>
            <testcase time="0.001">
                <failure message="hello world"/>
            </testcase>
        </testsuite>
    </testsuites>
    """
    
        upload = generate_junit(raw=wrong_xml, commit=commit)
        argument = {"url": upload.storage_path, "upload_id": upload.id_}
        first_result = TAProcessorTask().run_impl(
            dbsession,
            repoid=upload.report.commit.repoid,
            commitid=upload.report.commit.commitid,
            commit_yaml={"codecov": {"max_report_age": False}},
            argument=argument,
        )
        assert first_result is False
    
        testruns = [
            {
                "name": "test_divide",
                "outcome": "fail",
                "duration_seconds": 0.001,
                "failure_message": "hello world",
            },
        ]
    
        upload = generate_junit(testruns, commit=commit)
        argument = {"url": upload.storage_path, "upload_id": upload.id_}
        second_result = TAProcessorTask().run_impl(
            dbsession,
            repoid=upload.report.commit.repoid,
            commitid=upload.report.commit.commitid,
            commit_yaml={"codecov": {"max_report_age": False}},
            argument=argument,
        )
        assert second_result is True
    
        result = TAFinisherTask().run_impl(
            dbsession,
            chord_result=[first_result, second_result],
            repoid=upload.report.commit.repoid,
            commitid=upload.report.commit.commitid,
            commit_yaml={"codecov": {"max_report_age": False}},
        )
    
        assert result["notify_attempted"] is True
        assert result["notify_succeeded"] is True
        assert result["queue_notify"] is False
    
        short_form_service_name = services_short_dict.get(
            upload.report.commit.repository.owner.service
        )
    
        mock_repo_provider_service.edit_comment.assert_called_once()
>       mock_repo_provider_service.edit_comment.assert_called_once_with(
            mock_pull_request_information.database_pull.pullid,
            mock_pull_request_information.database_pull.commentid,
            f"""### :x: Unsupported file format
    
    > Upload processing failed due to unsupported file format. Please review the parser error message:
    > `Error parsing JUnit XML in hello_world.junit.xml at 4:32: ParserError: No name found`
    > For more help, visit our [troubleshooting guide](https://docs.codecov.com/docs/test-analytics#troubleshooting).
    
    ---
    ### :x: 1 Tests Failed:
    | Tests completed | Failed | Passed | Skipped |
    |---|---|---|---|
    | 1 | 1 | 0 | 0 |
    <details><summary>View the top 1 failed tests by shortest run time</summary>
    
    >
    > ```python
    > tests.test_parsers.TestParsers test_divide
    > ```
    >
    > <details><summary>Stack Traces | 0.001s run time</summary>
    >
    > >
    > > ```python
    > > hello world
    > > ```
    >
    > </details>
    
    </details>
    
    To view more test analytics, go to the [Test Analytics Dashboard](https://app.codecov.io/{short_form_service_name}/{upload.report.commit.repository.owner.username}/{upload.report.commit.repository.name}/tests/{upload.report.commit.branch})
    :loudspeaker:  Thoughts on this report? [Let us know!](https://github..../feedback/issues/304)""",
        )
E       AssertionError: expected call not found.
E       Expected: edit_comment(<AsyncMock name='mock.database_pull.pullid' id='140177610139232'>, <AsyncMock name='mock.database_pull.commentid' id='140177610141920'>, '### :x: Unsupported file format\n\n> Upload processing failed due to unsupported file format. Please review the parser error message:\n> `Error parsing JUnit XML in hello_world.junit.xml at 4:32: ParserError: No name found`\n> For more help, visit our [troubleshooting guide](https://docs.codecov.com/docs/test-analytics#troubleshooting).\n\n---\n### :x: 1 Tests Failed:\n| Tests completed | Failed | Passed | Skipped |\n|---|---|---|---|\n| 1 | 1 | 0 | 0 |\n<details><summary>View the top 1 failed tests by shortest run time</summary>\n\n> \n> ```python\n> tests.test_parsers.TestParsers test_divide\n> ```\n> \n> <details><summary>Stack Traces | 0.001s run time</summary>\n> \n> > \n> > ```python\n> > hello world\n> > ```\n> \n> </details>\n\n</details>\n\nTo view more test analytics, go to the [Test Analytics Dashboard](https://app.codecov..../general-spring-same/tests/main)\n:loudspeaker:  Thoughts on this report? [Let us know!](https://github..../feedback/issues/304)')
E         Actual: edit_comment(<AsyncMock name='mock.database_pull.pullid' id='140177610139232'>, <AsyncMock name='mock.database_pull.commentid' id='140177610141920'>, '### :x: Unsupported file format\n\n> Upload processing failed due to unsupported file format. Please review the parser error message:\n> `Error parsing JUnit XML at 4:32: ParserError: No name found`\n> For more help, visit our [troubleshooting guide](https://docs.codecov.com/docs/test-analytics#troubleshooting).\n\n---\n### :x: 1 Tests Failed:\n| Tests completed | Failed | Passed | Skipped |\n|---|---|---|---|\n| 1 | 1 | 0 | 0 |\n<details><summary>View the top 1 failed tests by shortest run time</summary>\n\n> \n> ```python\n> tests.test_parsers.TestParsers test_divide\n> ```\n> \n> <details><summary>Stack Traces | 0.001s run time</summary>\n> \n> > \n> > ```python\n> > hello world\n> > ```\n> \n> </details>\n\n</details>\n\nTo view more test analytics, go to the [Test Analytics Dashboard](https://app.codecov..../general-spring-same/tests/main)\n:loudspeaker:  Thoughts on this report? [Let us know!](https://github..../feedback/issues/304)')
E       
E       pytest introspection follows:
E       
E       Args:
E       assert (<AsyncMock n.../issues/304)') == (<AsyncMock n.../issues/304)')
E         
E         At index 2 diff: '### :x: Unsupported file format\n\n> Upload processing failed due to unsupported file format. Please review the parser error message:\n> `Error parsing JUnit XML at 4:32: ParserError: No name found`\n> For more help, visit our [troubleshooting guide](https://docs.codecov.com/docs/test-analytics#troubleshooting).\n\n---\n### :x: 1 Tests Failed:\n| Tests completed | Failed | Passed | Skipped |\n|---|---|---|---|\n| 1 | 1 | 0 | 0 |\n<details><summary>View the top 1 failed tests by shortest run time</summary>\n\n> \n> ```python\n> tests.test_parsers.TestPa...
E         
E         ...Full output truncated (2 lines hidden), use '-vv' to show

.../tests/unit/test_ta_finisher_task.py:478: AssertionError

To view more test analytics, go to the Test Analytics Dashboard
📢 Thoughts on this report? Let us know!

Copy link

github-actions bot commented Dec 23, 2024

❌ 2 Tests Failed:

Tests completed Failed Passed Skipped
1784 2 1781 1
View the top 2 failed tests by shortest run time
test_test_analytics_error_comment
Stack Traces | 0.136s run time
self = &lt;sqlalchemy.engine.base.Connection object at 0x7f7d9c9d1a90&gt;
dialect = &lt;sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0x7f7db97f7cb0&gt;
constructor = &lt;bound method DefaultExecutionContext._init_compiled of &lt;class 'sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2'&gt;&gt;
statement = 'SELECT owners.plan AS owners_plan \nFROM repos JOIN owners ON owners.ownerid = repos.ownerid \nWHERE repos.repoid = %(repoid_1)s \n LIMIT %(param_1)s'
parameters = {'param_1': 1, 'repoid_1': 1026}
args = (&lt;sqlalchemy.dialects.postgresql.psycopg2.PGCompiler_psycopg2 object at 0x7f7d8118a690&gt;, [immutabledict({})])
conn = &lt;sqlalchemy.pool.base._ConnectionFairy object at 0x7f7d819b7a50&gt;
context = &lt;sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0x7f7d8069b350&gt;

    def _execute_context(
        self, dialect, constructor, statement, parameters, *args
    ):
        """Create an :class:`.ExecutionContext` and execute, returning
        a :class:`_engine.ResultProxy`.
    
        """
    
        try:
            try:
                conn = self.__connection
            except AttributeError:
                # escape "except AttributeError" before revalidating
                # to prevent misleading stacktraces in Py3K
                conn = None
            if conn is None:
                conn = self._revalidate_connection()
    
            context = constructor(dialect, self, conn, *args)
        except BaseException as e:
            self._handle_dbapi_exception(
                e, util.text_type(statement), parameters, None, None
            )
    
        if context.compiled:
            context.pre_exec()
    
        cursor, statement, parameters = (
            context.cursor,
            context.statement,
            context.parameters,
        )
    
        if not context.executemany:
            parameters = parameters[0]
    
        if self._has_events or self.engine._has_events:
            for fn in self.dispatch.before_cursor_execute:
                statement, parameters = fn(
                    self,
                    cursor,
                    statement,
                    parameters,
                    context,
                    context.executemany,
                )
    
        if self._echo:
            self.engine.logger.info(statement)
            if not self.engine.hide_parameters:
                self.engine.logger.info(
                    "%r",
                    sql_util._repr_params(
                        parameters, batches=10, ismulti=context.executemany
                    ),
                )
            else:
                self.engine.logger.info(
                    "[SQL parameters hidden due to hide_parameters=True]"
                )
    
        evt_handled = False
        try:
            if context.executemany:
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_executemany:
                        if fn(cursor, statement, parameters, context):
                            evt_handled = True
                            break
                if not evt_handled:
                    self.dialect.do_executemany(
                        cursor, statement, parameters, context
                    )
            elif not parameters and context.no_parameters:
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_execute_no_params:
                        if fn(cursor, statement, context):
                            evt_handled = True
                            break
                if not evt_handled:
                    self.dialect.do_execute_no_params(
                        cursor, statement, context
                    )
            else:
                if self.dialect._has_events:
                    for fn in self.dialect.dispatch.do_execute:
                        if fn(cursor, statement, parameters, context):
                            evt_handled = True
                            break
                if not evt_handled:
&gt;                   self.dialect.do_execute(
                        cursor, statement, parameters, context
                    )

/usr/local/lib/python3.13/site-packages/sqlalchemy/engine/base.py:1276: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = &lt;sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0x7f7db97f7cb0&gt;
cursor = &lt;cursor object at 0x7f7da4a01d50; closed: -1&gt;
statement = 'SELECT owners.plan AS owners_plan \nFROM repos JOIN owners ON owners.ownerid = repos.ownerid \nWHERE repos.repoid = %(repoid_1)s \n LIMIT %(param_1)s'
parameters = {'param_1': 1, 'repoid_1': 1026}
context = &lt;sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0x7f7d8069b350&gt;

    def do_execute(self, cursor, statement, parameters, context=None):
&gt;       cursor.execute(statement, parameters)
E       psycopg2.errors.UndefinedTable: relation "repos" does not exist
E       LINE 2: FROM repos JOIN owners ON owners.ownerid = repos.ownerid 
E                    ^

/usr/local/lib/python3.13/site-packages/sqlalchemy/engine/default.py:608: UndefinedTable

The above exception was the direct cause of the following exception:

dbsession = &lt;sqlalchemy.orm.session.Session object at 0x7f7da497dd60&gt;
mocker = &lt;pytest_mock.plugin.MockFixture object at 0x7f7d9c9d1550&gt;
mock_storage = &lt;shared.storage.memory.MemoryStorageService object at 0x7f7da482a2d0&gt;
celery_app = &lt;Celery celery.tests at 0x7f7da65d82d0&gt;
mock_repo_provider_service = &lt;AsyncMock id='140178002626608'&gt;
mock_pull_request_information = &lt;AsyncMock id='140177002625552'&gt;
generate_junit = &lt;function generate_junit.&lt;locals&gt;._generate_junit at 0x7f7d942aa980&gt;

    def test_test_analytics_error_comment(
        dbsession,
        mocker,
        mock_storage,
        celery_app,
        mock_repo_provider_service,
        mock_pull_request_information,
        generate_junit,
    ):
        wrong_xml = """&lt;?xml version="1.0" encoding="utf-8"?&gt;
    &lt;testsuites&gt;
        &lt;testsuite&gt;
            &lt;testcase time="0.001"&gt;
                &lt;failure message="hello world"/&gt;
            &lt;/testcase&gt;
        &lt;/testsuite&gt;
    &lt;/testsuites&gt;
    """
        upload = generate_junit(raw=wrong_xml)
    
        argument = {"url": upload.storage_path, "upload_id": upload.id_}
    
        mocker.patch.object(TAProcessorTask, "app", celery_app)
        mocker.patch.object(TAFinisherTask, "app", celery_app)
    
        result = TAProcessorTask().run_impl(
            dbsession,
            repoid=upload.report.commit.repoid,
            commitid=upload.report.commit.commitid,
            commit_yaml={"codecov": {"max_report_age": False}},
            argument=argument,
        )
    
        assert result is False
    
&gt;       result = TAFinisherTask().run_impl(
            dbsession,
            chord_result=[result],
            repoid=upload.report.commit.repoid,
            commitid=upload.report.commit.commitid,
            commit_yaml={"codecov": {"max_report_age": False}},
        )

tasks/tests/unit/test_ta_finisher_task.py:372: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
tasks/ta_finisher.py:233: in run_impl
    notify_task_sig.apply_async()
/usr/local/lib/python3.13/site-packages/celery/canvas.py:400: in apply_async
    return _apply(args, kwargs, **options)
/usr/local/lib/python3.13/site-packages/sentry_sdk/tracing_utils.py:673: in func_with_tracing
    return func(*args, **kwargs)
tasks/base.py:150: in apply_async
    user_plan = _get_user_plan_from_task(db_session, self.name, kwargs)
celery_task_router.py:144: in _get_user_plan_from_task
    return func_to_use(dbsession, **task_kwargs)
celery_task_router.py:24: in _get_user_plan_from_repoid
    .first()
/usr/local/lib/python3.13/site-packages/sqlalchemy/orm/query.py:3429: in first
    ret = list(self[0:1])
/usr/local/lib/python3.13/site-packages/sqlalchemy/orm/query.py:3203: in __getitem__
    return list(res)
/usr/local/lib/python3.13/site-packages/sqlalchemy/orm/query.py:3535: in __iter__
    return self._execute_and_instances(context)
/usr/local/lib/python3.13/site-packages/sqlalchemy/orm/query.py:3560: in _execute_and_instances
    result = conn.execute(querycontext.statement, self._params)
/usr/local/lib/python3.13/site-packages/sqlalchemy/engine/base.py:1011: in execute
    return meth(self, multiparams, params)
/usr/local/lib/python3.13/site-packages/sqlalchemy/sql/elements.py:298: in _execute_on_connection
    return connection._execute_clauseelement(self, multiparams, params)
/usr/local/lib/python3.13/site-packages/sqlalchemy/engine/base.py:1124: in _execute_clauseelement
    ret = self._execute_context(
/usr/local/lib/python3.13/site-packages/sqlalchemy/engine/base.py:1316: in _execute_context
    self._handle_dbapi_exception(
/usr/local/lib/python3.13/site-packages/sqlalchemy/engine/base.py:1510: in _handle_dbapi_exception
    util.raise_(
/usr/local/lib/python3.13/site-packages/sqlalchemy/util/compat.py:182: in raise_
    raise exception
/usr/local/lib/python3.13/site-packages/sqlalchemy/engine/base.py:1276: in _execute_context
    self.dialect.do_execute(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = &lt;sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0x7f7db97f7cb0&gt;
cursor = &lt;cursor object at 0x7f7da4a01d50; closed: -1&gt;
statement = 'SELECT owners.plan AS owners_plan \nFROM repos JOIN owners ON owners.ownerid = repos.ownerid \nWHERE repos.repoid = %(repoid_1)s \n LIMIT %(param_1)s'
parameters = {'param_1': 1, 'repoid_1': 1026}
context = &lt;sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0x7f7d8069b350&gt;

    def do_execute(self, cursor, statement, parameters, context=None):
&gt;       cursor.execute(statement, parameters)
E       sqlalchemy.exc.ProgrammingError: (psycopg2.errors.UndefinedTable) relation "repos" does not exist
E       LINE 2: FROM repos JOIN owners ON owners.ownerid = repos.ownerid 
E                    ^
E       
E       [SQL: SELECT owners.plan AS owners_plan 
E       FROM repos JOIN owners ON owners.ownerid = repos.ownerid 
E       WHERE repos.repoid = %(repoid_1)s 
E        LIMIT %(param_1)s]
E       [parameters: {'repoid_1': 1026, 'param_1': 1}]
E       (Background on this error at: http://sqlalche.me/e/13/f405)

/usr/local/lib/python3.13/site-packages/sqlalchemy/engine/default.py:608: ProgrammingError
test_test_analytics_regular_comment_with_error
Stack Traces | 0.158s run time
dbsession = &lt;sqlalchemy.orm.session.Session object at 0x7f7da497f110&gt;
mocker = &lt;pytest_mock.plugin.MockFixture object at 0x7f7da4a193d0&gt;
mock_storage = &lt;shared.storage.memory.MemoryStorageService object at 0x7f7d8089d190&gt;
celery_app = &lt;Celery celery.tests at 0x7f7dbc129950&gt;
mock_repo_provider_service = &lt;AsyncMock id='140177737206816'&gt;
mock_pull_request_information = &lt;AsyncMock id='140177606934944'&gt;
generate_junit = &lt;function generate_junit.&lt;locals&gt;._generate_junit at 0x7f7d805128e0&gt;

    def test_test_analytics_regular_comment_with_error(
        dbsession,
        mocker,
        mock_storage,
        celery_app,
        mock_repo_provider_service,
        mock_pull_request_information,
        generate_junit,
    ):
        mocker.patch.object(TAProcessorTask, "app", celery_app)
        mocker.patch.object(TAFinisherTask, "app", celery_app)
    
        commit = CommitFactory.create()
    
        wrong_xml = """&lt;?xml version="1.0" encoding="utf-8"?&gt;
    &lt;testsuites&gt;
        &lt;testsuite&gt;
            &lt;testcase time="0.001"&gt;
                &lt;failure message="hello world"/&gt;
            &lt;/testcase&gt;
        &lt;/testsuite&gt;
    &lt;/testsuites&gt;
    """
    
        upload = generate_junit(raw=wrong_xml, commit=commit)
        argument = {"url": upload.storage_path, "upload_id": upload.id_}
        first_result = TAProcessorTask().run_impl(
            dbsession,
            repoid=upload.report.commit.repoid,
            commitid=upload.report.commit.commitid,
            commit_yaml={"codecov": {"max_report_age": False}},
            argument=argument,
        )
        assert first_result is False
    
        testruns = [
            {
                "name": "test_divide",
                "outcome": "fail",
                "duration_seconds": 0.001,
                "failure_message": "hello world",
            },
        ]
    
        upload = generate_junit(testruns, commit=commit)
        argument = {"url": upload.storage_path, "upload_id": upload.id_}
        second_result = TAProcessorTask().run_impl(
            dbsession,
            repoid=upload.report.commit.repoid,
            commitid=upload.report.commit.commitid,
            commit_yaml={"codecov": {"max_report_age": False}},
            argument=argument,
        )
        assert second_result is True
    
        result = TAFinisherTask().run_impl(
            dbsession,
            chord_result=[first_result, second_result],
            repoid=upload.report.commit.repoid,
            commitid=upload.report.commit.commitid,
            commit_yaml={"codecov": {"max_report_age": False}},
        )
    
        assert result["notify_attempted"] is True
        assert result["notify_succeeded"] is True
        assert result["queue_notify"] is False
    
        short_form_service_name = services_short_dict.get(
            upload.report.commit.repository.owner.service
        )
    
        mock_repo_provider_service.edit_comment.assert_called_once()
&gt;       mock_repo_provider_service.edit_comment.assert_called_once_with(
            mock_pull_request_information.database_pull.pullid,
            mock_pull_request_information.database_pull.commentid,
            f"""### :x: Unsupported file format
    
    &gt; Upload processing failed due to unsupported file format. Please review the parser error message:
    &gt; `Error parsing JUnit XML in hello_world.junit.xml at 4:32: ParserError: No name found`
    &gt; For more help, visit our [troubleshooting guide](https://docs.codecov.com/docs/test-analytics#troubleshooting).
    
    ---
    ### :x: 1 Tests Failed:
    | Tests completed | Failed | Passed | Skipped |
    |---|---|---|---|
    | 1 | 1 | 0 | 0 |
    &lt;details&gt;&lt;summary&gt;View the top 1 failed tests by shortest run time&lt;/summary&gt;
    
    &gt;
    &gt; ```python
    &gt; tests.test_parsers.TestParsers test_divide
    &gt; ```
    &gt;
    &gt; &lt;details&gt;&lt;summary&gt;Stack Traces | 0.001s run time&lt;/summary&gt;
    &gt;
    &gt; &gt;
    &gt; &gt; ```python
    &gt; &gt; hello world
    &gt; &gt; ```
    &gt;
    &gt; &lt;/details&gt;
    
    &lt;/details&gt;
    
    To view more test analytics, go to the [Test Analytics Dashboard](https://app.codecov.io/{short_form_service_name}/{upload.report.commit.repository.owner.username}/{upload.report.commit.repository.name}/tests/{upload.report.commit.branch})
    :loudspeaker:  Thoughts on this report? [Let us know!](https://github.com/codecov/feedback/issues/304)""",
        )
E       AssertionError: expected call not found.
E       Expected: edit_comment(&lt;AsyncMock name='mock.database_pull.pullid' id='140177610139232'&gt;, &lt;AsyncMock name='mock.database_pull.commentid' id='140177610141920'&gt;, '### :x: Unsupported file format\n\n&gt; Upload processing failed due to unsupported file format. Please review the parser error message:\n&gt; `Error parsing JUnit XML in hello_world.junit.xml at 4:32: ParserError: No name found`\n&gt; For more help, visit our [troubleshooting guide](https://docs.codecov.com/docs/test-analytics#troubleshooting).\n\n---\n### :x: 1 Tests Failed:\n| Tests completed | Failed | Passed | Skipped |\n|---|---|---|---|\n| 1 | 1 | 0 | 0 |\n&lt;details&gt;&lt;summary&gt;View the top 1 failed tests by shortest run time&lt;/summary&gt;\n\n&gt; \n&gt; ```python\n&gt; tests.test_parsers.TestParsers test_divide\n&gt; ```\n&gt; \n&gt; &lt;details&gt;&lt;summary&gt;Stack Traces | 0.001s run time&lt;/summary&gt;\n&gt; \n&gt; &gt; \n&gt; &gt; ```python\n&gt; &gt; hello world\n&gt; &gt; ```\n&gt; \n&gt; &lt;/details&gt;\n\n&lt;/details&gt;\n\nTo view more test analytics, go to the [Test Analytics Dashboard](https://app.codecov.io/gh/heidimoore/general-spring-same/tests/main)\n:loudspeaker:  Thoughts on this report? [Let us know!](https://github.com/codecov/feedback/issues/304)')
E         Actual: edit_comment(&lt;AsyncMock name='mock.database_pull.pullid' id='140177610139232'&gt;, &lt;AsyncMock name='mock.database_pull.commentid' id='140177610141920'&gt;, '### :x: Unsupported file format\n\n&gt; Upload processing failed due to unsupported file format. Please review the parser error message:\n&gt; `Error parsing JUnit XML at 4:32: ParserError: No name found`\n&gt; For more help, visit our [troubleshooting guide](https://docs.codecov.com/docs/test-analytics#troubleshooting).\n\n---\n### :x: 1 Tests Failed:\n| Tests completed | Failed | Passed | Skipped |\n|---|---|---|---|\n| 1 | 1 | 0 | 0 |\n&lt;details&gt;&lt;summary&gt;View the top 1 failed tests by shortest run time&lt;/summary&gt;\n\n&gt; \n&gt; ```python\n&gt; tests.test_parsers.TestParsers test_divide\n&gt; ```\n&gt; \n&gt; &lt;details&gt;&lt;summary&gt;Stack Traces | 0.001s run time&lt;/summary&gt;\n&gt; \n&gt; &gt; \n&gt; &gt; ```python\n&gt; &gt; hello world\n&gt; &gt; ```\n&gt; \n&gt; &lt;/details&gt;\n\n&lt;/details&gt;\n\nTo view more test analytics, go to the [Test Analytics Dashboard](https://app.codecov.io/gh/heidimoore/general-spring-same/tests/main)\n:loudspeaker:  Thoughts on this report? [Let us know!](https://github.com/codecov/feedback/issues/304)')
E       
E       pytest introspection follows:
E       
E       Args:
E       assert (&lt;AsyncMock n.../issues/304)') == (&lt;AsyncMock n.../issues/304)')
E         
E         At index 2 diff: '### :x: Unsupported file format\n\n&gt; Upload processing failed due to unsupported file format. Please review the parser error message:\n&gt; `Error parsing JUnit XML at 4:32: ParserError: No name found`\n&gt; For more help, visit our [troubleshooting guide](https://docs.codecov.com/docs/test-analytics#troubleshooting).\n\n---\n### :x: 1 Tests Failed:\n| Tests completed | Failed | Passed | Skipped |\n|---|---|---|---|\n| 1 | 1 | 0 | 0 |\n&lt;details&gt;&lt;summary&gt;View the top 1 failed tests by shortest run time&lt;/summary&gt;\n\n&gt; \n&gt; ```python\n&gt; tests.test_parsers.TestPa...
E         
E         ...Full output truncated (2 lines hidden), use '-vv' to show

tasks/tests/unit/test_ta_finisher_task.py:478: AssertionError

📣 Thoughts on this report? Let Codecov know! | Powered by Codecov

this commit changes the TA upload states to have the v2_persisted
state which represents the test run data being persisted to the db
and the v2_finished state representing that the upload was taken into
account when making the latest commment on the PR

this also removes the v2_failed state since i want an upload to both
be able to have valid test runs and have some failed parsing, the
v2_failed state made it seem like it was either processed or failed
when it could be both

the failures related to an upload will instead be represented by upload
errors
we want to start persisting errors so we can display in the relevant
comments (coverage or TA)
we want to be able to pass UploadError objects to the test results
notifier so it can display the specific parser error to the user to
give them more information on the issue
we want to fetch any relevant upload errors and pass them to the test
results notifier in the finisher so it can display them
- removes the TestResultsProcessingError enum
- ComparisonContext.test_results_error is now a str | None
- notify writers consume ComparisonContext.test_results_error as the
  error message to display
- notify task checks UploadError objects to populate test_results_error
this commit makes it so there is a single flow for TA finishing where
we will always attempt to comment if there is relevant data to comment
and we will only persist data if there is new data (uploads) to persist

in the new test results flow the error comment will no longer be used
and there will be a single notify function that has the capability of
choosing which section of the comment to show: if there are errors show
them, if there are relevant failures show them
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant