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.
This commit is contained in:
@@ -19,7 +19,11 @@ export function limit(n) { return { __type: 'limit', n }; }
|
||||
// writes
|
||||
export async function setDoc(docRef, data, opts) {
|
||||
recordCall({ fn: 'setDoc', path: docRef.path, data: clone(data), opts: opts || null });
|
||||
MOCK_DB.set(docRef.path, clone(data));
|
||||
if (opts && opts.merge) {
|
||||
MOCK_DB.merge(docRef.path, clone(data));
|
||||
} else {
|
||||
MOCK_DB.set(docRef.path, clone(data));
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
export async function updateDoc(docRef, patch) {
|
||||
@@ -70,6 +74,7 @@ export async function getDocs(collRefOrQuery) {
|
||||
// realtime — emit from mock DB, capture unsub
|
||||
export function onSnapshot(refOrQuery, onSuccess, onError) {
|
||||
const path = refOrQuery.path || (refOrQuery.ref && refOrQuery.ref.path);
|
||||
const constraints = refOrQuery.constraints || [];
|
||||
// fire immediately with current state
|
||||
const emit = () => {
|
||||
if (refOrQuery.__ref && refOrQuery.path && path.split('/').length % 2 === 0) {
|
||||
@@ -80,7 +85,8 @@ export function onSnapshot(refOrQuery, onSuccess, onError) {
|
||||
data: () => data,
|
||||
});
|
||||
} else {
|
||||
const docs = MOCK_DB.collection(path);
|
||||
let docs = MOCK_DB.collection(path);
|
||||
docs = applyConstraints(docs, constraints);
|
||||
onSuccess({ docs: docs.map(d => ({ id: d.id, data: () => d.data })) });
|
||||
}
|
||||
};
|
||||
@@ -90,6 +96,27 @@ export function onSnapshot(refOrQuery, onSuccess, onError) {
|
||||
return unsub;
|
||||
}
|
||||
|
||||
// Apply Firestore-style query constraints (orderBy desc/asc, limit) to mock docs.
|
||||
// Mirrors real SDK semantics enough for contract tests. Only orderBy + limit
|
||||
// supported (App's LOG_QUERY uses exactly these).
|
||||
function applyConstraints(docs, constraints) {
|
||||
let out = [...docs];
|
||||
for (const c of constraints) {
|
||||
if (c.__type === 'orderBy') {
|
||||
out.sort((a, b) => {
|
||||
const av = a.data[c.field];
|
||||
const bv = b.data[c.field];
|
||||
if (av === bv) return 0;
|
||||
const cmp = av > bv ? 1 : -1;
|
||||
return c.dir === 'desc' ? -cmp : cmp;
|
||||
});
|
||||
} else if (c.__type === 'limit') {
|
||||
out = out.slice(0, c.n);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function clone(v) {
|
||||
if (v === null || v === undefined) return v;
|
||||
return JSON.parse(JSON.stringify(v));
|
||||
|
||||
Reference in New Issue
Block a user