M3: fix path-shape drift via adapter contract + identity tests
Root cause (HAR-diagnosed): replay script wrote firebase-prefixed paths via raw REST, bypassing adapter norm(). Two path roots coexisted in db: bare 'campaigns/X' (adapter writes, from App) prefixed 'artifacts/.../campaigns/X' (replay raw writes) Adapter read bare, missed prefixed. UI showed stale test1 (legit manual UI write, not wiped) but replay campaigns invisible. A. replay-combat.js: use createWsStorage adapter instead of raw fetch. Same contract boundary as App. norm() runs on all paths. Can't drift. Mirror App.js getPath locally for path construction. B. contract.js: 4 new identity tests (setDoc prefixed -> getCollection bare, setDoc prefixed -> getDoc bare, setDoc prefixed -> getDoc prefixed, setDoc bare -> getCollection prefixed). Run against every impl (memory, ws). memory.js lacked norm() -> RED first, now GREEN after adding norm. C. db moved out of /tmp to ./data/tracker.sqlite (gitignored). Never tmp. Tests: 124 green (39 shared + 23 ws-contract + 62 FE).
This commit is contained in:
@@ -89,6 +89,38 @@ function runStorageContract(name, factory) {
|
||||
});
|
||||
});
|
||||
|
||||
describe('firebase-prefixed path identity', () => {
|
||||
// App passes firebase-prefixed paths (artifacts/{APP_ID}/public/data/...).
|
||||
// Adapter must normalize internally so write+read at prefixed path round-trips
|
||||
// AND collection queries at bare canonical path find prefixed-written docs.
|
||||
// Catches replay-script bug (wrote prefixed, adapter reads bare, missed).
|
||||
const PREFIX = 'artifacts/test-app/public/data';
|
||||
|
||||
test('setDoc prefixed then getCollection bare finds it', async () => {
|
||||
await storage.setDoc(`${PREFIX}/campaigns/c1`, { name: 'P1' });
|
||||
const docs = await storage.getCollection('campaigns');
|
||||
expect(docs.some(d => d.name === 'P1')).toBe(true);
|
||||
});
|
||||
|
||||
test('setDoc prefixed then getDoc same prefixed path returns it', async () => {
|
||||
await storage.setDoc(`${PREFIX}/campaigns/c2`, { name: 'P2' });
|
||||
const doc = await storage.getDoc(`${PREFIX}/campaigns/c2`);
|
||||
expect(doc).toEqual({ name: 'P2' });
|
||||
});
|
||||
|
||||
test('setDoc prefixed then getDoc bare path returns it', async () => {
|
||||
await storage.setDoc(`${PREFIX}/campaigns/c3`, { name: 'P3' });
|
||||
const doc = await storage.getDoc('campaigns/c3');
|
||||
expect(doc).toEqual({ name: 'P3' });
|
||||
});
|
||||
|
||||
test('setDoc bare then getCollection prefixed finds it', async () => {
|
||||
await storage.setDoc('campaigns/c4', { name: 'P4' });
|
||||
const docs = await storage.getCollection(`${PREFIX}/campaigns`);
|
||||
expect(docs.some(d => d.name === 'P4')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getCollection', () => {
|
||||
test('returns immediate child docs only (not nested)', async () => {
|
||||
await storage.setDoc('campaigns/a', { name: 'A' });
|
||||
|
||||
Reference in New Issue
Block a user