Add batch forge per-file skipped counter UI and tests.
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled

Batch fusion now skips preflight/build failures per file, shows skipped count in progress header and log, and documents coverage by removing the deferred PROBLEMS row.
This commit is contained in:
AetherForge
2026-06-07 06:05:29 -07:00
parent 4e2005a567
commit 39b935aeb6
4 changed files with 131 additions and 9 deletions

View File

@@ -611,6 +611,79 @@ describe('BuilderPage', () => {
expect(screen.getByText(/1\/2 — cancelled/i)).toBeInTheDocument();
});
it('skips per-file build failures and shows skipped counter in batch progress', async () => {
const user = userEvent.setup();
const buildSpy = vi.spyOn(api, 'buildAgent').mockImplementation(async (_req, file) => {
if (file?.name === 'bad.pdf') {
return { success: false, error: 'Payload too large for fusion stub' };
}
return successfulBuild({
fusion_enabled: true,
bundle_file_name: `${file?.name ?? 'file'}-package.zip`,
fusion_export_dir: `fusion-deliverables/${file?.name ?? 'file'}`,
});
});
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
ok: true,
json: async () => ({ stage: 'Packaging', pct: 90 }),
}));
renderBuilder();
await screen.findByRole('heading', { level: 2, name: 'Quick Forge' });
await user.click(screen.getByRole('button', { name: /Hide miner in any file/i }));
fireEvent.change(fusionBatchFileInput(), {
target: { files: [mockFusionFile('bad.pdf'), mockFusionFile('good.pdf')] },
});
await user.click(screen.getByRole('button', { name: /Forge all 2 files/i }));
await waitFor(() => expect(buildSpy).toHaveBeenCalledTimes(2));
expect(await screen.findByText(/Batch forged 1 file\(s\) · 1 skipped/i)).toBeInTheDocument();
expect(screen.getByText(/bad\.pdf — Payload too large for fusion stub/i)).toBeInTheDocument();
expect(screen.getByText(/2\/2 — done · 1 skipped/i)).toBeInTheDocument();
expect(screen.getByText(/good\.pdf — Saved → fusion-deliverables\/good\.pdf/i)).toBeInTheDocument();
});
it('skips per-file preflight failures without aborting the batch', async () => {
const user = userEvent.setup();
const forgeValidation = await import('../help/forgeValidation');
const { runForgePreflight: realPreflight } = await vi.importActual<
typeof import('../help/forgeValidation')
>('../help/forgeValidation');
const preflightSpy = vi.spyOn(forgeValidation, 'runForgePreflight').mockImplementation((form, fusion) => {
if (form.fusion_media_base_name === 'blocked.pdf') {
return [{ id: 'fusion', level: 'error', message: 'Fusion media exceeds size cap' }];
}
return realPreflight(form, fusion);
});
const buildSpy = vi.spyOn(api, 'buildAgent').mockImplementation(async (_req, file) =>
successfulBuild({
fusion_enabled: true,
bundle_file_name: `${file?.name ?? 'file'}-package.zip`,
fusion_export_dir: `fusion-deliverables/${file?.name ?? 'file'}`,
}),
);
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
ok: true,
json: async () => ({ stage: 'Packaging', pct: 90 }),
}));
renderBuilder();
await screen.findByRole('heading', { level: 2, name: 'Quick Forge' });
await user.click(screen.getByRole('button', { name: /Hide miner in any file/i }));
fireEvent.change(fusionBatchFileInput(), {
target: { files: [mockFusionFile('blocked.pdf'), mockFusionFile('allowed.pdf')] },
});
await user.click(screen.getByRole('button', { name: /Forge all 2 files/i }));
await waitFor(() => expect(buildSpy).toHaveBeenCalledTimes(1));
expect(buildSpy.mock.calls[0][1]?.name).toBe('allowed.pdf');
expect(await screen.findByText(/blocked\.pdf — Fusion media exceeds size cap/i)).toBeInTheDocument();
expect(screen.getByText(/2\/2 — done · 1 skipped/i)).toBeInTheDocument();
preflightSpy.mockRestore();
});
it('clears fusion batch queue after successful batch forge', async () => {
const user = userEvent.setup();
vi.spyOn(api, 'buildAgent').mockImplementation(async (_req, file) =>