test(04-07): add failing sharee-403 tests for T-04-08 owner-only isShared guard

- T-04-08 test 1: sharee PATCH { isShared: false } must get 403 and
  list_shares row unchanged (currently 200 + shares wiped — bug)
- T-04-08 test 2: sharee PATCH { isShared: true } must get 403 and
  no new shares inserted (currently 200 + shares fan-out — bug)
- Both tests fail now; GREEN once owner-only guard added to lists.ts
This commit is contained in:
Lucas Berger
2026-06-09 14:22:21 -04:00
parent 9b860617c5
commit 931f767922
+50
View File
@@ -448,6 +448,56 @@ describe('PATCH /api/lists/:id (authorized update)', () => {
const body = await res.json() as { name: string }
expect(body.name).toBe('Sharee Renamed')
})
it('T-04-08: sharee sending { isShared: false } gets 403 and list_shares is unchanged', async () => {
const ownerId = await seedUser('t04-08-owner')
const shareeId = await seedUser('t04-08-sharee')
const listId = await seedList(ownerId, 'T04-08 Shared List', true)
await shareList(listId, shareeId)
// Act as sharee
currentDevUserId = shareeId
const app = await getApp()
// Sharee tries to set isShared: false — must be blocked
const res = await app.request(jsonRequest('PATCH', `/api/lists/${listId}`, { isShared: false }))
expect(res.status).toBe(403)
const body = await res.json() as { error: string }
// Response should mention owner/sharing
expect(body.error).toMatch(/owner|shar/i)
// Prove list_shares is untouched — sharee row still exists
const { and, eq } = await import('drizzle-orm')
const shares = await db.select().from(listShares).where(
and(eq(listShares.listId, listId), eq(listShares.userId, shareeId)),
)
expect(shares.length).toBe(1)
})
it('T-04-08: sharee sending { isShared: true } gets 403 and no new shares are inserted', async () => {
const ownerId = await seedUser('t04-08-true-owner')
const shareeId = await seedUser('t04-08-true-sharee')
// Private list (isShared: false) but sharee already has explicit access
const listId = await seedList(ownerId, 'T04-08 Private List', false)
await shareList(listId, shareeId)
// Act as sharee
currentDevUserId = shareeId
const app = await getApp()
// Record share count before the attempted mutation
const { eq } = await import('drizzle-orm')
const beforeShares = await db.select().from(listShares).where(eq(listShares.listId, listId))
const beforeCount = beforeShares.length
// Sharee tries to set isShared: true — must be blocked
const res = await app.request(jsonRequest('PATCH', `/api/lists/${listId}`, { isShared: true }))
expect(res.status).toBe(403)
// Prove no new shares were inserted
const afterShares = await db.select().from(listShares).where(eq(listShares.listId, listId))
expect(afterShares.length).toBe(beforeCount)
})
})
// ---------------------------------------------------------------------------