feat: initialize parent-bff service with full core features

add complete parent-bff implementation including:
- GraphQL endpoint with depth/cost validation
- ChildGuard越权校验 with redis cache and singleflight
- parallel orchestration with partial failure fallback
- three-level cache fallback strategy (Redis + LRU + downstream)
- Kafka consumer for cache invalidation and notification push
- opossum circuit breaker for downstream services
- Prometheus metrics and SLO alerts
- Helm chart for k8s deployment with multi-environment configs
- Grafana dashboard for observability
- complete unit and integration tests
This commit is contained in:
SpecialX
2026-07-10 18:49:06 +08:00
parent a7d8f92227
commit 2229309a1e
88 changed files with 11221 additions and 0 deletions

View File

@@ -0,0 +1,178 @@
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",
);
});
});
});