Skip to content

Commit 9267429

Browse files
committed
Add real-I/O integration tests and expand VS Code assertions
- Add test/unit/mcpConfig.test.ts (9 tests): MCP config operations against real temp directories (write, read, preserve, idempotent, invalid JSON, empty file, windsurf format) - Add test/unit/managedLifecycle.test.ts (10 tests): managed install lifecycle with real file I/O (promotion, rollback, staging cleanup, failure persistence round-trip, failure cleared on success) - Add test/unit/binaryDiscovery.test.ts (10 tests): real executable discovery on PATH, non-executable skip, PATH ordering and dedup, version parsing edge cases, compatibility assessment - Expand test/suite/index.ts: verify config defaults, contributed commands structure, extension metadata, settings schema Test count: 52 -> 81 unit + 1 integration Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
1 parent f05a608 commit 9267429

4 files changed

Lines changed: 714 additions & 7 deletions

File tree

test/suite/index.ts

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,58 @@
11
import assert from "node:assert/strict";
22
import * as vscode from "vscode";
33

4+
const EXPECTED_COMMANDS = [
5+
"patchloom.initializeProject",
6+
"patchloom.setupWorkspace",
7+
"patchloom.configureMcp",
8+
"patchloom.quickAction",
9+
"patchloom.openPatchloomSettings",
10+
"patchloom.openPatchloomReleases",
11+
"patchloom.showStatus"
12+
];
13+
414
export async function run(): Promise<void> {
515
const extension = vscode.extensions.getExtension("patchloom.patchloom");
616
assert.ok(extension, "Patchloom extension should be registered");
717

818
await extension.activate();
919
assert.equal(extension.isActive, true);
1020

21+
// Command registration
1122
const commands = await vscode.commands.getCommands(true);
12-
assert.ok(commands.includes("patchloom.initializeProject"));
13-
assert.ok(commands.includes("patchloom.setupWorkspace"));
14-
assert.ok(commands.includes("patchloom.configureMcp"));
15-
assert.ok(commands.includes("patchloom.quickAction"));
16-
assert.ok(commands.includes("patchloom.openPatchloomSettings"));
17-
assert.ok(commands.includes("patchloom.openPatchloomReleases"));
18-
assert.ok(commands.includes("patchloom.showStatus"));
23+
for (const cmd of EXPECTED_COMMANDS) {
24+
assert.ok(commands.includes(cmd), `command ${cmd} should be registered`);
25+
}
26+
27+
// Configuration properties are contributed
28+
const config = vscode.workspace.getConfiguration("patchloom");
29+
const pathSetting = config.get<string>("path");
30+
assert.equal(pathSetting, "", "patchloom.path should default to empty string");
31+
32+
const showStatusBar = config.get<boolean>("showStatusBar");
33+
assert.equal(showStatusBar, true, "patchloom.showStatusBar should default to true");
34+
35+
// Package contributes the expected number of commands
36+
const packageJson = extension.packageJSON as Record<string, unknown>;
37+
const contributes = packageJson.contributes as Record<string, unknown>;
38+
const contributedCommands = contributes.commands as Array<Record<string, unknown>>;
39+
assert.equal(contributedCommands.length, EXPECTED_COMMANDS.length,
40+
`should contribute exactly ${EXPECTED_COMMANDS.length} commands`);
41+
42+
for (const cmd of contributedCommands) {
43+
assert.equal(cmd.category, "Patchloom", `command ${cmd.command} should have category Patchloom`);
44+
assert.ok(typeof cmd.title === "string" && cmd.title.length > 0,
45+
`command ${cmd.command} should have a non-empty title`);
46+
}
47+
48+
// Extension metadata
49+
assert.equal(packageJson.publisher, "patchloom");
50+
assert.ok(typeof packageJson.version === "string");
51+
assert.ok((packageJson.engines as Record<string, string>).vscode);
52+
53+
// Configuration schema has expected properties
54+
const configSchema = contributes.configuration as Record<string, unknown>;
55+
const properties = configSchema.properties as Record<string, unknown>;
56+
assert.ok(properties["patchloom.path"], "should contribute patchloom.path setting");
57+
assert.ok(properties["patchloom.showStatusBar"], "should contribute patchloom.showStatusBar setting");
1958
}

