Files
2026-07-13 13:39:12 +08:00

444 lines
14 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
// ─── toolLimitDetector ────────────────────────────────────────────────────────
test("toolLimitDetector: getEffectiveToolLimit returns MAX_TOOLS_LIMIT default", async () => {
const { getEffectiveToolLimit, clearDetectedLimits } = await import(
"../../open-sse/services/toolLimitDetector.ts"
);
clearDetectedLimits();
const limit = getEffectiveToolLimit("nonexistent-provider");
assert.equal(limit, 128, "default limit should be MAX_TOOLS_LIMIT (128)");
});
test("toolLimitDetector: setDetectedToolLimit only lowers the limit", async () => {
const { getEffectiveToolLimit, setDetectedToolLimit, clearDetectedLimits } = await import(
"../../open-sse/services/toolLimitDetector.ts"
);
clearDetectedLimits();
setDetectedToolLimit("test-provider", 50);
assert.equal(getEffectiveToolLimit("test-provider"), 50);
// Attempting to raise it back to 100 should be ignored (only lowers)
setDetectedToolLimit("test-provider", 100);
assert.equal(getEffectiveToolLimit("test-provider"), 50);
// Lowering further should work
setDetectedToolLimit("test-provider", 30);
assert.equal(getEffectiveToolLimit("test-provider"), 30);
});
test("toolLimitDetector: parseToolLimitFromError extracts numeric limits", async () => {
const { parseToolLimitFromError } = await import(
"../../open-sse/services/toolLimitDetector.ts"
);
assert.equal(parseToolLimitFromError("'tools': maximum number of items is 64"), 64);
assert.equal(parseToolLimitFromError("Maximum number of tools allowed is 32"), 32);
assert.equal(parseToolLimitFromError("Too many tools. Maximum 128"), 128);
assert.equal(parseToolLimitFromError("no limit here"), null);
assert.equal(parseToolLimitFromError("tool limit 0"), null, "rejects zero");
});
test("toolLimitDetector: shouldDetectLimit checks status 400 and keywords", async () => {
const { shouldDetectLimit } = await import("../../open-sse/services/toolLimitDetector.ts");
assert.equal(shouldDetectLimit("maximum number of tools exceeded", 400), true);
assert.equal(shouldDetectLimit("too many tools", 400), true);
assert.equal(shouldDetectLimit("maximum number of tools exceeded", 500), false);
assert.equal(shouldDetectLimit("some other error", 400), false);
});
test("toolLimitDetector: clearDetectedLimits resets all entries", async () => {
const { setDetectedToolLimit, getDetectedToolLimit, clearDetectedLimits } = await import(
"../../open-sse/services/toolLimitDetector.ts"
);
setDetectedToolLimit("clear-test-a", 10);
setDetectedToolLimit("clear-test-b", 20);
assert.equal(getDetectedToolLimit("clear-test-a"), 10);
clearDetectedLimits();
assert.equal(getDetectedToolLimit("clear-test-a"), 128, "should revert to default");
assert.equal(getDetectedToolLimit("clear-test-b"), 128, "should revert to default");
});
// ─── codexQuotaFetcher: connectionRegistry bounds ────────────────────────────
test("codexQuotaFetcher: register/unregister connection lifecycle", async () => {
const { registerCodexConnection, unregisterCodexConnection } = await import(
"../../open-sse/services/codexQuotaFetcher.ts"
);
// Should not throw
registerCodexConnection("conn-1", { accessToken: "tok-abc" });
registerCodexConnection("conn-2", { accessToken: "tok-def", workspaceId: "ws-1" });
unregisterCodexConnection("conn-1");
unregisterCodexConnection("conn-2");
// unregistering non-existent should not throw
unregisterCodexConnection("conn-nonexistent");
});
test("codexQuotaFetcher: connectionRegistry evicts oldest when full (100)", async () => {
const { registerCodexConnection, unregisterCodexConnection } = await import(
"../../open-sse/services/codexQuotaFetcher.ts"
);
for (let i = 0; i < 100; i++) {
registerCodexConnection(`evict-test-${i}`, { accessToken: `tok-${i}` });
}
registerCodexConnection("evict-test-overflow", { accessToken: "tok-overflow" });
assert.doesNotThrow(
() => unregisterCodexConnection("evict-test-0"),
"unregistering evicted conn-0 should be a no-op, not throw"
);
registerCodexConnection("evict-test-0", { accessToken: "tok-re" });
assert.doesNotThrow(
() => unregisterCodexConnection("evict-test-0"),
"re-registered conn-0 should unregister cleanly"
);
for (let i = 1; i <= 100; i++) {
unregisterCodexConnection(`evict-test-${i}`);
}
unregisterCodexConnection("evict-test-overflow");
unregisterCodexConnection("evict-test-0");
});
test("codexQuotaFetcher: invalidateCodexQuotaCache does not throw", async () => {
const { invalidateCodexQuotaCache } = await import(
"../../open-sse/services/codexQuotaFetcher.ts"
);
assert.doesNotThrow(() => invalidateCodexQuotaCache("nonexistent"));
});
test("codexQuotaFetcher: getCodexQuotaCooldownMs returns 0 when under threshold", async () => {
const { getCodexQuotaCooldownMs } = await import(
"../../open-sse/services/codexQuotaFetcher.ts"
);
const quota = {
used: 50,
total: 100,
percentUsed: 0.5,
resetAt: null,
window5h: { percentUsed: 0.5, resetAt: null },
window7d: { percentUsed: 0.3, resetAt: null },
limitReached: false,
};
assert.equal(getCodexQuotaCooldownMs(quota), 0);
});
test("codexQuotaFetcher: getCodexQuotaCooldownMs returns cooldown when 7d exhausted", async () => {
const { getCodexQuotaCooldownMs } = await import(
"../../open-sse/services/codexQuotaFetcher.ts"
);
const futureReset = new Date(Date.now() + 60_000).toISOString();
const quota = {
used: 96,
total: 100,
percentUsed: 0.96,
resetAt: futureReset,
window5h: { percentUsed: 0.5, resetAt: null },
window7d: { percentUsed: 0.96, resetAt: futureReset },
limitReached: false,
};
const cooldown = getCodexQuotaCooldownMs(quota);
assert.ok(cooldown > 0, "should return positive cooldown");
assert.ok(cooldown <= 60_000, "should be bounded by reset time");
});
// ─── quotaMonitor: alertSuppression bounds ───────────────────────────────────
test("quotaMonitor: clearQuotaMonitors resets active count to 0", async () => {
const { clearQuotaMonitors, getActiveMonitorCount } = await import(
"../../open-sse/services/quotaMonitor.ts"
);
clearQuotaMonitors();
assert.equal(getActiveMonitorCount(), 0);
});
test("quotaMonitor: isQuotaMonitorEnabled checks providerSpecificData flag", async () => {
const { isQuotaMonitorEnabled } = await import(
"../../open-sse/services/quotaMonitor.ts"
);
assert.equal(isQuotaMonitorEnabled({ providerSpecificData: { quotaMonitorEnabled: true } }), true);
assert.equal(isQuotaMonitorEnabled({ providerSpecificData: { quotaMonitorEnabled: false } }), false);
assert.equal(isQuotaMonitorEnabled({ providerSpecificData: {} }), false);
assert.equal(isQuotaMonitorEnabled({}), false);
assert.equal(isQuotaMonitorEnabled({ providerSpecificData: null }), false);
});
test("quotaMonitor: getQuotaMonitorSummary returns zeroed after clear", async () => {
const { clearQuotaMonitors, getQuotaMonitorSummary } = await import(
"../../open-sse/services/quotaMonitor.ts"
);
clearQuotaMonitors();
const summary = getQuotaMonitorSummary();
assert.equal(summary.active, 0);
assert.equal(summary.alerting, 0);
assert.equal(summary.exhausted, 0);
assert.equal(summary.errors, 0);
});
test("quotaMonitor: getQuotaMonitorSnapshots returns empty array after clear", async () => {
const { clearQuotaMonitors, getQuotaMonitorSnapshots } = await import(
"../../open-sse/services/quotaMonitor.ts"
);
clearQuotaMonitors();
const snapshots = getQuotaMonitorSnapshots();
assert.ok(Array.isArray(snapshots));
assert.equal(snapshots.length, 0);
});
test("quotaMonitor: getQuotaMonitorSnapshot returns null for unknown session", async () => {
const { clearQuotaMonitors, getQuotaMonitorSnapshot } = await import(
"../../open-sse/services/quotaMonitor.ts"
);
clearQuotaMonitors();
assert.equal(getQuotaMonitorSnapshot("nonexistent-session"), null);
});
// ─── ipFilter: tempBans with time advancement ────────────────────────────────
test("ipFilter: tempBanIP blocks then expires", async () => {
const { configureIPFilter, checkIP, tempBanIP, removeTempBan, resetIPFilter } = await import(
"../../open-sse/services/ipFilter.ts"
);
resetIPFilter();
configureIPFilter({ enabled: true, mode: "blacklist" });
tempBanIP("10.0.0.1", 50, "test ban");
const blocked = checkIP("10.0.0.1");
assert.equal(blocked.allowed, false);
assert.ok(blocked.reason?.includes("Temporarily banned"));
// Wait for ban to expire
await new Promise((r) => setTimeout(r, 80));
const unblocked = checkIP("10.0.0.1");
assert.equal(unblocked.allowed, true);
resetIPFilter();
});
test("ipFilter: removeTempBan immediately lifts ban", async () => {
const { configureIPFilter, checkIP, tempBanIP, removeTempBan, resetIPFilter } = await import(
"../../open-sse/services/ipFilter.ts"
);
resetIPFilter();
configureIPFilter({ enabled: true, mode: "blacklist" });
tempBanIP("10.0.0.2", 60_000, "long ban");
assert.equal(checkIP("10.0.0.2").allowed, false);
removeTempBan("10.0.0.2");
assert.equal(checkIP("10.0.0.2").allowed, true);
resetIPFilter();
});
test("ipFilter: blacklist blocks listed IPs", async () => {
const { configureIPFilter, checkIP, addToBlacklist, resetIPFilter } = await import(
"../../open-sse/services/ipFilter.ts"
);
resetIPFilter();
configureIPFilter({ enabled: true, mode: "blacklist" });
addToBlacklist("192.168.1.100");
const result = checkIP("192.168.1.100");
assert.equal(result.allowed, false);
assert.equal(result.reason, "IP blacklisted");
assert.equal(checkIP("192.168.1.101").allowed, true);
resetIPFilter();
});
test("ipFilter: whitelist mode only allows listed IPs", async () => {
const { configureIPFilter, checkIP, addToWhitelist, resetIPFilter } = await import(
"../../open-sse/services/ipFilter.ts"
);
resetIPFilter();
configureIPFilter({ enabled: true, mode: "whitelist" });
addToWhitelist("10.0.0.5");
assert.equal(checkIP("10.0.0.5").allowed, true);
assert.equal(checkIP("10.0.0.6").allowed, false);
resetIPFilter();
});
test("ipFilter: getIPFilterConfig reflects current state", async () => {
const { configureIPFilter, tempBanIP, getIPFilterConfig, resetIPFilter } = await import(
"../../open-sse/services/ipFilter.ts"
);
resetIPFilter();
configureIPFilter({ enabled: true, mode: "whitelist", whitelist: ["10.0.0.1"] });
tempBanIP("10.0.0.2", 60_000, "config check");
const config = getIPFilterConfig();
assert.equal(config.enabled, true);
assert.equal(config.mode, "whitelist");
assert.ok(config.whitelist.includes("10.0.0.1"));
assert.ok(config.tempBans.length >= 1);
resetIPFilter();
});
// ─── circuitBreaker: creation and stale breaker cleanup ──────────────────────
test("circuitBreaker: getCircuitBreaker creates and returns breaker", async () => {
const { getCircuitBreaker, resetAllCircuitBreakers } = await import(
"../../src/shared/utils/circuitBreaker.ts"
);
resetAllCircuitBreakers();
const breaker = getCircuitBreaker("test-create-1");
assert.ok(breaker, "breaker should be created");
assert.equal(breaker.name, "test-create-1");
assert.equal(breaker.state, "CLOSED");
assert.equal(breaker.failureCount, 0);
resetAllCircuitBreakers();
});
test("circuitBreaker: getCircuitBreaker returns same instance for same name", async () => {
const { getCircuitBreaker, resetAllCircuitBreakers } = await import(
"../../src/shared/utils/circuitBreaker.ts"
);
resetAllCircuitBreakers();
const a = getCircuitBreaker("test-same-instance");
const b = getCircuitBreaker("test-same-instance");
assert.equal(a, b, "should return the same instance");
resetAllCircuitBreakers();
});
test("circuitBreaker: breaker transitions CLOSED -> OPEN after threshold failures", async () => {
const { getCircuitBreaker, resetAllCircuitBreakers } = await import(
"../../src/shared/utils/circuitBreaker.ts"
);
resetAllCircuitBreakers();
const breaker = getCircuitBreaker("test-threshold", {
failureThreshold: 3,
resetTimeout: 60_000,
});
for (let i = 0; i < 3; i++) {
try {
await breaker.execute(async () => {
throw new Error("fail");
});
} catch {}
}
assert.equal(breaker.state, "OPEN");
// Should reject while open
await assert.rejects(
() => breaker.execute(async () => "ok"),
{ name: "CircuitBreakerOpenError" }
);
resetAllCircuitBreakers();
});
test("circuitBreaker: canExecute returns correct value per state", async () => {
const { getCircuitBreaker, resetAllCircuitBreakers } = await import(
"../../src/shared/utils/circuitBreaker.ts"
);
resetAllCircuitBreakers();
const breaker = getCircuitBreaker("test-can-execute", {
failureThreshold: 2,
resetTimeout: 60_000,
});
assert.equal(breaker.canExecute(), true, "CLOSED -> canExecute");
for (let i = 0; i < 2; i++) {
try {
await breaker.execute(async () => {
throw new Error("fail");
});
} catch {}
}
assert.equal(breaker.canExecute(), false, "OPEN -> !canExecute");
resetAllCircuitBreakers();
});
test("circuitBreaker: getStatus returns expected shape", async () => {
const { getCircuitBreaker, resetAllCircuitBreakers } = await import(
"../../src/shared/utils/circuitBreaker.ts"
);
resetAllCircuitBreakers();
const breaker = getCircuitBreaker("test-status");
const status = breaker.getStatus();
assert.equal(status.name, "test-status");
assert.equal(status.state, "CLOSED");
assert.equal(status.failureCount, 0);
assert.equal(status.lastFailureTime, null);
assert.equal(typeof status.retryAfterMs, "number");
resetAllCircuitBreakers();
});
test("circuitBreaker: reset returns breaker to CLOSED", async () => {
const { getCircuitBreaker, resetAllCircuitBreakers } = await import(
"../../src/shared/utils/circuitBreaker.ts"
);
resetAllCircuitBreakers();
const breaker = getCircuitBreaker("test-reset", {
failureThreshold: 1,
resetTimeout: 60_000,
});
try {
await breaker.execute(async () => {
throw new Error("fail");
});
} catch {}
assert.equal(breaker.state, "OPEN");
breaker.reset();
assert.equal(breaker.state, "CLOSED");
assert.equal(breaker.failureCount, 0);
resetAllCircuitBreakers();
});