Skip to content

Commit 639eed0

Browse files
committed
Wrap concurrent tests with process-global test lock
Add sessionPromptTest and settingsTest helpers wrapping Deno.test with withProcessGlobalTestLock to prevent parallel test interference from HOME/env mutations.\n\n- Remove stale cleanupLocalCatalogFixtures calls in session-catalog\n- Remove redundant write-version.js check in ACP server test\n- Add JSDoc type annotation in worktree test
1 parent 321662c commit 639eed0

6 files changed

Lines changed: 388 additions & 335 deletions

File tree

src/acp/server.test.js

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -180,13 +180,6 @@ Deno.test("ACP server diagnostics stay out of protocol output", async () => {
180180
});
181181

182182
Deno.test("CLI --mode acp routes to ACP stdio without stdout diagnostics", async () => {
183-
const versionResult = await new Deno.Command(Deno.execPath(), {
184-
args: ["run", "-A", "scripts/write-version.js"],
185-
stdout: "null",
186-
stderr: "null",
187-
}).output();
188-
assertEquals(versionResult.code, 0);
189-
190183
const child = new Deno.Command(Deno.execPath(), {
191184
args: ["run", "-A", "src/cli.js", "--mode", "acp"],
192185
stdin: "piped",

src/shared/owner-coordination/sessions.test.js

Lines changed: 39 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { assert, assertEquals, assertRejects } from "@std/assert";
2+
import { withProcessGlobalTestLock } from "../../testing/process-global-lock.js";
23
import { getRunWieldSessionDir } from "../session/root-session.js";
34
import { openOwnerCoordinationDatabase } from "./database.js";
45
import { registerProject } from "./projects.js";
@@ -39,41 +40,46 @@ async function writeTranscript(cwd, piSessionId, options = {}) {
3940
}
4041

4142
Deno.test("Session listing lazily catalogs legacy transcripts without storing message bodies", async () => {
42-
const previousHome = Deno.env.get("HOME");
43-
const dir = await Deno.makeTempDir({ prefix: "runwield-session-catalog-" });
44-
Deno.env.set("HOME", dir);
45-
const database = openOwnerCoordinationDatabase({ dbPath: `${dir}/owner.sqlite3` });
46-
try {
47-
const root = `${dir}/repo`;
48-
await Deno.mkdir(root);
49-
const project = registerProject(database, { root, idFactory: idFactory(), now: () => "t1" });
50-
const transcriptPath = await writeTranscript(root, "pi-1", {
51-
body: JSON.stringify({ type: "message", message: { content: [{ type: "text", text: "secret body" }] } }),
52-
});
53-
const before = await Deno.stat(transcriptPath);
54-
const result = await listProjectSessions(database, project.projectId, {
55-
idFactory: idFactory(),
56-
now: () => "t2",
57-
});
58-
const after = await Deno.stat(transcriptPath);
43+
await withProcessGlobalTestLock(async () => {
44+
const previousHome = Deno.env.get("HOME");
45+
const dir = await Deno.makeTempDir({ prefix: "runwield-session-catalog-" });
46+
Deno.env.set("HOME", dir);
47+
const database = openOwnerCoordinationDatabase({ dbPath: `${dir}/owner.sqlite3` });
48+
try {
49+
const root = `${dir}/repo`;
50+
await Deno.mkdir(root);
51+
const project = registerProject(database, { root, idFactory: idFactory(), now: () => "t1" });
52+
const transcriptPath = await writeTranscript(root, "pi-1", {
53+
body: JSON.stringify({
54+
type: "message",
55+
message: { content: [{ type: "text", text: "secret body" }] },
56+
}),
57+
});
58+
const before = await Deno.stat(transcriptPath);
59+
const result = await listProjectSessions(database, project.projectId, {
60+
idFactory: idFactory(),
61+
now: () => "t2",
62+
});
63+
const after = await Deno.stat(transcriptPath);
5964

60-
assertEquals(result.diagnostics, []);
61-
assertEquals(result.sessions.length, 1);
62-
assertEquals(result.sessions[0].projectId, project.projectId);
63-
assertEquals(result.sessions[0].piSessionId, "pi-1");
64-
assertEquals(result.sessions[0].transcriptPath, transcriptPath);
65-
assertEquals(before.mtime?.getTime(), after.mtime?.getTime());
65+
assertEquals(result.diagnostics, []);
66+
assertEquals(result.sessions.length, 1);
67+
assertEquals(result.sessions[0].projectId, project.projectId);
68+
assertEquals(result.sessions[0].piSessionId, "pi-1");
69+
assertEquals(result.sessions[0].transcriptPath, transcriptPath);
70+
assertEquals(before.mtime?.getTime(), after.mtime?.getTime());
6671

67-
const rows = database.handle.prepare("SELECT display_name FROM runwield_sessions").all();
68-
assertEquals(rows, [{ display_name: null }]);
69-
const raw = JSON.stringify(database.handle.prepare("SELECT * FROM session_transcript_locators").all());
70-
assert(!raw.includes("secret body"));
71-
} finally {
72-
database.close();
73-
if (previousHome === undefined) Deno.env.delete("HOME");
74-
else Deno.env.set("HOME", previousHome);
75-
await Deno.remove(dir, { recursive: true });
76-
}
72+
const rows = database.handle.prepare("SELECT display_name FROM runwield_sessions").all();
73+
assertEquals(rows, [{ display_name: null }]);
74+
const raw = JSON.stringify(database.handle.prepare("SELECT * FROM session_transcript_locators").all());
75+
assert(!raw.includes("secret body"));
76+
} finally {
77+
database.close();
78+
if (previousHome === undefined) Deno.env.delete("HOME");
79+
else Deno.env.set("HOME", previousHome);
80+
await Deno.remove(dir, { recursive: true });
81+
}
82+
});
7783
});
7884

7985
Deno.test("Session catalog scans registered symlink alias session directories", async () => {

src/shared/session/session-catalog.test.js

Lines changed: 65 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,7 @@ import { HostedSession } from "./hosted-session.js";
1616
import { getAgentDisplayName, listAvailableAgents } from "./agents.js";
1717
import { getCustomSetting } from "../settings.js";
1818
import { loadPlan } from "../../plan-store.js";
19-
20-
const localPromptsDir = join(Deno.cwd(), ".wld", "prompts");
21-
const localSkillsDir = join(Deno.cwd(), ".wld", "skills");
22-
23-
async function cleanupLocalCatalogFixtures() {
24-
await Deno.remove(join(localPromptsDir, "code-review.md")).catch(() => {});
25-
await Deno.remove(join(localPromptsDir, "coverage-local.md")).catch(() => {});
26-
await Deno.remove(join(localSkillsDir, "coverage-skill"), { recursive: true }).catch(() => {});
27-
}
19+
import { withProcessGlobalTestLock } from "../../testing/process-global-lock.js";
2820

2921
Deno.test("two project roots keep local catalogs settings and Plans isolated", async () => {
3022
const alpha = await Deno.makeTempDir({ prefix: "runwield-project-alpha-" });
@@ -185,7 +177,6 @@ Deno.test("expandPromptTemplate strips front matter and appends user instruction
185177
});
186178

187179
Deno.test("listSkills and expandSkillCommand read local skill definitions", async () => {
188-
await cleanupLocalCatalogFixtures();
189180
const projectRoot = await Deno.makeTempDir({ prefix: "runwield-local-skill-" });
190181
const skillName = `coverage-skill-${crypto.randomUUID()}`;
191182
const skillDir = join(projectRoot, ".wld", "skills", skillName);
@@ -221,12 +212,10 @@ Deno.test("listSkills and expandSkillCommand read local skill definitions", asyn
221212
);
222213
} finally {
223214
await Deno.remove(projectRoot, { recursive: true }).catch(() => {});
224-
await cleanupLocalCatalogFixtures();
225215
}
226216
});
227217

228218
Deno.test("listSkills advertises bundled skills from the runtime-readable cache", async () => {
229-
await cleanupLocalCatalogFixtures();
230219
const skills = await listSkills();
231220
const ketch = skills.find((item) => item.name === "ketch");
232221

@@ -285,72 +274,72 @@ Deno.test("readGlobalAgentMd falls through configured global instruction paths",
285274
});
286275

287276
Deno.test("assembleFinalSystemPrompt fills tools, instruction files, skills, and bundled paths", async () => {
288-
await cleanupLocalCatalogFixtures();
289-
const originalHome = Deno.env.get("HOME");
290-
const tempHome = await Deno.makeTempDir({ prefix: "runwield-assemble-prompt-" });
291-
const projectRoot = await Deno.makeTempDir({ prefix: "runwield-assemble-project-" });
292-
const projectHarnessPath = join(projectRoot, "RUNWIELD.md");
293-
const skillName = `coverage-skill-${crypto.randomUUID()}`;
294-
const skillDir = join(projectRoot, ".wld", "skills", skillName);
295-
const skillPath = join(skillDir, "SKILL.md");
296-
297-
try {
298-
Deno.env.set("HOME", tempHome);
299-
await Deno.mkdir(join(tempHome, ".wld"), { recursive: true });
300-
await Deno.writeTextFile(join(tempHome, ".wld", "RUNWIELD.md"), "Global prompt context");
301-
await Deno.writeTextFile(projectHarnessPath, "Project prompt context");
302-
await Deno.mkdir(skillDir, { recursive: true });
303-
await Deno.writeTextFile(
304-
skillPath,
305-
[
306-
"---",
307-
`name: "${skillName}"`,
308-
'description: "Available for prompt assembly"',
309-
"---",
310-
"Use this skill carefully.",
311-
].join("\n"),
312-
);
313-
314-
const prompt = await assembleFinalSystemPrompt(
315-
/** @type {any} */ ({
316-
systemPrompt: [
317-
"Tools:",
318-
"{{AVAILABLE_TOOLS}}",
319-
"Global:",
320-
"{{GLOBAL_AGENTSMD}}",
321-
"Project:",
322-
"{{PROJECT_AGENTSMD}}",
323-
"Memories:",
324-
"{{MEMORIES}}",
325-
"Skills:",
326-
"{{SKILLS}}",
327-
"Bundled:",
328-
"{{BUNDLED_AGENT_DEFS_DIR}}",
277+
await withProcessGlobalTestLock(async () => {
278+
const originalHome = Deno.env.get("HOME");
279+
const tempHome = await Deno.makeTempDir({ prefix: "runwield-assemble-prompt-" });
280+
const projectRoot = await Deno.makeTempDir({ prefix: "runwield-assemble-project-" });
281+
const projectHarnessPath = join(projectRoot, "RUNWIELD.md");
282+
const skillName = `coverage-skill-${crypto.randomUUID()}`;
283+
const skillDir = join(projectRoot, ".wld", "skills", skillName);
284+
const skillPath = join(skillDir, "SKILL.md");
285+
286+
try {
287+
Deno.env.set("HOME", tempHome);
288+
await Deno.mkdir(join(tempHome, ".wld"), { recursive: true });
289+
await Deno.writeTextFile(join(tempHome, ".wld", "RUNWIELD.md"), "Global prompt context");
290+
await Deno.writeTextFile(projectHarnessPath, "Project prompt context");
291+
await Deno.mkdir(skillDir, { recursive: true });
292+
await Deno.writeTextFile(
293+
skillPath,
294+
[
295+
"---",
296+
`name: "${skillName}"`,
297+
'description: "Available for prompt assembly"',
298+
"---",
299+
"Use this skill carefully.",
329300
].join("\n"),
330-
}),
331-
["read", "custom_tool", "unknown_tool"],
332-
/** @type {any[]} */ ([{
333-
name: "custom_tool",
334-
description: "custom description",
335-
promptSnippet: "custom snippet",
336-
}]),
337-
projectRoot,
338-
);
301+
);
339302

340-
assertStringIncludes(prompt, "- read -");
341-
assertStringIncludes(prompt, "- custom_tool - custom snippet");
342-
assertStringIncludes(prompt, "- unknown_tool - Built-in tool");
343-
assertStringIncludes(prompt, "Global prompt context");
344-
assertStringIncludes(prompt, "Project prompt context");
345-
assertStringIncludes(prompt, `${skillName} - Available for prompt assembly (read: ${skillPath})`);
346-
assertStringIncludes(prompt, "agent-definitions");
347-
} finally {
348-
if (originalHome === undefined) Deno.env.delete("HOME");
349-
else Deno.env.set("HOME", originalHome);
350-
await Deno.remove(tempHome, { recursive: true }).catch(() => {});
351-
await Deno.remove(projectRoot, { recursive: true }).catch(() => {});
352-
await cleanupLocalCatalogFixtures();
353-
}
303+
const prompt = await assembleFinalSystemPrompt(
304+
/** @type {any} */ ({
305+
systemPrompt: [
306+
"Tools:",
307+
"{{AVAILABLE_TOOLS}}",
308+
"Global:",
309+
"{{GLOBAL_AGENTSMD}}",
310+
"Project:",
311+
"{{PROJECT_AGENTSMD}}",
312+
"Memories:",
313+
"{{MEMORIES}}",
314+
"Skills:",
315+
"{{SKILLS}}",
316+
"Bundled:",
317+
"{{BUNDLED_AGENT_DEFS_DIR}}",
318+
].join("\n"),
319+
}),
320+
["read", "custom_tool", "unknown_tool"],
321+
/** @type {any[]} */ ([{
322+
name: "custom_tool",
323+
description: "custom description",
324+
promptSnippet: "custom snippet",
325+
}]),
326+
projectRoot,
327+
);
328+
329+
assertStringIncludes(prompt, "- read -");
330+
assertStringIncludes(prompt, "- custom_tool - custom snippet");
331+
assertStringIncludes(prompt, "- unknown_tool - Built-in tool");
332+
assertStringIncludes(prompt, "Global prompt context");
333+
assertStringIncludes(prompt, "Project prompt context");
334+
assertStringIncludes(prompt, `${skillName} - Available for prompt assembly (read: ${skillPath})`);
335+
assertStringIncludes(prompt, "agent-definitions");
336+
} finally {
337+
if (originalHome === undefined) Deno.env.delete("HOME");
338+
else Deno.env.set("HOME", originalHome);
339+
await Deno.remove(tempHome, { recursive: true }).catch(() => {});
340+
await Deno.remove(projectRoot, { recursive: true }).catch(() => {});
341+
}
342+
});
354343
});
355344

356345
Deno.test("steerRootSession sends image content only while root is streaming", async () => {

0 commit comments

Comments
 (0)