Files
netfelix-audio-fix/server/api/__tests__/execute.test.ts
Felix Förtsch b1a9eeb481
Some checks failed
Build and Push Docker Image / build (push) Has been cancelled
rework scan page, add ingest-source browsing, bump version to 2026.04.15.8
2026-04-15 18:31:00 +02:00

86 lines
3.5 KiB
TypeScript

import { describe, expect, test } from "bun:test";
import { enqueueUnseenJobs, extractErrorSummary, shouldSendLiveUpdate, yieldAfterChunk } from "../execute";
describe("extractErrorSummary", () => {
test("pulls the real error line out of ffmpeg's banner", () => {
const lines = [
"[stderr] ffmpeg version 7.1.3 ...",
"[stderr] built with gcc 14",
"[stderr] Stream #0:2(eng): Subtitle: dvd_subtitle (dvdsub), 1280x720",
"[stderr] Stream mapping:",
"[stderr] Stream #0:2 -> #0:0 (copy)",
"[stderr] [srt @ 0x55] Unsupported subtitles codec: dvd_subtitle",
"[stderr] [out#0/srt @ 0x55] Could not write header (incorrect codec parameters ?): Invalid argument",
"[stderr] Conversion failed!",
];
const summary = extractErrorSummary(lines, new Error("FFmpeg exited with code 234"));
expect(summary).toContain("Unsupported subtitles codec: dvd_subtitle");
expect(summary).toContain("Invalid argument");
expect(summary).toContain("Conversion failed!");
// Should NOT include the banner lines.
expect(summary).not.toContain("ffmpeg version");
expect(summary).not.toContain("Stream #0:2");
});
test("dedupes identical fatal lines (e.g. repeated warnings)", () => {
const lines = ["[stderr] Conversion failed!", "[stderr] Conversion failed!", "[stderr] Conversion failed!"];
const summary = extractErrorSummary(lines);
expect(summary?.split("\n").length).toBe(1);
});
test("falls back to the thrown error when no fatal line is found", () => {
const lines = ["[stderr] ffmpeg version 7", "[stderr] Duration: 00:10:00"];
const summary = extractErrorSummary(lines, new Error("FFmpeg exited with code 1"));
expect(summary).toBe("Error: FFmpeg exited with code 1");
});
test("returns null when neither a fatal line nor a thrown error is available", () => {
expect(extractErrorSummary([])).toBe(null);
expect(extractErrorSummary(["[stderr] ffmpeg version 7"])).toBe(null);
});
test("only scans the tail — a banner from a prior run doesn't leak through", () => {
// 70 filler lines, real error at the very end; scan window is 60.
const filler = Array.from({ length: 70 }, (_, i) => `[stderr] banner line ${i}`);
const lines = [...filler, "[stderr] Error: no space left on device"];
const summary = extractErrorSummary(lines);
expect(summary).toBe("Error: no space left on device");
});
});
describe("shouldSendLiveUpdate", () => {
test("throttles updates until interval passes", () => {
expect(shouldSendLiveUpdate(1_000, 800, 500)).toBe(false);
expect(shouldSendLiveUpdate(1_301, 800, 500)).toBe(true);
});
});
describe("yieldAfterChunk", () => {
test("yields once threshold is reached, resets chunk counter", async () => {
let yieldCalls = 0;
const sleep = async (_ms: number) => {
yieldCalls += 1;
};
let chunks = 0;
chunks = await yieldAfterChunk(chunks, 3, sleep);
expect(chunks).toBe(1);
chunks = await yieldAfterChunk(chunks, 3, sleep);
expect(chunks).toBe(2);
chunks = await yieldAfterChunk(chunks, 3, sleep);
expect(chunks).toBe(0);
expect(yieldCalls).toBe(1);
});
});
describe("enqueueUnseenJobs", () => {
test("appends only unseen job ids to the active queue", () => {
const queue = [{ id: 1 }, { id: 2 }] as { id: number }[];
const seen = new Set([1, 2]);
const added = enqueueUnseenJobs(queue, seen, [{ id: 2 }, { id: 3 }, { id: 4 }] as { id: number }[]);
expect(added).toBe(2);
expect(queue.map((j) => j.id)).toEqual([1, 2, 3, 4]);
expect(seen.has(3)).toBeTrue();
expect(seen.has(4)).toBeTrue();
});
});