/** * U-009: Live Cursor — Concurrent Update Handling */ import { describe, it, expect, beforeAll, afterAll } from 'vitest' import { prisma } from '../setup' import { createTestUser, createTestProgram, createTestPipeline, createTestTrack, createTestStage, createTestProject, createTestPSS, createTestCohort, createTestCohortProject, cleanupTestData, } from '../helpers' import { startSession, setActiveProject, jumpToProject, pauseResume, openCohortWindow, closeCohortWindow, } from '@/server/services/live-control' let programId: string let userIds: string[] = [] beforeAll(async () => { const program = await createTestProgram({ name: 'Live Control Test' }) programId = program.id }) afterAll(async () => { await cleanupTestData(programId, userIds) }) describe('U-009: Live Cursor Operations', () => { it('starts a session for a LIVE_FINAL stage', 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', stageType: 'LIVE_FINAL', status: 'STAGE_ACTIVE', }) // Create cohort with projects const project1 = await createTestProject(programId, { title: 'Live Project 1' }) const project2 = await createTestProject(programId, { title: 'Live Project 2' }) const cohort = await createTestCohort(stage.id, { name: 'Final Cohort' }) await createTestCohortProject(cohort.id, project1.id, 0) await createTestCohortProject(cohort.id, project2.id, 1) const result = await startSession(stage.id, admin.id, prisma) expect(result.success).toBe(true) expect(result.sessionId).not.toBeNull() expect(result.cursorId).not.toBeNull() // Verify cursor state const cursor = await prisma.liveProgressCursor.findUnique({ where: { stageId: stage.id }, }) expect(cursor).not.toBeNull() expect(cursor!.activeProjectId).toBe(project1.id) // First project expect(cursor!.activeOrderIndex).toBe(0) expect(cursor!.isPaused).toBe(false) }) it('rejects starting a session on non-LIVE_FINAL stage', 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: 'Evaluation Stage', stageType: 'EVALUATION', status: 'STAGE_ACTIVE', }) const result = await startSession(stage.id, admin.id, prisma) expect(result.success).toBe(false) expect(result.errors!.some(e => e.includes('LIVE_FINAL'))).toBe(true) }) it('handles concurrent cursor updates without corruption', 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: 'Concurrent Test', stageType: 'LIVE_FINAL', status: 'STAGE_ACTIVE', }) const project1 = await createTestProject(programId, { title: 'ConcP1' }) const project2 = await createTestProject(programId, { title: 'ConcP2' }) const project3 = await createTestProject(programId, { title: 'ConcP3' }) const cohort = await createTestCohort(stage.id, { name: 'Conc Cohort' }) await createTestCohortProject(cohort.id, project1.id, 0) await createTestCohortProject(cohort.id, project2.id, 1) await createTestCohortProject(cohort.id, project3.id, 2) // Start session await startSession(stage.id, admin.id, prisma) // Fire 2 concurrent setActiveProject calls const [result1, result2] = await Promise.all([ setActiveProject(stage.id, project2.id, admin.id, prisma), setActiveProject(stage.id, project3.id, admin.id, prisma), ]) // Both should succeed (last-write-wins) expect(result1.success).toBe(true) expect(result2.success).toBe(true) // Final cursor state should be consistent (one of the two writes wins) const cursor = await prisma.liveProgressCursor.findUnique({ where: { stageId: stage.id }, }) expect(cursor).not.toBeNull() // The active project should be either project2 or project3, not corrupted expect([project2.id, project3.id]).toContain(cursor!.activeProjectId) }) it('jump, pause/resume, and cohort window operations work correctly', 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: 'Full Live Test', stageType: 'LIVE_FINAL', status: 'STAGE_ACTIVE', }) const p1 = await createTestProject(programId, { title: 'P1' }) const p2 = await createTestProject(programId, { title: 'P2' }) const cohort = await createTestCohort(stage.id, { name: 'Test Cohort', isOpen: false }) await createTestCohortProject(cohort.id, p1.id, 0) await createTestCohortProject(cohort.id, p2.id, 1) await startSession(stage.id, admin.id, prisma) // Jump to index 1 const jumpResult = await jumpToProject(stage.id, 1, admin.id, prisma) expect(jumpResult.success).toBe(true) expect(jumpResult.projectId).toBe(p2.id) // Pause const pauseResult = await pauseResume(stage.id, true, admin.id, prisma) expect(pauseResult.success).toBe(true) const cursorPaused = await prisma.liveProgressCursor.findUnique({ where: { stageId: stage.id } }) expect(cursorPaused!.isPaused).toBe(true) // Resume const resumeResult = await pauseResume(stage.id, false, admin.id, prisma) expect(resumeResult.success).toBe(true) // Open cohort window const openResult = await openCohortWindow(cohort.id, admin.id, prisma) expect(openResult.success).toBe(true) const openCohort = await prisma.cohort.findUnique({ where: { id: cohort.id } }) expect(openCohort!.isOpen).toBe(true) expect(openCohort!.windowOpenAt).not.toBeNull() // Close cohort 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() }) })