feat(final-docs): hasRequired/allUploaded on FinalDocumentStatus for optional-uploads mode

This commit is contained in:
Matt
2026-06-10 14:58:39 +02:00
parent 2c311bc65a
commit d89f67ba57
2 changed files with 52 additions and 3 deletions

View File

@@ -25,7 +25,7 @@ import { BUCKET_NAME, generateObjectKey } from '@/lib/minio'
const programIds: string[] = []
async function makeFinaleProgram(
opts: { roundStatus?: 'ROUND_ACTIVE' | 'ROUND_DRAFT' | 'ROUND_CLOSED'; closeAt?: Date; skipRequirements?: boolean; uploadsEnabled?: boolean } = {},
opts: { roundStatus?: 'ROUND_ACTIVE' | 'ROUND_DRAFT' | 'ROUND_CLOSED'; closeAt?: Date; skipRequirements?: boolean; uploadsEnabled?: boolean; optionalRequirements?: boolean } = {},
) {
const program = await createTestProgram()
programIds.push(program.id)
@@ -41,10 +41,10 @@ async function makeFinaleProgram(
return { program, comp, round, reqPlan: undefined, reqVideo: undefined }
}
const reqPlan = await prisma.fileRequirement.create({
data: { id: uid('req'), roundId: round.id, name: 'Final Business Plan', acceptedMimeTypes: ['application/pdf'], isRequired: true, sortOrder: 1 },
data: { id: uid('req'), roundId: round.id, name: 'Final Business Plan', acceptedMimeTypes: ['application/pdf'], isRequired: !opts.optionalRequirements, sortOrder: 1 },
})
const reqVideo = await prisma.fileRequirement.create({
data: { id: uid('req'), roundId: round.id, name: '1-minute Video', acceptedMimeTypes: ['video/*'], isRequired: true, sortOrder: 2 },
data: { id: uid('req'), roundId: round.id, name: '1-minute Video', acceptedMimeTypes: ['video/*'], isRequired: !opts.optionalRequirements, sortOrder: 2 },
})
return { program, comp, round, reqPlan, reqVideo }
}
@@ -128,6 +128,49 @@ describe('getFinalDocumentStatusForProject', () => {
expect(status!.requirements).toHaveLength(0)
expect(status!.allRequiredUploaded).toBe(false)
})
it('all-optional round: hasRequired false, allUploaded flips when every slot has a file', async () => {
const { program, round, reqPlan, reqVideo } = await makeFinaleProgram({ optionalRequirements: true })
const project = await createTestProject(program.id)
await createTestProjectRoundState(project.id, round.id)
const before = await getFinalDocumentStatusForProject(prisma, project.id)
expect(before!.hasRequired).toBe(false)
expect(before!.allUploaded).toBe(false)
expect(before!.allRequiredUploaded).toBe(false)
for (const req of [reqPlan!, reqVideo!]) {
await prisma.projectFile.create({
data: {
id: uid('file'), projectId: project.id, roundId: round.id, requirementId: req.id,
fileType: 'SUPPORTING_DOC', fileName: `f-${req.id}`, mimeType: 'application/pdf', size: 10,
bucket: 'b', objectKey: uid('key'),
},
})
}
const after = await getFinalDocumentStatusForProject(prisma, project.id)
expect(after!.hasRequired).toBe(false)
expect(after!.allUploaded).toBe(true)
})
it('mixed round: hasRequired true; allUploaded only when optional slots are filled too', async () => {
const { program, round, reqPlan } = await makeFinaleProgram()
await prisma.fileRequirement.update({ where: { id: reqPlan!.id }, data: { isRequired: false } })
const project = await createTestProject(program.id)
await createTestProjectRoundState(project.id, round.id)
const status = await getFinalDocumentStatusForProject(prisma, project.id)
expect(status!.hasRequired).toBe(true) // reqVideo still required
expect(status!.allUploaded).toBe(false)
})
it('zero slots: allUploaded false (no vacuous completeness)', async () => {
const { program, round } = await makeFinaleProgram({ skipRequirements: true })
const project = await createTestProject(program.id)
await createTestProjectRoundState(project.id, round.id)
const status = await getFinalDocumentStatusForProject(prisma, project.id)
expect(status!.hasRequired).toBe(false)
expect(status!.allUploaded).toBe(false)
})
})
describe('applicant.getFinalDocumentStatus', () => {