import { describe, expect, it, vi } from 'vitest'; import { ReleaseJobManager } from '@electron/services/release-job'; import { ProjectReleaseBuildError } from '@electron/services/project-release-builder'; function preparedRelease() { return { sourceArchive: { path: '/tmp/project.zip', name: 'project.zip' as const, bytes: Buffer.from('zip'), summary: { archivePath: '/tmp/project.zip', archiveName: 'project.zip', sha256: 'a'.repeat(64), fileCount: 1, sourceBytes: 3, archiveBytes: 3, excludedCount: 0, excludedPaths: [], manifest: { schema_version: 1 as const, kind: 'web' as const, runtime: 'static' as const, build: { preset: 'vite' as const, package_manager: 'npm' as const, entry: 'index.html' as const }, }, }, }, builtArchive: { path: '/tmp/built.zip', name: 'built-project.zip' as const, bytes: Buffer.from('built') }, contract: {} as never, distRoot: '/tmp/dist', staticArtifact: {} as never, dispose: vi.fn(async () => undefined), }; } describe('ReleaseJobManager', () => { it('serializes jobs per project and publishes terminal status', async () => { const prepare = vi.fn(async () => preparedRelease()); const manager = new ReleaseJobManager({ prepare }); const first = manager.start({ projectId: 'project-1', projectPath: '/tmp/project-1', clientVersion: '2.0.0' }); expect(first.state).toBe('running'); expect(() => manager.start({ projectId: 'project-1', projectPath: '/tmp/project-1', clientVersion: '2.0.0' })) .toThrowError(ProjectReleaseBuildError); await vi.waitFor(() => expect(manager.get(first.jobId)?.state).toBe('complete')); expect(prepare).toHaveBeenCalledTimes(1); expect(manager.get(first.jobId)?.result?.package.archiveName).toBe('project.zip'); }); it('cancels an in-flight build and releases the project lock', async () => { let resolveBuild!: (value: ReturnType) => void; const prepare = vi.fn(() => new Promise>((resolve) => { resolveBuild = resolve; })); const manager = new ReleaseJobManager({ prepare }); const job = manager.start({ projectId: 'project-2', projectPath: '/tmp/project-2', clientVersion: '2.0.0' }); expect(manager.cancel(job.jobId)?.state).toBe('cancelled'); resolveBuild(preparedRelease()); await vi.waitFor(() => expect(manager.get(job.jobId)?.state).toBe('cancelled')); await new Promise((resolve) => setTimeout(resolve, 0)); expect(() => manager.start({ projectId: 'project-2', projectPath: '/tmp/project-2', clientVersion: '2.0.0' })).not.toThrow(); }); });