/** * I-006: Live Runtime — Jump / Reorder / Open-Close Windows */ import { describe, it, expect, beforeAll, afterAll } from 'vitest' import { prisma } from '../setup' import { createTestUser, createTestProgram, createTestPipeline, createTestTrack, createTestStage, createTestProject, createTestCohort, createTestCohortProject, cleanupTestData, } from '../helpers' import { startSession, jumpToProject, reorderQueue, pauseResume, openCohortWindow, closeCohortWindow, } from '@/server/services/live-control' let programId: string let userIds: string[] = [] beforeAll(async () => { const program = await createTestProgram({ name: 'Live Runtime Test' }) programId = program.id }) afterAll(async () => { await cleanupTestData(programId, userIds) }) describe('I-006: Live Runtime Operations', () => { it('full live session lifecycle: start → jump → reorder → pause → resume → open/close window', async () => { const admin = await createTestUser('SUPER_ADMIN') userIds.push(admin.id) const pipeline = await createTestPipeline(programId) const track = await createTestTrack(pipeline.id) const stage = await createTestStage(track.id, { name: 'Live Final Session', stageType: 'LIVE_FINAL', status: 'STAGE_ACTIVE', }) // Create 4 projects const projects = [] for (let i = 0; i < 4; i++) { const p = await createTestProject(programId, { title: `Live Runtime P${i}` }) projects.push(p) } // Create cohort with projects const cohort = await createTestCohort(stage.id, { name: 'Runtime Cohort', isOpen: false }) const cohortProjects = [] for (let i = 0; i < projects.length; i++) { const cp = await createTestCohortProject(cohort.id, projects[i].id, i) cohortProjects.push(cp) } // 1. Start session const sessionResult = await startSession(stage.id, admin.id, prisma) expect(sessionResult.success).toBe(true) // Verify cursor starts at first project let cursor = await prisma.liveProgressCursor.findUnique({ where: { stageId: stage.id } }) expect(cursor!.activeProjectId).toBe(projects[0].id) expect(cursor!.activeOrderIndex).toBe(0) // 2. Jump to project at index 2 const jumpResult = await jumpToProject(stage.id, 2, admin.id, prisma) expect(jumpResult.success).toBe(true) expect(jumpResult.projectId).toBe(projects[2].id) cursor = await prisma.liveProgressCursor.findUnique({ where: { stageId: stage.id } }) expect(cursor!.activeProjectId).toBe(projects[2].id) expect(cursor!.activeOrderIndex).toBe(2) // 3. Reorder queue (reverse order) const reorderedIds = [...cohortProjects].reverse().map(cp => cp.id) const reorderResult = await reorderQueue(stage.id, reorderedIds, admin.id, prisma) expect(reorderResult.success).toBe(true) // Verify reorder updated sortOrder const reorderedCPs = await prisma.cohortProject.findMany({ where: { cohortId: cohort.id }, orderBy: { sortOrder: 'asc' }, }) expect(reorderedCPs[0].projectId).toBe(projects[3].id) // Was last, now first expect(reorderedCPs[3].projectId).toBe(projects[0].id) // Was first, now last // 4. Pause const pauseResult = await pauseResume(stage.id, true, admin.id, prisma) expect(pauseResult.success).toBe(true) cursor = await prisma.liveProgressCursor.findUnique({ where: { stageId: stage.id } }) expect(cursor!.isPaused).toBe(true) // 5. Resume const resumeResult = await pauseResume(stage.id, false, admin.id, prisma) expect(resumeResult.success).toBe(true) cursor = await prisma.liveProgressCursor.findUnique({ where: { stageId: stage.id } }) expect(cursor!.isPaused).toBe(false) // 6. Open voting window const openResult = await openCohortWindow(cohort.id, admin.id, prisma) expect(openResult.success).toBe(true) const openedCohort = await prisma.cohort.findUnique({ where: { id: cohort.id } }) expect(openedCohort!.isOpen).toBe(true) expect(openedCohort!.windowOpenAt).not.toBeNull() // 7. Close voting window const closeResult = await closeCohortWindow(cohort.id, admin.id, prisma) expect(closeResult.success).toBe(true) const closedCohort = await prisma.cohort.findUnique({ where: { id: cohort.id } }) expect(closedCohort!.isOpen).toBe(false) expect(closedCohort!.windowCloseAt).not.toBeNull() // Verify audit trail const auditLogs = await prisma.decisionAuditLog.findMany({ where: { OR: [ { eventType: { startsWith: 'live.' } }, ], }, orderBy: { createdAt: 'asc' }, }) expect(auditLogs.length).toBeGreaterThanOrEqual(5) // session_started, cursor_updated, queue_reordered, paused, resumed, opened, closed }) })