Repository navigation
Expand file tree
/
Copy patherror-handler.test.ts
More file actions
95 lines (84 loc) · 2.86 KB
/
Copy patherror-handler.test.ts
File metadata and controls
95 lines (84 loc) · 2.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
import { describe, it, expect, beforeAll, vi } from "vitest";
vi.mock("../../src/db", () => ({
prisma: {
user: { findUnique: vi.fn(), upsert: vi.fn() },
group: { findUnique: vi.fn() },
groupMember: { findUnique: vi.fn() },
auditLog: { create: vi.fn() },
$disconnect: vi.fn(),
},
}));
vi.mock("../../src/services/stellar", async (importActual) => {
const actual = await importActual<typeof import("../../src/services/stellar")>();
return {
...actual,
stellar: {
...actual.stellar,
loadAccount: vi.fn(async () => ({
exists: false,
sequence: "0",
balances: [],
signers: [],
thresholds: { low: 0, med: 0, high: 0 },
})),
},
};
});
import { buildApp } from "../../src/app";
import { z } from "zod";
let app: Awaited<ReturnType<typeof buildApp>>;
beforeAll(async () => {
app = await buildApp();
app.get("/test-zod/simple", async () => {
z.object({ name: z.string().min(1) }).parse({ name: "" });
});
app.get("/test-zod/nested", async () => {
z.object({
user: z.object({
age: z.number().min(18),
}),
}).parse({ user: { age: 15 } });
});
});
describe("Zod validation error handler", () => {
it("returns 400 with structured details for simple Zod validation failure", async () => {
const res = await app.inject({ method: "GET", url: "/test-zod/simple" });
expect(res.statusCode).toBe(400);
const body = res.json();
expect(body.error.code).toBe("VALIDATION_ERROR");
expect(Array.isArray(body.error.details)).toBe(true);
expect(body.error.details[0]).toMatchObject({
field: expect.any(String),
message: expect.any(String),
code: expect.any(String),
});
expect(body.requestId).toBeTruthy();
});
it("returns field-level details for nested Zod schemas", async () => {
const res = await app.inject({ method: "GET", url: "/test-zod/nested" });
expect(res.statusCode).toBe(400);
const body = res.json();
expect(body.error.code).toBe("VALIDATION_ERROR");
expect(Array.isArray(body.error.details)).toBe(true);
expect(body.error.details[0].field).toBe("user.age");
expect(body.error.details[0].message).toContain("18");
expect(body.requestId).toBeTruthy();
});
it("does not affect 404 responses for unknown routes", async () => {
const res = await app.inject({ method: "GET", url: "/nonexistent-route-xyz" });
expect(res.statusCode).toBe(404);
const body = res.json();
expect(body.code).toBe("NOT_FOUND");
expect(body.statusCode).toBeUndefined();
});
it("returns 400 for malformed POST body to a route with Zod validation", async () => {
const res = await app.inject({
method: "POST",
url: "/auth/challenge",
payload: {},
});
expect(res.statusCode).toBe(400);
const body = res.json();
expect(body.code).toBe("VALIDATION_ERROR");
});
});