feat(20-01): add PATCH /members/:id member-profile update with last-admin guard
- Add isAdmin field to GET /members select and mapped member object (D-02)
- Add updateMemberSchema (displayName optional string, isAdmin optional boolean)
- Register adminRouter.patch('/members/:id') with noEchoHook and requireAdmin (inherited)
- Handler: parsePositiveIntParam id validation (400), existence check (404),
D-03 last-admin guard via COUNT(*) query (409), partial set() update (200)
- Fix Test D: switch to adminId2 for GET after self-demotion (adminId1 no longer admin)
- All 44 tests green including 7 new PATCH/isAdmin tests
This commit is contained in:
@@ -1151,6 +1151,7 @@ describe('PATCH /api/admin/members/:id', () => {
|
||||
it('Test D (self-demotion allowed): with two admins, PATCH { isAdmin: false } returns 200; one admin remains', async () => {
|
||||
const adminId1 = await seedUser('admin-demote-1', true);
|
||||
const adminId2 = await seedUser('admin-demote-2', true);
|
||||
// Log in as adminId1 to perform the self-demotion
|
||||
currentDevUserId = adminId1;
|
||||
const app = await getApp();
|
||||
|
||||
@@ -1159,7 +1160,9 @@ describe('PATCH /api/admin/members/:id', () => {
|
||||
);
|
||||
expect(res.status).toBe(200);
|
||||
|
||||
// Only adminId2 should remain as admin
|
||||
// Switch to adminId2 to verify the outcome — adminId1 is now non-admin
|
||||
// and can no longer call GET /members (would 403).
|
||||
currentDevUserId = adminId2;
|
||||
const getRes = await app.fetch(jsonRequest('GET', '/api/admin/members'));
|
||||
expect(getRes.status).toBe(200);
|
||||
const getBody = (await getRes.json()) as {
|
||||
|
||||
Reference in New Issue
Block a user