Files
Edu/services/parent-bff/test/unit/application-error.test.ts
SpecialX 8f21ca6a68 chore(parent-bff): merge parent-bff module into main
Merge feat/parent-bff-ai05 into main, conflicts resolved in favor of feature branch
2026-07-10 19:00:29 +08:00

178 lines
5.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, it, expect } from "vitest";
import {
ApplicationError,
ValidationError,
UnauthorizedError,
ChildNotBoundError,
NotFoundError,
ConflictError,
BusinessError,
BadGatewayError,
GatewayTimeoutError,
ServiceUnavailableError,
InternalError,
} from "../../src/shared/errors/application-error.js";
describe("ApplicationError 体系", () => {
describe("ValidationError", () => {
it("statusCode 为 400", () => {
const err = new ValidationError("参数错误");
expect(err.statusCode).toBe(400);
expect(err.code).toBe("BFF_PARENT_VALIDATION_ERROR");
expect(err.type).toBe("validation");
expect(err.message).toBe("参数错误");
});
it("携带 details", () => {
const err = new ValidationError("参数错误", { field: "name" });
expect(err.details).toEqual({ field: "name" });
});
});
describe("UnauthorizedError", () => {
it("statusCode 为 401", () => {
const err = new UnauthorizedError("未授权");
expect(err.statusCode).toBe(401);
expect(err.code).toBe("BFF_PARENT_UNAUTHORIZED");
});
});
describe("ChildNotBoundError", () => {
it("statusCode 为 403", () => {
const err = new ChildNotBoundError("parent-001", "student-999", [
"student-001",
"student-002",
]);
expect(err.statusCode).toBe(403);
expect(err.code).toBe("BFF_PARENT_CHILD_NOT_BOUND");
expect(err.type).toBe("child_not_bound");
expect(err.details).toEqual({
parentId: "parent-001",
requestedChildId: "student-999",
boundChildren: ["student-001", "student-002"],
});
});
it("message 包含 parentId 和 childId", () => {
const err = new ChildNotBoundError("parent-001", "student-999", []);
expect(err.message).toContain("student-999");
expect(err.message).toContain("parent-001");
});
});
describe("NotFoundError", () => {
it("statusCode 为 404", () => {
const err = new NotFoundError("Parent", "parent-999");
expect(err.statusCode).toBe(404);
expect(err.code).toBe("BFF_PARENT_NOT_FOUND");
expect(err.message).toContain("Parent");
expect(err.message).toContain("parent-999");
});
});
describe("ConflictError", () => {
it("statusCode 为 409", () => {
const err = new ConflictError("冲突");
expect(err.statusCode).toBe(409);
expect(err.code).toBe("BFF_PARENT_CONFLICT");
});
});
describe("BusinessError", () => {
it("statusCode 为 422", () => {
const err = new BusinessError("业务错误");
expect(err.statusCode).toBe(422);
expect(err.code).toBe("BFF_PARENT_BUSINESS_ERROR");
});
});
describe("BadGatewayError", () => {
it("statusCode 为 502", () => {
const err = new BadGatewayError("下游错误");
expect(err.statusCode).toBe(502);
expect(err.code).toBe("BFF_PARENT_BAD_GATEWAY");
});
});
describe("GatewayTimeoutError", () => {
it("statusCode 为 504", () => {
const err = new GatewayTimeoutError("超时");
expect(err.statusCode).toBe(504);
expect(err.code).toBe("BFF_PARENT_GATEWAY_TIMEOUT");
});
});
describe("ServiceUnavailableError", () => {
it("statusCode 为 503", () => {
const err = new ServiceUnavailableError("不可用");
expect(err.statusCode).toBe(503);
expect(err.code).toBe("BFF_PARENT_SERVICE_UNAVAILABLE");
});
});
describe("InternalError", () => {
it("statusCode 为 500", () => {
const err = new InternalError("内部错误");
expect(err.statusCode).toBe(500);
expect(err.code).toBe("BFF_PARENT_INTERNAL_ERROR");
});
});
describe("toJSON", () => {
it("序列化为 ActionState 信封格式", () => {
const err = new ValidationError("参数错误", { field: "name" });
err.traceId = "trace-001";
const json = err.toJSON();
expect(json.success).toBe(false);
// 从 unknown 转换toJSON 返回 Record<string, unknown>
const error = json.error as {
code: string;
message: string;
details: unknown;
traceId?: string;
};
expect(error.code).toBe("BFF_PARENT_VALIDATION_ERROR");
expect(error.message).toBe("参数错误");
expect(error.details).toEqual({ field: "name" });
expect(error.traceId).toBe("trace-001");
});
it("无 traceId 时序列化正常", () => {
const err = new NotFoundError("Child", "child-999");
const json = err.toJSON();
expect(json.success).toBe(false);
// 从 unknown 转换toJSON 返回 Record<string, unknown>
const error = json.error as { traceId?: string };
expect(error.traceId).toBeUndefined();
});
});
describe("继承关系", () => {
it("所有错误继承自 ApplicationError", () => {
const errors = [
new ValidationError(""),
new UnauthorizedError(""),
new ChildNotBoundError("", "", []),
new NotFoundError("", ""),
new ConflictError(""),
new BusinessError(""),
new BadGatewayError(""),
new GatewayTimeoutError(""),
new ServiceUnavailableError(""),
new InternalError(""),
];
for (const err of errors) {
expect(err).toBeInstanceOf(ApplicationError);
expect(err).toBeInstanceOf(Error);
}
});
it("name 属性为构造函数名", () => {
expect(new ValidationError("").name).toBe("ValidationError");
expect(new ChildNotBoundError("", "", []).name).toBe(
"ChildNotBoundError",
);
});
});
});