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 cov comment #982

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

Conversation

joseph-sentry
Copy link
Contributor

No description provided.

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
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
Copy link

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

@codecov-staging
Copy link

❌ 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.132s run time
self = <sqlalchemy.engine.base.Connection object at 0x7f9948d35b50>
dialect = <sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0x7f996529fcb0>
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 0x7f9930febbd0>, [immutabledict({})])
conn = <sqlalchemy.pool.base._ConnectionFairy object at 0x7f9930342150>
context = <sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0x7f9930534e50>

    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 0x7f996529fcb0>
cursor = <cursor object at 0x7f9951c11300; 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 0x7f9930534e50>

    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 0x7f99512f5130>
mocker = <pytest_mock.plugin.MockFixture object at 0x7f9948d35790>
mock_storage = <shared.storage.memory.MemoryStorageService object at 0x7f99303a50d0>
celery_app = <Celery celery.tests at 0x7f9952e50690>
mock_repo_provider_service = <AsyncMock id='140296456834416'>
mock_pull_request_information = <AsyncMock id='140296456843824'>
generate_junit = <function generate_junit.<locals>._generate_junit at 0x7f99303671a0>

    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:254: 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 0x7f996529fcb0>
cursor = <cursor object at 0x7f9951c11300; 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 0x7f9930534e50>

    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.155s run time
dbsession = <sqlalchemy.orm.session.Session object at 0x7f99510acc80>
mocker = <pytest_mock.plugin.MockFixture object at 0x7f9948d34710>
mock_storage = <shared.storage.memory.MemoryStorageService object at 0x7f99307976b0>
celery_app = <Celery celery.tests at 0x7f9952e525d0>
mock_repo_provider_service = <AsyncMock id='140296466723712'>
mock_pull_request_information = <AsyncMock id='140296466716656'>
generate_junit = <function generate_junit.<locals>._generate_junit at 0x7f99305b2480>

    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='140296510428496'>, <AsyncMock name='mock.database_pull.commentid' id='140296510424128'>, '### :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..../same-concern-want/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='140296510428496'>, <AsyncMock name='mock.database_pull.commentid' id='140296510424128'>, '### :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..../same-concern-want/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 27, 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.132s run time
self = <sqlalchemy.engine.base.Connection object at 0x7f9948d35b50>
dialect = <sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0x7f996529fcb0>
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 0x7f9930febbd0>, [immutabledict({})])
conn = <sqlalchemy.pool.base._ConnectionFairy object at 0x7f9930342150>
context = <sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0x7f9930534e50>

    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 0x7f996529fcb0>
cursor = <cursor object at 0x7f9951c11300; 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 0x7f9930534e50>

    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 0x7f99512f5130>
mocker = <pytest_mock.plugin.MockFixture object at 0x7f9948d35790>
mock_storage = <shared.storage.memory.MemoryStorageService object at 0x7f99303a50d0>
celery_app = <Celery celery.tests at 0x7f9952e50690>
mock_repo_provider_service = <AsyncMock id='140296456834416'>
mock_pull_request_information = <AsyncMock id='140296456843824'>
generate_junit = <function generate_junit.<locals>._generate_junit at 0x7f99303671a0>

    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:254: 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 0x7f996529fcb0>
cursor = <cursor object at 0x7f9951c11300; 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 0x7f9930534e50>

    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.155s run time
dbsession = <sqlalchemy.orm.session.Session object at 0x7f99510acc80>
mocker = <pytest_mock.plugin.MockFixture object at 0x7f9948d34710>
mock_storage = <shared.storage.memory.MemoryStorageService object at 0x7f99307976b0>
celery_app = <Celery celery.tests at 0x7f9952e525d0>
mock_repo_provider_service = <AsyncMock id='140296466723712'>
mock_pull_request_information = <AsyncMock id='140296466716656'>
generate_junit = <function generate_junit.<locals>._generate_junit at 0x7f99305b2480>

    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='140296510428496'>, <AsyncMock name='mock.database_pull.commentid' id='140296510424128'>, '### :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..../same-concern-want/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='140296510428496'>, <AsyncMock name='mock.database_pull.commentid' id='140296510424128'>, '### :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..../same-concern-want/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

❌ 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.132s run time
self = <sqlalchemy.engine.base.Connection object at 0x7f9948d35b50>
dialect = <sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0x7f996529fcb0>
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 0x7f9930febbd0>, [immutabledict({})])
conn = <sqlalchemy.pool.base._ConnectionFairy object at 0x7f9930342150>
context = <sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0x7f9930534e50>

    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 0x7f996529fcb0>
cursor = <cursor object at 0x7f9951c11300; 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 0x7f9930534e50>

    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 0x7f99512f5130>
mocker = <pytest_mock.plugin.MockFixture object at 0x7f9948d35790>
mock_storage = <shared.storage.memory.MemoryStorageService object at 0x7f99303a50d0>
celery_app = <Celery celery.tests at 0x7f9952e50690>
mock_repo_provider_service = <AsyncMock id='140296456834416'>
mock_pull_request_information = <AsyncMock id='140296456843824'>
generate_junit = <function generate_junit.<locals>._generate_junit at 0x7f99303671a0>

    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:254: 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 0x7f996529fcb0>
