import { describe, it, expect } from 'vitest'; import { listHandler, createHandler, POST } from '@/app/api/v1/yachts/route'; import { withPermission } from '@/lib/api/helpers'; import { makeMockCtx, makeMockRequest } from '../../helpers/route-tester'; import { makePort, makeClient, makeYacht, makeFullPermissions, makeViewerPermissions, } from '../../helpers/factories'; describe('POST /api/v1/yachts (createHandler)', () => { it('creates a yacht and returns 201', async () => { const port = await makePort(); const client = await makeClient({ portId: port.id }); const ctx = makeMockCtx({ portId: port.id, permissions: makeFullPermissions() }); const req = makeMockRequest('POST', 'http://localhost/api/v1/yachts', { body: { name: 'Sea Breeze', owner: { type: 'client', id: client.id } }, }); const res = await createHandler(req, ctx, {}); expect(res.status).toBe(201); const body = await res.json(); expect(body.data.name).toBe('Sea Breeze'); expect(body.data.currentOwnerId).toBe(client.id); }); it('returns 400 on invalid body (empty name)', async () => { const port = await makePort(); const client = await makeClient({ portId: port.id }); const ctx = makeMockCtx({ portId: port.id, permissions: makeFullPermissions() }); const req = makeMockRequest('POST', 'http://localhost/api/v1/yachts', { body: { name: '', owner: { type: 'client', id: client.id } }, }); const res = await createHandler(req, ctx, {}); expect(res.status).toBe(400); }); it('returns 400 when owner.id does not exist', async () => { const port = await makePort(); const ctx = makeMockCtx({ portId: port.id, permissions: makeFullPermissions() }); const req = makeMockRequest('POST', 'http://localhost/api/v1/yachts', { body: { name: 'Phantom', owner: { type: 'client', id: 'nonexistent' } }, }); const res = await createHandler(req, ctx, {}); expect(res.status).toBe(400); }); }); describe('GET /api/v1/yachts (listHandler)', () => { it('returns tenant-scoped yachts with pagination metadata', async () => { const port = await makePort(); const client = await makeClient({ portId: port.id }); await makeYacht({ portId: port.id, ownerType: 'client', ownerId: client.id, name: 'Listed', }); const ctx = makeMockCtx({ portId: port.id, permissions: makeFullPermissions() }); const req = makeMockRequest('GET', 'http://localhost/api/v1/yachts?page=1&limit=20&order=desc'); const res = await listHandler(req, ctx, {}); expect(res.status).toBe(200); const body = await res.json(); expect(body.data.some((y: { name: string }) => y.name === 'Listed')).toBe(true); expect(body.pagination.page).toBe(1); expect(body.pagination.pageSize).toBe(20); expect(typeof body.pagination.total).toBe('number'); }); it('returns 400 for invalid query params (non-numeric page)', async () => { const port = await makePort(); const ctx = makeMockCtx({ portId: port.id, permissions: makeFullPermissions() }); const req = makeMockRequest( 'GET', 'http://localhost/api/v1/yachts?page=abc&limit=20&order=desc', ); const res = await listHandler(req, ctx, {}); expect(res.status).toBe(400); }); }); describe('POST /api/v1/yachts — permission gate', () => { it('viewer (no yachts.create) receives 403 through full pipeline', async () => { const port = await makePort(); const client = await makeClient({ portId: port.id }); const gated = withPermission('yachts', 'create', createHandler); const ctx = makeMockCtx({ portId: port.id, permissions: makeViewerPermissions() }); const req = makeMockRequest('POST', 'http://localhost/api/v1/yachts', { body: { name: 'X', owner: { type: 'client', id: client.id } }, }); const res = await gated(req, ctx, {}); expect(res.status).toBe(403); // Sanity check that the withAuth-wrapped HTTP export exists. expect(POST).toBeDefined(); }); });