mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-04 16:02:17 +00:00
* fix(cli): stub the API client over its real exports in tests Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(cli): mock the API client once and dispatch to per-suite stubs Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
95 lines
3.3 KiB
TypeScript
95 lines
3.3 KiB
TypeScript
/**
|
|
* Unit tests for pushWorkspaceKey in settings.ts.
|
|
*
|
|
* Covers WIN-2005: changing the encryption key in encryption_key.yaml and
|
|
* pushing it must (by default) re-encrypt the remote secrets with the new key.
|
|
*
|
|
* Verifies that:
|
|
* - an unchanged key is a no-op (no setWorkspaceEncryptionKey call)
|
|
* - a changed key in non-interactive mode re-encrypts by default
|
|
* (skip_reencrypt = false), so secret plaintext values are preserved
|
|
* - the --skip-reencrypt-on-key-change flag keeps the remote ciphertexts
|
|
* untouched (skip_reencrypt = true)
|
|
* - WMILL_NO_REENCRYPT_ON_KEY_CHANGE=true does the same via env var
|
|
*/
|
|
|
|
import { expect, test, describe, beforeEach, afterEach } from "bun:test";
|
|
import { mockServices } from "./mock_services.ts";
|
|
|
|
// Track calls to mocked wmill functions
|
|
let remoteKey = "";
|
|
let setEncryptionKeyCalls: {
|
|
workspace: string;
|
|
requestBody: { new_key: string; skip_reencrypt?: boolean };
|
|
}[] = [];
|
|
|
|
// Mock the wmill module before importing settings.ts
|
|
mockServices({
|
|
getWorkspaceEncryptionKey: async (_args: { workspace: string }) => ({
|
|
key: remoteKey,
|
|
}),
|
|
setWorkspaceEncryptionKey: async (args: {
|
|
workspace: string;
|
|
requestBody: { new_key: string; skip_reencrypt?: boolean };
|
|
}) => {
|
|
setEncryptionKeyCalls.push(args);
|
|
},
|
|
});
|
|
|
|
import { pushWorkspaceKey } from "../src/core/settings.ts";
|
|
|
|
describe("pushWorkspaceKey", () => {
|
|
const ws = "test-workspace";
|
|
|
|
beforeEach(() => {
|
|
remoteKey = "";
|
|
setEncryptionKeyCalls = [];
|
|
delete process.env.WMILL_NO_REENCRYPT_ON_KEY_CHANGE;
|
|
});
|
|
|
|
afterEach(() => {
|
|
delete process.env.WMILL_NO_REENCRYPT_ON_KEY_CHANGE;
|
|
});
|
|
|
|
test("no-op when local key matches the remote key", async () => {
|
|
remoteKey = "samekey";
|
|
await pushWorkspaceKey(ws, "encryption_key", undefined, "samekey", {
|
|
noninteractive: true,
|
|
});
|
|
expect(setEncryptionKeyCalls.length).toBe(0);
|
|
});
|
|
|
|
test("changed key re-encrypts by default in non-interactive mode", async () => {
|
|
remoteKey = "oldkey";
|
|
await pushWorkspaceKey(ws, "encryption_key", undefined, "newkey", {
|
|
noninteractive: true,
|
|
});
|
|
expect(setEncryptionKeyCalls.length).toBe(1);
|
|
expect(setEncryptionKeyCalls[0].requestBody.new_key).toBe("newkey");
|
|
// skip_reencrypt false => backend re-encrypts existing secrets with new key
|
|
expect(setEncryptionKeyCalls[0].requestBody.skip_reencrypt).toBe(false);
|
|
});
|
|
|
|
test("--skip-reencrypt-on-key-change skips re-encryption", async () => {
|
|
remoteKey = "oldkey";
|
|
await pushWorkspaceKey(ws, "encryption_key", undefined, "newkey", {
|
|
noninteractive: true,
|
|
skipReencrypt: true,
|
|
});
|
|
expect(setEncryptionKeyCalls.length).toBe(1);
|
|
expect(setEncryptionKeyCalls[0].requestBody.new_key).toBe("newkey");
|
|
expect(setEncryptionKeyCalls[0].requestBody.skip_reencrypt).toBe(true);
|
|
});
|
|
|
|
test("WMILL_NO_REENCRYPT_ON_KEY_CHANGE=true skips re-encryption non-interactively", async () => {
|
|
remoteKey = "oldkey";
|
|
process.env.WMILL_NO_REENCRYPT_ON_KEY_CHANGE = "true";
|
|
await pushWorkspaceKey(ws, "encryption_key", undefined, "newkey", {
|
|
noninteractive: true,
|
|
});
|
|
expect(setEncryptionKeyCalls.length).toBe(1);
|
|
expect(setEncryptionKeyCalls[0].requestBody.new_key).toBe("newkey");
|
|
expect(setEncryptionKeyCalls[0].requestBody.skip_reencrypt).toBe(true);
|
|
});
|
|
});
|