n8n/packages/testing/playwright/scripts/select-affected-e2e.test.ts
n8n-cat-bot[bot] ff3657ded7
Some checks are pending
Build: Benchmark Image / build (push) Waiting to run
CI: Master (Build, Test, Lint) / Build for Github Cache (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (22.22.3) (push) Waiting to run
CI: Master (Build, Test, Lint) / Unit tests (24.15.0) (push) Waiting to run
CI: Master (Build, Test, Lint) / Lint (push) Waiting to run
CI: Master (Build, Test, Lint) / Performance (push) Waiting to run
CI: Master (Build, Test, Lint) / Notify Slack on failure (push) Blocked by required conditions
Util: Sync API Docs / sync-public-api (push) Waiting to run
ci: Activate V8 E2E impact map filter in PR CI (#31757)
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-04 16:15:01 +00:00

73 lines
2.5 KiB
TypeScript

import * as fs from 'node:fs';
import * as os from 'node:os';
import * as path from 'node:path';
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
// @ts-expect-error — JS module without types; runtime export is what we test.
import { buildArgs, resolveMapPath } from './select-affected-e2e.mjs';
// The wrapper is the bridge between CI (raw changed-files list) and janitor
// select-e2e. Its only job is to keep selection FAIL-OPEN: any failure in
// locating the map must degrade to broad — never throw, never hide selection.
describe('select-affected-e2e wrapper — fail-open contract', () => {
let tempDir: string;
beforeEach(() => {
tempDir = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'select-wrapper-')));
});
afterEach(() => {
fs.rmSync(tempDir, { recursive: true, force: true });
});
describe('resolveMapPath()', () => {
it('returns null when the committed map is missing (wrapper degrades to broad)', () => {
const missing = path.join(tempDir, 'never-existed.json');
expect(resolveMapPath({ mapPath: missing })).toBeNull();
});
it('returns the path when the map exists and is readable', () => {
const p = path.join(tempDir, 'present.json');
fs.writeFileSync(p, '{}');
expect(resolveMapPath({ mapPath: p })).toBe(p);
});
it('returns null on an unreadable map (e.g. perms), never throws', () => {
// On non-root environments chmod 000 makes accessSync(R_OK) throw.
// Skip the assertion on root (CI sometimes runs as root and can read anything).
if (process.getuid?.() === 0) return;
const p = path.join(tempDir, 'unreadable.json');
fs.writeFileSync(p, '{}');
fs.chmodSync(p, 0o000);
try {
expect(resolveMapPath({ mapPath: p })).toBeNull();
} finally {
fs.chmodSync(p, 0o644);
}
});
});
describe('buildArgs()', () => {
// Omitting --map is exactly how the wrapper signals fail-open broad to
// the janitor CLI — assert by absence, not by a placeholder value.
it('omits --map when mapPath is null (fail-open broad)', () => {
const args = buildArgs({ changedFiles: 'a.ts,b.ts', mapPath: null });
expect(args).toEqual(['select-e2e', '--changed-files=a.ts,b.ts']);
expect(args.some((a: string) => a.startsWith('--map='))).toBe(false);
});
it('passes --map and --all-specs through when provided', () => {
expect(
buildArgs({ changedFiles: 'a.ts', mapPath: '/tmp/m.json', allSpecs: '/tmp/s.txt' }),
).toEqual([
'select-e2e',
'--changed-files=a.ts',
'--map=/tmp/m.json',
'--all-specs=/tmp/s.txt',
]);
});
});
});