feat: add caching for backend objects

This commit is contained in:
2026-04-21 19:38:57 -04:00
parent 5b4955f07e
commit 7f1e82acc2
14 changed files with 637 additions and 62 deletions

View File

@@ -393,6 +393,68 @@ describe('open-session window bypasses pre-booking limits', () => {
})
})
// ── Historical booking constraint ────────────────────────────────────────────
describe('historical booking constraint', () => {
const emailMember = `test-hist-member-${Date.now()}@oysqn.test`
const emailAdmin = `test-hist-admin-${Date.now()}@oysqn.test`
let memberUserId: string
let adminUserId: string
let boatId: string
let memberToken: string
let adminToken: string
function pastSlot(): { start_time: string; end_time: string } {
const d = new Date()
d.setUTCDate(d.getUTCDate() - 7)
d.setUTCHours(9, 0, 0, 0)
const e = new Date(d)
e.setUTCHours(12, 30, 0, 0)
return { start_time: d.toISOString(), end_time: e.toISOString() }
}
beforeAll(async () => {
boatId = await createTestBoat()
memberUserId = await createTestUser(emailMember, [], 'member')
adminUserId = await createTestUser(emailAdmin, [], 'admin')
memberToken = await getSessionToken(emailMember)
adminToken = await getSessionToken(emailAdmin)
})
afterAll(async () => {
await adminClient.from('reservations').delete().eq('boat_id', boatId)
await adminClient.from('boats').delete().eq('id', boatId)
await adminClient.auth.admin.deleteUser(memberUserId)
await adminClient.auth.admin.deleteUser(adminUserId)
})
it('rejects a historical booking by a regular member', async () => {
const slot = pastSlot()
const { status, body } = await callCreateReservation(memberToken, { boat_id: boatId, ...slot })
expect(status).toBe(422)
expect(body.error.code).toBe('historical_booking_not_allowed')
})
it('allows a historical booking by an admin', async () => {
const slot = pastSlot()
const { status } = await callCreateReservation(adminToken, { boat_id: boatId, ...slot })
expect(status).toBe(201)
})
it('rejects a historical booking by a skipper (non-admin role)', async () => {
const emailSkipper = `test-hist-skipper-${Date.now()}@oysqn.test`
const skipperId = await createTestUser(emailSkipper, [], 'skipper')
const skipperToken = await getSessionToken(emailSkipper)
try {
const slot = pastSlot()
const { status, body } = await callCreateReservation(skipperToken, { boat_id: boatId, ...slot })
expect(status).toBe(422)
expect(body.error.code).toBe('historical_booking_not_allowed')
} finally {
await adminClient.auth.admin.deleteUser(skipperId)
}
})
})
// ── RBAC visibility: reservation_slots view ───────────────────────────────────
describe('RBAC: reservation_slots view', () => {
const emailOwner = `test-rbac-owner-${Date.now()}@oysqn.test`