178 lines
5.5 KiB
TypeScript
178 lines
5.5 KiB
TypeScript
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",
|
||
);
|
||
});
|
||
});
|
||
});
|