feat(cli): prepare and validate application documents offline
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { chmodSync, mkdtempSync, readFileSync, realpathSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { rootCertificates } from "node:tls";
|
||||
import { afterEach, expect, test } from "vitest";
|
||||
|
||||
const binary = process.env.THT_INSTALLATION_TEST_CLI;
|
||||
const roots: string[] = [];
|
||||
afterEach(() => roots.splice(0).forEach((path) => rmSync(path, { recursive: true, force: true })));
|
||||
function run(...args: string[]) {
|
||||
return spawnSync(binary!, [...args, "--json"], { encoding: "utf8", env: { ...process.env, PATH: "" }, input: "" });
|
||||
}
|
||||
|
||||
test.skipIf(!binary)("operator prepares, completes and repeatedly validates before any stack exists", () => {
|
||||
const root = realpathSync(mkdtempSync(join(tmpdir(), "application-documents-"))); roots.push(root);
|
||||
const workspace = join(root, "workspaces"), directory = join(root, "installation");
|
||||
expect(run("workspace", "prepare", "--directory", workspace, "--id", "practice", "--name", "Practice").status).toBe(0);
|
||||
expect(run("installation", "prepare", "--directory", directory).status).toBe(0);
|
||||
const installation = join(directory, "thothii-installation.yaml");
|
||||
const validate = () => run("--installation", installation, "installation", "validate", "--workspaces", workspace);
|
||||
expect(validate().status).toBe(1); // Visible placeholders cannot be approved.
|
||||
expect(run("installation", "credentials", "--directory", directory).status).toBe(0);
|
||||
for (const name of ["thothii-installation.yaml", "operator.env", "database-bootstrap.yaml"]) {
|
||||
const path = join(directory, name);
|
||||
writeFileSync(path, readFileSync(path, "utf8").replaceAll("CHANGE_ME", "practice"));
|
||||
}
|
||||
for (const [name, contents] of Object.entries({ "secrets.env": "OPENAI_API_KEY=PRIVATE_PROVIDER_VALUE\n", "database-password": "PRIVATE_DATABASE_VALUE", "git-credentials": "", "git-ca.pem": rootCertificates[0] })) {
|
||||
writeFileSync(join(directory, "secrets", name), contents, { mode: 0o600 });
|
||||
}
|
||||
const before = readFileSync(installation, "utf8");
|
||||
for (let index = 0; index < 2; index++) {
|
||||
const checked = validate();
|
||||
expect(checked.stderr).toBe("");
|
||||
expect(checked.stdout).not.toContain("PRIVATE_");
|
||||
expect(checked.status, checked.stdout).toBe(0);
|
||||
expect(JSON.parse(checked.stdout).deferred_checks).toContain("release-assets");
|
||||
}
|
||||
expect(readFileSync(installation, "utf8")).toBe(before);
|
||||
writeFileSync(installation, before.replace("interaction: openai/gpt-4.1-mini", "interaction: openai/nonexistent"));
|
||||
expect(validate().status).toBe(1);
|
||||
writeFileSync(installation, before);
|
||||
const authFile = join(directory, "secrets/pi-auth.json");
|
||||
writeFileSync(authFile, "not-json");
|
||||
expect(validate().status).toBe(1);
|
||||
writeFileSync(installation, before.replace("{mode: secret_env, apiKeyEnv: OPENAI_API_KEY}", "{mode: pi_auth}"));
|
||||
writeFileSync(authFile, JSON.stringify({ openai: { unrelated: true } }));
|
||||
expect(validate().status).toBe(1);
|
||||
writeFileSync(authFile, JSON.stringify({ " OpenAI ": { type: "api_key", key: "PRIVATE_PI_KEY" } }));
|
||||
expect(validate().status).toBe(1);
|
||||
writeFileSync(authFile, JSON.stringify({ openai: { type: "api_key", key: "PRIVATE_PI_KEY" } }));
|
||||
expect(validate().status).toBe(0);
|
||||
writeFileSync(installation, before);
|
||||
writeFileSync(authFile, "{}");
|
||||
const bootstrap = join(directory, "database-bootstrap.yaml");
|
||||
const originalBootstrap = readFileSync(bootstrap, "utf8");
|
||||
writeFileSync(join(workspace, "practice", "private-password"), "PRIVATE_DATABASE_VALUE", { mode: 0o600 });
|
||||
writeFileSync(bootstrap, originalBootstrap.replace(join(directory, "secrets/database-password"), join(workspace, "practice/private-password")));
|
||||
expect(validate().status).toBe(1);
|
||||
writeFileSync(bootstrap, originalBootstrap);
|
||||
chmodSync(join(directory, "secrets/database-password"), 0o644);
|
||||
expect(validate().status).toBe(1);
|
||||
chmodSync(join(directory, "secrets/database-password"), 0o600);
|
||||
const env = join(directory, "operator.env");
|
||||
writeFileSync(env, readFileSync(env, "utf8") + "THOTH_HTTP_PORT=8081\n");
|
||||
expect(validate().status).toBe(1);
|
||||
}, 15_000);
|
||||
Reference in New Issue
Block a user