test/unit/binaryDiscovery.test.ts

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
import assert from "node:assert/strict";
2+
import * as fs from "node:fs/promises";
3+
import * as os from "node:os";
4+
import * as path from "node:path";
5+
import test from "node:test";
6+
import {
7+
assessPatchloomCompatibility,
8+
comparePatchloomVersions,
9+
findOnPath,
10+
parsePatchloomVersion,
11+
resolvePatchloomStatusWithInputs
12+
} from "../../src/binary/patchloom";
13+
14+
async function withTempDir(fn: (dir: string) => Promise<void>): Promise<void> {
15+
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "patchloom-discovery-test-"));
16+
try {
17+
await fn(dir);
18+
} finally {
19+
await fs.rm(dir, { recursive: true, force: true });
20+
}
21+
}
22+
23+
test("findOnPath discovers a real executable in a temp directory", async () => {
24+
await withTempDir(async (dir) => {
25+
const fakeBinary = path.join(dir, "patchloom");
26+
await fs.writeFile(fakeBinary, "#!/bin/sh\necho patchloom 0.1.0\n", { mode: 0o755 });
27+
28+
const found = await findOnPath(dir, "linux");
29+
assert.equal(found, fakeBinary);
30+
});
31+
});
32+
33+
test("findOnPath skips non-executable files", async () => {
34+
await withTempDir(async (dir) => {
35+
const fakeBinary = path.join(dir, "patchloom");
36+
await fs.writeFile(fakeBinary, "#!/bin/sh\necho patchloom 0.1.0\n", { mode: 0o644 });
37+
38+
const found = await findOnPath(dir, "linux");
39+
assert.equal(found, undefined);
40+
});
41+
});
42+
43+
test("findOnPath searches multiple PATH directories in order", async () => {
44+
await withTempDir(async (parent) => {
45+
const firstDir = path.join(parent, "first");
46+
const secondDir = path.join(parent, "second");
47+
await fs.mkdir(firstDir);
48+
await fs.mkdir(secondDir);
49+
50+
await fs.writeFile(path.join(firstDir, "patchloom"), "#!/bin/sh\necho first\n", { mode: 0o755 });
51+
await fs.writeFile(path.join(secondDir, "patchloom"), "#!/bin/sh\necho second\n", { mode: 0o755 });
52+
53+
const found = await findOnPath(`${firstDir}:${secondDir}`, "linux");
54+
assert.equal(found, path.join(firstDir, "patchloom"), "should find the first match in PATH order");
55+
});
56+
});
57+
58+
test("findOnPath returns undefined for empty PATH", async () => {
59+
const found = await findOnPath("", "linux");
60+
assert.equal(found, undefined);
61+
});
62+
63+
test("findOnPath deduplicates PATH entries", async () => {
64+
await withTempDir(async (dir) => {
65+
const fakeBinary = path.join(dir, "patchloom");
66+
await fs.writeFile(fakeBinary, "#!/bin/sh\necho v1\n", { mode: 0o755 });
67+
let checkCount = 0;
68+
69+
const found = await findOnPath(`${dir}:${dir}:${dir}`, "linux", async (candidate) => {
70+
checkCount++;
71+
try {
72+
await fs.access(candidate, fs.constants.X_OK);
73+
return true;
74+
} catch {
75+
return false;
76+
}
77+
});
78+
79+
assert.equal(found, fakeBinary);
80+
assert.equal(checkCount, 1, "should only check each directory once");
81+
});
82+
});
83+
84+
test("resolvePatchloomStatusWithInputs discovers a real executable via PATH", async () => {
85+
await withTempDir(async (dir) => {
86+
const fakeBinary = path.join(dir, "patchloom");
87+
await fs.writeFile(fakeBinary, "#!/bin/sh\necho patchloom 0.1.0\n", { mode: 0o755 });
88+
89+
const status = await resolvePatchloomStatusWithInputs({
90+
configuredPath: "",
91+
pathValue: dir,
92+
canExecute: async (candidate) => {
93+
try {
94+
await fs.access(candidate, fs.constants.X_OK);
95+
return true;
96+
} catch {
97+
return false;
98+
}
99+
},
100+
getVersion: async () => "patchloom 0.1.0"
101+
});
102+
103+
assert.equal(status.ready, true);
104+
assert.equal(status.source, "path");
105+
assert.equal(status.binaryPath, fakeBinary);
106+
assert.equal(status.version, "patchloom 0.1.0");
107+
});
108+
});
109+
110+
test("resolvePatchloomStatusWithInputs reports configured path that does not exist", async () => {
111+
await withTempDir(async (dir) => {
112+
const nonexistent = path.join(dir, "no-such-binary");
113+
114+
const status = await resolvePatchloomStatusWithInputs({
115+
configuredPath: nonexistent,
116+
pathValue: "",
117+
canExecute: async (candidate) => {
118+
try {
119+
await fs.access(candidate, fs.constants.X_OK);
120+
return true;
121+
} catch {
122+
return false;
123+
}
124+
},
125+
getVersion: async () => undefined
126+
});
127+
128+
assert.equal(status.ready, false);
129+
assert.equal(status.source, "setting");
130+
assert.match(status.message, /not executable/);
131+
});
132+
});
133+
134+
test("parsePatchloomVersion handles edge cases", () => {
135+
assert.equal(parsePatchloomVersion(undefined), undefined);
136+
assert.equal(parsePatchloomVersion(""), undefined);
137+
assert.equal(parsePatchloomVersion("0.1.0"), "0.1.0");
138+
assert.equal(parsePatchloomVersion("patchloom v1.2.3+build.42"), "1.2.3+build.42");
139+
assert.equal(parsePatchloomVersion("no version here"), undefined);
140+
});
141+
142+
test("comparePatchloomVersions handles major, minor, and patch differences", () => {
143+
assert.ok(comparePatchloomVersions("1.0.0", "0.9.9") > 0);
144+
assert.ok(comparePatchloomVersions("0.1.0", "0.0.99") > 0);
145+
assert.ok(comparePatchloomVersions("0.0.1", "0.0.0") > 0);
146+
assert.ok(comparePatchloomVersions("0.0.0", "0.0.1") < 0);
147+
assert.equal(comparePatchloomVersions("1.2.3", "1.2.3"), 0);
148+
});
149+
150+
test("assessPatchloomCompatibility correctly identifies supported versions", () => {
151+
const supported = assessPatchloomCompatibility("patchloom 0.1.0");
152+
assert.equal(supported.compatibility, "supported");
153+
assert.equal(supported.detectedVersion, "0.1.0");
154+
155+
const newer = assessPatchloomCompatibility("patchloom 1.0.0");
156+
assert.equal(newer.compatibility, "supported");
157+
158+
const noVersion = assessPatchloomCompatibility("some unknown output");
159+
assert.equal(noVersion.compatibility, "unknown");
160+
});

0 commit comments

Comments
 (0)