/** * Integration test: berth maintenance-log service (add / get / update / delete). * * The schema + add/get already existed; this covers the new update + delete * paths and the tenant guard (an entry can only be reached through its own * berth + port). Runs against the real test DB. */ import { beforeAll, describe, expect, it } from 'vitest'; import { db } from '@/lib/db'; import { berthMaintenanceLog } from '@/lib/db/schema/berths'; import { eq } from 'drizzle-orm'; let svc: typeof import('@/lib/services/berths.service'); let makePort: typeof import('../helpers/factories').makePort; let makeBerth: typeof import('../helpers/factories').makeBerth; let makeAuditMeta: typeof import('../helpers/factories').makeAuditMeta; beforeAll(async () => { svc = await import('@/lib/services/berths.service'); const f = await import('../helpers/factories'); makePort = f.makePort; makeBerth = f.makeBerth; makeAuditMeta = f.makeAuditMeta; }); describe('berth maintenance log', () => { async function setup() { const port = await makePort(); const berth = await makeBerth({ portId: port.id }); const meta = makeAuditMeta({ portId: port.id }); return { port, berth, meta }; } it('adds, lists (newest first), updates, and deletes an entry', async () => { const { port, berth, meta } = await setup(); const created = await svc.addMaintenanceLog( berth.id, port.id, { category: 'repair', description: 'Replaced cleat bolt', cost: 120.5, costCurrency: 'EUR', responsibleParty: 'Dockside Ltd', performedDate: '2026-05-10', }, meta, ); expect(created.category).toBe('repair'); expect(created.cost).toBe('120.5'); // A second, more recent entry to assert ordering. await svc.addMaintenanceLog( berth.id, port.id, { category: 'inspection', description: 'Annual check', performedDate: '2026-05-20' }, meta, ); const list = await svc.getMaintenanceLogs(berth.id, port.id); expect(list).toHaveLength(2); // Newest performedDate first. expect(list[0]!.performedDate).toBe('2026-05-20'); expect(list[1]!.performedDate).toBe('2026-05-10'); const updated = await svc.updateMaintenanceLog( berth.id, created.id, port.id, { description: 'Replaced cleat bolt + washer', cost: null }, meta, ); expect(updated.description).toBe('Replaced cleat bolt + washer'); // cost cleared to null expect(updated.cost).toBeNull(); await svc.deleteMaintenanceLog(berth.id, created.id, port.id, meta); const afterDelete = await db .select() .from(berthMaintenanceLog) .where(eq(berthMaintenanceLog.id, created.id)); expect(afterDelete).toHaveLength(0); const remaining = await svc.getMaintenanceLogs(berth.id, port.id); expect(remaining).toHaveLength(1); expect(remaining[0]!.category).toBe('inspection'); }); it('refuses to update or delete an entry through a different port (tenant guard)', async () => { const { port, berth, meta } = await setup(); const otherPort = await makePort(); const entry = await svc.addMaintenanceLog( berth.id, port.id, { category: 'routine', description: 'Pressure wash', performedDate: '2026-05-15' }, meta, ); await expect( svc.updateMaintenanceLog(berth.id, entry.id, otherPort.id, { description: 'hijack' }, meta), ).rejects.toThrow(); await expect( svc.deleteMaintenanceLog(berth.id, entry.id, otherPort.id, meta), ).rejects.toThrow(); // Untouched. const [row] = await db .select() .from(berthMaintenanceLog) .where(eq(berthMaintenanceLog.id, entry.id)); expect(row!.description).toBe('Pressure wash'); }); });