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:
@@ -448,6 +448,56 @@ describe('PATCH /api/lists/:id (authorized update)', () => {
|
|||||||
const body = await res.json() as { name: string }
|
const body = await res.json() as { name: string }
|
||||||
expect(body.name).toBe('Sharee Renamed')
|
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)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user