Skip to content

Commit d0f77b4

Browse files
committed
test: make unit fixtures portable across operating systems
1 parent 9d87a55 commit d0f77b4

11 files changed

Lines changed: 179 additions & 204 deletions

‎src/auth.test.ts‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { describe, it, expect, beforeEach, afterEach, vi } from "vite-plus/test";
22
import { existsSync, readFileSync, writeFileSync } from "node:fs";
3-
import { join } from "node:path";
3+
import { join, resolve } from "node:path";
44
import { configAuthentication, propagateProjectNpmrcAuth } from "./auth.js";
55
import { exportVariable, info } from "@actions/core";
66

@@ -21,7 +21,7 @@ vi.mock("node:fs", async () => {
2121
});
2222

2323
describe("configAuthentication", () => {
24-
const runnerTemp = "/tmp/runner";
24+
const runnerTemp = resolve("/tmp/runner");
2525

2626
beforeEach(() => {
2727
vi.stubEnv("RUNNER_TEMP", runnerTemp);
@@ -189,8 +189,8 @@ describe("configAuthentication", () => {
189189
});
190190

191191
describe("propagateProjectNpmrcAuth", () => {
192-
const runnerTemp = "/tmp/runner";
193-
const projectDir = "/workspace/project";
192+
const runnerTemp = resolve("/tmp/runner");
193+
const projectDir = resolve("/workspace/project");
194194
const npmrcPath = join(projectDir, ".npmrc");
195195
const supplementalPath = join(runnerTemp, ".npmrc");
196196

‎src/azure/install-viteplus.test.ts‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { describe, expect, it, vi } from "vite-plus/test";
22
import { writeFileSync } from "node:fs";
3+
import { join } from "node:path";
34
import { installVitePlus } from "./install-viteplus.js";
45

56
function writeDirsFile(env: Record<string, string>, bin: string): void {
@@ -122,8 +123,9 @@ describe("installVitePlus", () => {
122123
logWarningFn: () => undefined,
123124
});
124125

125-
expect(prependPath).toHaveBeenCalledWith("/home/runner/.vite-plus/bin");
126-
expect(env.PATH).toBe("/home/runner/.vite-plus/bin:/usr/bin");
126+
const bin = join("/home/runner", ".vite-plus", "bin");
127+
expect(prependPath).toHaveBeenCalledWith(bin);
128+
expect(env.PATH).toBe(`${bin}:/usr/bin`);
127129
});
128130

129131
it.each([

‎src/ci/auth.test.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ describe("portable project auth", () => {
3636
expect(result).toContain("//registry.example/npm/:_authToken=${NODE_AUTH_TOKEN}");
3737
expect(result).not.toContain("secret");
3838
expect(readFileSync(path.join(project, ".npmrc"), "utf8")).toBe(content);
39-
expect(statSync(npmrc).mode & 0o777).toBe(0o600);
39+
// Windows does not expose POSIX owner/group permission bits.
40+
if (process.platform !== "win32") expect(statSync(npmrc).mode & 0o777).toBe(0o600);
4041
expect(exporter).toHaveBeenCalledWith("NODE_AUTH_TOKEN", "secret");
4142
expect(exporter).toHaveBeenCalledWith("CUSTOM_TOKEN", "other-secret");
4243
expect(env.PNPM_CONFIG_USERCONFIG).toBe(npmrc);

‎src/gitlab/auth.test.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ describe("configureAuth", () => {
3131
expect(targetEnv.NODE_AUTH_TOKEN).toBe("XXXXX-XXXXX-XXXXX-XXXXX");
3232
expect(path.basename(path.dirname(npmrc))).toMatch(/^setup-vp-npmrc-/);
3333
expect(npmrc).not.toBe(path.join(tmpdir(), `setup-vp-npmrc.${process.pid}`));
34-
expect(statSync(npmrc).mode & 0o777).toBe(0o600);
34+
// Windows does not expose POSIX owner/group permission bits.
35+
if (process.platform !== "win32") expect(statSync(npmrc).mode & 0o777).toBe(0o600);
3536
expect(readFileSync(npmrc, "utf8")).toBe(
3637
"//npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN}\n@myorg:registry=https://npm.pkg.github.com/\n",
3738
);

‎src/gitlab/bootstrap.test.ts‎

Lines changed: 39 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -22,46 +22,50 @@ describe("GitLab bootstrap", () => {
2222
expect(result.status, result.stderr).toBe(0);
2323
});
2424

25-
it.each([0, 23])("uses the image Node and preserves runtime exit code %i", (code) => {
26-
const fixture = mkdtempSync(join(tmpdir(), "setup-vp-bootstrap-"));
27-
const bin = join(fixture, "image bin");
28-
mkdirSync(bin);
29-
symlinkSync(process.execPath, join(bin, "node"));
30-
writeFileSync(
31-
join(bin, "curl"),
32-
`#!/usr/bin/env bash
25+
// This exercises the Unix bootstrap; Windows runners use bootstrap.ps1.
26+
it.skipIf(process.platform === "win32").each([0, 23])(
27+
"uses the image Node and preserves runtime exit code %i",
28+
(code) => {
29+
const fixture = mkdtempSync(join(tmpdir(), "setup-vp-bootstrap-"));
30+
const bin = join(fixture, "image bin");
31+
mkdirSync(bin);
32+
symlinkSync(process.execPath, join(bin, "node"));
33+
writeFileSync(
34+
join(bin, "curl"),
35+
`#!/usr/bin/env bash
3336
case "$6" in
3437
*/test-ref/dist/gitlab/index.mjs) cp "$SETUP_VP_TEST_DIR/runtime.mjs" "$8" ;;
3538
*) exit 90 ;;
3639
esac
3740
`,
38-
);
39-
chmodSync(join(bin, "curl"), 0o755);
40-
writeFileSync(
41-
join(fixture, "runtime.mjs"),
42-
`console.log("runtime started:", process.execPath); process.exit(${code});`,
43-
);
44-
try {
45-
const result = spawnSync("bash", [bootstrapPath], {
46-
encoding: "utf8",
47-
env: {
48-
...process.env,
49-
PATH: `${bin}:${process.env.PATH}`,
50-
TMPDIR: fixture,
51-
SETUP_VP_TEST_DIR: fixture,
52-
SETUP_VP_SETUP_REF: "test-ref",
53-
SETUP_VP_NODE_MANAGER: "false",
54-
},
55-
});
56-
expect(result.status, result.stderr).toBe(code);
57-
expect(result.stdout).toContain(`runtime started: ${process.execPath}`);
58-
expect(
59-
readdirSync(fixture).filter((name) => name.startsWith("setup-vp-gitlab-runtime.")),
60-
).toEqual([]);
61-
} finally {
62-
rmSync(fixture, { recursive: true, force: true });
63-
}
64-
});
41+
);
42+
chmodSync(join(bin, "curl"), 0o755);
43+
writeFileSync(
44+
join(fixture, "runtime.mjs"),
45+
`console.log("runtime started:", process.execPath); process.exit(${code});`,
46+
);
47+
try {
48+
const result = spawnSync("bash", [bootstrapPath], {
49+
encoding: "utf8",
50+
env: {
51+
...process.env,
52+
PATH: `${bin}:${process.env.PATH}`,
53+
TMPDIR: fixture,
54+
SETUP_VP_TEST_DIR: fixture,
55+
SETUP_VP_SETUP_REF: "test-ref",
56+
SETUP_VP_NODE_MANAGER: "false",
57+
},
58+
});
59+
expect(result.status, result.stderr).toBe(code);
60+
expect(result.stdout).toContain(`runtime started: ${process.execPath}`);
61+
expect(
62+
readdirSync(fixture).filter((name) => name.startsWith("setup-vp-gitlab-runtime.")),
63+
).toEqual([]);
64+
} finally {
65+
rmSync(fixture, { recursive: true, force: true });
66+
}
67+
},
68+
);
6569

6670
it("uses the shared runtime on Windows and checks native command failures", () => {
6771
const bootstrap = readFileSync(new URL("../../gitlab/bootstrap.ps1", import.meta.url), "utf8");

‎src/gitlab/install-sfw.test.ts‎

Lines changed: 39 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
1-
import { chmodSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
1+
import { mkdtempSync, rmSync } from "node:fs";
2+
import { spawnSync } from "node:child_process";
23
import { EventEmitter } from "node:events";
34
import type { IncomingMessage } from "node:http";
45
import { tmpdir } from "node:os";
56
import path from "node:path";
6-
import { afterEach, describe, expect, it } from "vite-plus/test";
7+
import { afterEach, describe, expect, it, vi } from "vite-plus/test";
78
import { downloadFile, getSfwAssetName, setupSfw } from "./install-sfw.js";
9+
import { commandPath } from "../ci/process.js";
10+
11+
vi.mock("node:child_process", () => ({ spawnSync: vi.fn() }));
12+
vi.mock("../ci/process.js", () => ({ commandPath: vi.fn() }));
813

914
const tempDirs: string[] = [];
1015

@@ -14,11 +19,8 @@ function tempDir(): string {
1419
return dir;
1520
}
1621

17-
function shellQuote(value: string): string {
18-
return `'${value.replaceAll("'", "'\\''")}'`;
19-
}
20-
2122
afterEach(() => {
23+
vi.resetAllMocks();
2224
for (const dir of tempDirs.splice(0)) {
2325
rmSync(dir, { recursive: true, force: true });
2426
}
@@ -40,61 +42,44 @@ describe("GitLab sfw setup", () => {
4042
});
4143

4244
it("uses an existing sfw command from PATH", async () => {
43-
const dir = tempDir();
44-
const binDir = path.join(dir, "bin");
45-
mkdirSync(binDir);
46-
const sfwBin = path.join(binDir, "sfw");
47-
writeFileSync(sfwBin, "#!/usr/bin/env sh\nexit 0\n", "utf8");
48-
chmodSync(sfwBin, 0o755);
49-
50-
const previousPath = process.env.PATH;
51-
try {
52-
process.env.PATH = `${binDir}:${previousPath || ""}`;
53-
expect(await setupSfw([{}], { SETUP_VP_SFW: "true", PATH: process.env.PATH })).toBe("sfw");
54-
} finally {
55-
process.env.PATH = previousPath;
56-
}
45+
vi.mocked(commandPath).mockReturnValue(path.join(tempDir(), "sfw"));
46+
47+
expect(await setupSfw([{}], { SETUP_VP_SFW: "true" })).toBe("sfw");
48+
expect(commandPath).toHaveBeenCalledWith("sfw");
49+
expect(spawnSync).not.toHaveBeenCalled();
5750
});
5851

5952
it("uses curl for default downloads", async () => {
6053
const dir = tempDir();
61-
const binDir = path.join(dir, "bin");
6254
const outputPath = path.join(dir, "sfw");
63-
const logFile = path.join(dir, "curl.log");
64-
mkdirSync(binDir);
65-
66-
const curlBin = path.join(binDir, "curl");
67-
writeFileSync(
68-
curlBin,
55+
const curl = path.join(dir, "bin with spaces", "curl");
56+
vi.mocked(commandPath).mockReturnValue(curl);
57+
vi.mocked(spawnSync).mockReturnValue({
58+
pid: 1,
59+
output: [],
60+
stdout: Buffer.alloc(0),
61+
stderr: Buffer.alloc(0),
62+
status: 0,
63+
signal: null,
64+
});
65+
66+
await downloadFile("https://example.test/sfw", outputPath);
67+
68+
expect(commandPath).toHaveBeenCalledWith("curl");
69+
expect(spawnSync).toHaveBeenCalledExactlyOnceWith(
70+
curl,
6971
[
70-
"#!/usr/bin/env sh",
71-
`printf '%s\\n' "$*" > ${shellQuote(logFile)}`,
72-
'out=""',
73-
'while [ "$#" -gt 0 ]; do',
74-
' if [ "$1" = "-o" ]; then',
75-
" shift",
76-
' out="$1"',
77-
" break",
78-
" fi",
79-
" shift",
80-
"done",
81-
'printf "sfw" > "$out"',
82-
].join("\n"),
83-
"utf8",
72+
"-fsSL",
73+
"--connect-timeout",
74+
"5",
75+
"--max-time",
76+
"60",
77+
"https://example.test/sfw",
78+
"-o",
79+
outputPath,
80+
],
81+
{ stdio: "ignore" },
8482
);
85-
chmodSync(curlBin, 0o755);
86-
87-
const previousPath = process.env.PATH;
88-
try {
89-
process.env.PATH = `${binDir}:${previousPath || ""}`;
90-
await downloadFile("https://example.test/sfw", outputPath);
91-
} finally {
92-
process.env.PATH = previousPath;
93-
}
94-
95-
expect(readFileSync(outputPath, "utf8")).toBe("sfw");
96-
expect(readFileSync(logFile, "utf8")).toContain("https://example.test/sfw");
97-
expect(readFileSync(logFile, "utf8")).toContain("--max-time 60");
9883
});
9984

10085
it("times out stalled downloads", async () => {

0 commit comments

Comments
 (0)