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

chore(aci milestone 3): refactor migration helper tests #84285

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

mifu67
Copy link
Contributor

@mifu67 mifu67 commented Jan 30, 2025

Let's do this the right way!

  • Create a BaseMetricAlertMigrationTest with some helper methods that create the expected objects for a dual written alert rule/trigger/action. We can call these in setUp() for each individual test class.
  • Split the tests for each combination of write/update/delete and alert rule/trigger/action into their own individual class, so we can test each helper individually.
  • Rename the update rule helper to be named consistently with the rest of the dual update/delete helpers.

@github-actions github-actions bot added the Scope: Backend Automatically applied to PRs that change backend components label Jan 30, 2025
Copy link

codecov bot commented Jan 30, 2025

❌ 5 Tests Failed:

Tests completed Failed Passed Skipped
23686 5 23681 292
View the top 1 failed tests by shortest run time
tests.symbolicator.test_payload_full.SymbolicatorResolvingIntegrationTest::test_resolving_with_candidates_sentry_source
Stack Traces | 0.097s run time
#x1B[1m#x1B[.../db/postgres/decorators.py#x1B[0m:91: in inner
    return func(self, sql, *args, **kwargs)
#x1B[1m#x1B[.../db/postgres/base.py#x1B[0m:84: in execute
    return self.cursor.execute(sql, clean_bad_params(params))
#x1B[1m#x1B[31mE   psycopg2.errors.UniqueViolation: duplicate key value violates unique constraint "auth_user_username_key"#x1B[0m
#x1B[1m#x1B[31mE   DETAIL:  Key (username)=(admin@localhost) already exists.#x1B[0m

#x1B[33mDuring handling of the above exception, another exception occurred:#x1B[0m
#x1B[1m#x1B[31m.venv/lib/python3.13.../db/backends/utils.py#x1B[0m:105: in _execute
    return self.cursor.execute(sql, params)
#x1B[1m#x1B[.../db/postgres/decorators.py#x1B[0m:77: in inner
    raise_the_exception(self.db, e)
#x1B[1m#x1B[.../db/postgres/decorators.py#x1B[0m:75: in inner
    return func(self, *args, **kwargs)
#x1B[1m#x1B[.../db/postgres/decorators.py#x1B[0m:18: in inner
    return func(self, *args, **kwargs)
#x1B[1m#x1B[.../db/postgres/decorators.py#x1B[0m:93: in inner
    raise type(e)(f"{e!r}\nSQL: {sql}").with_traceback(e.__traceback__)
#x1B[1m#x1B[.../db/postgres/decorators.py#x1B[0m:91: in inner
    return func(self, sql, *args, **kwargs)
#x1B[1m#x1B[.../db/postgres/base.py#x1B[0m:84: in execute
    return self.cursor.execute(sql, clean_bad_params(params))
#x1B[1m#x1B[31mE   psycopg2.errors.UniqueViolation: UniqueViolation('duplicate key value violates unique constraint "auth_user_username_key"\nDETAIL:  Key (username)=(admin@localhost) already exists.\n')#x1B[0m
#x1B[1m#x1B[31mE   SQL: INSERT INTO "auth_user" ("password", "last_login", "username", "first_name", "email", "is_staff", "is_active", "is_unclaimed", "is_superuser", "is_managed", "is_sentry_app", "is_password_expired", "last_password_change", "flags", "session_nonce", "date_joined", "last_active", "avatar_type", "avatar_url") VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) RETURNING "auth_user"."id"#x1B[0m

#x1B[33mThe above exception was the direct cause of the following exception:#x1B[0m
#x1B[1m#x1B[31mtests/symbolicator/test_payload_full.py#x1B[0m:92: in initialize
    self.project.update_option("sentry:builtin_symbol_sources", [])
#x1B[1m#x1B[31m.venv/lib/python3.13.../django/utils/functional.py#x1B[0m:47: in __get__
    res = instance.__dict__[self.name] = self.func(instance)
#x1B[1m#x1B[.../sentry/testutils/fixtures.py#x1B[0m:97: in project
    name="Bar", slug="bar", teams=[self.team], fire_project_created=True
