netbird-gitops/src/reconcile/executor.test.ts
2026-03-06 13:21:08 +02:00

228 lines
6.0 KiB
TypeScript

import { assertEquals } from "@std/assert";
import { executeOperations } from "./executor.ts";
import type { Operation } from "./operations.ts";
import type { ActualState } from "../state/actual.ts";
function emptyActual(): ActualState {
return {
groups: [],
groupsByName: new Map(),
groupsById: new Map(),
setupKeys: [],
setupKeysByName: new Map(),
peers: [],
peersByName: new Map(),
peersById: new Map(),
policies: [],
policiesByName: new Map(),
routes: [],
routesByNetworkId: new Map(),
dns: [],
dnsByName: new Map(),
};
}
Deno.test("executor calls createGroup for create_group op", async () => {
const calls: string[] = [];
const mockClient = {
createGroup: (data: { name: string }) => {
calls.push(`createGroup:${data.name}`);
return Promise.resolve({
id: "new-g1",
name: data.name,
peers_count: 0,
peers: [],
issued: "api" as const,
});
},
};
const ops: Operation[] = [
{ type: "create_group", name: "pilots" },
];
const { results } = await executeOperations(
ops,
mockClient as never,
emptyActual(),
);
assertEquals(calls, ["createGroup:pilots"]);
assertEquals(results[0].status, "success");
});
Deno.test("executor aborts on first failure", async () => {
const mockClient = {
createGroup: () => Promise.reject(new Error("API down")),
createSetupKey: () => Promise.resolve({ id: 1, key: "k", name: "key1" }),
};
const ops: Operation[] = [
{ type: "create_group", name: "pilots" },
{ type: "create_setup_key", name: "key1" },
];
const { results } = await executeOperations(
ops,
mockClient as never,
emptyActual(),
);
assertEquals(results[0].status, "failed");
assertEquals(results.length, 1); // second op never executed
});
Deno.test("executor tracks created group IDs for setup key auto_groups", async () => {
const calls: Array<{ method: string; data: unknown }> = [];
const mockClient = {
createGroup: (data: { name: string }) => {
calls.push({ method: "createGroup", data });
return Promise.resolve({
id: "new-g1",
name: data.name,
peers_count: 0,
peers: [],
issued: "api" as const,
});
},
createSetupKey: (data: Record<string, unknown>) => {
calls.push({ method: "createSetupKey", data });
return Promise.resolve({
id: 1,
name: data.name,
key: "raw-key-123",
type: data.type,
expires: "2026-04-01T00:00:00Z",
valid: true,
revoked: false,
used_times: 0,
state: "valid" as const,
auto_groups: data.auto_groups,
usage_limit: data.usage_limit,
});
},
};
const ops: Operation[] = [
{ type: "create_group", name: "pilots" },
{
type: "create_setup_key",
name: "key1",
details: {
type: "one-off",
auto_groups: ["pilots"],
usage_limit: 1,
expires_in: 604800,
},
},
];
const { results, createdKeys } = await executeOperations(
ops,
mockClient as never,
emptyActual(),
);
assertEquals(results.length, 2);
assertEquals(results[0].status, "success");
assertEquals(results[1].status, "success");
// The setup key call should have resolved "pilots" -> "new-g1"
const setupKeyCall = calls.find((c) => c.method === "createSetupKey");
assertEquals(
(setupKeyCall?.data as Record<string, unknown>).auto_groups,
["new-g1"],
);
// Created keys map stores the raw key
assertEquals(createdKeys.get("key1"), "raw-key-123");
});
Deno.test("executor resolves group IDs from actual state", async () => {
const calls: Array<{ method: string; data: unknown }> = [];
const actual = emptyActual();
actual.groupsByName.set("pilots", {
id: "existing-g1",
name: "pilots",
peers_count: 0,
peers: [],
issued: "api",
});
const mockClient = {
createSetupKey: (data: Record<string, unknown>) => {
calls.push({ method: "createSetupKey", data });
return Promise.resolve({
id: 1,
name: data.name,
key: "raw-key-456",
type: data.type,
expires: "2026-04-01T00:00:00Z",
valid: true,
revoked: false,
used_times: 0,
state: "valid" as const,
auto_groups: data.auto_groups,
usage_limit: data.usage_limit,
});
},
};
const ops: Operation[] = [
{
type: "create_setup_key",
name: "key1",
details: {
type: "one-off",
auto_groups: ["pilots"],
usage_limit: 1,
expires_in: 604800,
},
},
];
const { results } = await executeOperations(
ops,
mockClient as never,
actual,
);
assertEquals(results[0].status, "success");
const setupKeyCall = calls.find((c) => c.method === "createSetupKey");
assertEquals(
(setupKeyCall?.data as Record<string, unknown>).auto_groups,
["existing-g1"],
);
});
Deno.test("executor deletes group by resolving ID from actual", async () => {
const calls: string[] = [];
const actual = emptyActual();
actual.groupsByName.set("stale-group", {
id: "g-old",
name: "stale-group",
peers_count: 0,
peers: [],
issued: "api",
});
const mockClient = {
deleteGroup: (id: string) => {
calls.push(`deleteGroup:${id}`);
return Promise.resolve();
},
};
const ops: Operation[] = [
{ type: "delete_group", name: "stale-group" },
];
const { results } = await executeOperations(
ops,
mockClient as never,
actual,
);
assertEquals(calls, ["deleteGroup:g-old"]);
assertEquals(results[0].status, "success");
});
Deno.test("executor stores error message on failure", async () => {
const mockClient = {
createGroup: () => Promise.reject(new Error("rate limited")),
};
const ops: Operation[] = [
{ type: "create_group", name: "pilots" },
];
const { results } = await executeOperations(
ops,
mockClient as never,
emptyActual(),
);
assertEquals(results[0].status, "failed");
assertEquals(results[0].error, "rate limited");
});