cursor = <cursor object at 0x7f9951c11300; 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 0x7f9930534e50>

    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.155s run time
dbsession = <sqlalchemy.orm.session.Session object at 0x7f99510acc80>
mocker = <pytest_mock.plugin.MockFixture object at 0x7f9948d34710>
mock_storage = <shared.storage.memory.MemoryStorageService object at 0x7f99307976b0>
celery_app = <Celery celery.tests at 0x7f9952e525d0>
mock_repo_provider_service = <AsyncMock id='140296466723712'>
mock_pull_request_information = <AsyncMock id='140296466716656'>
generate_junit = <function generate_junit.<locals>._generate_junit at 0x7f99305b2480>

    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='140296510428496'>, <AsyncMock name='mock.database_pull.commentid' id='140296510424128'>, '### :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..../same-concern-want/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='140296510428496'>, <AsyncMock name='mock.database_pull.commentid' id='140296510424128'>, '### :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..../same-concern-want/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

❌ 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.132s run time
self = &lt;sqlalchemy.engine.base.Connection object at 0x7f9948d35b50&gt;
dialect = &lt;sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0x7f996529fcb0&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 0x7f9930febbd0&gt;, [immutabledict({})])
conn = &lt;sqlalchemy.pool.base._ConnectionFairy object at 0x7f9930342150&gt;
context = &lt;sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0x7f9930534e50&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 0x7f996529fcb0&gt;
cursor = &lt;cursor object at 0x7f9951c11300; 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 0x7f9930534e50&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 0x7f99512f5130&gt;
mocker = &lt;pytest_mock.plugin.MockFixture object at 0x7f9948d35790&gt;
mock_storage = &lt;shared.storage.memory.MemoryStorageService object at 0x7f99303a50d0&gt;
celery_app = &lt;Celery celery.tests at 0x7f9952e50690&gt;
mock_repo_provider_service = &lt;AsyncMock id='140296456834416'&gt;
mock_pull_request_information = &lt;AsyncMock id='140296456843824'&gt;
generate_junit = &lt;function generate_junit.&lt;locals&gt;._generate_junit at 0x7f99303671a0&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:254: 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 0x7f996529fcb0&gt;
cursor = &lt;cursor object at 0x7f9951c11300; 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 0x7f9930534e50&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.155s run time
dbsession = &lt;sqlalchemy.orm.session.Session object at 0x7f99510acc80&gt;
mocker = &lt;pytest_mock.plugin.MockFixture object at 0x7f9948d34710&gt;
mock_storage = &lt;shared.storage.memory.MemoryStorageService object at 0x7f99307976b0&gt;
celery_app = &lt;Celery celery.tests at 0x7f9952e525d0&gt;
mock_repo_provider_service = &lt;AsyncMock id='140296466723712'&gt;
mock_pull_request_information = &lt;AsyncMock id='140296466716656'&gt;
generate_junit = &lt;function generate_junit.&lt;locals&gt;._generate_junit at 0x7f99305b2480&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='140296510428496'&gt;, &lt;AsyncMock name='mock.database_pull.commentid' id='140296510424128'&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/jim96/same-concern-want/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='140296510428496'&gt;, &lt;AsyncMock name='mock.database_pull.commentid' id='140296510424128'&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/jim96/same-concern-want/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

Copy link

codecov bot commented Dec 27, 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.132s run time
self = <sqlalchemy.engine.base.Connection object at 0x7f9948d35b50>
dialect = <sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0x7f996529fcb0>
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 0x7f9930febbd0>, [immutabledict({})])
conn = <sqlalchemy.pool.base._ConnectionFairy object at 0x7f9930342150>
context = <sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0x7f9930534e50>

    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 0x7f996529fcb0>
cursor = <cursor object at 0x7f9951c11300; 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 0x7f9930534e50>

    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 0x7f99512f5130>
mocker = <pytest_mock.plugin.MockFixture object at 0x7f9948d35790>
mock_storage = <shared.storage.memory.MemoryStorageService object at 0x7f99303a50d0>
celery_app = <Celery celery.tests at 0x7f9952e50690>
mock_repo_provider_service = <AsyncMock id='140296456834416'>
mock_pull_request_information = <AsyncMock id='140296456843824'>
generate_junit = <function generate_junit.<locals>._generate_junit at 0x7f99303671a0>

    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:254: 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 0x7f996529fcb0>
cursor = <cursor object at 0x7f9951c11300; 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 0x7f9930534e50>

    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.155s run time
dbsession = <sqlalchemy.orm.session.Session object at 0x7f99510acc80>
mocker = <pytest_mock.plugin.MockFixture object at 0x7f9948d34710>
mock_storage = <shared.storage.memory.MemoryStorageService object at 0x7f99307976b0>
celery_app = <Celery celery.tests at 0x7f9952e525d0>
mock_repo_provider_service = <AsyncMock id='140296466723712'>
mock_pull_request_information = <AsyncMock id='140296466716656'>
generate_junit = <function generate_junit.<locals>._generate_junit at 0x7f99305b2480>

    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='140296510428496'>, <AsyncMock name='mock.database_pull.commentid' id='140296510424128'>, '### :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..../same-concern-want/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='140296510428496'>, <AsyncMock name='mock.database_pull.commentid' id='140296510424128'>, '### :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..../same-concern-want/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!

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