From ef2001b1c114fd6efb321124b07d0c58ffce775d Mon Sep 17 00:00:00 2001 From: Devvyn <22340871+opticon454@users.noreply.github.com> Date: Sun, 11 Oct 2026 11:00:34 +0800 Subject: [PATCH] fix(test): make a bare vitest invocation use the isolated CI config `npx vitest run` with no --config found no config file, so test/setup.ts never ran and HOME was not redirected: the suite wrote fixtures (clis.json with a test entry, users.json, webhook.json, intents.json, .env, settings.json values) into the developer's real ~/.codeman, and a running Codeman then loaded them on its next restart. A root vitest.config.ts re-exports config/vitest.ci.config.ts, so the default invocation is the isolated one; npm test and the browser/mobile/perf scripts keep passing their own --config. The shared temp-HOME teardown also retries on ENOTEMPTY, which failed whole files intermittently after every test had passed. --- .changeset/b569450a.md | 5 +++++ test/setup.ts | 4 +++- test/test-env-isolation.test.ts | 13 +++++++++++++ vitest.config.ts | 8 ++++++++ 4 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 .changeset/b569450a.md create mode 100644 vitest.config.ts diff --git a/.changeset/b569450a.md b/.changeset/b569450a.md new file mode 100644 index 00000000..578f328d --- /dev/null +++ b/.changeset/b569450a.md @@ -0,0 +1,5 @@ +--- +"aicodeman": patch +--- + +Test suite: a bare `npx vitest run` now resolves the isolated CI config instead of running with no setup file (which wrote fixtures into the real `~/.codeman`), and the shared temp-HOME teardown retries on `ENOTEMPTY`. diff --git a/test/setup.ts b/test/setup.ts index 0ef7e35b..7a546103 100644 --- a/test/setup.ts +++ b/test/setup.ts @@ -121,7 +121,9 @@ afterAll(async () => { if (originalPlaywrightBrowsersPath === undefined) delete process.env.PLAYWRIGHT_BROWSERS_PATH; else process.env.PLAYWRIGHT_BROWSERS_PATH = originalPlaywrightBrowsersPath; - rmSync(testHome, { recursive: true, force: true }); + // maxRetries: a straggling writer (a server persisting on shutdown) can recreate a file between + // rmSync's readdir and rmdir, which fails the whole file with ENOTEMPTY after every test passed. + rmSync(testHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 }); }); // afterAll never fires for a fully-skipped test file (no tests execute), which diff --git a/test/test-env-isolation.test.ts b/test/test-env-isolation.test.ts index 9ea5e0c7..93cafc47 100644 --- a/test/test-env-isolation.test.ts +++ b/test/test-env-isolation.test.ts @@ -78,3 +78,16 @@ describe('test environment isolation', () => { expect(deleted).toEqual(STRIPPED_ENV_VARS.map(([name]) => name).sort()); }); }); + +describe('a bare vitest invocation is still isolated', () => { + // Without a root config, `npx vitest run` loads no setup file, so HOME is never redirected and + // the suite writes fixtures into the developer's real ~/.codeman. Pin that the root config + // exists and carries the setup file, rather than trusting everyone to pass --config. + it('has a root vitest.config.ts that loads test/setup.ts', () => { + const root = fileURLToPath(new URL('..', import.meta.url)); + const src = readFileSync(`${root}vitest.config.ts`, 'utf8'); + expect(src).toContain('./config/vitest.ci.config'); + const ci = readFileSync(`${root}config/vitest.ci.config.ts`, 'utf8'); + expect(ci).toContain("setupFiles: ['./test/setup.ts']"); + }); +}); diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 00000000..58fedb7a --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,8 @@ +/** + * Bare `vitest` / `npx vitest run` (no `--config`) resolves THIS file. Without it there is no + * config at all, so no `setupFiles`: `test/setup.ts` never runs, HOME is not redirected, and the + * suite reads and writes the developer's real `~/.codeman` (settings, clis.json, users.json, + * .env, ...). It points at the CI gate so the default invocation is always the isolated one; + * `npm test`, `test:browser`, `test:mobile` and `test:perf` still pass their own `--config`. + */ +export { default } from './config/vitest.ci.config';