import { mkdtemp, rm } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import { FileSystemBackendArtifactStore } from '../../server/artifacts/backend-artifact-store'; import { GpoBackupJobProcessor } from '../../server/gpo/gpo-backup-job-processor'; import { GpoBackupSubmissionService } from '../../server/gpo/gpo-backup-submission-service'; import { listProjectGpoBackupStatus } from '../../server/gpo/gpo-backup-status-service'; import { directGpoBackupWorkerExecutor, directGpoInventoryExecutor } from '../../server/gpo/gpo-inventory-executor'; import { LocalAssessorRepository } from '../../src/persistence/local-assessor-repository'; import { syntheticGpoBackupZip } from './gpo-synthetic-fixture'; const submittedAt = '2026-07-21T10:00:00.000Z'; const project = { id: 'gpo-status-project', name: 'Synthetic status', createdAt: submittedAt, updatedAt: submittedAt, attributes: {} }; describe('persistent project GPO status', () => { let directory: string; let repository: LocalAssessorRepository; let store: FileSystemBackendArtifactStore; beforeEach(async () => { directory = await mkdtemp(join(tmpdir(), 'assessor-gpo-status-')); repository = new LocalAssessorRepository(); store = new FileSystemBackendArtifactStore(directory); await repository.saveProject(project); }); afterEach(async () => { await rm(directory, { recursive: true, force: true }); }); it('derives queued and completed-partial display states from persistent repository data', async () => { await new GpoBackupSubmissionService(repository, store, directGpoInventoryExecutor, 1024 * 1024).submit({ projectId: project.id, fileName: 'status.zip', bytes: syntheticGpoBackupZip(), receivedAt: submittedAt }); await expect(listProjectGpoBackupStatus(repository, project.id, submittedAt)).resolves.toMatchObject({ projectId: project.id, records: [{ fileName: 'status.zip', state: 'queued', attempts: 0, gpoCount: 0, backupInstanceCount: 0 }] }); await new GpoBackupJobProcessor(repository, store, directGpoBackupWorkerExecutor, { maximumArtifactBytes: 1024 * 1024, leaseDurationMs: 60_000, retryDelayMs: 1_000, now: () => new Date(submittedAt) }).processNext('status-worker'); await expect(listProjectGpoBackupStatus(repository, project.id, '2026-07-21T10:01:00.000Z')).resolves.toMatchObject({ records: [{ fileName: 'status.zip', state: 'partial', attempts: 1, processingStatus: 'partial', gpoCount: 1, backupInstanceCount: 1 }] }); }); });