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

feat: ta processor handles placeholder storage #983

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

Conversation

joseph-sentry
Copy link
Contributor

we want to be able to specify an upload that was not able to find a file
so we can make a comment on the PR letting the user know that a file
was not found.
this upload is represented using the "placeholder" storage path

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

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

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

in the new test results flow the error comment will no longer be used
and there will be a single notify function that has the capability of
choosing which section of the comment to show: if there are errors show
them, if there are relevant failures show them
we want to be able to specify an upload that was not able to find a file
so we can make a comment on the PR letting the user know that a file
was not found.

this upload is represented using the "placeholder" storage path
Copy link

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

Copy link

codecov bot commented Dec 27, 2024

❌ 3 Tests Failed:

Tests completed Failed Passed Skipped
1784 3 1781 1
View the top 3 failed tests by shortest run time
tasks/tests/unit/test_ta_finisher_task.py::::test_ta_file_not_found
Stack Traces | 0.133s run time
self = <sqlalchemy.engine.base.Connection object at 0x7facbc82e450>
dialect = <sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0x7facdf79bcb0>
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': 1029}
args = (<sqlalchemy.dialects.postgresql.psycopg2.PGCompiler_psycopg2 object at 0x7fac9f187ac0>, [immutabledict({})])
conn = <sqlalchemy.pool.base._ConnectionFairy object at 0x7facb0b609d0>
context = <sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0x7facb02c9c50>

    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 0x7facdf79bcb0>
cursor = <cursor object at 0x7facc4aa5030; 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': 1029}
context = <sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0x7facb02c9c50>

    def do_execute(self, cursor, statement, parameters, context=None):
>       cursor.execute(statement, parameters)
E       psycopg2.errors.InFailedSqlTransaction: current transaction is aborted, commands ignored until end of transaction block

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

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

dbsession = <sqlalchemy.orm.session.Session object at 0x7facc48fb110>
mocker = <pytest_mock.plugin.MockFixture object at 0x7facc4ab71d0>
mock_storage = <shared.storage.memory.MemoryStorageService object at 0x7fac9e20f2f0>
celery_app = <Celery celery.tests at 0x7faccc5dec10>
mock_repo_provider_service = <AsyncMock id='140380005903936'>
mock_pull_request_information = <AsyncMock id='140380005898560'>
generate_junit = <function generate_junit.<locals>._generate_junit at 0x7facb0240fe0>

    def test_ta_file_not_found(
        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()
    
        upload = generate_junit(raw="a", commit=commit)
        upload.storage_path = "placeholder"
        dbsession.flush()
    
        argument = {"url": "placeholder", "upload_id": upload.id_}
        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:552: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
tasks/ta_finisher.py:233: in run_impl
    notify_task_sig.apply_async()
.../local/lib/python3.13.../site-packages/celery/canvas.py:400: in apply_async
    return _apply(args, kwargs, **options)
.../local/lib/python3.13.../site-packages/sentry_sdk/tracing_utils.py:673: in func_with_tracing
    return func(*args, **kwargs)
tasks/base.py:150: in apply_async
    user_plan = _get_user_plan_from_task(db_session, self.name, kwargs)
celery_task_router.py:144: in _get_user_plan_from_task
    return func_to_use(dbsession, **task_kwargs)
celery_task_router.py:24: in _get_user_plan_from_repoid
    .first()
.../local/lib/python3.13.../sqlalchemy/orm/query.py:3429: in first
    ret = list(self[0:1])
.../local/lib/python3.13.../sqlalchemy/orm/query.py:3203: in __getitem__
    return list(res)
.../local/lib/python3.13.../sqlalchemy/orm/query.py:3535: in __iter__
    return self._execute_and_instances(context)
.../local/lib/python3.13.../sqlalchemy/orm/query.py:3560: in _execute_and_instances
    result = conn.execute(querycontext.statement, self._params)
.../local/lib/python3.13.../sqlalchemy/engine/base.py:1011: in execute
    return meth(self, multiparams, params)
.../local/lib/python3.13.../sqlalchemy/sql/elements.py:298: in _execute_on_connection
    return connection._execute_clauseelement(self, multiparams, params)
.../local/lib/python3.13.../sqlalchemy/engine/base.py:1124: in _execute_clauseelement
    ret = self._execute_context(
.../local/lib/python3.13.../sqlalchemy/engine/base.py:1316: in _execute_context
    self._handle_dbapi_exception(
.../local/lib/python3.13.../sqlalchemy/engine/base.py:1510: in _handle_dbapi_exception
    util.raise_(
.../local/lib/python3.13.../sqlalchemy/util/compat.py:182: in raise_
    raise exception
.../local/lib/python3.13.../sqlalchemy/engine/base.py:1276: in _execute_context
    self.dialect.do_execute(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0x7facdf79bcb0>
cursor = <cursor object at 0x7facc4aa5030; 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': 1029}
context = <sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0x7facb02c9c50>

    def do_execute(self, cursor, statement, parameters, context=None):
>       cursor.execute(statement, parameters)
E       sqlalchemy.exc.InternalError: (psycopg2.errors.InFailedSqlTransaction) current transaction is aborted, commands ignored until end of transaction block
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': 1029, 'param_1': 1}]
E       (Background on this error at: http://sqlalche..../e/13/2j85)

.../local/lib/python3.13.../sqlalchemy/engine/default.py:608: InternalError
tasks/tests/unit/test_ta_finisher_task.py::::test_test_analytics_error_comment
Stack Traces | 0.139s run time
self = <sqlalchemy.engine.base.Connection object at 0x7facbc82e450>
dialect = <sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0x7facdf79bcb0>
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 0x7fac9f1849e0>, [immutabledict({})])
conn = <sqlalchemy.pool.base._ConnectionFairy object at 0x7facb0b609d0>
context = <sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0x7facb02c8050>

    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 0x7facdf79bcb0>
cursor = <cursor object at 0x7facc48522f0; 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 0x7facb02c8050>

    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 0x7facc482b2f0>
mocker = <pytest_mock.plugin.MockFixture object at 0x7facbc8c74d0>
mock_storage = <shared.storage.memory.MemoryStorageService object at 0x7facbc51b890>
celery_app = <Celery celery.tests at 0x7face2004690>
mock_repo_provider_service = <AsyncMock id='140380503147920'>
mock_pull_request_information = <AsyncMock id='140380171380656'>
generate_junit = <function generate_junit.<locals>._generate_junit at 0x7facbc771260>

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

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

self = <sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0x7facdf79bcb0>
cursor = <cursor object at 0x7facc48522f0; 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 0x7facb02c8050>

    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.167s run time
dbsession = <sqlalchemy.orm.session.Session object at 0x7faccc2876b0>
mocker = <pytest_mock.plugin.MockFixture object at 0x7facc4ab4590>
mock_storage = <shared.storage.memory.MemoryStorageService object at 0x7fac9eb29fd0>
celery_app = <Celery celery.tests at 0x7faccc5dfed0>
mock_repo_provider_service = <AsyncMock id='140379371142320'>
mock_pull_request_information = <AsyncMock id='140380503812688'>
generate_junit = <function generate_junit.<locals>._generate_junit at 0x7fac9e6c6de0>

    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='140380007015360'>, <AsyncMock name='mock.database_pull.commentid' id='140380007010320'>, '### :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..../fund-stand-meeting/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='140380007015360'>, <AsyncMock name='mock.database_pull.commentid' id='140380007010320'>, '### :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..../fund-stand-meeting/tests/main)\n:loudspeaker:  Thoughts on this report? [Let us know!](https://github..../feedback/issues/304)')
E       
E       pytest introspection follows:
E       
E       Args:
E       assert (<AsyncMock n.../issues/304)') == (<AsyncMock n.../issues/304)')
E         
E         At index 2 diff: '### :x: Unsupported file format\n\n> Upload processing failed due to unsupported file format. Please review the parser error message:\n> `Error parsing JUnit XML at 4:32: ParserError: No name found`\n> For more help, visit our [troubleshooting guide](https://docs.codecov.com/docs/test-analytics#troubleshooting).\n\n---\n### :x: 1 Tests Failed:\n| Tests completed | Failed | Passed | Skipped |\n|---|---|---|---|\n| 1 | 1 | 0 | 0 |\n<details><summary>View the top 1 failed tests by shortest run time</summary>\n\n> \n> ```python\n> tests.test_parsers.TestPa...
E         
E         ...Full output truncated (2 lines hidden), use '-vv' to show

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

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

@codecov-staging
Copy link

❌ 3 Tests Failed:

Tests completed Failed Passed Skipped
1784 3 1781 1
View the top 3 failed tests by shortest run time
tasks/tests/unit/test_ta_finisher_task.py::::test_ta_file_not_found
Stack Traces | 0.133s run time
self = <sqlalchemy.engine.base.Connection object at 0x7facbc82e450>
dialect = <sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0x7facdf79bcb0>
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': 1029}
args = (<sqlalchemy.dialects.postgresql.psycopg2.PGCompiler_psycopg2 object at 0x7fac9f187ac0>, [immutabledict({})])
conn = <sqlalchemy.pool.base._ConnectionFairy object at 0x7facb0b609d0>
context = <sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0x7facb02c9c50>

    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 0x7facdf79bcb0>
cursor = <cursor object at 0x7facc4aa5030; 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': 1029}
context = <sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0x7facb02c9c50>

    def do_execute(self, cursor, statement, parameters, context=None):
>       cursor.execute(statement, parameters)
E       psycopg2.errors.InFailedSqlTransaction: current transaction is aborted, commands ignored until end of transaction block

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

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

dbsession = <sqlalchemy.orm.session.Session object at 0x7facc48fb110>
mocker = <pytest_mock.plugin.MockFixture object at 0x7facc4ab71d0>
mock_storage = <shared.storage.memory.MemoryStorageService object at 0x7fac9e20f2f0>
celery_app = <Celery celery.tests at 0x7faccc5dec10>
mock_repo_provider_service = <AsyncMock id='140380005903936'>
mock_pull_request_information = <AsyncMock id='140380005898560'>
generate_junit = <function generate_junit.<locals>._generate_junit at 0x7facb0240fe0>

    def test_ta_file_not_found(
        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()
    
        upload = generate_junit(raw="a", commit=commit)
        upload.storage_path = "placeholder"
        dbsession.flush()
    
        argument = {"url": "placeholder", "upload_id": upload.id_}
        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:552: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
tasks/ta_finisher.py:233: in run_impl
    notify_task_sig.apply_async()
.../local/lib/python3.13.../site-packages/celery/canvas.py:400: in apply_async
    return _apply(args, kwargs, **options)
.../local/lib/python3.13.../site-packages/sentry_sdk/tracing_utils.py:673: in func_with_tracing
    return func(*args, **kwargs)
tasks/base.py:150: in apply_async
    user_plan = _get_user_plan_from_task(db_session, self.name, kwargs)
celery_task_router.py:144: in _get_user_plan_from_task
    return func_to_use(dbsession, **task_kwargs)
celery_task_router.py:24: in _get_user_plan_from_repoid
    .first()
.../local/lib/python3.13.../sqlalchemy/orm/query.py:3429: in first
    ret = list(self[0:1])
.../local/lib/python3.13.../sqlalchemy/orm/query.py:3203: in __getitem__
    return list(res)
.../local/lib/python3.13.../sqlalchemy/orm/query.py:3535: in __iter__
    return self._execute_and_instances(context)
.../local/lib/python3.13.../sqlalchemy/orm/query.py:3560: in _execute_and_instances
    result = conn.execute(querycontext.statement, self._params)
.../local/lib/python3.13.../sqlalchemy/engine/base.py:1011: in execute
    return meth(self, multiparams, params)
.../local/lib/python3.13.../sqlalchemy/sql/elements.py:298: in _execute_on_connection
    return connection._execute_clauseelement(self, multiparams, params)
.../local/lib/python3.13.../sqlalchemy/engine/base.py:1124: in _execute_clauseelement
    ret = self._execute_context(
.../local/lib/python3.13.../sqlalchemy/engine/base.py:1316: in _execute_context
    self._handle_dbapi_exception(
.../local/lib/python3.13.../sqlalchemy/engine/base.py:1510: in _handle_dbapi_exception
    util.raise_(
.../local/lib/python3.13.../sqlalchemy/util/compat.py:182: in raise_
    raise exception
.../local/lib/python3.13.../sqlalchemy/engine/base.py:1276: in _execute_context
    self.dialect.do_execute(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0x7facdf79bcb0>
cursor = <cursor object at 0x7facc4aa5030; 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': 1029}
context = <sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0x7facb02c9c50>

    def do_execute(self, cursor, statement, parameters, context=None):
>       cursor.execute(statement, parameters)
E       sqlalchemy.exc.InternalError: (psycopg2.errors.InFailedSqlTransaction) current transaction is aborted, commands ignored until end of transaction block
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': 1029, 'param_1': 1}]
E       (Background on this error at: http://sqlalche..../e/13/2j85)

.../local/lib/python3.13.../sqlalchemy/engine/default.py:608: InternalError
tasks/tests/unit/test_ta_finisher_task.py::::test_test_analytics_error_comment
Stack Traces | 0.139s run time
self = <sqlalchemy.engine.base.Connection object at 0x7facbc82e450>
dialect = <sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0x7facdf79bcb0>
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 0x7fac9f1849e0>, [immutabledict({})])
conn = <sqlalchemy.pool.base._ConnectionFairy object at 0x7facb0b609d0>
context = <sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0x7facb02c8050>

    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 0x7facdf79bcb0>
cursor = <cursor object at 0x7facc48522f0; 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 0x7facb02c8050>

    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 0x7facc482b2f0>
mocker = <pytest_mock.plugin.MockFixture object at 0x7facbc8c74d0>
mock_storage = <shared.storage.memory.MemoryStorageService object at 0x7facbc51b890>
celery_app = <Celery celery.tests at 0x7face2004690>
mock_repo_provider_service = <AsyncMock id='140380503147920'>
mock_pull_request_information = <AsyncMock id='140380171380656'>
generate_junit = <function generate_junit.<locals>._generate_junit at 0x7facbc771260>

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

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

self = <sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0x7facdf79bcb0>
cursor = <cursor object at 0x7facc48522f0; 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 0x7facb02c8050>

    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.167s run time
dbsession = <sqlalchemy.orm.session.Session object at 0x7faccc2876b0>
mocker = <pytest_mock.plugin.MockFixture object at 0x7facc4ab4590>
mock_storage = <shared.storage.memory.MemoryStorageService object at 0x7fac9eb29fd0>
celery_app = <Celery celery.tests at 0x7faccc5dfed0>
mock_repo_provider_service = <AsyncMock id='140379371142320'>
mock_pull_request_information = <AsyncMock id='140380503812688'>
generate_junit = <function generate_junit.<locals>._generate_junit at 0x7fac9e6c6de0>

    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='140380007015360'>, <AsyncMock name='mock.database_pull.commentid' id='140380007010320'>, '### :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..../fund-stand-meeting/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='140380007015360'>, <AsyncMock name='mock.database_pull.commentid' id='140380007010320'>, '### :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..../fund-stand-meeting/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

❌ 3 Tests Failed:

Tests completed Failed Passed Skipped
1784 3 1781 1
View the top 3 failed tests by shortest run time
tasks/tests/unit/test_ta_finisher_task.py::::test_ta_file_not_found
Stack Traces | 0.133s run time
self = <sqlalchemy.engine.base.Connection object at 0x7facbc82e450>
dialect = <sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0x7facdf79bcb0>
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': 1029}
args = (<sqlalchemy.dialects.postgresql.psycopg2.PGCompiler_psycopg2 object at 0x7fac9f187ac0>, [immutabledict({})])
conn = <sqlalchemy.pool.base._ConnectionFairy object at 0x7facb0b609d0>
context = <sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0x7facb02c9c50>

    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 0x7facdf79bcb0>
cursor = <cursor object at 0x7facc4aa5030; 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': 1029}
context = <sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0x7facb02c9c50>

    def do_execute(self, cursor, statement, parameters, context=None):
>       cursor.execute(statement, parameters)
E       psycopg2.errors.InFailedSqlTransaction: current transaction is aborted, commands ignored until end of transaction block

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

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

dbsession = <sqlalchemy.orm.session.Session object at 0x7facc48fb110>
mocker = <pytest_mock.plugin.MockFixture object at 0x7facc4ab71d0>
mock_storage = <shared.storage.memory.MemoryStorageService object at 0x7fac9e20f2f0>
celery_app = <Celery celery.tests at 0x7faccc5dec10>
mock_repo_provider_service = <AsyncMock id='140380005903936'>
mock_pull_request_information = <AsyncMock id='140380005898560'>
generate_junit = <function generate_junit.<locals>._generate_junit at 0x7facb0240fe0>

    def test_ta_file_not_found(
        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()
    
        upload = generate_junit(raw="a", commit=commit)
        upload.storage_path = "placeholder"
        dbsession.flush()
    
        argument = {"url": "placeholder", "upload_id": upload.id_}
        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:552: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
tasks/ta_finisher.py:233: in run_impl
    notify_task_sig.apply_async()
.../local/lib/python3.13.../site-packages/celery/canvas.py:400: in apply_async
    return _apply(args, kwargs, **options)
.../local/lib/python3.13.../site-packages/sentry_sdk/tracing_utils.py:673: in func_with_tracing
    return func(*args, **kwargs)
tasks/base.py:150: in apply_async
    user_plan = _get_user_plan_from_task(db_session, self.name, kwargs)
celery_task_router.py:144: in _get_user_plan_from_task
    return func_to_use(dbsession, **task_kwargs)
celery_task_router.py:24: in _get_user_plan_from_repoid
    .first()
.../local/lib/python3.13.../sqlalchemy/orm/query.py:3429: in first
    ret = list(self[0:1])
.../local/lib/python3.13.../sqlalchemy/orm/query.py:3203: in __getitem__
    return list(res)
.../local/lib/python3.13.../sqlalchemy/orm/query.py:3535: in __iter__
    return self._execute_and_instances(context)
.../local/lib/python3.13.../sqlalchemy/orm/query.py:3560: in _execute_and_instances
    result = conn.execute(querycontext.statement, self._params)
.../local/lib/python3.13.../sqlalchemy/engine/base.py:1011: in execute
    return meth(self, multiparams, params)
.../local/lib/python3.13.../sqlalchemy/sql/elements.py:298: in _execute_on_connection
    return connection._execute_clauseelement(self, multiparams, params)
.../local/lib/python3.13.../sqlalchemy/engine/base.py:1124: in _execute_clauseelement
    ret = self._execute_context(
.../local/lib/python3.13.../sqlalchemy/engine/base.py:1316: in _execute_context
    self._handle_dbapi_exception(
.../local/lib/python3.13.../sqlalchemy/engine/base.py:1510: in _handle_dbapi_exception
    util.raise_(
.../local/lib/python3.13.../sqlalchemy/util/compat.py:182: in raise_
    raise exception
.../local/lib/python3.13.../sqlalchemy/engine/base.py:1276: in _execute_context
    self.dialect.do_execute(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0x7facdf79bcb0>
cursor = <cursor object at 0x7facc4aa5030; 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': 1029}
context = <sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0x7facb02c9c50>

    def do_execute(self, cursor, statement, parameters, context=None):
>       cursor.execute(statement, parameters)
E       sqlalchemy.exc.InternalError: (psycopg2.errors.InFailedSqlTransaction) current transaction is aborted, commands ignored until end of transaction block
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': 1029, 'param_1': 1}]
E       (Background on this error at: http://sqlalche..../e/13/2j85)

.../local/lib/python3.13.../sqlalchemy/engine/default.py:608: InternalError
tasks/tests/unit/test_ta_finisher_task.py::::test_test_analytics_error_comment
Stack Traces | 0.139s run time
self = <sqlalchemy.engine.base.Connection object at 0x7facbc82e450>
dialect = <sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0x7facdf79bcb0>
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 0x7fac9f1849e0>, [immutabledict({})])
conn = <sqlalchemy.pool.base._ConnectionFairy object at 0x7facb0b609d0>
context = <sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0x7facb02c8050>

    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 0x7facdf79bcb0>
cursor = <cursor object at 0x7facc48522f0; 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 0x7facb02c8050>

    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 0x7facc482b2f0>
mocker = <pytest_mock.plugin.MockFixture object at 0x7facbc8c74d0>
mock_storage = <shared.storage.memory.MemoryStorageService object at 0x7facbc51b890>
celery_app = <Celery celery.tests at 0x7face2004690>
mock_repo_provider_service = <AsyncMock id='140380503147920'>
mock_pull_request_information = <AsyncMock id='140380171380656'>
generate_junit = <function generate_junit.<locals>._generate_junit at 0x7facbc771260>

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

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

self = <sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0x7facdf79bcb0>
cursor = <cursor object at 0x7facc48522f0; 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 0x7facb02c8050>

    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.167s run time
dbsession = <sqlalchemy.orm.session.Session object at 0x7faccc2876b0>
mocker = <pytest_mock.plugin.MockFixture object at 0x7facc4ab4590>
mock_storage = <shared.storage.memory.MemoryStorageService object at 0x7fac9eb29fd0>
celery_app = <Celery celery.tests at 0x7faccc5dfed0>
mock_repo_provider_service = <AsyncMock id='140379371142320'>
mock_pull_request_information = <AsyncMock id='140380503812688'>
generate_junit = <function generate_junit.<locals>._generate_junit at 0x7fac9e6c6de0>

    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='140380007015360'>, <AsyncMock name='mock.database_pull.commentid' id='140380007010320'>, '### :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..../fund-stand-meeting/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='140380007015360'>, <AsyncMock name='mock.database_pull.commentid' id='140380007010320'>, '### :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..../fund-stand-meeting/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

❌ 3 Tests Failed:

Tests completed Failed Passed Skipped
1784 3 1781 1
View the top 3 failed tests by shortest run time
tasks/tests/unit/test_ta_finisher_task.py::::test_ta_file_not_found
Stack Traces | 0.133s run time
self = <sqlalchemy.engine.base.Connection object at 0x7facbc82e450>
dialect = <sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0x7facdf79bcb0>
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': 1029}
args = (<sqlalchemy.dialects.postgresql.psycopg2.PGCompiler_psycopg2 object at 0x7fac9f187ac0>, [immutabledict({})])
conn = <sqlalchemy.pool.base._ConnectionFairy object at 0x7facb0b609d0>
context = <sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0x7facb02c9c50>

    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 0x7facdf79bcb0>
cursor = <cursor object at 0x7facc4aa5030; 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': 1029}
context = <sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0x7facb02c9c50>

    def do_execute(self, cursor, statement, parameters, context=None):
>       cursor.execute(statement, parameters)
E       psycopg2.errors.InFailedSqlTransaction: current transaction is aborted, commands ignored until end of transaction block

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

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

dbsession = <sqlalchemy.orm.session.Session object at 0x7facc48fb110>
mocker = <pytest_mock.plugin.MockFixture object at 0x7facc4ab71d0>
mock_storage = <shared.storage.memory.MemoryStorageService object at 0x7fac9e20f2f0>
celery_app = <Celery celery.tests at 0x7faccc5dec10>
mock_repo_provider_service = <AsyncMock id='140380005903936'>
mock_pull_request_information = <AsyncMock id='140380005898560'>
generate_junit = <function generate_junit.<locals>._generate_junit at 0x7facb0240fe0>

    def test_ta_file_not_found(
        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()
    
        upload = generate_junit(raw="a", commit=commit)
        upload.storage_path = "placeholder"
        dbsession.flush()
    
        argument = {"url": "placeholder", "upload_id": upload.id_}
        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:552: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
tasks/ta_finisher.py:233: in run_impl
    notify_task_sig.apply_async()
.../local/lib/python3.13.../site-packages/celery/canvas.py:400: in apply_async
    return _apply(args, kwargs, **options)
.../local/lib/python3.13.../site-packages/sentry_sdk/tracing_utils.py:673: in func_with_tracing
    return func(*args, **kwargs)
tasks/base.py:150: in apply_async
    user_plan = _get_user_plan_from_task(db_session, self.name, kwargs)
celery_task_router.py:144: in _get_user_plan_from_task
    return func_to_use(dbsession, **task_kwargs)
celery_task_router.py:24: in _get_user_plan_from_repoid
    .first()
.../local/lib/python3.13.../sqlalchemy/orm/query.py:3429: in first
    ret = list(self[0:1])
.../local/lib/python3.13.../sqlalchemy/orm/query.py:3203: in __getitem__
    return list(res)
.../local/lib/python3.13.../sqlalchemy/orm/query.py:3535: in __iter__
    return self._execute_and_instances(context)
.../local/lib/python3.13.../sqlalchemy/orm/query.py:3560: in _execute_and_instances
    result = conn.execute(querycontext.statement, self._params)
.../local/lib/python3.13.../sqlalchemy/engine/base.py:1011: in execute
    return meth(self, multiparams, params)
.../local/lib/python3.13.../sqlalchemy/sql/elements.py:298: in _execute_on_connection
    return connection._execute_clauseelement(self, multiparams, params)
.../local/lib/python3.13.../sqlalchemy/engine/base.py:1124: in _execute_clauseelement
    ret = self._execute_context(
.../local/lib/python3.13.../sqlalchemy/engine/base.py:1316: in _execute_context
    self._handle_dbapi_exception(
.../local/lib/python3.13.../sqlalchemy/engine/base.py:1510: in _handle_dbapi_exception
    util.raise_(
.../local/lib/python3.13.../sqlalchemy/util/compat.py:182: in raise_
    raise exception
.../local/lib/python3.13.../sqlalchemy/engine/base.py:1276: in _execute_context
    self.dialect.do_execute(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0x7facdf79bcb0>
cursor = <cursor object at 0x7facc4aa5030; 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': 1029}
context = <sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0x7facb02c9c50>

    def do_execute(self, cursor, statement, parameters, context=None):
>       cursor.execute(statement, parameters)
E       sqlalchemy.exc.InternalError: (psycopg2.errors.InFailedSqlTransaction) current transaction is aborted, commands ignored until end of transaction block
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': 1029, 'param_1': 1}]
E       (Background on this error at: http://sqlalche..../e/13/2j85)

.../local/lib/python3.13.../sqlalchemy/engine/default.py:608: InternalError
tasks/tests/unit/test_ta_finisher_task.py::::test_test_analytics_error_comment
Stack Traces | 0.139s run time
self = <sqlalchemy.engine.base.Connection object at 0x7facbc82e450>
dialect = <sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0x7facdf79bcb0>
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 0x7fac9f1849e0>, [immutabledict({})])
conn = <sqlalchemy.pool.base._ConnectionFairy object at 0x7facb0b609d0>
context = <sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0x7facb02c8050>

    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 0x7facdf79bcb0>
cursor = <cursor object at 0x7facc48522f0; 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 0x7facb02c8050>

    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 0x7facc482b2f0>
mocker = <pytest_mock.plugin.MockFixture object at 0x7facbc8c74d0>
mock_storage = <shared.storage.memory.MemoryStorageService object at 0x7facbc51b890>
celery_app = <Celery celery.tests at 0x7face2004690>
mock_repo_provider_service = <AsyncMock id='140380503147920'>
mock_pull_request_information = <AsyncMock id='140380171380656'>
generate_junit = <function generate_junit.<locals>._generate_junit at 0x7facbc771260>

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

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

self = <sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0x7facdf79bcb0>
cursor = <cursor object at 0x7facc48522f0; 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 0x7facb02c8050>

    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.167s run time
dbsession = <sqlalchemy.orm.session.Session object at 0x7faccc2876b0>
mocker = <pytest_mock.plugin.MockFixture object at 0x7facc4ab4590>
mock_storage = <shared.storage.memory.MemoryStorageService object at 0x7fac9eb29fd0>
celery_app = <Celery celery.tests at 0x7faccc5dfed0>
mock_repo_provider_service = <AsyncMock id='140379371142320'>
mock_pull_request_information = <AsyncMock id='140380503812688'>
generate_junit = <function generate_junit.<locals>._generate_junit at 0x7fac9e6c6de0>

    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='140380007015360'>, <AsyncMock name='mock.database_pull.commentid' id='140380007010320'>, '### :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..../fund-stand-meeting/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='140380007015360'>, <AsyncMock name='mock.database_pull.commentid' id='140380007010320'>, '### :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..../fund-stand-meeting/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

❌ 3 Tests Failed:

Tests completed Failed Passed Skipped
1785 3 1781 1
View the top 3 failed tests by shortest run time
test_ta_file_not_found
Stack Traces | 0.133s run time
self = &lt;sqlalchemy.engine.base.Connection object at 0x7facbc82e450&gt;
dialect = &lt;sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0x7facdf79bcb0&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': 1029}
args = (&lt;sqlalchemy.dialects.postgresql.psycopg2.PGCompiler_psycopg2 object at 0x7fac9f187ac0&gt;, [immutabledict({})])
conn = &lt;sqlalchemy.pool.base._ConnectionFairy object at 0x7facb0b609d0&gt;
context = &lt;sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0x7facb02c9c50&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 0x7facdf79bcb0&gt;
cursor = &lt;cursor object at 0x7facc4aa5030; 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': 1029}
context = &lt;sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0x7facb02c9c50&gt;

    def do_execute(self, cursor, statement, parameters, context=None):
&gt;       cursor.execute(statement, parameters)
E       psycopg2.errors.InFailedSqlTransaction: current transaction is aborted, commands ignored until end of transaction block

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

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

dbsession = &lt;sqlalchemy.orm.session.Session object at 0x7facc48fb110&gt;
mocker = &lt;pytest_mock.plugin.MockFixture object at 0x7facc4ab71d0&gt;
mock_storage = &lt;shared.storage.memory.MemoryStorageService object at 0x7fac9e20f2f0&gt;
celery_app = &lt;Celery celery.tests at 0x7faccc5dec10&gt;
mock_repo_provider_service = &lt;AsyncMock id='140380005903936'&gt;
mock_pull_request_information = &lt;AsyncMock id='140380005898560'&gt;
generate_junit = &lt;function generate_junit.&lt;locals&gt;._generate_junit at 0x7facb0240fe0&gt;

    def test_ta_file_not_found(
        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()
    
        upload = generate_junit(raw="a", commit=commit)
        upload.storage_path = "placeholder"
        dbsession.flush()
    
        argument = {"url": "placeholder", "upload_id": upload.id_}
        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:552: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
tasks/ta_finisher.py:233: in run_impl
    notify_task_sig.apply_async()
/usr/local/lib/python3.13/site-packages/celery/canvas.py:400: in apply_async
    return _apply(args, kwargs, **options)
/usr/local/lib/python3.13/site-packages/sentry_sdk/tracing_utils.py:673: in func_with_tracing
    return func(*args, **kwargs)
tasks/base.py:150: in apply_async
    user_plan = _get_user_plan_from_task(db_session, self.name, kwargs)
celery_task_router.py:144: in _get_user_plan_from_task
    return func_to_use(dbsession, **task_kwargs)
celery_task_router.py:24: in _get_user_plan_from_repoid
    .first()
/usr/local/lib/python3.13/site-packages/sqlalchemy/orm/query.py:3429: in first
    ret = list(self[0:1])
/usr/local/lib/python3.13/site-packages/sqlalchemy/orm/query.py:3203: in __getitem__
    return list(res)
/usr/local/lib/python3.13/site-packages/sqlalchemy/orm/query.py:3535: in __iter__
    return self._execute_and_instances(context)
/usr/local/lib/python3.13/site-packages/sqlalchemy/orm/query.py:3560: in _execute_and_instances
    result = conn.execute(querycontext.statement, self._params)
/usr/local/lib/python3.13/site-packages/sqlalchemy/engine/base.py:1011: in execute
    return meth(self, multiparams, params)
/usr/local/lib/python3.13/site-packages/sqlalchemy/sql/elements.py:298: in _execute_on_connection
    return connection._execute_clauseelement(self, multiparams, params)
/usr/local/lib/python3.13/site-packages/sqlalchemy/engine/base.py:1124: in _execute_clauseelement
    ret = self._execute_context(
/usr/local/lib/python3.13/site-packages/sqlalchemy/engine/base.py:1316: in _execute_context
    self._handle_dbapi_exception(
/usr/local/lib/python3.13/site-packages/sqlalchemy/engine/base.py:1510: in _handle_dbapi_exception
    util.raise_(
/usr/local/lib/python3.13/site-packages/sqlalchemy/util/compat.py:182: in raise_
    raise exception
/usr/local/lib/python3.13/site-packages/sqlalchemy/engine/base.py:1276: in _execute_context
    self.dialect.do_execute(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = &lt;sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0x7facdf79bcb0&gt;
cursor = &lt;cursor object at 0x7facc4aa5030; 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': 1029}
context = &lt;sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0x7facb02c9c50&gt;

    def do_execute(self, cursor, statement, parameters, context=None):
&gt;       cursor.execute(statement, parameters)
E       sqlalchemy.exc.InternalError: (psycopg2.errors.InFailedSqlTransaction) current transaction is aborted, commands ignored until end of transaction block
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': 1029, 'param_1': 1}]
E       (Background on this error at: http://sqlalche.me/e/13/2j85)

/usr/local/lib/python3.13/site-packages/sqlalchemy/engine/default.py:608: InternalError
test_test_analytics_error_comment
Stack Traces | 0.139s run time
self = &lt;sqlalchemy.engine.base.Connection object at 0x7facbc82e450&gt;
dialect = &lt;sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0x7facdf79bcb0&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 0x7fac9f1849e0&gt;, [immutabledict({})])
conn = &lt;sqlalchemy.pool.base._ConnectionFairy object at 0x7facb0b609d0&gt;
context = &lt;sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0x7facb02c8050&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 0x7facdf79bcb0&gt;
cursor = &lt;cursor object at 0x7facc48522f0; 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 0x7facb02c8050&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 0x7facc482b2f0&gt;
mocker = &lt;pytest_mock.plugin.MockFixture object at 0x7facbc8c74d0&gt;
mock_storage = &lt;shared.storage.memory.MemoryStorageService object at 0x7facbc51b890&gt;
celery_app = &lt;Celery celery.tests at 0x7face2004690&gt;
mock_repo_provider_service = &lt;AsyncMock id='140380503147920'&gt;
mock_pull_request_information = &lt;AsyncMock id='140380171380656'&gt;
generate_junit = &lt;function generate_junit.&lt;locals&gt;._generate_junit at 0x7facbc771260&gt;

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

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

self = &lt;sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0x7facdf79bcb0&gt;
cursor = &lt;cursor object at 0x7facc48522f0; 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 0x7facb02c8050&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.167s run time
dbsession = &lt;sqlalchemy.orm.session.Session object at 0x7faccc2876b0&gt;
mocker = &lt;pytest_mock.plugin.MockFixture object at 0x7facc4ab4590&gt;
mock_storage = &lt;shared.storage.memory.MemoryStorageService object at 0x7fac9eb29fd0&gt;
celery_app = &lt;Celery celery.tests at 0x7faccc5dfed0&gt;
mock_repo_provider_service = &lt;AsyncMock id='140379371142320'&gt;
mock_pull_request_information = &lt;AsyncMock id='140380503812688'&gt;
generate_junit = &lt;function generate_junit.&lt;locals&gt;._generate_junit at 0x7fac9e6c6de0&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='140380007015360'&gt;, &lt;AsyncMock name='mock.database_pull.commentid' id='140380007010320'&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/steindavid/fund-stand-meeting/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='140380007015360'&gt;, &lt;AsyncMock name='mock.database_pull.commentid' id='140380007010320'&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/steindavid/fund-stand-meeting/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

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