/** * Error-path tests for ScaffoldService — tests the archiver finalize error handler. * These tests use jest.mock('archiver') to simulate archiver failures. */ import { PassThrough } from 'stream'; // Mock archiver before importing ScaffoldService const mockFinalize = jest.fn(); const mockArchive = { pipe: jest.fn(), append: jest.fn(), finalize: mockFinalize, }; jest.mock('archiver', () => jest.fn(() => mockArchive)); // Mock metrics to avoid registry conflicts jest.mock('../../../src/metrics/registry', () => ({ scaffoldGeneratedTotal: { labels: jest.fn().mockReturnValue({ inc: jest.fn() }) }, scaffoldGenerationDurationMs: { labels: jest.fn().mockReturnValue({ observe: jest.fn() }) }, })); import { ScaffoldService } from '../../../src/services/ScaffoldService'; const BASE_OPTIONS = { agentId: 'agent-uuid-1234', agentName: 'my-test-agent', clientId: 'client-id-5678', apiUrl: 'https://api.sentryagent.ai', }; describe('ScaffoldService — archiver error path', () => { let service: ScaffoldService; beforeEach(() => { jest.clearAllMocks(); // Reset pipe to actually pipe to the PassThrough so the stream reference is correct mockArchive.pipe.mockImplementation(() => {}); service = new ScaffoldService(); }); it('destroys the PassThrough stream when archiver finalize rejects with an Error', async () => { const archiverError = new Error('archiver finalize failure'); mockFinalize.mockReturnValue(Promise.reject(archiverError)); const { stream } = await service.generateScaffold({ ...BASE_OPTIONS, language: 'typescript', }); // Attach error listener to suppress unhandled error event from stream.destroy(err) await new Promise((resolve) => { stream.on('error', () => resolve()); }); expect((stream as PassThrough).destroyed).toBe(true); }); it('destroys the PassThrough stream when archiver finalize rejects with a non-Error value', async () => { // Covers the `err instanceof Error ? err : new Error(String(err))` false branch mockFinalize.mockReturnValue(Promise.reject('string-error')); const { stream } = await service.generateScaffold({ ...BASE_OPTIONS, language: 'typescript', }); // Attach error listener to suppress unhandled error event from stream.destroy(err) await new Promise((resolve) => { stream.on('error', () => resolve()); }); expect((stream as PassThrough).destroyed).toBe(true); }); });