Skip to content

Commit b9f1e3c

Browse files
committed
test: cover config.toml plugin installation flow
1 parent 3e34a57 commit b9f1e3c

1 file changed

Lines changed: 107 additions & 82 deletions

File tree

test/install-codex-auth.test.ts

Lines changed: 107 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,25 @@
11
import { afterEach, describe, expect, it, vi } from "vitest";
2-
import { mkdtempSync, readFileSync, rmSync, existsSync, writeFileSync, readdirSync, mkdirSync } from "node:fs";
2+
import {
3+
existsSync,
4+
mkdirSync,
5+
mkdtempSync,
6+
readdirSync,
7+
rmSync,
8+
writeFileSync,
9+
readFileSync,
10+
} from "node:fs";
311
import { tmpdir } from "node:os";
412
import path from "node:path";
5-
import { spawnSync, execFile } from "node:child_process";
13+
import { execFile, spawnSync } from "node:child_process";
614
import { promisify } from "node:util";
715
import {
816
FILE_RETRY_BASE_DELAY_MS,
917
FILE_RETRY_MAX_ATTEMPTS,
10-
normalizePluginList,
18+
PLUGIN_MARKETPLACE,
19+
PLUGIN_NAME,
20+
PLUGIN_VERSION,
21+
makePluginKey,
22+
mergePluginConfigToml,
1123
resolveInstallPaths,
1224
withFileOperationRetry,
1325
} from "../scripts/install-codex-auth-utils.js";
@@ -34,113 +46,126 @@ function retryableError(code: string): Error & { code: string } {
3446
}
3547

