coulomb-social/tests/test_storage_migration_lifecycle.py
tegwick 789e69902d
Some checks failed
Use-case regression gate / usecase-regressions (push) Failing after 7m5s
Harden original-date migration and verify Coulomb recovery admission
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a17f16-d99c-7ea3-95f0-8212897fba6b
2026-10-27 23:51:27 +01:00

62 lines
2.8 KiB
Python

"""Exercise the real forward/reverse migration, including a refused legacy DB."""
import pytest
from django.db import IntegrityError, connection, transaction
from django.db.migrations.executor import MigrationExecutor
from coulomb_social.apps.spaces.models import Space, SpaceMembership
from tests.test_space_permissions import make_member
OLD = ("spaces", "0002_space_visual")
NEW = ("spaces", "0003_space_slug_global_uniq")
def migrate(target):
executor = MigrationExecutor(connection)
executor.migrate([target])
return executor
@pytest.mark.django_db(transaction=True)
def test_forward_refusal_rollback_and_reapply_preserve_rows():
owner = make_member("migration-owner")
space = Space.objects.create(
tenant_id=owner.tenant_id, slug="retained", title="Retained", created_by=owner,
)
SpaceMembership.objects.create(space=space, member=owner, role="owner")
before_spaces = list(Space.objects.order_by("pk").values())
before_grants = list(SpaceMembership.objects.order_by("pk").values())
duplicate = None
try:
migrate(OLD)
assert list(Space.objects.order_by("pk").values()) == before_spaces
assert list(SpaceMembership.objects.order_by("pk").values()) == before_grants
# The tenant constraint must survive rollback of the global constraint.
with pytest.raises(IntegrityError), transaction.atomic():
Space.objects.create(tenant_id=space.tenant_id, slug=space.slug, title="Denied")
duplicate = Space.objects.create(
tenant_id="tenant:legacy", slug=space.slug, title="Legacy collision", is_active=False,
)
collided = list(Space.objects.order_by("pk").values())
with pytest.raises(RuntimeError, match="does not rename, merge, or delete"):
migrate(NEW)
assert list(Space.objects.order_by("pk").values()) == collided
assert list(SpaceMembership.objects.order_by("pk").values()) == before_grants
assert NEW not in MigrationExecutor(connection).loader.applied_migrations
# Reconciliation here removes only the test's extra synthetic row.
duplicate.delete()
duplicate = None
migrate(NEW)
with pytest.raises(IntegrityError), transaction.atomic():
Space.objects.create(tenant_id="tenant:other", slug=space.slug, title="Denied")
assert list(Space.objects.order_by("pk").values()) == before_spaces
assert list(SpaceMembership.objects.order_by("pk").values()) == before_grants
assert NEW in MigrationExecutor(connection).loader.applied_migrations
migrate(OLD)
migrate(NEW)
assert list(Space.objects.order_by("pk").values()) == before_spaces
assert list(SpaceMembership.objects.order_by("pk").values()) == before_grants
finally:
if duplicate is not None:
duplicate.delete()
migrate(NEW)