#x1B[1m#x1B[31m.venv/lib/python3.13.../django/utils/functional.py#x1B[0m:47: in __get__
    res = instance.__dict__[self.name] = self.func(instance)
#x1B[1m#x1B[.../hostedtoolcache/Python/3.13.1....../x64/lib/python3.13/contextlib.py#x1B[0m:85: in inner
    return func(*args, **kwds)
#x1B[1m#x1B[.../sentry/testutils/fixtures.py#x1B[0m:87: in team
    team = self.create_team(organization=self.organization, name="foo", slug="foo")
#x1B[1m#x1B[31m.venv/lib/python3.13.../django/utils/functional.py#x1B[0m:47: in __get__
    res = instance.__dict__[self.name] = self.func(instance)
#x1B[1m#x1B[.../sentry/testutils/fixtures.py#x1B[0m:82: in organization
    return self.create_organization(name="baz", slug="baz", owner=self.user)
#x1B[1m#x1B[31m.venv/lib/python3.13.../django/utils/functional.py#x1B[0m:47: in __get__
    res = instance.__dict__[self.name] = self.func(instance)
#x1B[1m#x1B[.../sentry/testutils/fixtures.py#x1B[0m:71: in user
    return self.create_user(
#x1B[1m#x1B[.../sentry/testutils/fixtures.py#x1B[0m:269: in create_user
    return Factories.create_user(*args, **kwargs)
#x1B[1m#x1B[.../hostedtoolcache/Python/3.13.1....../x64/lib/python3.13/contextlib.py#x1B[0m:85: in inner
    return func(*args, **kwds)
#x1B[1m#x1B[.../sentry/testutils/factories.py#x1B[0m:906: in create_user
    user.save()
#x1B[1m#x1B[.../sentry/silo/base.py#x1B[0m:158: in override
    return original_method(*args, **kwargs)
#x1B[1m#x1B[.../users/models/user.py#x1B[0m:225: in save
    result = super().save(*args, **kwargs)
#x1B[1m#x1B[31m.venv/lib/python3.13.../contrib/auth/base_user.py#x1B[0m:62: in save
    super().save(*args, **kwargs)
#x1B[1m#x1B[31m.venv/lib/python3.13.../db/models/base.py#x1B[0m:892: in save
    self.save_base(
#x1B[1m#x1B[.../sentry/silo/base.py#x1B[0m:158: in override
    return original_method(*args, **kwargs)
#x1B[1m#x1B[31m.venv/lib/python3.13.../db/models/base.py#x1B[0m:998: in save_base
    updated = self._save_table(
#x1B[1m#x1B[31m.venv/lib/python3.13.../db/models/base.py#x1B[0m:1161: in _save_table
    results = self._do_insert(
#x1B[1m#x1B[31m.venv/lib/python3.13.../db/models/base.py#x1B[0m:1202: in _do_insert
    return manager._insert(
#x1B[1m#x1B[31m.venv/lib/python3.13.../db/models/manager.py#x1B[0m:87: in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
#x1B[1m#x1B[31m.venv/lib/python3.13.../db/models/query.py#x1B[0m:1847: in _insert
    return query.get_compiler(using=using).execute_sql(returning_fields)
#x1B[1m#x1B[31m.venv/lib/python3.13.../models/sql/compiler.py#x1B[0m:1836: in execute_sql
    cursor.execute(sql, params)
#x1B[1m#x1B[31m.venv/lib/python3.13.../db/backends/utils.py#x1B[0m:122: in execute
    return super().execute(sql, params)
#x1B[1m#x1B[31m.venv/lib/python3.13.../site-packages/sentry_sdk/utils.py#x1B[0m:1858: in runner
    return original_function(*args, **kwargs)
#x1B[1m#x1B[31m.venv/lib/python3.13.../db/backends/utils.py#x1B[0m:79: in execute
    return self._execute_with_wrappers(
#x1B[1m#x1B[31m.venv/lib/python3.13.../db/backends/utils.py#x1B[0m:92: in _execute_with_wrappers
    return executor(sql, params, many, context)
#x1B[1m#x1B[.../sentry/testutils/hybrid_cloud.py#x1B[0m:133: in __call__
    return execute(*params)
#x1B[1m#x1B[31m.venv/lib/python3.13.../db/backends/utils.py#x1B[0m:100: in _execute
    with self.db.wrap_database_errors:
#x1B[1m#x1B[31m.venv/lib/python3.13.../django/db/utils.py#x1B[0m:91: in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
#x1B[1m#x1B[31m.venv/lib/python3.13.../db/backends/utils.py#x1B[0m:105: in _execute
    return self.cursor.execute(sql, params)
#x1B[1m#x1B[.../db/postgres/decorators.py#x1B[0m:77: in inner
    raise_the_exception(self.db, e)
#x1B[1m#x1B[.../db/postgres/decorators.py#x1B[0m:75: in inner
    return func(self, *args, **kwargs)
#x1B[1m#x1B[.../db/postgres/decorators.py#x1B[0m:18: in inner
    return func(self, *args, **kwargs)
#x1B[1m#x1B[.../db/postgres/decorators.py#x1B[0m:93: in inner
    raise type(e)(f"{e!r}\nSQL: {sql}").with_traceback(e.__traceback__)
#x1B[1m#x1B[.../db/postgres/decorators.py#x1B[0m:91: in inner
    return func(self, sql, *args, **kwargs)
#x1B[1m#x1B[.../db/postgres/base.py#x1B[0m:84: in execute
    return self.cursor.execute(sql, clean_bad_params(params))
#x1B[1m#x1B[31mE   django.db.utils.IntegrityError: UniqueViolation('duplicate key value violates unique constraint "auth_user_username_key"\nDETAIL:  Key (username)=(admin@localhost) already exists.\n')#x1B[0m
#x1B[1m#x1B[31mE   SQL: INSERT INTO "auth_user" ("password", "last_login", "username", "first_name", "email", "is_staff", "is_active", "is_unclaimed", "is_superuser", "is_managed", "is_sentry_app", "is_password_expired", "last_password_change", "flags", "session_nonce", "date_joined", "last_active", "avatar_type", "avatar_url") VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) RETURNING "auth_user"."id"#x1B[0m
View the full list of 2 ❄️ flaky tests
tests.symbolicator.test_unreal_full.SymbolicatorUnrealIntegrationTest::test_unreal_apple_crash_with_attachments

Flake rate in main: 9.38% (Passed 29 times, Failed 3 times)

Stack Traces | 0.071s run time
#x1B[1m#x1B[.../db/postgres/decorators.py#x1B[0m:91: in inner
    return func(self, sql, *args, **kwargs)
#x1B[1m#x1B[.../db/postgres/base.py#x1B[0m:84: in execute
    return self.cursor.execute(sql, clean_bad_params(params))
#x1B[1m#x1B[31mE   psycopg2.errors.UniqueViolation: duplicate key value violates unique constraint "auth_user_username_key"#x1B[0m
#x1B[1m#x1B[31mE   DETAIL:  Key (username)=(admin@localhost) already exists.#x1B[0m

#x1B[33mDuring handling of the above exception, another exception occurred:#x1B[0m
#x1B[1m#x1B[31m.venv/lib/python3.13.../db/backends/utils.py#x1B[0m:105: in _execute
    return self.cursor.execute(sql, params)
#x1B[1m#x1B[.../db/postgres/decorators.py#x1B[0m:77: in inner
    raise_the_exception(self.db, e)
#x1B[1m#x1B[.../db/postgres/decorators.py#x1B[0m:75: in inner
    return func(self, *args, **kwargs)
#x1B[1m#x1B[.../db/postgres/decorators.py#x1B[0m:18: in inner
    return func(self, *args, **kwargs)
#x1B[1m#x1B[.../db/postgres/decorators.py#x1B[0m:93: in inner
    raise type(e)(f"{e!r}\nSQL: {sql}").with_traceback(e.__traceback__)
#x1B[1m#x1B[.../db/postgres/decorators.py#x1B[0m:91: in inner
    return func(self, sql, *args, **kwargs)
#x1B[1m#x1B[.../db/postgres/base.py#x1B[0m:84: in execute
    return self.cursor.execute(sql, clean_bad_params(params))
#x1B[1m#x1B[31mE   psycopg2.errors.UniqueViolation: UniqueViolation('duplicate key value violates unique constraint "auth_user_username_key"\nDETAIL:  Key (username)=(admin@localhost) already exists.\n')#x1B[0m
#x1B[1m#x1B[31mE   SQL: INSERT INTO "auth_user" ("password", "last_login", "username", "first_name", "email", "is_staff", "is_active", "is_unclaimed", "is_superuser", "is_managed", "is_sentry_app", "is_password_expired", "last_password_change", "flags", "session_nonce", "date_joined", "last_active", "avatar_type", "avatar_url") VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) RETURNING "auth_user"."id"#x1B[0m

#x1B[33mThe above exception was the direct cause of the following exception:#x1B[0m
#x1B[1m#x1B[31mtests/symbolicator/test_unreal_full.py#x1B[0m:42: in initialize
    self.project.update_option("sentry:builtin_symbol_sources", [])
#x1B[1m#x1B[31m.venv/lib/python3.13.../django/utils/functional.py#x1B[0m:47: in __get__
    res = instance.__dict__[self.name] = self.func(instance)
#x1B[1m#x1B[.../sentry/testutils/fixtures.py#x1B[0m:97: in project
    name="Bar", slug="bar", teams=[self.team], fire_project_created=True
#x1B[1m#x1B[31m.venv/lib/python3.13.../django/utils/functional.py#x1B[0m:47: in __get__
    res = instance.__dict__[self.name] = self.func(instance)
#x1B[1m#x1B[.../hostedtoolcache/Python/3.13.1....../x64/lib/python3.13/contextlib.py#x1B[0m:85: in inner
    return func(*args, **kwds)
#x1B[1m#x1B[.../sentry/testutils/fixtures.py#x1B[0m:87: in team
    team = self.create_team(organization=self.organization, name="foo", slug="foo")
#x1B[1m#x1B[31m.venv/lib/python3.13.../django/utils/functional.py#x1B[0m:47: in __get__
    res = instance.__dict__[self.name] = self.func(instance)
#x1B[1m#x1B[.../sentry/testutils/fixtures.py#x1B[0m:82: in organization
    return self.create_organization(name="baz", slug="baz", owner=self.user)
#x1B[1m#x1B[31m.venv/lib/python3.13.../django/utils/functional.py#x1B[0m:47: in __get__
    res = instance.__dict__[self.name] = self.func(instance)
#x1B[1m#x1B[.../sentry/testutils/fixtures.py#x1B[0m:71: in user
    return self.create_user(
#x1B[1m#x1B[.../sentry/testutils/fixtures.py#x1B[0m:269: in create_user
    return Factories.create_user(*args, **kwargs)
#x1B[1m#x1B[.../hostedtoolcache/Python/3.13.1....../x64/lib/python3.13/contextlib.py#x1B[0m:85: in inner
    return func(*args, **kwds)
#x1B[1m#x1B[.../sentry/testutils/factories.py#x1B[0m:906: in create_user
    user.save()
#x1B[1m#x1B[.../sentry/silo/base.py#x1B[0m:158: in override
    return original_method(*args, **kwargs)
#x1B[1m#x1B[.../users/models/user.py#x1B[0m:225: in save
    result = super().save(*args, **kwargs)
#x1B[1m#x1B[31m.venv/lib/python3.13.../contrib/auth/base_user.py#x1B[0m:62: in save
    super().save(*args, **kwargs)
#x1B[1m#x1B[31m.venv/lib/python3.13.../db/models/base.py#x1B[0m:892: in save
    self.save_base(
#x1B[1m#x1B[.../sentry/silo/base.py#x1B[0m:158: in override
    return original_method(*args, **kwargs)
#x1B[1m#x1B[31m.venv/lib/python3.13.../db/models/base.py#x1B[0m:998: in save_base
    updated = self._save_table(
#x1B[1m#x1B[31m.venv/lib/python3.13.../db/models/base.py#x1B[0m:1161: in _save_table
    results = self._do_insert(
#x1B[1m#x1B[31m.venv/lib/python3.13.../db/models/base.py#x1B[0m:1202: in _do_insert
    return manager._insert(
#x1B[1m#x1B[31m.venv/lib/python3.13.../db/models/manager.py#x1B[0m:87: in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
#x1B[1m#x1B[31m.venv/lib/python3.13.../db/models/query.py#x1B[0m:1847: in _insert
    return query.get_compiler(using=using).execute_sql(returning_fields)
#x1B[1m#x1B[31m.venv/lib/python3.13.../models/sql/compiler.py#x1B[0m:1836: in execute_sql
    cursor.execute(sql, params)
#x1B[1m#x1B[31m.venv/lib/python3.13.../db/backends/utils.py#x1B[0m:122: in execute
    return super().execute(sql, params)
#x1B[1m#x1B[31m.venv/lib/python3.13.../site-packages/sentry_sdk/utils.py#x1B[0m:1858: in runner
    return original_function(*args, **kwargs)
#x1B[1m#x1B[31m.venv/lib/python3.13.../db/backends/utils.py#x1B[0m:79: in execute
    return self._execute_with_wrappers(
#x1B[1m#x1B[31m.venv/lib/python3.13.../db/backends/utils.py#x1B[0m:92: in _execute_with_wrappers
    return executor(sql, params, many, context)
#x1B[1m#x1B[.../sentry/testutils/hybrid_cloud.py#x1B[0m:133: in __call__
    return execute(*params)
#x1B[1m#x1B[31m.venv/lib/python3.13.../db/backends/utils.py#x1B[0m:100: in _execute
    with self.db.wrap_database_errors:
#x1B[1m#x1B[31m.venv/lib/python3.13.../django/db/utils.py#x1B[0m:91: in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
#x1B[1m#x1B[31m.venv/lib/python3.13.../db/backends/utils.py#x1B[0m:105: in _execute
    return self.cursor.execute(sql, params)
#x1B[1m#x1B[.../db/postgres/decorators.py#x1B[0m:77: in inner
    raise_the_exception(self.db, e)
#x1B[1m#x1B[.../db/postgres/decorators.py#x1B[0m:75: in inner
    return func(self, *args, **kwargs)
#x1B[1m#x1B[.../db/postgres/decorators.py#x1B[0m:18: in inner
    return func(self, *args, **kwargs)
#x1B[1m#x1B[.../db/postgres/decorators.py#x1B[0m:93: in inner
    raise type(e)(f"{e!r}\nSQL: {sql}").with_traceback(e.__traceback__)
#x1B[1m#x1B[.../db/postgres/decorators.py#x1B[0m:91: in inner
    return func(self, sql, *args, **kwargs)
#x1B[1m#x1B[.../db/postgres/base.py#x1B[0m:84: in execute
    return self.cursor.execute(sql, clean_bad_params(params))
#x1B[1m#x1B[31mE   django.db.utils.IntegrityError: UniqueViolation('duplicate key value violates unique constraint "auth_user_username_key"\nDETAIL:  Key (username)=(admin@localhost) already exists.\n')#x1B[0m
#x1B[1m#x1B[31mE   SQL: INSERT INTO "auth_user" ("password", "last_login", "username", "first_name", "email", "is_staff", "is_active", "is_unclaimed", "is_superuser", "is_managed", "is_sentry_app", "is_password_expired", "last_password_change", "flags", "session_nonce", "date_joined", "last_active", "avatar_type", "avatar_url") VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) RETURNING "auth_user"."id"#x1B[0m
tests.symbolicator.test_unreal_full.SymbolicatorUnrealIntegrationTest::test_unreal_crash_with_attachments

Flake rate in main: 9.38% (Passed 29 times, Failed 3 times)

Stack Traces | 0.078s run time
#x1B[1m#x1B[.../db/postgres/decorators.py#x1B[0m:91: in inner
    return func(self, sql, *args, **kwargs)
#x1B[1m#x1B[.../db/postgres/base.py#x1B[0m:84: in execute
    return self.cursor.execute(sql, clean_bad_params(params))
#x1B[1m#x1B[31mE   psycopg2.errors.UniqueViolation: duplicate key value violates unique constraint "auth_user_username_key"#x1B[0m
#x1B[1m#x1B[31mE   DETAIL:  Key (username)=(admin@localhost) already exists.#x1B[0m

#x1B[33mDuring handling of the above exception, another exception occurred:#x1B[0m
#x1B[1m#x1B[31m.venv/lib/python3.13.../db/backends/utils.py#x1B[0m:105: in _execute
    return self.cursor.execute(sql, params)
#x1B[1m#x1B[.../db/postgres/decorators.py#x1B[0m:77: in inner
    raise_the_exception(self.db, e)
#x1B[1m#x1B[.../db/postgres/decorators.py#x1B[0m:75: in inner
    return func(self, *args, **kwargs)
#x1B[1m#x1B[.../db/postgres/decorators.py#x1B[0m:18: in inner
    return func(self, *args, **kwargs)
#x1B[1m#x1B[.../db/postgres/decorators.py#x1B[0m:93: in inner
    raise type(e)(f"{e!r}\nSQL: {sql}").with_traceback(e.__traceback__)
#x1B[1m#x1B[.../db/postgres/decorators.py#x1B[0m:91: in inner
    return func(self, sql, *args, **kwargs)
#x1B[1m#x1B[.../db/postgres/base.py#x1B[0m:84: in execute
    return self.cursor.execute(sql, clean_bad_params(params))
#x1B[1m#x1B[31mE   psycopg2.errors.UniqueViolation: UniqueViolation('duplicate key value violates unique constraint "auth_user_username_key"\nDETAIL:  Key (username)=(admin@localhost) already exists.\n')#x1B[0m
#x1B[1m#x1B[31mE   SQL: INSERT INTO "auth_user" ("password", "last_login", "username", "first_name", "email", "is_staff", "is_active", "is_unclaimed", "is_superuser", "is_managed", "is_sentry_app", "is_password_expired", "last_password_change", "flags", "session_nonce", "date_joined", "last_active", "avatar_type", "avatar_url") VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) RETURNING "auth_user"."id"#x1B[0m

#x1B[33mThe above exception was the direct cause of the following exception:#x1B[0m
#x1B[1m#x1B[31mtests/symbolicator/test_unreal_full.py#x1B[0m:42: in initialize
    self.project.update_option("sentry:builtin_symbol_sources", [])
#x1B[1m#x1B[31m.venv/lib/python3.13.../django/utils/functional.py#x1B[0m:47: in __get__
    res = instance.__dict__[self.name] = self.func(instance)
#x1B[1m#x1B[.../sentry/testutils/fixtures.py#x1B[0m:97: in project
    name="Bar", slug="bar", teams=[self.team], fire_project_created=True
#x1B[1m#x1B[31m.venv/lib/python3.13.../django/utils/functional.py#x1B[0m:47: in __get__
    res = instance.__dict__[self.name] = self.func(instance)
#x1B[1m#x1B[.../hostedtoolcache/Python/3.13.1....../x64/lib/python3.13/contextlib.py#x1B[0m:85: in inner
    return func(*args, **kwds)
#x1B[1m#x1B[.../sentry/testutils/fixtures.py#x1B[0m:87: in team
    team = self.create_team(organization=self.organization, name="foo", slug="foo")
#x1B[1m#x1B[31m.venv/lib/python3.13.../django/utils/functional.py#x1B[0m:47: in __get__
    res = instance.__dict__[self.name] = self.func(instance)
#x1B[1m#x1B[.../sentry/testutils/fixtures.py#x1B[0m:82: in organization
    return self.create_organization(name="baz", slug="baz", owner=self.user)
#x1B[1m#x1B[31m.venv/lib/python3.13.../django/utils/functional.py#x1B[0m:47: in __get__
    res = instance.__dict__[self.name] = self.func(instance)
#x1B[1m#x1B[.../sentry/testutils/fixtures.py#x1B[0m:71: in user
    return self.create_user(
#x1B[1m#x1B[.../sentry/testutils/fixtures.py#x1B[0m:269: in create_user
    return Factories.create_user(*args, **kwargs)
#x1B[1m#x1B[.../hostedtoolcache/Python/3.13.1....../x64/lib/python3.13/contextlib.py#x1B[0m:85: in inner
    return func(*args, **kwds)
#x1B[1m#x1B[.../sentry/testutils/factories.py#x1B[0m:906: in create_user
    user.save()
#x1B[1m#x1B[.../sentry/silo/base.py#x1B[0m:158: in override
    return original_method(*args, **kwargs)
#x1B[1m#x1B[.../users/models/user.py#x1B[0m:225: in save
    result = super().save(*args, **kwargs)
#x1B[1m#x1B[31m.venv/lib/python3.13.../contrib/auth/base_user.py#x1B[0m:62: in save
    super().save(*args, **kwargs)
#x1B[1m#x1B[31m.venv/lib/python3.13.../db/models/base.py#x1B[0m:892: in save
    self.save_base(
#x1B[1m#x1B[.../sentry/silo/base.py#x1B[0m:158: in override
    return original_method(*args, **kwargs)
#x1B[1m#x1B[31m.venv/lib/python3.13.../db/models/base.py#x1B[0m:998: in save_base
    updated = self._save_table(
#x1B[1m#x1B[31m.venv/lib/python3.13.../db/models/base.py#x1B[0m:1161: in _save_table
    results = self._do_insert(
#x1B[1m#x1B[31m.venv/lib/python3.13.../db/models/base.py#x1B[0m:1202: in _do_insert
    return manager._insert(
#x1B[1m#x1B[31m.venv/lib/python3.13.../db/models/manager.py#x1B[0m:87: in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
#x1B[1m#x1B[31m.venv/lib/python3.13.../db/models/query.py#x1B[0m:1847: in _insert
    return query.get_compiler(using=using).execute_sql(returning_fields)
#x1B[1m#x1B[31m.venv/lib/python3.13.../models/sql/compiler.py#x1B[0m:1836: in execute_sql
    cursor.execute(sql, params)
#x1B[1m#x1B[31m.venv/lib/python3.13.../db/backends/utils.py#x1B[0m:122: in execute
    return super().execute(sql, params)
#x1B[1m#x1B[31m.venv/lib/python3.13.../site-packages/sentry_sdk/utils.py#x1B[0m:1858: in runner
    return original_function(*args, **kwargs)
#x1B[1m#x1B[31m.venv/lib/python3.13.../db/backends/utils.py#x1B[0m:79: in execute
    return self._execute_with_wrappers(
#x1B[1m#x1B[31m.venv/lib/python3.13.../db/backends/utils.py#x1B[0m:92: in _execute_with_wrappers
    return executor(sql, params, many, context)
#x1B[1m#x1B[.../sentry/testutils/hybrid_cloud.py#x1B[0m:133: in __call__
    return execute(*params)
#x1B[1m#x1B[31m.venv/lib/python3.13.../db/backends/utils.py#x1B[0m:100: in _execute
    with self.db.wrap_database_errors:
#x1B[1m#x1B[31m.venv/lib/python3.13.../django/db/utils.py#x1B[0m:91: in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
#x1B[1m#x1B[31m.venv/lib/python3.13.../db/backends/utils.py#x1B[0m:105: in _execute
    return self.cursor.execute(sql, params)
#x1B[1m#x1B[.../db/postgres/decorators.py#x1B[0m:77: in inner
    raise_the_exception(self.db, e)
#x1B[1m#x1B[.../db/postgres/decorators.py#x1B[0m:75: in inner
    return func(self, *args, **kwargs)
#x1B[1m#x1B[.../db/postgres/decorators.py#x1B[0m:18: in inner
    return func(self, *args, **kwargs)
#x1B[1m#x1B[.../db/postgres/decorators.py#x1B[0m:93: in inner
    raise type(e)(f"{e!r}\nSQL: {sql}").with_traceback(e.__traceback__)
#x1B[1m#x1B[.../db/postgres/decorators.py#x1B[0m:91: in inner
    return func(self, sql, *args, **kwargs)
#x1B[1m#x1B[.../db/postgres/base.py#x1B[0m:84: in execute
    return self.cursor.execute(sql, clean_bad_params(params))
#x1B[1m#x1B[31mE   django.db.utils.IntegrityError: UniqueViolation('duplicate key value violates unique constraint "auth_user_username_key"\nDETAIL:  Key (username)=(admin@localhost) already exists.\n')#x1B[0m
#x1B[1m#x1B[31mE   SQL: INSERT INTO "auth_user" ("password", "last_login", "username", "first_name", "email", "is_staff", "is_active", "is_unclaimed", "is_superuser", "is_managed", "is_sentry_app", "is_password_expired", "last_password_change", "flags", "session_nonce", "date_joined", "last_active", "avatar_type", "avatar_url") VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) RETURNING "auth_user"."id"#x1B[0m

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

Comment on lines +2224 to +2246
def create_alert_rule_detector(
alert_rule: AlertRule | None = None,
detector: Detector | None = None,
**kwargs,
) -> AlertRuleDetector:
if alert_rule is None:
alert_rule = Factories.create_alert_rule()
if detector is None:
detector = Factories.create_detector()
return AlertRuleDetector.objects.create(alert_rule=alert_rule, detector=detector)

@staticmethod
@assume_test_silo_mode(SiloMode.REGION)
def create_alert_rule_workflow(
alert_rule: AlertRule | None = None,
workflow: Workflow | None = None,
**kwargs,
) -> AlertRuleWorkflow:
if alert_rule is None:
alert_rule = Factories.create_alert_rule()
if workflow is None:
workflow = Factories.create_workflow()
return AlertRuleWorkflow.objects.create(alert_rule=alert_rule, workflow=workflow)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just because these are for our migration only, let's add these to a migration base test class instead. (if we wanted to keep these around after the migration, this is 💯)

Comment on lines +663 to +668
def create_alert_rule_detector(self, *args, **kwargs):
return Factories.create_alert_rule_detector(*args, **kwargs)

def create_alert_rule_workflow(self, *args, **kwargs):
return Factories.create_alert_rule_workflow(*args, **kwargs)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment about these not being intended to hav a long shelf life.

Comment on lines +257 to +259
# check workflow-related tables
assert not Workflow.objects.filter(id=self.workflow.id).exists()
assert not AlertRuleWorkflow.objects.filter(id=self.alert_rule_workflow.id).exists()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like it might be a good separate test rather than 1 assertion chunk. The reason for that is if we fail to remove a workflow, we won't know if the detector stuff is deleting correctly or not because this test will stop execution at the first failed assertion.

Comment on lines +261 to +266
# check detector-related tables
assert not Detector.objects.filter(id=self.detector.id).exists()
assert not AlertRuleDetector.objects.filter(id=self.alert_rule_detector.id).exists()
assert not DetectorWorkflow.objects.filter(id=self.detector_workflow.id).exists()
assert not DetectorState.objects.filter(id=self.detector_state.id).exists()
assert not DataSourceDetector.objects.filter(id=self.data_source_detector.id).exists()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like another good logic chunk to pull out as a separate test -- are these all on_cascade deletions?

Comment on lines +268 to +271
# check data condition groups
assert not DataConditionGroup.objects.filter(
id=self.detector_data_condition_group.id
).exists()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this feels like a detector-related table as well, since it should be part of the cascade deletion of a detector

Comment on lines +273 to +274
# check data source
assert not DataSource.objects.filter(id=self.data_source.id).exists()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then a good final test block. Each of these test blocks would be more or less what you have here just split out to different def test_..

for example:

def test_dual_delete_migrate_alert_rule__workflow(self):
        dual_delete_migrated_alert_rule(self.metric_alert)
        with self.tasks():
            run_scheduled_deletions()
            
        assert not Workflow.objects.filter(id=self.workflow.id).exists()
        assert not AlertRuleWorkflow.objects.filter(id=self.alert_rule_workflow.id).exists()
        
def test_dual_delete_migrate_alert_rule__data_sources(self):
        dual_delete_migrated_alert_rule(self.metric_alert)
        with self.tasks():
            run_scheduled_deletions()

        assert not DataSource.objects.filter(id=self.data_source.id).exists()

These tests are a bit repetitive, and if you want to go deeper there's https://docs.pytest.org/en/stable/how-to/parametrize.html -- which would allow us to turn those 4 tests back into a single method. That said, it's not that important and the verbosity can help if we handle these differently in the future.

detector.refresh_from_db()

assert detector.owner_user_id == self.user.id
assert detector.owner_team_id is None

def test_update_metric_alert_config(self):
migrate_alert_rule(self.metric_alert, self.rpc_user)
detector = self.detector
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit; i'd just use self.detector instead of detector later; that way it's clear you're using a variable from the class instance

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Scope: Backend Automatically applied to PRs that change backend components
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants