test(adapter): multi-step booking lifecycle tests#70
Conversation
Sequenced mock DB builder enables testing methods that make multiple sequential queries: createBooking (service+client+ practitioner+insert), getBooking (4-table join), rescheduleBooking (select+update+getBooking). 36 tests total (up from 28). Covers error paths (service not found, booking not found) and edge cases (new client during booking, missing service fallback).
Greptile SummaryAdds Confidence Score: 5/5Safe to merge — all findings are P2 style suggestions with no correctness impact. The sequenced mock design is correct: the shared selectCall/insertCall closure counters correctly track sequential DB queries across all tested methods. All 8 new tests match the adapter's actual call sequences. The single comment is a documentation nit on an already-passing test. No files require special attention. Important Files Changed
Sequence DiagramsequenceDiagram
participant T as Test
participant A as HomegrownAdapter
participant DB as SequencedMockDb
note over T,DB: createBooking (existing client)
T->>A: createBooking(request)
A->>DB: select → from(services) → where → limit(1)
DB-->>A: [SERVICE_ROW] (seq[0])
A->>DB: select → from(clients) → where(email) → limit(1)
DB-->>A: [CLIENT_ROW] (seq[1])
A->>DB: update(clients).set().where()
DB-->>A: undefined
A->>DB: select → from(practitioners) → where(handle) → limit(1)
DB-->>A: [PRACTITIONER_ROW] (seq[2])
A->>DB: insert(bookings).values().returning()
DB-->>A: [BOOKING_ROW] (ins[0])
A-->>T: Booking domain object
note over T,DB: getBooking (4-table join)
T->>A: getBooking(id)
A->>DB: select → from(bookings) → where → limit(1)
DB-->>A: [BOOKING_ROW] (seq[0])
A->>DB: select → from(services) → where → limit(1)
DB-->>A: [SERVICE_ROW] (seq[1])
A->>DB: select → from(clients) → where → limit(1)
DB-->>A: [CLIENT_ROW] (seq[2])
A->>DB: select → from(practitioners) → where → limit(1)
DB-->>A: [PRACTITIONER_ROW] (seq[3])
A-->>T: Booking with joined data
note over T,DB: rescheduleBooking
T->>A: rescheduleBooking(id, newDatetime)
A->>DB: select → from(bookings) → where → limit(1)
DB-->>A: [BOOKING_ROW] (seq[0])
A->>DB: update(bookings).set().where()
DB-->>A: undefined
A->>A: getBooking(id) [internal, 4 selects seq[1..4]]
A-->>T: Updated Booking
Reviews (1): Last reviewed commit: "test(adapter): add multi-step booking li..." | Re-trigger Greptile |
| it('handles missing service gracefully', async () => { | ||
| const mockDb = createSequencedMockDb([ | ||
| [BOOKING_ROW], // booking exists | ||
| [], // service not found | ||
| [CLIENT_ROW], // client | ||
| ]); | ||
|
|
||
| const adapter = createHomegrownAdapter({ getDb: async () => mockDb }); | ||
| const result = await Effect.runPromise(adapter.getBooking('booking-uuid-1')); | ||
|
|
||
| // Falls back to 'Unknown' for service name, 'USD' for currency | ||
| expect(result.serviceName).toBe('Unknown'); | ||
| expect(result.currency).toBe('USD'); | ||
| }); |
There was a problem hiding this comment.
Undocumented 4th select in sequence
BOOKING_ROW.practitionerId is 'prac-uuid-1' (truthy), so getBooking will always execute the conditional practitioner select — meaning 4 selects fire here, not 3. The 4th call resolves to selectSequence[3] ?? [], returning an empty array, which is handled gracefully (providerName = undefined). The test passes, but a future reader counting the sequence entries will be off by one. Consider either adding a 4th entry or using a BOOKING_ROW variant with practitionerId: null to make the intent explicit.
| it('handles missing service gracefully', async () => { | |
| const mockDb = createSequencedMockDb([ | |
| [BOOKING_ROW], // booking exists | |
| [], // service not found | |
| [CLIENT_ROW], // client | |
| ]); | |
| const adapter = createHomegrownAdapter({ getDb: async () => mockDb }); | |
| const result = await Effect.runPromise(adapter.getBooking('booking-uuid-1')); | |
| // Falls back to 'Unknown' for service name, 'USD' for currency | |
| expect(result.serviceName).toBe('Unknown'); | |
| expect(result.currency).toBe('USD'); | |
| }); | |
| it('handles missing service gracefully', async () => { | |
| const mockDb = createSequencedMockDb([ | |
| [BOOKING_ROW], // booking exists | |
| [], // service not found | |
| [CLIENT_ROW], // client | |
| [], // practitioner (BOOKING_ROW.practitionerId is set; graceful fallback) | |
| ]); |
Summary
createSequencedMockDb) for testing methods with multiple sequential queriescreateBooking(4-step: resolve service → find/create client → get practitioner → insert)getBooking(4-table join: booking + service + client + practitioner)rescheduleBooking(select existing → update → getBooking refresh)Follows up on #69 (merged).
Test plan
Tracker: TIN-104