quality / commitlint (push) Skipped
CD / update-wiki (push) Successful in 8s
quality / changes (push) Successful in 8s
quality / web (push) Skipped
quality / docker-check (push) Skipped
quality / api (push) Successful in 1m20s
CD / quality (push) Successful in 1m31s
CD / publish (push) Successful in 2m4s
Co-authored-by: Cursor <cursoragent@cursor.com>
302 lines
8.4 KiB
TypeScript
302 lines
8.4 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import {
|
|
resolveDesiredAIps,
|
|
selectActiveIpsByMode,
|
|
shouldRecordFailoverDnsDiff,
|
|
type LbIpRow,
|
|
type LbTargetConfig,
|
|
} from "../src/services/service-config-service.js";
|
|
import { WEIGHTED_SLOT_MS } from "../src/services/routing/weighted.js";
|
|
|
|
function row(
|
|
ip: string,
|
|
opts: Partial<Pick<LbIpRow, "weight" | "priority" | "health">> = {},
|
|
): LbIpRow {
|
|
return {
|
|
ip,
|
|
weight: opts.weight ?? 1,
|
|
priority: opts.priority ?? 1,
|
|
health: opts.health ?? "up",
|
|
};
|
|
}
|
|
|
|
const weightedConfig: LbTargetConfig = {
|
|
lb_mode: "weighted",
|
|
health_check_enabled: true,
|
|
};
|
|
|
|
describe("selectActiveIpsByMode", () => {
|
|
it("round_robin returns all healthy ips, falls back to all if none healthy", () => {
|
|
const config: LbTargetConfig = {
|
|
lb_mode: "round_robin",
|
|
health_check_enabled: true,
|
|
};
|
|
const rows = [
|
|
row("1.1.1.1", { health: "up" }),
|
|
row("2.2.2.2", { health: "down" }),
|
|
row("3.3.3.3", { health: "up" }),
|
|
];
|
|
expect(selectActiveIpsByMode(config, rows).sort()).toEqual([
|
|
"1.1.1.1",
|
|
"3.3.3.3",
|
|
]);
|
|
});
|
|
|
|
it("round_robin returns all ips when none checked (unknown)", () => {
|
|
const config: LbTargetConfig = {
|
|
lb_mode: "round_robin",
|
|
health_check_enabled: true,
|
|
};
|
|
const rows = [
|
|
row("1.1.1.1", { health: "unknown" }),
|
|
row("2.2.2.2", { health: "unknown" }),
|
|
];
|
|
expect(selectActiveIpsByMode(config, rows).sort()).toEqual([
|
|
"1.1.1.1",
|
|
"2.2.2.2",
|
|
]);
|
|
});
|
|
|
|
it("failover returns only min-priority healthy ips", () => {
|
|
const config: LbTargetConfig = {
|
|
lb_mode: "failover",
|
|
health_check_enabled: true,
|
|
};
|
|
const rows = [
|
|
row("1.1.1.1", { priority: 1, health: "up" }),
|
|
row("2.2.2.2", { priority: 2, health: "up" }),
|
|
row("3.3.3.3", { priority: 1, health: "down" }),
|
|
];
|
|
expect(selectActiveIpsByMode(config, rows)).toEqual(["1.1.1.1"]);
|
|
});
|
|
|
|
it("failover returns recovering unknown primary immediately", () => {
|
|
const config: LbTargetConfig = {
|
|
lb_mode: "failover",
|
|
health_check_enabled: true,
|
|
};
|
|
const rows = [
|
|
row("1.1.1.1", { priority: 1, health: "unknown" }),
|
|
row("2.2.2.2", { priority: 2, health: "up" }),
|
|
];
|
|
expect(selectActiveIpsByMode(config, rows)).toEqual(["1.1.1.1"]);
|
|
});
|
|
|
|
it("failover falls back to min-priority ip among all when none healthy", () => {
|
|
const config: LbTargetConfig = {
|
|
lb_mode: "failover",
|
|
health_check_enabled: true,
|
|
};
|
|
const rows = [
|
|
row("1.1.1.1", { priority: 3, health: "down" }),
|
|
row("2.2.2.2", { priority: 1, health: "down" }),
|
|
row("3.3.3.3", { priority: 2, health: "down" }),
|
|
];
|
|
expect(selectActiveIpsByMode(config, rows)).toEqual(["2.2.2.2"]);
|
|
});
|
|
|
|
it("weighted 1:3 picks the lighter ip on slot 0 and the heavier on slot 1", () => {
|
|
const rows = [
|
|
row("1.1.1.1", { weight: 1, health: "up" }),
|
|
row("2.2.2.2", { weight: 3, health: "up" }),
|
|
];
|
|
expect(selectActiveIpsByMode(weightedConfig, rows, 0)).toEqual(["1.1.1.1"]);
|
|
expect(selectActiveIpsByMode(weightedConfig, rows, WEIGHTED_SLOT_MS)).toEqual([
|
|
"2.2.2.2",
|
|
]);
|
|
});
|
|
|
|
it("weighted excludes down ips from the cycle", () => {
|
|
const rows = [
|
|
row("1.1.1.1", { weight: 1, health: "up" }),
|
|
row("2.2.2.2", { weight: 3, health: "down" }),
|
|
];
|
|
expect(selectActiveIpsByMode(weightedConfig, rows, 0)).toEqual(["1.1.1.1"]);
|
|
expect(
|
|
selectActiveIpsByMode(weightedConfig, rows, WEIGHTED_SLOT_MS),
|
|
).toEqual(["1.1.1.1"]);
|
|
});
|
|
|
|
it("weighted includes recovering unknown in the cycle", () => {
|
|
const rows = [
|
|
row("1.1.1.1", { weight: 1, health: "up" }),
|
|
row("2.2.2.2", { weight: 3, health: "unknown" }),
|
|
];
|
|
expect(selectActiveIpsByMode(weightedConfig, rows, 0)).toEqual(["1.1.1.1"]);
|
|
expect(
|
|
selectActiveIpsByMode(weightedConfig, rows, WEIGHTED_SLOT_MS),
|
|
).toEqual(["2.2.2.2"]);
|
|
});
|
|
|
|
it("weighted with one ip always returns that ip", () => {
|
|
expect(
|
|
selectActiveIpsByMode(weightedConfig, [row("1.1.1.1", { weight: 5 })], 0),
|
|
).toEqual(["1.1.1.1"]);
|
|
});
|
|
|
|
it("weighted with all unknown rotates across every ip", () => {
|
|
const rows = [
|
|
row("1.1.1.1", { weight: 1, health: "unknown" }),
|
|
row("2.2.2.2", { weight: 3, health: "unknown" }),
|
|
];
|
|
expect(selectActiveIpsByMode(weightedConfig, rows, 0)).toEqual(["1.1.1.1"]);
|
|
expect(selectActiveIpsByMode(weightedConfig, rows, WEIGHTED_SLOT_MS)).toEqual([
|
|
"2.2.2.2",
|
|
]);
|
|
});
|
|
|
|
it("weighted returns empty array for no rows", () => {
|
|
expect(selectActiveIpsByMode(weightedConfig, [], 0)).toEqual([]);
|
|
});
|
|
|
|
it("round_robin puts recovering unknown back with live ips", () => {
|
|
const config: LbTargetConfig = {
|
|
lb_mode: "round_robin",
|
|
health_check_enabled: true,
|
|
};
|
|
const rows = [
|
|
row("1.1.1.1", { health: "up" }),
|
|
row("2.2.2.2", { health: "unknown" }),
|
|
];
|
|
expect(selectActiveIpsByMode(config, rows).sort()).toEqual([
|
|
"1.1.1.1",
|
|
"2.2.2.2",
|
|
]);
|
|
});
|
|
|
|
it("round_robin keeps degraded in the pool with live ips", () => {
|
|
const config: LbTargetConfig = {
|
|
lb_mode: "round_robin",
|
|
health_check_enabled: true,
|
|
};
|
|
const rows = [
|
|
row("1.1.1.1", { health: "up" }),
|
|
row("2.2.2.2", { health: "degraded" }),
|
|
];
|
|
expect(selectActiveIpsByMode(config, rows).sort()).toEqual([
|
|
"1.1.1.1",
|
|
"2.2.2.2",
|
|
]);
|
|
});
|
|
|
|
it("returns empty array for no rows", () => {
|
|
const config: LbTargetConfig = {
|
|
lb_mode: "round_robin",
|
|
health_check_enabled: true,
|
|
};
|
|
expect(selectActiveIpsByMode(config, [])).toEqual([]);
|
|
});
|
|
});
|
|
|
|
describe("resolveDesiredAIps", () => {
|
|
it("keeps a dedicated single IP even when down", () => {
|
|
expect(
|
|
resolveDesiredAIps(
|
|
weightedConfig,
|
|
[row("1.1.1.1", { health: "down" })],
|
|
["1.1.1.1"],
|
|
),
|
|
).toEqual(["1.1.1.1"]);
|
|
});
|
|
|
|
it("does not drain when the service has only one unique IP", () => {
|
|
expect(
|
|
resolveDesiredAIps(
|
|
weightedConfig,
|
|
[row("1.1.1.1", { health: "down" })],
|
|
["1.1.1.1", "1.1.1.1"],
|
|
0,
|
|
["1.1.1.1"],
|
|
),
|
|
).toEqual(["1.1.1.1", "1.1.1.1"]);
|
|
});
|
|
|
|
it("does not apply overlay when service pool is a single IP", () => {
|
|
const rows = [
|
|
row("1.1.1.1", { health: "up" }),
|
|
row("2.2.2.2", { health: "down" }),
|
|
];
|
|
expect(
|
|
resolveDesiredAIps(
|
|
weightedConfig,
|
|
rows,
|
|
["1.1.1.1", "2.2.2.2"],
|
|
0,
|
|
["1.1.1.1"],
|
|
),
|
|
).toEqual(["1.1.1.1", "2.2.2.2"]);
|
|
});
|
|
|
|
it("applies weighted overlay on a shared pool", () => {
|
|
const rows = [
|
|
row("1.1.1.1", { weight: 1, health: "up" }),
|
|
row("2.2.2.2", { weight: 3, health: "up" }),
|
|
];
|
|
expect(
|
|
resolveDesiredAIps(weightedConfig, rows, ["1.1.1.1", "2.2.2.2"], 0),
|
|
).toEqual(selectActiveIpsByMode(weightedConfig, rows, 0));
|
|
});
|
|
});
|
|
|
|
describe("shouldRecordFailoverDnsDiff", () => {
|
|
it("skips duplicate listings of the same IP", () => {
|
|
expect(
|
|
shouldRecordFailoverDnsDiff({
|
|
configuredIps: ["1.1.1.1", "1.1.1.1"],
|
|
lbMode: "failover",
|
|
added: [],
|
|
removed: ["1.1.1.1"],
|
|
downIps: new Set(["1.1.1.1"]),
|
|
}),
|
|
).toBe(false);
|
|
});
|
|
|
|
it("skips dedicated extra-FQDN", () => {
|
|
expect(
|
|
shouldRecordFailoverDnsDiff({
|
|
configuredIps: ["1.1.1.1"],
|
|
lbMode: "failover",
|
|
added: [],
|
|
removed: ["1.1.1.1"],
|
|
downIps: new Set(["1.1.1.1"]),
|
|
}),
|
|
).toBe(false);
|
|
});
|
|
|
|
it("skips weighted live-to-live slot swap", () => {
|
|
expect(
|
|
shouldRecordFailoverDnsDiff({
|
|
configuredIps: ["1.1.1.1", "2.2.2.2"],
|
|
lbMode: "weighted",
|
|
added: ["2.2.2.2"],
|
|
removed: ["1.1.1.1"],
|
|
downIps: new Set(),
|
|
}),
|
|
).toBe(false);
|
|
});
|
|
|
|
it("logs weighted swap when a down ip leaves the pool", () => {
|
|
expect(
|
|
shouldRecordFailoverDnsDiff({
|
|
configuredIps: ["1.1.1.1", "2.2.2.2"],
|
|
lbMode: "weighted",
|
|
added: ["2.2.2.2"],
|
|
removed: ["1.1.1.1"],
|
|
downIps: new Set(["1.1.1.1"]),
|
|
}),
|
|
).toBe(true);
|
|
});
|
|
|
|
it("logs failover diffs on a shared pool", () => {
|
|
expect(
|
|
shouldRecordFailoverDnsDiff({
|
|
configuredIps: ["1.1.1.1", "2.2.2.2"],
|
|
lbMode: "failover",
|
|
added: ["2.2.2.2"],
|
|
removed: ["1.1.1.1"],
|
|
downIps: new Set(["1.1.1.1"]),
|
|
}),
|
|
).toBe(true);
|
|
});
|
|
});
|