Files
diegosouzapw--omniroute/tests/unit/resilience-settings-normalize-split.test.ts
2026-07-13 13:39:12 +08:00

125 lines
4.8 KiB
TypeScript

/**
* Split-guard — resilience/settings ↔ settings/types + settings/normalize
*
* Guards the decomposition of the (fully pure) resilience settings module into
* two leaves: settings/types.ts (the shape) and settings/normalize.ts (coercion
* + per-section normalizers). Characterizes the coercers/normalizers and proves
* the host still exposes DEFAULT_RESILIENCE_SETTINGS + resolve/merge/compat with
* the re-exported types. DB-free — the whole layer is pure.
*/
import { describe, it } from "node:test";
import assert from "node:assert/strict";
import {
asRecord,
toInteger,
toBoolean,
normalizeProviderBreakerProfile,
normalizeWaitForCooldownSettings,
} from "../../src/lib/resilience/settings/normalize.ts";
import {
DEFAULT_RESILIENCE_SETTINGS,
resolveResilienceSettings,
mergeResilienceSettings,
buildLegacyResilienceCompat,
} from "../../src/lib/resilience/settings.ts";
import type { ResilienceSettings } from "../../src/lib/resilience/settings.ts";
describe("resilience/settings normalize split-guard", () => {
it("asRecord returns {} for non-objects and passes objects through", () => {
assert.deepEqual(asRecord(null), {});
assert.deepEqual(asRecord(42), {});
assert.deepEqual(asRecord([1, 2]), {});
assert.deepEqual(asRecord({ a: 1 }), { a: 1 });
});
it("toInteger clamps to [min,max], truncates, and falls back on NaN", () => {
assert.equal(toInteger(5.9, 0), 5);
assert.equal(toInteger("7", 0), 7);
assert.equal(toInteger("nope", 3), 3);
assert.equal(toInteger(-10, 0, { min: 1, max: 100 }), 1);
assert.equal(toInteger(9999, 0, { min: 1, max: 100 }), 100);
});
it("toBoolean only accepts real booleans", () => {
assert.equal(toBoolean(true, false), true);
assert.equal(toBoolean("true", false), false);
assert.equal(toBoolean(undefined, true), true);
});
it("normalizeProviderBreakerProfile keeps degradation below failure threshold", () => {
const out = normalizeProviderBreakerProfile(
{ failureThreshold: 3, degradationThreshold: 10, resetTimeoutMs: 60000 },
{ failureThreshold: 5, degradationThreshold: 4, resetTimeoutMs: 30000 }
);
assert.equal(out.failureThreshold, 3);
assert.equal(out.degradationThreshold, 2); // clamped to failureThreshold - 1
assert.equal(out.resetTimeoutMs, 60000);
});
it("normalizeWaitForCooldownSettings derives ms and disables on zero retries/wait", () => {
const on = normalizeWaitForCooldownSettings(
{ enabled: true, maxRetries: 2, maxRetryWaitSec: 30 },
{ enabled: true, maxRetries: 3, maxRetryWaitSec: 30, maxRetryWaitMs: 30000 }
);
assert.equal(on.enabled, true);
assert.equal(on.maxRetryWaitMs, 30000);
const off = normalizeWaitForCooldownSettings(
{ enabled: true, maxRetries: 0, maxRetryWaitSec: 30 },
{ enabled: true, maxRetries: 3, maxRetryWaitSec: 30, maxRetryWaitMs: 30000 }
);
assert.equal(off.enabled, false); // maxRetries 0 forces disabled
});
it("host exposes DEFAULT_RESILIENCE_SETTINGS with the full section set", () => {
const keys = Object.keys(DEFAULT_RESILIENCE_SETTINGS).sort();
assert.deepEqual(keys, [
"comboCooldownWait",
"connectionCooldown",
"providerBreaker",
"providerCooldown",
"quotaPreflight",
"quotaShareConcurrencyLimit",
"requestQueue",
"streamRecovery",
"waitForCooldown",
]);
});
it("host resolveResilienceSettings(null) yields normalized defaults", () => {
const resolved: ResilienceSettings = resolveResilienceSettings(null);
assert.equal(typeof resolved.requestQueue.requestsPerMinute, "number");
assert.equal(typeof resolved.providerBreaker.oauth.failureThreshold, "number");
// degradation is always kept below failure by the normalizer.
assert.ok(
resolved.providerBreaker.oauth.degradationThreshold <
resolved.providerBreaker.oauth.failureThreshold ||
resolved.providerBreaker.oauth.failureThreshold <= 1
);
});
it("host mergeResilienceSettings applies a partial patch", () => {
const base = resolveResilienceSettings(null);
const merged = mergeResilienceSettings(base, {
requestQueue: { concurrentRequests: 7 },
});
assert.equal(merged.requestQueue.concurrentRequests, 7);
assert.equal(merged.requestQueue.requestsPerMinute, base.requestQueue.requestsPerMinute);
});
it("host buildLegacyResilienceCompat round-trips connection cooldown into profiles", () => {
const compat = buildLegacyResilienceCompat(DEFAULT_RESILIENCE_SETTINGS);
assert.equal(
compat.profiles.oauth.transientCooldown,
DEFAULT_RESILIENCE_SETTINGS.connectionCooldown.oauth.baseCooldownMs
);
assert.equal(
compat.defaults.requestsPerMinute,
DEFAULT_RESILIENCE_SETTINGS.requestQueue.requestsPerMinute
);
});
});