Files
windmill/cli/test/instance_configs_unit.test.ts
Ruben Fiszel fa668707c0 fix: move alert config from config table to global_settings (#8762)
* feat: move alert config from config table to global_settings

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: update ee-repo-ref.txt

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: rename alert setting to alert_job_queue_waiting

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: update ee-repo-ref.txt

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: add CLI unit tests for pullInstanceConfigs/pushInstanceConfigs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: update ee-repo-ref.txt

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: update ee-repo-ref.txt to merged main

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 15:54:44 +00:00

298 lines
8.9 KiB
TypeScript

/**
* Unit tests for pullInstanceConfigs / pushInstanceConfigs in settings.ts.
*
* Verifies that:
* - pullInstanceConfigs writes only worker group configs (no alerts)
* - pushInstanceConfigs calls updateConfig with worker__ prefix
* - pushInstanceConfigs calls deleteConfig with worker__ prefix for removed configs
* - pushInstanceConfigs skips unchanged configs
*/
import { expect, test, describe, beforeEach, afterEach, mock } from "bun:test";
import { writeFile, readFile, mkdir, rm } from "node:fs/promises";
import { mkdtempSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { stringify as yamlStringify } from "yaml";
// Track calls to mocked wmill functions
let listWorkerGroupsResult: any[] = [];
let updateConfigCalls: { name: string; requestBody: any }[] = [];
let deleteConfigCalls: { name: string }[] = [];
// Mock the wmill module before importing settings.ts
mock.module("../gen/services.gen.ts", () => ({
listWorkerGroups: async () => listWorkerGroupsResult,
updateConfig: async (args: { name: string; requestBody: any }) => {
updateConfigCalls.push(args);
},
deleteConfig: async (args: { name: string }) => {
deleteConfigCalls.push(args);
},
listConfigs: async () => {
throw new Error("listConfigs should not be called");
},
}));
import {
pullInstanceConfigs,
pushInstanceConfigs,
readLocalConfigs,
} from "../src/core/settings.ts";
describe("instance configs", () => {
let tempDir: string;
beforeEach(() => {
tempDir = mkdtempSync(join(tmpdir(), "wm-config-test-"));
listWorkerGroupsResult = [];
updateConfigCalls = [];
deleteConfigCalls = [];
});
afterEach(async () => {
await rm(tempDir, { recursive: true, force: true });
});
// =========================================================================
// readLocalConfigs
// =========================================================================
describe("readLocalConfigs", () => {
test("reads configs from instance_configs.yaml", async () => {
const configs = [
{ name: "default", config: { worker_tags: ["deno", "bun"] } },
{ name: "gpu", config: { dedicated_worker: "ws:f/gpu_script" } },
];
await writeFile(
join(tempDir, "instance_configs.yaml"),
yamlStringify(configs),
"utf-8"
);
const result = await readLocalConfigs({
prefix: tempDir,
folderPerInstance: true,
prefixSettings: true,
});
expect(result).toEqual(configs);
});
test("returns empty array when file does not exist", async () => {
const result = await readLocalConfigs({
prefix: tempDir,
folderPerInstance: true,
prefixSettings: true,
});
expect(result).toEqual([]);
});
});
// =========================================================================
// pullInstanceConfigs
// =========================================================================
describe("pullInstanceConfigs", () => {
test("writes worker group configs to instance_configs.yaml", async () => {
listWorkerGroupsResult = [
{ name: "default", config: { worker_tags: ["deno", "bun"] } },
{ name: "native", config: { worker_tags: ["nativets"] } },
];
// readLocalConfigs sets instanceConfigsPath when prefix is used
const opts = {
prefix: tempDir,
folderPerInstance: true,
prefixSettings: true,
};
await readLocalConfigs(opts);
await pullInstanceConfigs(opts);
const content = await readFile(
join(tempDir, "instance_configs.yaml"),
"utf-8"
);
expect(content).toContain("default");
expect(content).toContain("native");
expect(content).toContain("deno");
// Should not contain alert entries (listWorkerGroups filters them)
expect(content).not.toContain("alert");
});
test("preview mode returns change count without writing file", async () => {
listWorkerGroupsResult = [
{ name: "default", config: { worker_tags: ["deno"] } },
];
const changes = await pullInstanceConfigs(
{
prefix: tempDir,
folderPerInstance: true,
prefixSettings: true,
},
true
);
// One remote config not in local = 1 change
expect(changes).toBe(1);
});
test("preview mode returns 0 when remote matches local", async () => {
const configs = [
{ name: "default", config: { worker_tags: ["deno"] } },
];
listWorkerGroupsResult = configs;
await writeFile(
join(tempDir, "instance_configs.yaml"),
yamlStringify(configs),
"utf-8"
);
const changes = await pullInstanceConfigs(
{
prefix: tempDir,
folderPerInstance: true,
prefixSettings: true,
},
true
);
expect(changes).toBe(0);
});
});
// =========================================================================
// pushInstanceConfigs
// =========================================================================
describe("pushInstanceConfigs", () => {
test("calls updateConfig with worker__ prefix for new configs", async () => {
listWorkerGroupsResult = [];
const localConfigs = [
{ name: "mygroup", config: { worker_tags: ["python3"] } },
];
await writeFile(
join(tempDir, "instance_configs.yaml"),
yamlStringify(localConfigs),
"utf-8"
);
await pushInstanceConfigs({
prefix: tempDir,
folderPerInstance: true,
prefixSettings: true,
});
expect(updateConfigCalls).toHaveLength(1);
expect(updateConfigCalls[0].name).toBe("worker__mygroup");
expect(updateConfigCalls[0].requestBody).toEqual({
worker_tags: ["python3"],
});
});
test("calls deleteConfig with worker__ prefix for removed configs", async () => {
listWorkerGroupsResult = [
{ name: "old_group", config: { worker_tags: ["bash"] } },
];
// Empty local configs = old_group should be deleted
await writeFile(
join(tempDir, "instance_configs.yaml"),
yamlStringify([]),
"utf-8"
);
await pushInstanceConfigs({
prefix: tempDir,
folderPerInstance: true,
prefixSettings: true,
});
expect(deleteConfigCalls).toHaveLength(1);
expect(deleteConfigCalls[0].name).toBe("worker__old_group");
});
test("skips unchanged configs", async () => {
const configs = [
{ name: "default", config: { worker_tags: ["deno"] } },
];
listWorkerGroupsResult = configs;
await writeFile(
join(tempDir, "instance_configs.yaml"),
yamlStringify(configs),
"utf-8"
);
await pushInstanceConfigs({
prefix: tempDir,
folderPerInstance: true,
prefixSettings: true,
});
expect(updateConfigCalls).toHaveLength(0);
expect(deleteConfigCalls).toHaveLength(0);
});
test("updates changed configs and deletes removed ones", async () => {
listWorkerGroupsResult = [
{ name: "keep", config: { worker_tags: ["old_tag"] } },
{ name: "remove_me", config: { worker_tags: ["bash"] } },
];
const localConfigs = [
{ name: "keep", config: { worker_tags: ["new_tag"] } },
{ name: "add_me", config: { worker_tags: ["python3"] } },
];
await writeFile(
join(tempDir, "instance_configs.yaml"),
yamlStringify(localConfigs),
"utf-8"
);
await pushInstanceConfigs({
prefix: tempDir,
folderPerInstance: true,
prefixSettings: true,
});
// "keep" was changed, "add_me" is new
expect(updateConfigCalls).toHaveLength(2);
const updateNames = updateConfigCalls.map((c) => c.name).sort();
expect(updateNames).toEqual(["worker__add_me", "worker__keep"]);
// "remove_me" was deleted
expect(deleteConfigCalls).toHaveLength(1);
expect(deleteConfigCalls[0].name).toBe("worker__remove_me");
});
test("preview mode returns change count without calling API", async () => {
listWorkerGroupsResult = [
{ name: "default", config: { worker_tags: ["deno"] } },
];
const localConfigs = [
{ name: "default", config: { worker_tags: ["bun"] } },
{ name: "new_group", config: { worker_tags: ["go"] } },
];
await writeFile(
join(tempDir, "instance_configs.yaml"),
yamlStringify(localConfigs),
"utf-8"
);
const changes = await pushInstanceConfigs(
{
prefix: tempDir,
folderPerInstance: true,
prefixSettings: true,
},
true
);
// "default" changed + "new_group" added = 2 changes
expect(changes).toBe(2);
expect(updateConfigCalls).toHaveLength(0);
expect(deleteConfigCalls).toHaveLength(0);
});
});
});