From 1b76b8bf2a002f7074da34856bc2609e2a7dd700 Mon Sep 17 00:00:00 2001 From: Jannis Braun <151788261+TheZwiss@users.noreply.github.com> Date: Tue, 28 Apr 2026 20:46:24 +0200 Subject: [PATCH] test(invites): tighten check-invite assertions to byte-identical responses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per quality review: replace per-property assertions with toEqual() object-equality on the invalid-response bodies. Locks the enumeration- shield contract — revoked/unknown/malformed/missing must all return the SAME body, not just bodies that happen to satisfy individual assertions. --- packages/server/src/routes/auth.test.ts | 41 ++++++++++--------------- 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/packages/server/src/routes/auth.test.ts b/packages/server/src/routes/auth.test.ts index 05f31bd5..99adbdab 100644 --- a/packages/server/src/routes/auth.test.ts +++ b/packages/server/src/routes/auth.test.ts @@ -160,10 +160,10 @@ describe('GET /api/auth/check-invite', () => { url: `/api/auth/check-invite?token=${token}`, }); expect(res.statusCode).toBe(200); - const body = res.json(); - expect(body.valid).toBe(false); - expect(body.reason).toBe('invalid'); - expect(body.name).toBeUndefined(); + // toEqual locks the byte-identical-response contract: the enumeration + // shield depends on revoked/unknown/malformed all returning the SAME + // body, not just bodies that happen to satisfy individual assertions. + expect(res.json()).toEqual({ valid: false, reason: 'invalid' }); }); it("returns valid: false, reason: 'invalid' for unknown token", async () => { @@ -174,10 +174,7 @@ describe('GET /api/auth/check-invite', () => { url: `/api/auth/check-invite?token=${token}`, }); expect(res.statusCode).toBe(200); - const body = res.json(); - expect(body.valid).toBe(false); - expect(body.reason).toBe('invalid'); - expect(body.name).toBeUndefined(); + expect(res.json()).toEqual({ valid: false, reason: 'invalid' }); }); it("returns valid: false, reason: 'invalid' for malformed token", async () => { @@ -186,27 +183,23 @@ describe('GET /api/auth/check-invite', () => { url: '/api/auth/check-invite?token=tooshort', }); expect(res.statusCode).toBe(200); - const body = res.json(); - expect(body.valid).toBe(false); - expect(body.reason).toBe('invalid'); - expect(body.name).toBeUndefined(); + expect(res.json()).toEqual({ valid: false, reason: 'invalid' }); }); - it('does not include name field on invalid responses', async () => { - // Sweep across all invalid permutations to assert the absence-of-leak - // contract once for the whole endpoint, not just per-status. + it('returns byte-identical bodies across all invalid permutations', async () => { + // The enumeration shield depends on revoked/unknown/malformed/missing + // all returning the SAME body. Object equality (toEqual) catches any + // future code path that adds an extra field on one branch but not others. const cases = [ - { url: '/api/auth/check-invite' }, - { url: '/api/auth/check-invite?token=' }, - { url: '/api/auth/check-invite?token=tooshort' }, - { url: `/api/auth/check-invite?token=${'Z'.repeat(22)}` }, + '/api/auth/check-invite', + '/api/auth/check-invite?token=', + '/api/auth/check-invite?token=tooshort', + `/api/auth/check-invite?token=${'Z'.repeat(22)}`, ]; - for (const c of cases) { - const res = await app.inject({ method: 'GET', url: c.url }); + for (const url of cases) { + const res = await app.inject({ method: 'GET', url }); expect(res.statusCode).toBe(200); - const body = res.json(); - expect(body.valid).toBe(false); - expect(body).not.toHaveProperty('name'); + expect(res.json()).toEqual({ valid: false, reason: 'invalid' }); } }); });