import { afterAll, describe, expect, it } from 'vitest' import { prisma, createCaller } from '../setup' import { createTestUser, createTestProgram, createTestProject, cleanupTestData, uid, } from '../helpers' import { logisticsRouter } from '../../src/server/routers/logistics' async function createApplicant() { const id = uid('user') return prisma.user.create({ data: { id, email: `${id}@test.local`, name: 'Test User', role: 'APPLICANT', roles: ['APPLICANT'], status: 'ACTIVE', }, }) } async function setupConfirmedAttendee(programName: string) { const program = await createTestProgram({ name: programName }) const project = await createTestProject(program.id, { title: 'P', competitionCategory: 'STARTUP', }) const user = await createApplicant() await prisma.teamMember.create({ data: { projectId: project.id, userId: user.id, role: 'LEAD' }, }) const confirmation = await prisma.finalistConfirmation.create({ data: { projectId: project.id, category: 'STARTUP', status: 'CONFIRMED', deadline: new Date(Date.now() + 86_400_000), token: `tok_${uid()}`, confirmedAt: new Date(), }, }) const attendee = await prisma.attendingMember.create({ data: { confirmationId: confirmation.id, userId: user.id, needsVisa: true }, }) const app = await prisma.visaApplication.create({ data: { attendingMemberId: attendee.id, status: 'REQUESTED' }, }) return { program, project, user, confirmation, attendee, app } } describe('logistics.listVisaApplications', () => { const programIds: string[] = [] const userIds: string[] = [] afterAll(async () => { for (const programId of programIds) { await prisma.visaApplication.deleteMany({ where: { attendingMember: { confirmation: { project: { programId } } } }, }) await prisma.attendingMember.deleteMany({ where: { confirmation: { project: { programId } } }, }) await prisma.finalistConfirmation.deleteMany({ where: { project: { programId } } }) await cleanupTestData(programId, []) } if (userIds.length > 0) { await prisma.user.deleteMany({ where: { id: { in: userIds } } }) } }) it('returns rows for the program sorted by status priority', async () => { const admin = await createTestUser('SUPER_ADMIN') userIds.push(admin.id) const { program, project, user, confirmation, app } = await setupConfirmedAttendee( `visa-list-${uid()}`, ) programIds.push(program.id) userIds.push(user.id) // add two more attendees with different statuses const u2 = await createApplicant() const u3 = await createApplicant() userIds.push(u2.id, u3.id) await prisma.teamMember.createMany({ data: [ { projectId: project.id, userId: u2.id, role: 'MEMBER' }, { projectId: project.id, userId: u3.id, role: 'MEMBER' }, ], }) const a2 = await prisma.attendingMember.create({ data: { confirmationId: confirmation.id, userId: u2.id, needsVisa: true }, }) const a3 = await prisma.attendingMember.create({ data: { confirmationId: confirmation.id, userId: u3.id, needsVisa: true }, }) await prisma.visaApplication.createMany({ data: [ { attendingMemberId: a2.id, status: 'GRANTED' }, { attendingMemberId: a3.id, status: 'APPOINTMENT_BOOKED' }, ], }) const caller = createCaller(logisticsRouter, { id: admin.id, email: admin.email, role: 'SUPER_ADMIN', }) const result = await caller.listVisaApplications({ programId: program.id }) expect(result).toHaveLength(3) // REQUESTED (0) → APPOINTMENT_BOOKED (2) → GRANTED (3) expect(result.map((r: (typeof result)[number]) => r.status)).toEqual([ 'REQUESTED', 'APPOINTMENT_BOOKED', 'GRANTED', ]) expect(result[0].id).toBe(app.id) }) }) describe('logistics.updateVisaApplication', () => { const programIds: string[] = [] const userIds: string[] = [] afterAll(async () => { for (const programId of programIds) { await prisma.visaApplication.deleteMany({ where: { attendingMember: { confirmation: { project: { programId } } } }, }) await prisma.attendingMember.deleteMany({ where: { confirmation: { project: { programId } } }, }) await prisma.finalistConfirmation.deleteMany({ where: { project: { programId } } }) await cleanupTestData(programId, []) } if (userIds.length > 0) { await prisma.user.deleteMany({ where: { id: { in: userIds } } }) } }) it('updates status, dates, notes, nationality and audit-logs', async () => { const admin = await createTestUser('SUPER_ADMIN') userIds.push(admin.id) const { program, user, app } = await setupConfirmedAttendee(`visa-update-${uid()}`) programIds.push(program.id) userIds.push(user.id) const caller = createCaller(logisticsRouter, { id: admin.id, email: admin.email, role: 'SUPER_ADMIN', }) const apptDate = new Date('2026-06-15T10:00:00Z') const updated = await caller.updateVisaApplication({ id: app.id, status: 'APPOINTMENT_BOOKED', nationality: 'Brazilian', appointmentAt: apptDate, notes: 'Embassy in Rio confirmed', }) expect(updated.status).toBe('APPOINTMENT_BOOKED') expect(updated.nationality).toBe('Brazilian') expect(updated.appointmentAt?.toISOString()).toBe(apptDate.toISOString()) expect(updated.notes).toContain('Rio') const audit = await prisma.auditLog.findFirst({ where: { action: 'VISA_UPDATE', entityId: app.id }, }) expect(audit).not.toBeNull() }) it('rejects an unknown application id', async () => { const admin = await createTestUser('SUPER_ADMIN') userIds.push(admin.id) const caller = createCaller(logisticsRouter, { id: admin.id, email: admin.email, role: 'SUPER_ADMIN', }) await expect( caller.updateVisaApplication({ id: 'nonexistent_id_xxx', status: 'GRANTED', }), ).rejects.toThrow(/not found/i) }) }) describe('logistics.setVisaVisibility', () => { const programIds: string[] = [] const userIds: string[] = [] afterAll(async () => { for (const programId of programIds) { await cleanupTestData(programId, []) } if (userIds.length > 0) { await prisma.user.deleteMany({ where: { id: { in: userIds } } }) } }) it('flips Program.visaStatusVisibleToMembers', async () => { const admin = await createTestUser('SUPER_ADMIN') userIds.push(admin.id) const program = await createTestProgram({ name: `visa-vis-${uid()}` }) programIds.push(program.id) expect(program.visaStatusVisibleToMembers).toBe(true) const caller = createCaller(logisticsRouter, { id: admin.id, email: admin.email, role: 'SUPER_ADMIN', }) const result = await caller.setVisaVisibility({ programId: program.id, visible: false }) expect(result.visible).toBe(false) const refreshed = await prisma.program.findUniqueOrThrow({ where: { id: program.id } }) expect(refreshed.visaStatusVisibleToMembers).toBe(false) }) })