|
1 | 1 | from __future__ import annotations |
2 | 2 |
|
| 3 | +import importlib.util |
3 | 4 | import json |
4 | 5 | import re |
5 | 6 | import uuid |
6 | 7 | from copy import deepcopy |
7 | 8 | from pathlib import Path |
8 | | -from typing import ClassVar |
| 9 | +from typing import Any, ClassVar |
9 | 10 | from unittest import mock |
10 | 11 |
|
11 | 12 | from django.core.exceptions import ImproperlyConfigured |
|
30 | 31 | ) |
31 | 32 | from events.models import Event, EventAlias, EventPublicIdSequence |
32 | 33 |
|
| 34 | +ROOT = Path(__file__).resolve().parents[2] |
| 35 | + |
| 36 | + |
| 37 | +def load_migration_function(relative_path: str, function_name: str) -> Any: |
| 38 | + module_spec = importlib.util.spec_from_file_location( |
| 39 | + f"loaded_{Path(relative_path).stem}", ROOT / relative_path |
| 40 | + ) |
| 41 | + assert module_spec is not None and module_spec.loader is not None |
| 42 | + module = importlib.util.module_from_spec(module_spec) |
| 43 | + module_spec.loader.exec_module(module) |
| 44 | + return getattr(module, function_name) |
| 45 | + |
| 46 | + |
| 47 | +class _HistoricalAppsStub: |
| 48 | + def get_model(self, app_label: str, model_name: str) -> Any: |
| 49 | + if (app_label, model_name) != ("events", "Event"): |
| 50 | + raise AssertionError("unexpected historical model requested") |
| 51 | + return Event |
| 52 | + |
33 | 53 |
|
34 | 54 | class EventIdentityManifestTests(TestCase): |
35 | 55 | def test_checked_manifest_and_database_freeze_all_numeric_mappings_and_aliases(self) -> None: |
@@ -111,6 +131,41 @@ def test_unavailable_database_serves_the_manifest_identity_snapshot(self) -> Non |
111 | 131 | expected_paths, |
112 | 132 | ) |
113 | 133 |
|
| 134 | + def test_collation_divergent_public_ids_realign_to_the_manifest(self) -> None: |
| 135 | + align = load_migration_function( |
| 136 | + "events/migrations/0008_align_public_event_ids_to_manifest.py", |
| 137 | + "align_public_ids_to_manifest", |
| 138 | + ) |
| 139 | + manifest = load_identity_manifest() |
| 140 | + expected = {str(item.id): item.public_id for item in manifest.events} |
| 141 | + |
| 142 | + first, second = manifest.events[11], manifest.events[12] |
| 143 | + Event.objects.filter(id=first.id).update(public_id=100_000) |
| 144 | + Event.objects.filter(id=second.id).update(public_id=first.public_id) |
| 145 | + Event.objects.filter(id=first.id).update(public_id=second.public_id) |
| 146 | + with self.assertRaisesMessage( |
| 147 | + ImproperlyConfigured, |
| 148 | + "Public Event UUID/public-ID mapping is incomplete", |
| 149 | + ): |
| 150 | + public_projection() |
| 151 | + |
| 152 | + align(_HistoricalAppsStub(), None) |
| 153 | + |
| 154 | + self.assertEqual( |
| 155 | + dict(Event.objects.values_list("id", "public_id")), |
| 156 | + {uuid.UUID(key): value for key, value in expected.items()}, |
| 157 | + ) |
| 158 | + projection = public_projection() |
| 159 | + self.assertEqual( |
| 160 | + {event["public_path"] for event in projection["events"]}, |
| 161 | + {item.canonical_path for item in manifest.events}, |
| 162 | + ) |
| 163 | + align(_HistoricalAppsStub(), None) |
| 164 | + self.assertEqual( |
| 165 | + dict(Event.objects.values_list("id", "public_id")), |
| 166 | + {uuid.UUID(key): value for key, value in expected.items()}, |
| 167 | + ) |
| 168 | + |
114 | 169 | def test_manifest_import_replay_is_byte_stable_and_a_preflight_noop(self) -> None: |
115 | 170 | before = tuple(Event.objects.order_by("id").values_list("id", "public_id", "slug")) |
116 | 171 | first = import_identity_manifest(dry_run=True) |
|
0 commit comments