fix(core): Isolate instance settings writes (no-changelog) (#39118)

Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
This commit is contained in:
Milorad FIlipović 2026-09-18 21:19:44 +00:00 committed by GitHub
parent 30b3226101
commit 898480070f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 44 additions and 0 deletions

View File

@ -45,6 +45,9 @@ throw new UnexpectedError('message', { extra: { context } });
**Testing:**
- Vitest (unit), Playwright (E2E)
- Mock external dependencies
- Keep filesystem tests in a test-owned temporary directory
- Set `N8N_USER_FOLDER` before importing settings code
- Trace branches activated by mocks and isolate every reachable mutation
- Work from package directory: `pushd packages/cli && pnpm test`
**Database:**

View File

@ -73,6 +73,12 @@ Write a regression test that:
- Asserts the CORRECT behavior (test will fail on current code)
- Also includes a "happy path" test to prove the setup works
Complete the test safety gate in `/AGENTS.md` before you run the test:
- Trace side effects from imports, constructors, hooks, and mocked branches
- Keep filesystem access in a test-owned temporary directory
- Set `N8N_USER_FOLDER` before importing modules that resolve it
- When a mock changes a state check, inspect and isolate the branch that it activates
## Step 8: Run and Score
Run the test from the package directory (e.g., `cd packages/nodes-base && pnpm test <file>`).

View File

@ -342,6 +342,17 @@ Testing rules:
- Run tests and `pnpm typecheck` from the owning package.
- Confirm unit test cases with the user before you write them.
- Mock external dependencies. Use `nock` for HTTP services.
- Trace side effects from imports, constructors, hooks, and mocked branches before
you run a new or changed test.
- Do not let tests read from or write to the developer's home directory,
`~/.n8n`, or other user-owned locations.
- Use a test-owned temporary directory for filesystem tests. Set
+ `N8N_USER_FOLDER` before you import modules that resolve it. n8n writes to `${N8N_USER_FOLDER}/.n8n`, so expect the `.n8n` subfolder there.
- When a mock changes a state check such as `existsSync()`, inspect the branch
that it activates. Mock every reachable filesystem mutation unless filesystem
behavior is under test.
- Run tests that can initialize n8n settings with an isolated
`N8N_USER_FOLDER` first. Clean up only paths that the test created.
- Reuse immutable hoisted `mock<T>(...)` fixtures. Do not replace typed entity mocks with `as unknown as T`.
- Use `createVitestConfigWithDecorators` for Vitest packages that use `@n8n/di` decorators.
- Check for unused computed properties after you change a Pinia store.

View File

@ -23,6 +23,7 @@ vi.mock('node:fs', async (importActual) => ({
...(await importActual()),
existsSync: vi.fn().mockReturnValue(false),
renameSync: vi.fn(),
writeFileSync: vi.fn(),
}));
import { DirectedGraph } from '../partial-execution-utils';

View File

@ -64,6 +64,7 @@ vi.mock('node:fs', async (importActual) => ({
...(await importActual()),
existsSync: vi.fn().mockReturnValue(false),
renameSync: vi.fn(),
writeFileSync: vi.fn(),
}));
const nodeTypes = Helpers.NodeTypes();

View File

@ -1 +1,23 @@
import { mkdtempSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import 'reflect-metadata';
const originalUserFolder = process.env.N8N_USER_FOLDER;
const testUserFolder = mkdtempSync(join(tmpdir(), 'n8n-core-test-'));
process.env.N8N_USER_FOLDER = testUserFolder;
afterAll(() => {
if (originalUserFolder === undefined) {
delete process.env.N8N_USER_FOLDER;
} else {
process.env.N8N_USER_FOLDER = originalUserFolder;
}
rmSync(testUserFolder, { recursive: true, force: true });
});
// Best-effort cleanup for runs interrupted before afterAll executes (SIGINT/worker kill).
process.on('exit', () => rmSync(testUserFolder, { recursive: true, force: true }));