david raistrick
27b2272ced
fix(storage): neutral queryConstraints honored by both adapters
...
Bug: server adapter accepted queryConstraints (orderBy/limit) but ignored them.
Combat log [orderBy('timestamp','desc'), limit(500)] returned ALL logs unordered
in server mode — unbounded growth. No test caught it: shared contract tested
subscribeCollection with 0 constraints; the firebase-only queryConstraint test
never touched the server adapter. Parity gap.
Fix (Plan A — neutral shape):
- index.js: orderBy()/limit() now NEUTRAL builders returning {__type}. Removed
SDK orderBy/limit re-export.
- firebase.js: subscribeCollection translates neutral -> SDK query constraints.
- server.js: applyConstraints() helper sorts/limits client-side (backend returns
all). Constraints stored per collection, applied on initial fetch + WS change.
- contract.js: added 3 queryConstraint tests (orderBy desc, limit, no-constraints)
to SHARED contract — both adapters now run identical assertions.
- firebase.contract.test.js: removed now-redundant firebase-only constraint
block (covered by shared contract).
Data impact: ZERO. Constraints are query-time only, never stored. Combat log
docs keep timestamp field. No Firebase data migration needed.
208 tests green.
2026-07-04 16:06:28 -04:00
david raistrick
79af61cb8b
match main firebase behavior; fix contract + mismatches
...
Contract rewritten to match main prod truth:
- require id injection in all doc/collection results
- honor setDoc({merge:true}) (main L1624 + 4 activeDisplay sites)
- drop invented bare<->prefixed cross-lookup tests (main never does this)
- add setDoc{merge}, batch set-only/update-only contract coverage
Fix mismatches vs main:
- subscribeCollection hook now forwards queryConstraints (was dropped;
LOG_QUERY sort+limit honored again). Mock onSnapshot honors orderBy+limit.
2 new contract tests prove the chain.
- subscribeDoc/subscribeCollection adapters now forward errCb. App hooks +
DisplayView propagate subscribe errors to UI (match main onSnapshot 3rd arg).
- ws adapter signatures accept queryConstraints + errCb (interface match).
activeDisplay writes match main:
- 5 unguarded sites -> setDoc({merge:true}) (create-if-missing, not updateDoc)
- 3 guarded sites stay updateDoc
Delete memory adapter: third storage system added complexity, zero value.
firebase-mock covers fast tests, ws covers server path. Factory throws on
unknown mode now.
Delete phantom 'Phase A/B' comment in firebase.js header.
Tests updated to assert setDoc{merge} (not updateDoc) for activeDisplay writes.
83/83 green.
2026-07-04 12:22:19 -04:00
david raistrick
9fd0f3ec38
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).
2026-06-29 15:13:03 -04:00
david raistrick
52866784b2
M2: replace shape-specific backend with generic KV doc store (firebase mirror)
...
Backend was endpoint-mapper (POST /api/campaigns ignores path id, etc). Adapter
translation layer brittle, untested, lost doc identity. Generic contract (Layer 2)
test caught 15 bugs immediately.
Rewrite to firebase-mirror KV model:
- server/db.js: docs table (path PK, parent, data JSON). getDoc/setDoc/updateDoc/
deleteDoc/getCollection/batchWrite. parent = path prefix for collection queries.
- server/index.js: generic REST (GET/PUT/PATCH/DELETE /api/doc, GET /api/collection,
POST /api/collection addDoc, POST /api/batch). WS subscribe by path (doc|collection),
broadcast to doc subs at changed path + collection subs at parent path.
- src/storage/ws.js: thin passthrough adapter. norm() strips firebase prefix.
initial value via REST (independent of WS connect), subsequent changes via WS.
- shared/turn.js kept (M4 use). server/handlers.js + server.test.js removed (logic
now in App, backend is dumb KV).
- src/storage/contract.js: flush() bumped to 50ms (WS roundtrip > setTimeout(0)).
Layer 2 test (server/ws-contract.test.js): spins fresh backend per test, runs same
storage contract spec against createWsStorage. Catches adapter translation bugs
that firebase-mock Layer 1 tests cannot.
nanoid v5 ESM breaks jest CJS -> replaced with crypto.randomUUID (Node builtin).
Tests: 114 green (39 shared + 19 ws-contract + 56 frontend).
2026-06-29 13:00:24 -04:00
david raistrick
84dd17e174
test: Firebase mock harness + createCampaign characterization
...
- src/__mocks__/firebase/*: jest manual mocks (app/auth/firestore)
- src/__mocks__/firebase/_mock-db.js: in-memory DB + call recorder
- src/setupTests.js: jest-dom, env stubs, crypto polyfill, DB reset
- src/App.characterization.test.js: createCampaign -> setDoc path/payload locked
- src/storage/contract.js (renamed from .test.js, helper not suite)
21 tests green (memory 19 + createCampaign 2).
2026-06-28 17:59:50 -04:00