Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions test/helpers/fs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { promises as fs } from "node:fs";

/**
* Removes a file or a directory for a test's cleanup, retrying a busy one: Windows can report EBUSY on a copy of
* node a test has just run (a lingering handle, or a scan). Node retries only with `recursive: true` (harmless on a
* file); its backoff is linear, so 6 × 200 ms waits at most 4.2 s — inside vitest's 10 s hook timeout, twice. An
* error that outlasts the retries still throws.
*/
export const removeWithRetry = (p: string): Promise<void> => fs.rm(p, { recursive: true, force: true, maxRetries: 6, retryDelay: 200 });
7 changes: 4 additions & 3 deletions test/local-bin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { afterAll, afterEach, beforeAll, describe, expect, it } from "vitest";
import { execProgram, isLocalBin, onPath, pathEntries, spawnInherited } from "../src/core/proc.js";
import { dockerRunner, dockerVersion } from "../src/core/services.js";
import { tmpDir } from "./helpers/forge.js";
import { removeWithRetry } from "./helpers/fs.js";

// 0.28.2 — `npx craftar` puts `<cwd>/node_modules/.bin`, and every ancestor's, first on PATH. craftar runs in a
// workspace or a Forge, so a program committed there would be the one it runs: those entries are skipped, in the
Expand Down Expand Up @@ -45,8 +46,8 @@ beforeAll(async () => {
await fs.chmod(path.join(PROJECT_BIN, TOOL + EXE), 0o755);
});
afterAll(async () => {
await fs.rm(path.join(LOCAL, TOOL + EXE), { force: true });
await fs.rm(PROJECT, { recursive: true, force: true });
await removeWithRetry(path.join(LOCAL, TOOL + EXE));
await removeWithRetry(PROJECT);
});

describe("isLocalBin — the node_modules/.bin of a directory or of one above it", () => {
Expand Down Expand Up @@ -118,7 +119,7 @@ describe("onPath — a program in such an entry is not found", () => {
expect(await onPath(TOOL, env, PROJECT)).toBeNull();
expect(await onPath(TOOL, env, path.join(PROJECT, "packages", "app"))).toBeNull();
const elsewhere = await fs.realpath(await tmpDir("craftar-local-bin-elsewhere-"));
cleanups.push(() => fs.rm(elsewhere, { recursive: true, force: true }));
cleanups.push(() => removeWithRetry(elsewhere));
expect(await onPath(TOOL, env, elsewhere)).toBe(path.join(PROJECT_BIN, TOOL + EXE));
});

Expand Down
3 changes: 2 additions & 1 deletion test/proc.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import path from "node:path";
import { afterAll, afterEach, beforeAll, describe, expect, it } from "vitest";
import { capture, execProgram, onPath, pathEntries, spawnInherited } from "../src/core/proc.js";
import { tmpDir } from "./helpers/forge.js";
import { removeWithRetry } from "./helpers/fs.js";

// design D3 — the two ways craftar runs an external program besides git: no shell, an argument array.

Expand Down Expand Up @@ -70,7 +71,7 @@ beforeAll(async () => {
await fs.copyFile(process.execPath, path.join(PLANTED, TOOL + EXE));
await fs.chmod(path.join(PLANTED, TOOL + EXE), 0o755);
});
afterAll(() => fs.rm(PLANTED, { recursive: true, force: true }));
afterAll(() => removeWithRetry(PLANTED));

/** The process environment with PATH replaced, whatever the key's case (Windows spells it `Path`). */
function withPath(value: string): NodeJS.ProcessEnv {
Expand Down
Loading