Better error handling
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
import { describe, test, expect } from "bun:test";
|
||||
import { classifyError } from "../src/lib/errors";
|
||||
|
||||
// Pure pattern-matching (M6 error taxonomy) — no browser needed.
|
||||
|
||||
describe("classifyError", () => {
|
||||
test("decode-stage failures are always unsupported-file, regardless of message", () => {
|
||||
const result = classifyError(new Error("Unable to decode audio data"), { stage: "decode" });
|
||||
expect(result.kind).toBe("unsupported-file");
|
||||
expect(result.retryable).toBe(false);
|
||||
expect(result.suggestion).toMatch(/different file|convert/i);
|
||||
});
|
||||
|
||||
test("recognizes out-of-memory messages", () => {
|
||||
for (const message of [
|
||||
"RuntimeError: Aborted(OOM)",
|
||||
"Cannot enlarge memory arrays to size 123456, (OOM)",
|
||||
"Failed to allocate memory for tensor",
|
||||
"could not allocate buffer",
|
||||
]) {
|
||||
const result = classifyError(new Error(message));
|
||||
expect(result.kind).toBe("out-of-memory");
|
||||
expect(result.retryable).toBe(false);
|
||||
expect(result.suggestion).toMatch(/smaller model/i);
|
||||
}
|
||||
});
|
||||
|
||||
test("a worker crash with an empty message is treated as out-of-memory (D-027)", () => {
|
||||
const result = classifyError(new Error(""), { stage: "worker-crash" });
|
||||
expect(result.kind).toBe("out-of-memory");
|
||||
expect(result.message.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
test("a worker crash with a real message is not assumed to be OOM", () => {
|
||||
const result = classifyError(new Error("TypeError: x is not a function"), {
|
||||
stage: "worker-crash",
|
||||
});
|
||||
expect(result.kind).toBe("generic");
|
||||
});
|
||||
|
||||
test("recognizes WebGPU device-lost messages", () => {
|
||||
for (const message of [
|
||||
"GPUDeviceLostInfo: device was lost",
|
||||
"WebGPU device was lost: reason=destroyed",
|
||||
"the GPU device has been lost",
|
||||
]) {
|
||||
const result = classifyError(new Error(message));
|
||||
expect(result.kind).toBe("device-lost");
|
||||
expect(result.retryable).toBe(true);
|
||||
expect(result.suggestion).toMatch(/backend|try again/i);
|
||||
}
|
||||
});
|
||||
|
||||
test("recognizes network failures", () => {
|
||||
for (const message of [
|
||||
"TypeError: Failed to fetch",
|
||||
"NetworkError when attempting to fetch resource.",
|
||||
"Load failed",
|
||||
]) {
|
||||
const result = classifyError(new Error(message), { stage: "load" });
|
||||
expect(result.kind).toBe("network");
|
||||
expect(result.retryable).toBe(true);
|
||||
}
|
||||
});
|
||||
|
||||
test("offline context forces a network classification with a clear message", () => {
|
||||
const result = classifyError(new Error("something obscure"), {
|
||||
stage: "load",
|
||||
offline: true,
|
||||
});
|
||||
expect(result.kind).toBe("network");
|
||||
expect(result.retryable).toBe(true);
|
||||
expect(result.message).toMatch(/offline/i);
|
||||
expect(result.suggestion).toMatch(/restart/i);
|
||||
});
|
||||
|
||||
test("load-stage network suggestion mentions the download restarting", () => {
|
||||
const result = classifyError(new Error("Failed to fetch"), { stage: "load" });
|
||||
expect(result.suggestion).toMatch(/restart/i);
|
||||
});
|
||||
|
||||
test("transcribe-stage network suggestion is stage-agnostic", () => {
|
||||
const result = classifyError(new Error("Failed to fetch"), { stage: "transcribe" });
|
||||
expect(result.suggestion).not.toMatch(/restart/i);
|
||||
});
|
||||
|
||||
test("falls back to a generic, non-retryable classification", () => {
|
||||
const result = classifyError(new Error("Something went wrong doing the thing"));
|
||||
expect(result).toEqual({
|
||||
kind: "generic",
|
||||
message: "Something went wrong doing the thing",
|
||||
retryable: false,
|
||||
});
|
||||
});
|
||||
|
||||
test("accepts non-Error raw values", () => {
|
||||
expect(classifyError("plain string failure").message).toBe("plain string failure");
|
||||
expect(classifyError(42).message).toBe("42");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user