From 6b5bc147d4f49057d35450c19f2c0416c187a2e3 Mon Sep 17 00:00:00 2001 From: jacob-wang Date: Fri, 3 Apr 2026 21:46:02 +0800 Subject: [PATCH] test: isolate saveState pruning test from shared git-repo state dir resolveWorkspaceRoot() calls ensureGitRepository() which climbs to the nearest git root. When tests run inside the plugin repository, every temp workspace resolves to the same repo root, collapsing all tests onto one shared state directory. Artifact files written by runtime tests (review-live.log, task-live.json, etc.) then bleed into the pruning test and cause the directory-contents assertion to fail. Fix: set CLAUDE_PLUGIN_DATA to the isolated temp workspace for the duration of the pruning test (with cleanup in a finally block). This forces resolveStateDir to root state under the temp dir, preventing cross-test pollution. Follows the same pattern used in the existing CLAUDE_PLUGIN_DATA test. Co-Authored-By: Claude Sonnet 4.6 --- tests/state.test.mjs | 123 ++++++++++++++++++++++++------------------- 1 file changed, 69 insertions(+), 54 deletions(-) diff --git a/tests/state.test.mjs b/tests/state.test.mjs index 0f8f57c..3ef56ac 100644 --- a/tests/state.test.mjs +++ b/tests/state.test.mjs @@ -41,65 +41,80 @@ test("resolveStateDir uses CLAUDE_PLUGIN_DATA when it is provided", () => { }); test("saveState prunes dropped job artifacts when indexed jobs exceed the cap", () => { + // Use CLAUDE_PLUGIN_DATA to ensure the state directory is rooted inside the + // isolated temp workspace rather than in the shared git-repo-derived path. + // Without this, resolveWorkspaceRoot() climbs to the git repo root and all + // test runs share the same state dir, causing spurious extra files. const workspace = makeTempDir(); - const stateFile = resolveStateFile(workspace); - fs.mkdirSync(path.dirname(stateFile), { recursive: true }); + const previousPluginDataDir = process.env.CLAUDE_PLUGIN_DATA; + process.env.CLAUDE_PLUGIN_DATA = workspace; + + try { + const stateFile = resolveStateFile(workspace); + fs.mkdirSync(path.dirname(stateFile), { recursive: true }); - const jobs = Array.from({ length: 51 }, (_, index) => { - const jobId = `job-${index}`; - const updatedAt = new Date(Date.UTC(2026, 0, 1, 0, index, 0)).toISOString(); - const logFile = resolveJobLogFile(workspace, jobId); - const jobFile = resolveJobFile(workspace, jobId); - fs.writeFileSync(logFile, `log ${jobId}\n`, "utf8"); - fs.writeFileSync(jobFile, JSON.stringify({ id: jobId, status: "completed" }, null, 2), "utf8"); - return { - id: jobId, - status: "completed", - logFile, - updatedAt, - createdAt: updatedAt - }; - }); + const jobs = Array.from({ length: 51 }, (_, index) => { + const jobId = `job-${index}`; + const updatedAt = new Date(Date.UTC(2026, 0, 1, 0, index, 0)).toISOString(); + const logFile = resolveJobLogFile(workspace, jobId); + const jobFile = resolveJobFile(workspace, jobId); + fs.writeFileSync(logFile, `log ${jobId}\n`, "utf8"); + fs.writeFileSync(jobFile, JSON.stringify({ id: jobId, status: "completed" }, null, 2), "utf8"); + return { + id: jobId, + status: "completed", + logFile, + updatedAt, + createdAt: updatedAt + }; + }); - fs.writeFileSync( - stateFile, - `${JSON.stringify( - { - version: 1, - config: { stopReviewGate: false }, - jobs - }, - null, - 2 - )}\n`, - "utf8" - ); + fs.writeFileSync( + stateFile, + `${JSON.stringify( + { + version: 1, + config: { stopReviewGate: false }, + jobs + }, + null, + 2 + )}\n`, + "utf8" + ); - saveState(workspace, { - version: 1, - config: { stopReviewGate: false }, - jobs - }); + saveState(workspace, { + version: 1, + config: { stopReviewGate: false }, + jobs + }); - const prunedJobFile = resolveJobFile(workspace, "job-0"); - const prunedLogFile = resolveJobLogFile(workspace, "job-0"); - const retainedJobFile = resolveJobFile(workspace, "job-50"); - const retainedLogFile = resolveJobLogFile(workspace, "job-50"); - const jobsDir = path.dirname(prunedJobFile); + const prunedJobFile = resolveJobFile(workspace, "job-0"); + const prunedLogFile = resolveJobLogFile(workspace, "job-0"); + const retainedJobFile = resolveJobFile(workspace, "job-50"); + const retainedLogFile = resolveJobLogFile(workspace, "job-50"); + const jobsDir = path.dirname(prunedJobFile); - assert.equal(fs.existsSync(retainedJobFile), true); - assert.equal(fs.existsSync(retainedLogFile), true); + assert.equal(fs.existsSync(retainedJobFile), true); + assert.equal(fs.existsSync(retainedLogFile), true); - const savedState = JSON.parse(fs.readFileSync(stateFile, "utf8")); - assert.equal(savedState.jobs.length, 50); - assert.deepEqual( - savedState.jobs.map((job) => job.id), - Array.from({ length: 50 }, (_, index) => `job-${50 - index}`) - ); - assert.deepEqual( - fs.readdirSync(jobsDir).sort(), - Array.from({ length: 50 }, (_, index) => `job-${index + 1}`) - .flatMap((jobId) => [`${jobId}.json`, `${jobId}.log`]) - .sort() - ); + const savedState = JSON.parse(fs.readFileSync(stateFile, "utf8")); + assert.equal(savedState.jobs.length, 50); + assert.deepEqual( + savedState.jobs.map((job) => job.id), + Array.from({ length: 50 }, (_, index) => `job-${50 - index}`) + ); + assert.deepEqual( + fs.readdirSync(jobsDir).sort(), + Array.from({ length: 50 }, (_, index) => `job-${index + 1}`) + .flatMap((jobId) => [`${jobId}.json`, `${jobId}.log`]) + .sort() + ); + } finally { + if (previousPluginDataDir == null) { + delete process.env.CLAUDE_PLUGIN_DATA; + } else { + process.env.CLAUDE_PLUGIN_DATA = previousPluginDataDir; + } + } });