- Add calendarEvents.hasRrule boolean + idx_calendar_events_has_rrule index (Phase 2 pre-filter) - Add calendars.isShared boolean for shared-family calendar identification - Create apps/pwa/vitest.config.ts with jsdom environment - Add vitest, @testing-library/react, jsdom, @testing-library/jest-dom to PWA devDependencies - Add "test": "vitest run" script to apps/pwa/package.json - Create three ICS fixtures: weekly-dst.ics (DST spanning), allday-birthday.ics, exdate-series.ics - Create RED test stub expand.test.ts with concrete DST wall-clock assertions (10:00 local both sides of March 2026 boundary) - Create RED test stub events.test.ts with 400 validation and color/isShared field contracts - Create RED test stub hydrateEvents.test.ts with Temporal type and calendarId routing contracts (shared→"shared", personal→String(ownerUserId)) - Create RED test stub calendarConfig.test.ts with firstDayOfWeek 0→7 translation contract
69 lines
2.6 KiB
TypeScript
69 lines
2.6 KiB
TypeScript
/**
|
|
* RED test stubs for GET /api/events — Wave 0 state.
|
|
*
|
|
* These tests encode the contract for the evolved windowed /api/events route.
|
|
* They reference the current events route which does not yet support windowed queries,
|
|
* color joins, or the isShared flag — all tests are expected to fail (RED) until Plan 02.
|
|
*
|
|
* Contracts locked here:
|
|
* 1. Missing/malformed start or end params → 400 (input validation guard)
|
|
* 2. Valid window returns occurrences each with a color field and isShared flag
|
|
*/
|
|
|
|
import { describe, it, expect, vi } from 'vitest'
|
|
|
|
// Mock DB to avoid real DB connections in unit tests
|
|
vi.mock('../../src/db/client.js', () => ({
|
|
db: {
|
|
select: vi.fn().mockReturnValue({
|
|
from: vi.fn().mockReturnValue({
|
|
innerJoin: vi.fn().mockReturnValue({
|
|
innerJoin: vi.fn().mockReturnValue({
|
|
where: vi.fn().mockResolvedValue([]),
|
|
}),
|
|
}),
|
|
where: vi.fn().mockResolvedValue([]),
|
|
}),
|
|
}),
|
|
},
|
|
}))
|
|
|
|
describe('GET /api/events', () => {
|
|
|
|
it('returns 400 when start param is missing', async () => {
|
|
const { app } = await import('../../src/index.js')
|
|
const res = await app.request('/api/events?end=2026-07-01')
|
|
expect(res.status).toBe(400)
|
|
})
|
|
|
|
it('returns 400 when end param is missing', async () => {
|
|
const { app } = await import('../../src/index.js')
|
|
const res = await app.request('/api/events?start=2026-06-01')
|
|
expect(res.status).toBe(400)
|
|
})
|
|
|
|
it('returns 400 when start param is malformed (not YYYY-MM-DD)', async () => {
|
|
const { app } = await import('../../src/index.js')
|
|
const res = await app.request('/api/events?start=not-a-date&end=2026-07-01')
|
|
expect(res.status).toBe(400)
|
|
})
|
|
|
|
it('returns occurrences with color and isShared fields for valid window', async () => {
|
|
const { app } = await import('../../src/index.js')
|
|
const res = await app.request('/api/events?start=2026-06-01&end=2026-07-01')
|
|
// Route not yet evolved — currently returns raw events without windowing or color join.
|
|
// This test will pass once Plan 02 implements the windowed query + expansion.
|
|
expect(res.status).toBe(200)
|
|
const body = await res.json() as { occurrences: Array<{ color: string; isShared: boolean }> }
|
|
expect(body).toHaveProperty('occurrences')
|
|
expect(Array.isArray(body.occurrences)).toBe(true)
|
|
// Each occurrence must carry color + isShared (may be empty array if DB is mocked empty)
|
|
for (const occ of body.occurrences) {
|
|
expect(occ).toHaveProperty('color')
|
|
expect(typeof occ.color).toBe('string')
|
|
expect(occ).toHaveProperty('isShared')
|
|
expect(typeof occ.isShared).toBe('boolean')
|
|
}
|
|
})
|
|
})
|