Milestone v1.0: FamilySync MVP #1

Merged
luckberg merged 376 commits from gsd/v1.0-milestone into main 2026-06-10 17:39:19 -04:00
2 changed files with 39 additions and 12 deletions
Showing only changes of commit 7a48659cae - Show all commits
+9 -1
View File
@@ -139,7 +139,15 @@ function wireMockChain() {
return { where: vi.fn().mockResolvedValue([FAKE_CRED_ROW]) } return { where: vi.fn().mockResolvedValue([FAKE_CRED_ROW]) }
} }
if (tableName === 'calendar_events') { if (tableName === 'calendar_events') {
return { where: mockWhereCalEvents } // CR-02: the freshest-etag re-read now scopes to the writing member's calendar:
// .from(calendarEvents).innerJoin(calendars, ...).where(...).limit(1)
// mockWhereCalEvents stays the awaited terminal (returned by .limit) so existing
// mockWhereCalEvents.mockResolvedValue([{ etag }]) overrides still drive the etag.
return {
innerJoin: vi.fn().mockReturnValue({
where: vi.fn().mockReturnValue({ limit: mockWhereCalEvents }),
}),
}
} }
return { where: mockWherePending } return { where: mockWherePending }
}) })
+30 -11
View File
@@ -385,9 +385,14 @@ describe('PATCH /api/events/:uid/edit', () => {
userId: 1, userId: 1,
}, },
] ]
// After BUG 1 fix, the edit lookup uses .from(calendarEvents).innerJoin(calendars, ...).where(...) // After BUG 1 + CR-01 fix, the edit lookup uses
// Wire mockFromFn to expose innerJoin → where so the handler resolves mockDbRows. // .from(calendarEvents).innerJoin(calendars, ...).where(...).orderBy(...).limit(1).
const mockInnerJoinWhere = vi.fn().mockImplementation(() => Promise.resolve(mockDbRows)) // Wire mockFromFn to expose innerJoin → where → orderBy → limit so the handler resolves mockDbRows.
const mockInnerJoinWhere = vi.fn().mockReturnValue({
orderBy: vi.fn().mockReturnValue({
limit: vi.fn().mockImplementation(() => Promise.resolve(mockDbRows)),
}),
})
const mockInnerJoin = vi.fn().mockReturnValue({ where: mockInnerJoinWhere }) const mockInnerJoin = vi.fn().mockReturnValue({ where: mockInnerJoinWhere })
mockFromFn.mockReturnValue({ innerJoin: mockInnerJoin }) mockFromFn.mockReturnValue({ innerJoin: mockInnerJoin })
mockSelectFn.mockReturnValue({ from: mockFromFn }) mockSelectFn.mockReturnValue({ from: mockFromFn })
@@ -426,9 +431,14 @@ describe('DELETE /api/events/:uid', () => {
userId: 1, userId: 1,
}, },
] ]
// After BUG 1 fix, the delete lookup uses .from(calendarEvents).innerJoin(calendars, ...).where(...) // After BUG 1 + CR-01 fix, the delete lookup uses
// Wire mockFromFn to expose innerJoin → where so the handler resolves mockDbRows. // .from(calendarEvents).innerJoin(calendars, ...).where(...).orderBy(...).limit(1).
const mockInnerJoinWhere = vi.fn().mockImplementation(() => Promise.resolve(mockDbRows)) // Wire mockFromFn to expose innerJoin → where → orderBy → limit so the handler resolves mockDbRows.
const mockInnerJoinWhere = vi.fn().mockReturnValue({
orderBy: vi.fn().mockReturnValue({
limit: vi.fn().mockImplementation(() => Promise.resolve(mockDbRows)),
}),
})
const mockInnerJoin = vi.fn().mockReturnValue({ where: mockInnerJoinWhere }) const mockInnerJoin = vi.fn().mockReturnValue({ where: mockInnerJoinWhere })
mockFromFn.mockReturnValue({ innerJoin: mockInnerJoin }) mockFromFn.mockReturnValue({ innerJoin: mockInnerJoin })
mockSelectFn.mockReturnValue({ from: mockFromFn }) mockSelectFn.mockReturnValue({ from: mockFromFn })
@@ -543,8 +553,12 @@ describe('CR-01: canonical client payload (title/start/end) accepted by server',
userId: 1, userId: 1,
}, },
] ]
// Edit lookup uses innerJoin after BUG 1 fix // Edit lookup uses innerJoin → where → orderBy → limit after BUG 1 + CR-01 fix
const mockInnerJoinWhere = vi.fn().mockImplementation(() => Promise.resolve(mockDbRows)) const mockInnerJoinWhere = vi.fn().mockReturnValue({
orderBy: vi.fn().mockReturnValue({
limit: vi.fn().mockImplementation(() => Promise.resolve(mockDbRows)),
}),
})
const mockInnerJoin = vi.fn().mockReturnValue({ where: mockInnerJoinWhere }) const mockInnerJoin = vi.fn().mockReturnValue({ where: mockInnerJoinWhere })
mockFromFn.mockReturnValue({ innerJoin: mockInnerJoin }) mockFromFn.mockReturnValue({ innerJoin: mockInnerJoin })
mockSelectFn.mockReturnValue({ from: mockFromFn }) mockSelectFn.mockReturnValue({ from: mockFromFn })
@@ -674,9 +688,14 @@ describe('regression: edit/delete lookups join calendars (BUG 1)', () => {
beforeEach(() => { beforeEach(() => {
mockDbRows = [seededRow] mockDbRows = [seededRow]
// Wire from() → innerJoin() → where(); the innerJoin spy proves the handler // Wire from() → innerJoin() → where() → orderBy() → limit(); the innerJoin spy
// routes through the join rather than calling .where() directly on from(). // proves the handler routes through the join rather than calling .where() directly
const innerJoinWhere = vi.fn().mockImplementation(() => Promise.resolve(mockDbRows)) // on from(). The CR-01 fix appends .orderBy(...).limit(1) after .where(...).
const innerJoinWhere = vi.fn().mockReturnValue({
orderBy: vi.fn().mockReturnValue({
limit: vi.fn().mockImplementation(() => Promise.resolve(mockDbRows)),
}),
})
innerJoinSpy = vi.fn().mockReturnValue({ where: innerJoinWhere }) innerJoinSpy = vi.fn().mockReturnValue({ where: innerJoinWhere })
mockFromFn.mockReturnValue({ innerJoin: innerJoinSpy }) mockFromFn.mockReturnValue({ innerJoin: innerJoinSpy })
mockSelectFn.mockReturnValue({ from: mockFromFn }) mockSelectFn.mockReturnValue({ from: mockFromFn })