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:
99
services/parent-bff/test/unit/lru.cache.test.ts
Normal file
99
services/parent-bff/test/unit/lru.cache.test.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
import { describe, it, expect, beforeEach } from "vitest";
|
||||
import { LruCache } from "../../src/shared/cache/lru.cache.js";
|
||||
|
||||
describe("LruCache", () => {
|
||||
let cache: LruCache;
|
||||
|
||||
beforeEach(() => {
|
||||
cache = new LruCache(3);
|
||||
});
|
||||
|
||||
describe("set / get", () => {
|
||||
it("应该存取值", () => {
|
||||
cache.set("key1", "value1", 60);
|
||||
expect(cache.get("key1")).toBe("value1");
|
||||
});
|
||||
|
||||
it("未设置的 key 返回 null", () => {
|
||||
expect(cache.get("nonexistent")).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("TTL 过期", () => {
|
||||
it("过期后返回 null", async () => {
|
||||
cache.set("key1", "value1", 0.01);
|
||||
await new Promise((resolve) => setTimeout(resolve, 20));
|
||||
expect(cache.get("key1")).toBeNull();
|
||||
});
|
||||
|
||||
it("未过期时返回值", () => {
|
||||
cache.set("key1", "value1", 60);
|
||||
expect(cache.get("key1")).toBe("value1");
|
||||
});
|
||||
});
|
||||
|
||||
describe("LRU 淘汰", () => {
|
||||
it("容量满时淘汰最久未使用的条目", () => {
|
||||
cache.set("a", "1", 60);
|
||||
cache.set("b", "2", 60);
|
||||
cache.set("c", "3", 60);
|
||||
cache.set("d", "4", 60);
|
||||
|
||||
expect(cache.get("a")).toBeNull();
|
||||
expect(cache.get("b")).toBe("2");
|
||||
expect(cache.get("c")).toBe("3");
|
||||
expect(cache.get("d")).toBe("4");
|
||||
});
|
||||
|
||||
it("访问后更新 LRU 顺序", () => {
|
||||
cache.set("a", "1", 60);
|
||||
cache.set("b", "2", 60);
|
||||
cache.set("c", "3", 60);
|
||||
|
||||
cache.get("a");
|
||||
|
||||
cache.set("d", "4", 60);
|
||||
|
||||
expect(cache.get("a")).toBe("1");
|
||||
expect(cache.get("b")).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("delete", () => {
|
||||
it("删除后返回 null", () => {
|
||||
cache.set("key1", "value1", 60);
|
||||
cache.delete("key1");
|
||||
expect(cache.get("key1")).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("clear", () => {
|
||||
it("清空所有条目", () => {
|
||||
cache.set("a", "1", 60);
|
||||
cache.set("b", "2", 60);
|
||||
cache.clear();
|
||||
expect(cache.size).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("size", () => {
|
||||
it("返回当前条目数", () => {
|
||||
expect(cache.size).toBe(0);
|
||||
cache.set("a", "1", 60);
|
||||
expect(cache.size).toBe(1);
|
||||
cache.set("b", "2", 60);
|
||||
expect(cache.size).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
it("默认 maxSize 为 100", () => {
|
||||
const defaultCache = new LruCache();
|
||||
for (let i = 0; i < 100; i++) {
|
||||
defaultCache.set(`key${i}`, `val${i}`, 60);
|
||||
}
|
||||
expect(defaultCache.size).toBe(100);
|
||||
defaultCache.set("key100", "val100", 60);
|
||||
expect(defaultCache.size).toBe(100);
|
||||
expect(defaultCache.get("key0")).toBeNull();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user