chore(parent-bff): merge parent-bff module into main
Merge feat/parent-bff-ai05 into main, conflicts resolved in favor of feature branch
This commit is contained in:
177
services/parent-bff/test/unit/application-error.test.ts
Normal file
177
services/parent-bff/test/unit/application-error.test.ts
Normal file
@@ -0,0 +1,177 @@
|
||||
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",
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user