mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
fix(admin): an invite for a trip that does not exist answers 404 instead of an empty 201
createInvite returned { error: 'Trip not found', status: 404 } and the admin controller never looked at it: POST /api/admin/invites with an unknown trip_id answered 201 with an empty body and wrote an admin.invite_create audit row for resource 'undefined'. The service throws the DomainError now, so the route answers 404 { error: 'Trip not found' } and writes neither the invite nor the audit row.
This commit is contained in:
@@ -16,7 +16,7 @@
|
||||
"no-misleading-character-class": 1
|
||||
},
|
||||
"tests": {
|
||||
"@typescript-eslint/no-explicit-any": 1569,
|
||||
"@typescript-eslint/no-explicit-any": 1567,
|
||||
"@typescript-eslint/no-non-null-assertion": 1757,
|
||||
"@typescript-eslint/no-require-imports": 25,
|
||||
"@typescript-eslint/no-unsafe-function-type": 7,
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
"src/nest/auth/auth.service.ts": 5,
|
||||
"src/nest/auth/passkey.controller.ts": 1,
|
||||
"src/nest/auth/passkey.service.ts": 2,
|
||||
"src/nest/auth/registration-invites.service.ts": 1,
|
||||
"src/nest/backup/backup-archive.ts": 8,
|
||||
"src/nest/backup/backup.impl.ts": 1,
|
||||
"src/nest/collab/collab.controller.ts": 7,
|
||||
|
||||
@@ -70,8 +70,9 @@ export class RegistrationInvitesService {
|
||||
const parsed = Number.parseInt(String(data.trip_id));
|
||||
if (!Number.isInteger(parsed) || !(await this.trips.existsById(parsed))) {
|
||||
// Used to bind null silently, handing back a plain registration invite
|
||||
// the admin never asked for.
|
||||
return { error: 'Trip not found', status: 404 };
|
||||
// the admin never asked for. Thrown, so the controller writes neither
|
||||
// the invite nor its audit row and the admin sees the 404.
|
||||
throw new DomainError(404, 'Trip not found');
|
||||
}
|
||||
tripId = parsed;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import { resetTestDb, resetRateLimits } from '../helpers/test-db';
|
||||
import type { FactoryOrm } from '../helpers/factories/context';
|
||||
import { createUser, createAdmin, createInviteToken, createTrip, createBudgetItem, createJourney, createJourneyEntry, addJourneyContributor, addTripPhoto, createCategory, createTag, createTodoItem, createMcpToken, createBucketListItem, createVisitedCountry, createCollabNote, addTripMember } from '../helpers/factories';
|
||||
import { authCookie } from '../helpers/auth';
|
||||
import { findRow, insertRow, insertRowIgnoringConflict, updateRows } from '../helpers/factories/rows';
|
||||
import { countRows, findRow, insertRow, insertRowIgnoringConflict, updateRows } from '../helpers/factories/rows';
|
||||
import { makeShareToken } from '../helpers/factories/trips';
|
||||
import { makeVacayPlan } from '../helpers/factories/vacay';
|
||||
import { readUser } from '../helpers/factories/users';
|
||||
@@ -548,6 +548,19 @@ describe('Invite token management', () => {
|
||||
expect(res.body.invite.token).toBeDefined();
|
||||
});
|
||||
|
||||
it('ADMIN-013b — POST /admin/invites with a trip that does not exist answers 404 and writes nothing', async () => {
|
||||
const { user: admin } = createAdmin(testDb);
|
||||
|
||||
const res = await request(app)
|
||||
.post('/api/admin/invites')
|
||||
.set('Cookie', authCookie(admin.id))
|
||||
.send({ max_uses: 1, trip_id: 99999 });
|
||||
expect(res.status).toBe(404);
|
||||
expect(res.body).toEqual({ error: 'Trip not found' });
|
||||
expect(await countRows(orm, InviteTokens, {})).toBe(0);
|
||||
expect(await countRows(orm, AuditLog, { action: 'admin.invite_create' })).toBe(0);
|
||||
});
|
||||
|
||||
it('ADMIN-014 — DELETE /admin/invites/:id removes invite', async () => {
|
||||
const { user: admin } = createAdmin(testDb);
|
||||
const invite = createInviteToken(testDb, { created_by: admin.id });
|
||||
|
||||
@@ -84,8 +84,8 @@ describe('Invites', () => {
|
||||
describe('Invites — trip binding', () => {
|
||||
it('ADMIN-SVC-073 — createInvite 404s on a trip_id that does not resolve', async () => {
|
||||
const { user: admin } = createAdmin(testDb);
|
||||
expect(await svc.createInvite(admin.id, { trip_id: 99999 }) as any).toMatchObject({ status: 404, error: 'Trip not found' });
|
||||
expect(await svc.createInvite(admin.id, { trip_id: 'not-a-number' }) as any).toMatchObject({ status: 404 });
|
||||
await expect(svc.createInvite(admin.id, { trip_id: 99999 })).rejects.toMatchObject({ status: 404, publicMessage: 'Trip not found' });
|
||||
await expect(svc.createInvite(admin.id, { trip_id: 'not-a-number' })).rejects.toMatchObject({ status: 404 });
|
||||
expect(await countRows(await sharedTestOrm(testDb), InviteTokens)).toBe(0);
|
||||
// An absent/blank binding is still a plain registration invite.
|
||||
expect((await svc.createInvite(admin.id, {}) as any).tripId).toBeNull();
|
||||
|
||||
Reference in New Issue
Block a user