3648
describe("install-codex-auth script", () => {
37-
it("uses lowercase config template filenames", () => {
38-
const content = readFileSync(scriptPath, "utf8");
39-
expect(content).toContain('"codex-legacy.json"');
40-
expect(content).toContain('"codex-modern.json"');
41-
expect(content).not.toContain('"Codex-legacy.json"');
42-
expect(content).not.toContain('"Codex-modern.json"');
43-
});
44-
45-
it("normalizes plugin list with empty, duplicate, and non-string entries", () => {
46-
expect(normalizePluginList(undefined)).toEqual(["codex-multi-auth"]);
47-
expect(normalizePluginList(["codex-multi-auth", "a", "a", 123, null])).toEqual([
48-
"a",
49-
123,
50-
"codex-multi-auth",
51-
]);
52-
expect(normalizePluginList(["codex-multi-auth@1.0.0", "b"])).toEqual([
53-
"b",
54-
"codex-multi-auth",
55-
]);
56-
});
57-
58-
it("uses APPDATA/LOCALAPPDATA on windows path resolution", () => {
49+
it("resolves official Codex paths from CODEX_HOME", () => {
5950
const paths = resolveInstallPaths(
60-
"win32",
51+
"linux",
6152
{
62-
APPDATA: "C:\\Users\\test\\AppData\\Roaming",
63-
LOCALAPPDATA: "C:\\Users\\test\\AppData\\Local",
53+
CODEX_HOME: "/tmp/codex-home",
6454
},
65-
"C:\\Users\\test",
66-
);
67-
expect(paths.configPath).toBe(
68-
path.join("C:\\Users\\test\\AppData\\Roaming", "Codex", "Codex.json"),
55+
"/home/test",
6956
);
70-
expect(paths.cacheNodeModules).toBe(
57+
expect(paths.codexHomeDir).toBe("/tmp/codex-home");
58+
expect(paths.configPath).toBe(path.join("/tmp/codex-home", "config.toml"));
59+
expect(paths.pluginInstallDir).toBe(
7160
path.join(
72-
"C:\\Users\\test\\AppData\\Local",
73-
"Codex",
74-
"node_modules",
75-
"codex-multi-auth",
61+
"/tmp/codex-home",
62+
"plugins",
63+
"cache",
64+
PLUGIN_MARKETPLACE,
65+
PLUGIN_NAME,
66+
PLUGIN_VERSION,
7667
),
7768
);
7869
});
7970

80-
it("creates distinct backup files when installer runs concurrently", async () => {
81-
const home = mkdtempSync(path.join(tmpdir(), "codex-install-race-"));
71+
it("respects CODEX_CLI_CONFIG_PATH override", () => {
72+
const paths = resolveInstallPaths(
73+
"linux",
74+
{
75+
CODEX_HOME: "/tmp/codex-home",
76+
CODEX_CLI_CONFIG_PATH: "/tmp/custom/config.toml",
77+
},
78+
"/home/test",
79+
);
80+
expect(paths.configPath).toBe("/tmp/custom/config.toml");
81+
expect(paths.configDir).toBe(path.dirname("/tmp/custom/config.toml"));
82+
});
83+
84+
it("merges plugin settings into config.toml", () => {
85+
const merged = mergePluginConfigToml(
86+
["[features]", "plugins = false", "", "[plugins.\"codex-multi-auth@ndycode\"]", "enabled = false", ""].join("\n"),
87+
makePluginKey(),
88+
);
89+
expect(merged).toContain("[features]");
90+
expect(merged).toContain("plugins = true");
91+
expect(merged).toContain('[plugins."codex-multi-auth@ndycode"]');
92+
expect(merged).toContain("enabled = true");
93+
});
94+
95+
it("dry-run does not create config or plugin cache", () => {
96+
const home = mkdtempSync(path.join(tmpdir(), "codex-plugin-dryrun-"));
8297
tempRoots.push(home);
83-
const appData = path.join(home, "AppData", "Roaming");
84-
const localAppData = path.join(home, "AppData", "Local");
98+
const codexHome = path.join(home, ".codex");
8599
const env = {
86100
...process.env,
87101
HOME: home,
88102
USERPROFILE: home,
89-
APPDATA: appData,
90-
LOCALAPPDATA: localAppData,
103+
CODEX_HOME: codexHome,
91104
};
92-
const configDir = path.join(appData, "Codex");
93-
const configPath = path.join(configDir, "Codex.json");
94-
const initialConfig = JSON.stringify({ plugin: ["existing-plugin"] }, null, 2);
95-
96-
mkdirSync(configDir, { recursive: true });
97-
writeFileSync(configPath, `${initialConfig}\n`, "utf8");
98-
99-
const [first, second] = await Promise.all([
100-
execFileAsync(process.execPath, [scriptPath, "--modern", "--no-cache-clear"], {
101-
env,
102-
windowsHide: true,
103-
}),
104-
execFileAsync(process.execPath, [scriptPath, "--legacy", "--no-cache-clear"], {
105-
env,
106-
windowsHide: true,
107-
}),
108-
]);
109-
110-
expect(first.stderr).toBe("");
111-
expect(second.stderr).toBe("");
112-
expect(first.stdout).toContain("Backup created");
113-
expect(second.stdout).toContain("Backup created");
114-
const backups = readdirSync(configDir).filter((entry) =>
115-
entry.startsWith("Codex.json.bak-"),
116-
);
117-
expect(new Set(backups).size).toBe(backups.length);
118-
expect(backups.length).toBeGreaterThanOrEqual(2);
105+
106+
const result = spawnSync(process.execPath, [scriptPath, "--dry-run", "--modern"], {
107+
env,
108+
encoding: "utf8",
109+
windowsHide: true,
110+
});
111+
112+
expect(result.status).toBe(0);
113+
expect(`${result.stdout}\n${result.stderr}`).toContain("[dry-run]");
114+
expect(existsSync(path.join(codexHome, "config.toml"))).toBe(false);
115+
expect(existsSync(path.join(codexHome, "plugins", "cache"))).toBe(false);
119116
});
120117

121-
it("dry-run does not create global config on disk", () => {
122-
const home = mkdtempSync(path.join(tmpdir(), "codex-install-dryrun-"));
118+
it("installs plugin cache and updates config.toml", async () => {
119+
const home = mkdtempSync(path.join(tmpdir(), "codex-plugin-install-"));
123120
tempRoots.push(home);
124-
const appData = path.join(home, "AppData", "Roaming");
125-
const localAppData = path.join(home, "AppData", "Local");
121+
const codexHome = path.join(home, ".codex");
122+
const configPath = path.join(codexHome, "config.toml");
126123
const env = {
127124
...process.env,
128125
HOME: home,
129126
USERPROFILE: home,
130-
APPDATA: appData,
131-
LOCALAPPDATA: localAppData,
127+
CODEX_HOME: codexHome,
132128
};
133129

134-
const result = spawnSync(process.execPath, [scriptPath, "--dry-run", "--modern"], {
130+
mkdirSync(path.dirname(configPath), { recursive: true });
131+
writeFileSync(
132+
configPath,
133+
["model = \"gpt-5-codex\"", "", "[features]", "shell_tool = true", ""].join("\n"),
134+
"utf8",
135+
);
136+
137+
const result = await execFileAsync(process.execPath, [scriptPath, "--no-cache-clear"], {
135138
env,
136-
encoding: "utf8",
137139
windowsHide: true,
138140
});
139141

140-
expect(result.status).toBe(0);
141-
expect(`${result.stdout}\n${result.stderr}`).toContain("[dry-run]");
142-
const configPath = path.join(appData, "Codex", "Codex.json");
143-
expect(existsSync(configPath)).toBe(false);
142+
expect(result.stderr).toBe("");
143+
expect(result.stdout).toContain("Installed plugin cache");
144+
expect(result.stdout).toContain("Backup created");
145+
146+
const config = readFileSync(configPath, "utf8");
147+
expect(config).toContain("model = \"gpt-5-codex\"");
148+
expect(config).toContain("[features]");
149+
expect(config).toContain("plugins = true");
150+
expect(config).toContain('[plugins."codex-multi-auth@ndycode"]');
151+
expect(config).toContain("enabled = true");
152+
153+
const pluginManifestPath = path.join(
154+
codexHome,
155+
"plugins",
156+
"cache",
157+
PLUGIN_MARKETPLACE,
158+
PLUGIN_NAME,
159+
PLUGIN_VERSION,
160+
".codex-plugin",
161+
"plugin.json",
162+
);
163+
expect(existsSync(pluginManifestPath)).toBe(true);
164+
165+
const backups = readdirSync(codexHome).filter((entry) =>
166+
entry.startsWith("config.toml.bak-")
167+
);
168+
expect(backups.length).toBe(1);
144169
});
145170

146171
it("retries transient file-operation errors and eventually succeeds", async () => {

0 commit comments

Comments
 (0)