diff --git a/tests/browser/fast_reset.py b/tests/browser/fast_reset.py index 7374675f..5f2634bc 100644 --- a/tests/browser/fast_reset.py +++ b/tests/browser/fast_reset.py @@ -34,6 +34,8 @@ T = TypeVar("T") # proof, tests/browser/reset_equivalence.py, compares both ways). SEED_ON_LIVE_ENV = "PM_TEST_SEED_ON_LIVE" SCRATCH_SCHEMA = "reset_seed" +# API calls a seed makes that must see the published rows (see _SeedApi). +DEFERRED_CALLS = frozenset({"invalidate_gallery_cache"}) # Linux's shared-memory tmpfs (present in the Playwright image and on runners). MEMORY_ROOT = Path("/dev/shm") # The largest fixtures' scratch copy is about 8 MB (database plus a WAL that @@ -93,13 +95,25 @@ def table_digests(conn: sqlite3.Connection, schema: str = "main") -> dict[str, s class _SeedApi: - """The live API, except that ``db`` is the scratch database.""" + """The live API, except that ``db`` is the scratch database. + + Cache drops the seed asks for (``DEFERRED_CALLS``) are recorded and made + after the publish: dropped earlier, a request in between would cache the + live database's old rows again. + """ def __init__(self, api: Any, db: Any) -> None: object.__setattr__(self, "_api", api) object.__setattr__(self, "db", db) + object.__setattr__(self, "deferred", []) def __getattr__(self, name: str) -> Any: + if name in DEFERRED_CALLS: + + def record(*args: Any, **kwargs: Any) -> None: + self.deferred.append((name, args, kwargs)) + + return record return getattr(self._api, name) def __setattr__(self, name: str, value: Any) -> None: @@ -138,7 +152,8 @@ class ScratchSeeder: with target.using(): live.backup(target) before = table_digests(target) - result = seed(_SeedApi(self._api, scratch)) + target_api = _SeedApi(self._api, scratch) + result = seed(target_api) target = self._scratch_connection(scratch) if target.in_transaction: raise RuntimeError("the seed left a scratch transaction open") @@ -149,6 +164,8 @@ class ScratchSeeder: if live.execute("PRAGMA data_version").fetchone()[0] != version: raise RuntimeError("the live database changed while the seed ran") self._publish(live, scratch.model.db_path, changed, version) + for name, args, kwargs in target_api.deferred: + getattr(self._api, name)(*args, **kwargs) return result def _scratch_database(self): diff --git a/tests/test_browser_fast_reset.py b/tests/test_browser_fast_reset.py index 36b8ca54..ba53a41c 100644 --- a/tests/test_browser_fast_reset.py +++ b/tests/test_browser_fast_reset.py @@ -140,6 +140,36 @@ class TestScratchSeeder(unittest.TestCase): self.assertEqual(self.api.note, "set by the seed") self.assertIs(self.api.db, self.live) + def test_cache_drops_wait_until_the_rows_are_published(self): + seen = [] + + def invalidate_gallery_cache(retire=False): + with self.live.model.get_connection() as conn: + count = conn.execute("SELECT COUNT(*) FROM tags").fetchone()[0] + seen.append((count, retire)) + + self.api.invalidate_gallery_cache = invalidate_gallery_cache + + def seed(api): + reseed(api) + api.invalidate_gallery_cache(retire=True) + self.assertEqual(seen, [], "not dropped while live has the old rows") + + self.seeder.run(seed) + self.assertEqual(seen, [(30, True)]) + + def test_a_failing_seed_drops_no_cache(self): + seen = [] + self.api.invalidate_gallery_cache = lambda: seen.append(True) + + def broken(api): + api.invalidate_gallery_cache() + raise ValueError("seed failed") + + with self.assertRaises(ValueError): + self.seeder.run(broken) + self.assertEqual(seen, []) + def test_a_failing_seed_leaves_live_untouched(self): before = digests(self.live)