import { afterAll, beforeAll, describe, expect, it } from 'vitest' import { prisma, createCaller } from '../setup' import { createTestUser, createTestProgram, createTestProject, createTestCompetition, createTestRound, cleanupTestData, uid, } from '../helpers' import { finalistRouter } from '../../src/server/routers/finalist' beforeAll(() => { process.env.NEXTAUTH_SECRET = 'test-secret-for-finalist-tokens' process.env.NEXTAUTH_URL = 'http://localhost:3001' }) describe('finalist.selectFinalists', () => { const programIds: string[] = [] const userIds: string[] = [] afterAll(async () => { for (const programId of programIds) { await prisma.attendingMember.deleteMany({ where: { confirmation: { project: { programId } } }, }) await prisma.finalistConfirmation.deleteMany({ where: { project: { programId } } }) await prisma.finalistSlotQuota.deleteMany({ where: { programId } }) await cleanupTestData(programId, []) } if (userIds.length > 0) { await prisma.user.deleteMany({ where: { id: { in: userIds } } }) } }) it('creates PENDING confirmations with unique tokens for each selected project', async () => { const admin = await createTestUser('SUPER_ADMIN') userIds.push(admin.id) const program = await createTestProgram({ name: `select-${uid()}` }) programIds.push(program.id) const competition = await createTestCompetition(program.id, { status: 'ACTIVE' }) const round = await createTestRound(competition.id, { roundType: 'LIVE_FINAL', configJson: { confirmationWindowHours: 24 }, }) const p1 = await createTestProject(program.id, { title: 'P1', competitionCategory: 'STARTUP' }) const p2 = await createTestProject(program.id, { title: 'P2', competitionCategory: 'STARTUP' }) const caller = createCaller(finalistRouter, { id: admin.id, email: admin.email, role: 'SUPER_ADMIN', }) const result = await caller.selectFinalists({ programId: program.id, category: 'STARTUP', projectIds: [p1.id, p2.id], roundId: round.id, }) expect(result.created).toBe(2) const confirmations = await prisma.finalistConfirmation.findMany({ where: { project: { programId: program.id } }, }) expect(confirmations).toHaveLength(2) expect(new Set(confirmations.map((c) => c.token)).size).toBe(2) for (const c of confirmations) { expect(c.status).toBe('PENDING') expect(c.deadline.getTime()).toBeGreaterThan(Date.now() + 23 * 3_600_000) expect(c.deadline.getTime()).toBeLessThan(Date.now() + 25 * 3_600_000) } }) it('uses round configJson.confirmationWindowHours when configured', async () => { const admin = await createTestUser('SUPER_ADMIN') userIds.push(admin.id) const program = await createTestProgram({ name: `select-window-${uid()}` }) programIds.push(program.id) const competition = await createTestCompetition(program.id, { status: 'ACTIVE' }) const round = await createTestRound(competition.id, { roundType: 'LIVE_FINAL', configJson: { confirmationWindowHours: 48 }, }) const p1 = await createTestProject(program.id, { title: 'P1', competitionCategory: 'STARTUP' }) const caller = createCaller(finalistRouter, { id: admin.id, email: admin.email, role: 'SUPER_ADMIN', }) await caller.selectFinalists({ programId: program.id, category: 'STARTUP', projectIds: [p1.id], roundId: round.id, }) const confirmation = await prisma.finalistConfirmation.findFirstOrThrow({ where: { projectId: p1.id }, }) expect(confirmation.deadline.getTime()).toBeGreaterThan(Date.now() + 47 * 3_600_000) expect(confirmation.deadline.getTime()).toBeLessThan(Date.now() + 49 * 3_600_000) }) it('defaults to 24h when confirmationWindowHours is not in configJson', async () => { const admin = await createTestUser('SUPER_ADMIN') userIds.push(admin.id) const program = await createTestProgram({ name: `select-default-${uid()}` }) programIds.push(program.id) const competition = await createTestCompetition(program.id, { status: 'ACTIVE' }) const round = await createTestRound(competition.id, { roundType: 'LIVE_FINAL', configJson: {}, }) const p1 = await createTestProject(program.id, { title: 'P1', competitionCategory: 'STARTUP' }) const caller = createCaller(finalistRouter, { id: admin.id, email: admin.email, role: 'SUPER_ADMIN', }) await caller.selectFinalists({ programId: program.id, category: 'STARTUP', projectIds: [p1.id], roundId: round.id, }) const confirmation = await prisma.finalistConfirmation.findFirstOrThrow({ where: { projectId: p1.id }, }) expect(confirmation.deadline.getTime()).toBeGreaterThan(Date.now() + 23 * 3_600_000) expect(confirmation.deadline.getTime()).toBeLessThan(Date.now() + 25 * 3_600_000) }) it('rejects selecting more projects than the category quota', async () => { const admin = await createTestUser('SUPER_ADMIN') userIds.push(admin.id) const program = await createTestProgram({ name: `select-quota-${uid()}` }) programIds.push(program.id) const competition = await createTestCompetition(program.id, { status: 'ACTIVE' }) const round = await createTestRound(competition.id, { roundType: 'LIVE_FINAL', configJson: { confirmationWindowHours: 24 }, }) await prisma.finalistSlotQuota.create({ data: { programId: program.id, category: 'STARTUP', quota: 1 }, }) const p1 = await createTestProject(program.id, { title: 'P1', competitionCategory: 'STARTUP' }) const p2 = await createTestProject(program.id, { title: 'P2', competitionCategory: 'STARTUP' }) const caller = createCaller(finalistRouter, { id: admin.id, email: admin.email, role: 'SUPER_ADMIN', }) await expect( caller.selectFinalists({ programId: program.id, category: 'STARTUP', projectIds: [p1.id, p2.id], roundId: round.id, }), ).rejects.toThrow(/exceeds quota/i) }) it('rejects projects whose category does not match', async () => { const admin = await createTestUser('SUPER_ADMIN') userIds.push(admin.id) const program = await createTestProgram({ name: `select-cat-${uid()}` }) programIds.push(program.id) const competition = await createTestCompetition(program.id, { status: 'ACTIVE' }) const round = await createTestRound(competition.id, { roundType: 'LIVE_FINAL', configJson: { confirmationWindowHours: 24 }, }) const p1 = await createTestProject(program.id, { title: 'P1', competitionCategory: 'BUSINESS_CONCEPT', }) const caller = createCaller(finalistRouter, { id: admin.id, email: admin.email, role: 'SUPER_ADMIN', }) await expect( caller.selectFinalists({ programId: program.id, category: 'STARTUP', projectIds: [p1.id], roundId: round.id, }), ).rejects.toThrow(/category mismatch/i) }) })