Skip to content
File

Blob: tests/client/lib/classify-failure.test.ts

typescript80 lines
1import { describe, expect, it } from "vitest";
2import { classifyFailure, type FailureKind } from "@/client/lib/classify-failure";
3 
4function classify(err: unknown, online = true): FailureKind {
5 return classifyFailure(err, { online });
6}
7 
8describe("classifyFailure", () => {
9 describe("offline context", () => {
10 it("returns network regardless of error shape", () => {
11 expect(classify({ error: "forbidden", message: "Forbidden" }, false)).toBe("network");
12 expect(classify({ error: "unauthorized", message: "x" }, false)).toBe("network");
13 expect(classify(new Error("anything"), false)).toBe("network");
14 });
15 });
16 
17 describe("structured error codes", () => {
18 it("unauthorized maps to auth-ambiguous", () => {
19 expect(classify({ error: "unauthorized", message: "Token expired" })).toBe("auth-ambiguous");
20 });
21 
22 it("forbidden maps to forbidden", () => {
23 expect(classify({ error: "forbidden", message: "Access denied" })).toBe("forbidden");
24 });
25 
26 it("not_found maps to not-found", () => {
27 expect(classify({ error: "not_found", message: "Not found" })).toBe("not-found");
28 });
29 
30 it("internal_error maps to server", () => {
31 expect(classify({ error: "internal_error", message: "Server error" })).toBe("server");
32 });
33 
34 it("request_failed maps to network (transport failure)", () => {
35 expect(classify({ error: "request_failed", message: "Request failed with status 500" })).toBe("network");
36 });
37 });
38 
39 describe("non-API errors", () => {
40 it("TypeError maps to network", () => {
41 expect(classify(new TypeError("Failed to fetch"))).toBe("network");
42 });
43 
44 it("unknown error shape maps to unknown", () => {
45 expect(classify(new Error("something unexpected"))).toBe("unknown");
46 });
47 
48 it("string error maps to unknown", () => {
49 expect(classify("some string error")).toBe("unknown");
50 });
51 
52 it("null/undefined maps to unknown", () => {
53 expect(classify(null)).toBe("unknown");
54 expect(classify(undefined)).toBe("unknown");
55 });
56 });
57 
58 describe("does not parse message strings", () => {
59 it("does not classify based on status in message", () => {
60 // A request_failed with a 403 in the message is still "network", not "forbidden"
61 expect(classify({ error: "request_failed", message: "Request failed with status 403" })).toBe("network");
62 });
63 
64 it("does not use message.includes for classification", () => {
65 // An unknown error code with "403" in message is unknown, not forbidden
66 expect(classify({ error: "some_other_code", message: "got a 403 response" })).toBe("unknown");
67 });
68 });
69 
70 describe("expected API error codes", () => {
71 it("validation_error maps to unknown (not special-cased)", () => {
72 expect(classify({ error: "validation_error", message: "Bad input" })).toBe("unknown");
73 });
74 
75 it("bad_request maps to unknown", () => {
76 expect(classify({ error: "bad_request", message: "Invalid" })).toBe("unknown");
77 });
78 });
79});