feat(04-03): implement listsRouter POST/GET/PATCH/DELETE /api/lists (LIST-01)
- GET /: scoped access (owner + list_shares); activeCount/doneCount per list - POST /: auto-populates list_shares for all other members when isShared=true (D-01/D-02) - PATCH /🆔 rename + isShared toggle; reconciles list_shares on visibility change - DELETE /🆔 owner-only; cascade handles items/shares via FK onDelete cascade - resolveUserId helper copied verbatim from events.ts per project convention - zod createListSchema (name 1..255, isShared default true) + patchListSchema - T-04-02 / T-04-05 / T-04-07 / T-04-08 mitigations applied - listsRouter mounted at /api/lists in index.ts (after sseRouter) - Plan 06 SSE seam comments left at every mutation handler - [Rule 1 - Fix] zValidator returns 400 (not 422); tests corrected to match convention - All 23 tests green; full API suite 140 passed no regressions
This commit is contained in:
@@ -237,7 +237,7 @@ describe('POST /api/lists — create list (LIST-01, D-01)', () => {
|
||||
expect(body.isShared).toBe(true)
|
||||
})
|
||||
|
||||
it('rejects a name longer than 255 characters with 422', async () => {
|
||||
it('rejects a name longer than 255 characters with 400 (zod validation)', async () => {
|
||||
const userId = await seedUser('post-long-name')
|
||||
currentDevUserId = userId
|
||||
|
||||
@@ -245,10 +245,11 @@ describe('POST /api/lists — create list (LIST-01, D-01)', () => {
|
||||
const res = await app.request(
|
||||
jsonRequest('POST', '/api/lists', { name: 'a'.repeat(256), isShared: true }),
|
||||
)
|
||||
expect(res.status).toBe(422)
|
||||
// @hono/zod-validator returns 400 on schema violations (matches events.ts convention)
|
||||
expect(res.status).toBe(400)
|
||||
})
|
||||
|
||||
it('rejects an empty name with 422', async () => {
|
||||
it('rejects an empty name with 400 (zod validation)', async () => {
|
||||
const userId = await seedUser('post-empty-name')
|
||||
currentDevUserId = userId
|
||||
|
||||
@@ -256,7 +257,7 @@ describe('POST /api/lists — create list (LIST-01, D-01)', () => {
|
||||
const res = await app.request(
|
||||
jsonRequest('POST', '/api/lists', { name: '', isShared: true }),
|
||||
)
|
||||
expect(res.status).toBe(422)
|
||||
expect(res.status).toBe(400)
|
||||
})
|
||||
|
||||
it('returns 401 when no session is set', async () => {
|
||||
@@ -420,7 +421,8 @@ describe('PATCH /api/lists/:id (authorized update)', () => {
|
||||
|
||||
const app = await getApp()
|
||||
const res = await app.request(jsonRequest('PATCH', `/api/lists/${listId}`, {}))
|
||||
expect(res.status).toBe(422)
|
||||
// @hono/zod-validator returns 400 on schema violations (matches events.ts convention)
|
||||
expect(res.status).toBe(400)
|
||||
})
|
||||
|
||||
it('zod rejects a name longer than 255 characters (T-04-07)', async () => {
|
||||
@@ -430,7 +432,7 @@ describe('PATCH /api/lists/:id (authorized update)', () => {
|
||||
|
||||
const app = await getApp()
|
||||
const res = await app.request(jsonRequest('PATCH', `/api/lists/${listId}`, { name: 'a'.repeat(256) }))
|
||||
expect(res.status).toBe(422)
|
||||
expect(res.status).toBe(400)
|
||||
})
|
||||
|
||||
it('sharee can rename a shared list they have access to', async () => {
|
||||
|
||||
Reference in New Issue
Block a user