D2: verify GET /api/jobs returns error field for failed jobs
Verification tests for outcome-contract clause failed-job-error-exposed-for-ui-banner. - db.test.ts: listJobs includes error for failed jobs, null for non-failed - jobs-list-endpoint.test.ts: route-level integration with real DB, exercises GET handler end-to-end D2 already merged (f03b757+); non-blocking verification only.
This commit is contained in:
@@ -174,6 +174,24 @@ describe('listJobs', () => {
|
|||||||
it('returns an array (possibly empty)', () => {
|
it('returns an array (possibly empty)', () => {
|
||||||
expect(Array.isArray(listJobs())).toBe(true);
|
expect(Array.isArray(listJobs())).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('includes error field for failed jobs — D2 error banner data contract', () => {
|
||||||
|
const job = createJob('src', 'Failed Job', 'auto');
|
||||||
|
updateJob({ id: job.id, status: 'failed', error: 'simulated transcription failure for outcome-contract probe' });
|
||||||
|
const jobs = listJobs();
|
||||||
|
const found = jobs.find((j) => j.id === job.id);
|
||||||
|
expect(found).toBeDefined();
|
||||||
|
expect(found!.error).toBe('simulated transcription failure for outcome-contract probe');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns null error for non-failed jobs', () => {
|
||||||
|
const job = createJob('src', 'Done Job', 'auto');
|
||||||
|
updateJob({ id: job.id, status: 'done', error: null });
|
||||||
|
const jobs = listJobs();
|
||||||
|
const found = jobs.find((j) => j.id === job.id);
|
||||||
|
expect(found).toBeDefined();
|
||||||
|
expect(found!.error).toBeNull();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// ── Push subscriptions ────────────────────────────────────────────────────────
|
// ── Push subscriptions ────────────────────────────────────────────────────────
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
import { describe, it, expect, afterAll, vi } from 'vitest';
|
||||||
|
import { join } from 'path';
|
||||||
|
import { tmpdir } from 'os';
|
||||||
|
import { rm } from 'fs/promises';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Integration test for GET /api/jobs — verifies the error field is
|
||||||
|
* exposed for failed jobs and null for non-failed jobs, exercising
|
||||||
|
* the full db→API path with a real SQLite database.
|
||||||
|
*
|
||||||
|
* Covers outcome-contract clause failed-job-error-exposed-for-ui-banner.
|
||||||
|
* D2 already merged (f03b757+); this is non-blocking verification.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const TEST_DATA_DIR = join(tmpdir(), 'whisper-pwa-jobs-list-int-' + process.pid);
|
||||||
|
vi.stubEnv('DATA_DIR', TEST_DATA_DIR);
|
||||||
|
|
||||||
|
const { createJob, getJob, updateJob, listJobs } = await import('$lib/server/db.js');
|
||||||
|
const { GET } = await import('$lib/../routes/api/jobs/+server.js');
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await rm(TEST_DATA_DIR, { recursive: true, force: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('GET /api/jobs — error field exposure', () => {
|
||||||
|
it('includes error field for a failed job', async () => {
|
||||||
|
const job = createJob('probe-upload.webm', 'UI error probe', 'auto');
|
||||||
|
updateJob({
|
||||||
|
id: job.id,
|
||||||
|
status: 'failed',
|
||||||
|
error: 'simulated transcription failure for outcome-contract probe'
|
||||||
|
});
|
||||||
|
|
||||||
|
const res = await GET();
|
||||||
|
expect(res.status).toBe(200);
|
||||||
|
const body = await res.json();
|
||||||
|
expect(Array.isArray(body)).toBe(true);
|
||||||
|
|
||||||
|
const found = body.find((j: any) => j.id === job.id);
|
||||||
|
expect(found).toBeDefined();
|
||||||
|
expect(found.status).toBe('failed');
|
||||||
|
expect(found.error).toBe('simulated transcription failure for outcome-contract probe');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns null error for a non-failed job', async () => {
|
||||||
|
const job = createJob('probe-done.webm', 'Done probe', 'auto');
|
||||||
|
updateJob({ id: job.id, status: 'done', error: null });
|
||||||
|
|
||||||
|
const res = await GET();
|
||||||
|
expect(res.status).toBe(200);
|
||||||
|
const body = await res.json();
|
||||||
|
|
||||||
|
const found = body.find((j: any) => j.id === job.id);
|
||||||
|
expect(found).toBeDefined();
|
||||||
|
expect(found.error).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('preserves other job fields when error is present', async () => {
|
||||||
|
const job = createJob('my-recording.webm', 'My Recording', 'aggressive');
|
||||||
|
updateJob({
|
||||||
|
id: job.id,
|
||||||
|
status: 'failed',
|
||||||
|
error: 'transcription failed',
|
||||||
|
progress: 50,
|
||||||
|
whisperJobId: 'whisper-123'
|
||||||
|
});
|
||||||
|
|
||||||
|
const res = await GET();
|
||||||
|
const body = await res.json();
|
||||||
|
const found = body.find((j: any) => j.id === job.id);
|
||||||
|
|
||||||
|
expect(found).toBeDefined();
|
||||||
|
expect(found.status).toBe('failed');
|
||||||
|
expect(found.error).toBe('transcription failed');
|
||||||
|
expect(found.title).toBe('My Recording');
|
||||||
|
expect(found.source).toBe('my-recording.webm');
|
||||||
|
expect(found.audioMode).toBe('aggressive');
|
||||||
|
expect(found.progress).toBe(50);
|
||||||
|
expect(found.whisperJobId).toBe('whisper-123');
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user