im_wower
·
2026-03-29
public-api-base.test.mjs
1import assert from "node:assert/strict";
2import { execFileSync } from "node:child_process";
3import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
4import { tmpdir } from "node:os";
5import { join } from "node:path";
6import test from "node:test";
7import { fileURLToPath } from "node:url";
8
9const repoDir = fileURLToPath(new URL("../..", import.meta.url));
10const installScript = fileURLToPath(new URL("./install-launchd.sh", import.meta.url));
11const checkLaunchdScript = fileURLToPath(new URL("./check-launchd.sh", import.meta.url));
12const checkNodeScript = fileURLToPath(new URL("./check-node.sh", import.meta.url));
13const plistBuddy = "/usr/libexec/PlistBuddy";
14const sharedToken = "script-test-shared-token";
15const publicApiBase = "https://public.example.test";
16const conductorPlistLabel = "so.makefile.baa-conductor.plist";
17const d1AccountId = "acc-test";
18const d1DatabaseId = "db-test";
19const cloudflareApiToken = "token-test";
20
21function ensureRuntimeDirs() {
22 for (const relativePath of [
23 "state",
24 "runs",
25 "worktrees",
26 "logs",
27 "logs/launchd",
28 "tmp"
29 ]) {
30 mkdirSync(join(repoDir, relativePath), { recursive: true });
31 }
32}
33
34function runScript(scriptPath, args, envOverrides = {}) {
35 ensureRuntimeDirs();
36 execFileSync("bash", [scriptPath, ...args], {
37 cwd: repoDir,
38 env: {
39 ...process.env,
40 ...envOverrides
41 },
42 stdio: "pipe"
43 });
44}
45
46function plistValue(plistPath, key) {
47 return execFileSync(plistBuddy, ["-c", `Print ${key}`, plistPath], {
48 encoding: "utf8"
49 }).trim();
50}
51
52function deletePlistKey(plistPath, key) {
53 execFileSync(plistBuddy, ["-c", `Delete ${key}`, plistPath], {
54 stdio: "pipe"
55 });
56}
57
58function conductorInstallArgs(installDir, extraArgs = []) {
59 return [
60 "--repo-dir",
61 repoDir,
62 "--install-dir",
63 installDir,
64 "--service",
65 "conductor",
66 "--shared-token",
67 sharedToken,
68 "--local-api-base",
69 "http://100.71.210.78:4317",
70 "--local-api-allowed-hosts",
71 "100.71.210.78",
72 ...extraArgs
73 ];
74}
75
76test("install-launchd writes canonical and legacy conductor public API env names", (t) => {
77 const installDir = mkdtempSync(join(tmpdir(), "baa-conductor-public-api-install-"));
78 const plistPath = join(installDir, conductorPlistLabel);
79
80 t.after(() => {
81 rmSync(installDir, { force: true, recursive: true });
82 });
83
84 runScript(
85 installScript,
86 conductorInstallArgs(installDir, [
87 "--public-api-base",
88 publicApiBase,
89 "--codexd-local-api-base",
90 "http://127.0.0.1:4319"
91 ])
92 );
93
94 assert.equal(
95 plistValue(plistPath, ":EnvironmentVariables:BAA_CONDUCTOR_PUBLIC_API_BASE"),
96 publicApiBase
97 );
98 assert.equal(plistValue(plistPath, ":EnvironmentVariables:BAA_CONTROL_API_BASE"), publicApiBase);
99
100 runScript(
101 checkLaunchdScript,
102 conductorInstallArgs(installDir, [
103 "--public-api-base",
104 publicApiBase,
105 "--codexd-local-api-base",
106 "http://127.0.0.1:4319",
107 "--skip-dist-check"
108 ])
109 );
110
111 runScript(
112 checkNodeScript,
113 conductorInstallArgs(installDir, [
114 "--public-api-base",
115 publicApiBase,
116 "--codexd-api-base",
117 "http://127.0.0.1:4319",
118 "--skip-static-check",
119 "--skip-port-check",
120 "--skip-process-check",
121 "--skip-http-check",
122 "--skip-log-check"
123 ])
124 );
125});
126
127test("check-launchd accepts legacy conductor install copies with only BAA_CONTROL_API_BASE", (t) => {
128 const installDir = mkdtempSync(join(tmpdir(), "baa-conductor-public-api-legacy-"));
129 const plistPath = join(installDir, conductorPlistLabel);
130
131 t.after(() => {
132 rmSync(installDir, { force: true, recursive: true });
133 });
134
135 runScript(
136 installScript,
137 conductorInstallArgs(installDir, [
138 "--control-api-base",
139 publicApiBase,
140 "--codexd-local-api-base",
141 "http://127.0.0.1:4319"
142 ])
143 );
144
145 deletePlistKey(plistPath, ":EnvironmentVariables:BAA_CONDUCTOR_PUBLIC_API_BASE");
146
147 assert.equal(plistValue(plistPath, ":EnvironmentVariables:BAA_CONTROL_API_BASE"), publicApiBase);
148
149 runScript(
150 checkLaunchdScript,
151 conductorInstallArgs(installDir, [
152 "--public-api-base",
153 publicApiBase,
154 "--codexd-local-api-base",
155 "http://127.0.0.1:4319",
156 "--skip-dist-check"
157 ])
158 );
159 runScript(
160 checkLaunchdScript,
161 conductorInstallArgs(installDir, [
162 "--control-api-base",
163 publicApiBase,
164 "--codexd-local-api-base",
165 "http://127.0.0.1:4319",
166 "--skip-dist-check"
167 ])
168 );
169});
170
171test("install-launchd writes conductor D1 sync env vars from a secrets env file", (t) => {
172 const installDir = mkdtempSync(join(tmpdir(), "baa-conductor-d1-install-"));
173 const secretsDir = mkdtempSync(join(tmpdir(), "baa-conductor-d1-secrets-"));
174 const plistPath = join(installDir, conductorPlistLabel);
175 const secretsPath = join(secretsDir, "runtime-secrets.env");
176
177 t.after(() => {
178 rmSync(installDir, { force: true, recursive: true });
179 rmSync(secretsDir, { force: true, recursive: true });
180 });
181
182 writeFileSync(
183 secretsPath,
184 [
185 `D1_ACCOUNT_ID=${d1AccountId}`,
186 `D1_DATABASE_ID=${d1DatabaseId}`,
187 `CLOUDFLARE_API_TOKEN=${cloudflareApiToken}`
188 ].join("\n"),
189 "utf8"
190 );
191
192 runScript(
193 installScript,
194 conductorInstallArgs(installDir, [
195 "--d1-secrets-env",
196 secretsPath,
197 "--codexd-local-api-base",
198 "http://127.0.0.1:4319"
199 ]),
200 {
201 D1_ACCOUNT_ID: "",
202 D1_DATABASE_ID: "",
203 CLOUDFLARE_API_TOKEN: ""
204 }
205 );
206
207 assert.equal(plistValue(plistPath, ":EnvironmentVariables:D1_ACCOUNT_ID"), d1AccountId);
208 assert.equal(plistValue(plistPath, ":EnvironmentVariables:D1_DATABASE_ID"), d1DatabaseId);
209 assert.equal(
210 plistValue(plistPath, ":EnvironmentVariables:CLOUDFLARE_API_TOKEN"),
211 cloudflareApiToken
212 );
213
214 runScript(
215 checkLaunchdScript,
216 conductorInstallArgs(installDir, [
217 "--d1-secrets-env",
218 secretsPath,
219 "--codexd-local-api-base",
220 "http://127.0.0.1:4319",
221 "--skip-dist-check"
222 ]),
223 {
224 D1_ACCOUNT_ID: "",
225 D1_DATABASE_ID: "",
226 CLOUDFLARE_API_TOKEN: ""
227 }
228 );
229});