test(01-02): add CORE-10 firewall test + violator fixture
- src/sim/__test_violation__/violator.ts deliberately imports from
src/render/__firewall_target__.ts to trigger the firewall rule.
- src/sim/__test_violation__/lint-firewall.test.ts runs ESLint
programmatically (with ignore: false) against the violator and
asserts boundaries/element-types fires with severity=error and the
message mentions both 'sim' and 'render'.
- src/render/__firewall_target__.ts is a minimal export so the
boundaries plugin can resolve the import to a real path on disk.
Without a real target, the plugin marks the import as isUnknown
and silently skips the rule (verified empirically; see SUMMARY).
- eslint.config.js gains an import/resolver: typescript block so the
TS-aware resolver follows extension-less imports
('../../render/foo' -> src/render/foo.ts). Required by the
boundaries plugin's element classification of import targets.
- tsconfig.app.json excludes *.test.ts and src/sim/__test_violation__/
so 'tsc -b' does not try to typecheck Node-API-using test code with
the DOM-only project's lib settings; vitest still discovers them
via its own include glob.
- Added eslint-import-resolver-typescript as devDep.
Verifies green:
npm run lint -> 0 errors, 0 warnings (violator excluded)
npm test -> 2/2 pass (sentinel + firewall)
npm run build -> tsc -b clean, vite build clean
npx eslint --no-ignore src/sim/__test_violation__/violator.ts
-> exits 1 with the expected
boundaries/element-types error
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
// Target stub for the CORE-10 firewall test fixture.
|
||||
//
|
||||
// The deliberate-violation fixture at
|
||||
// src/sim/__test_violation__/violator.ts imports from this file so the
|
||||
// boundaries plugin can resolve the import to a real path under
|
||||
// src/render/ and classify it as the `render` element type.
|
||||
//
|
||||
// Without a real file to resolve to, eslint-plugin-boundaries marks the
|
||||
// target as `isUnknown: true` and the boundaries/element-types rule
|
||||
// silently skips the check (verified empirically via the plugin's
|
||||
// debug output during Plan 02 execution).
|
||||
//
|
||||
// This file is otherwise unused. It is NOT part of the runtime render
|
||||
// layer; src/render/ is intentionally empty in Phase 1 (only .gitkeep
|
||||
// existed before this file). Phase 2 will populate src/render/ with
|
||||
// real Phaser scenes and remove this stub if the firewall test is
|
||||
// rewritten to point at a real render module.
|
||||
|
||||
export const FIREWALL_TARGET_MARKER = 'render-target-for-firewall-test';
|
||||
@@ -0,0 +1,49 @@
|
||||
// CORE-10 firewall test: programmatically run ESLint against the
|
||||
// deliberate-violation fixture and assert that
|
||||
// `eslint-plugin-boundaries` flags the sim → render import.
|
||||
//
|
||||
// Per the Nyquist Rule, the rule needs an automated end-to-end check —
|
||||
// not just "lint exits 0 on clean code, trust me". This test invokes
|
||||
// the rule machinery via the ESLint Node API and inspects the messages
|
||||
// directly.
|
||||
//
|
||||
// The fixture (./violator.ts) is excluded from `npm run lint` via the
|
||||
// `ignores` block in eslint.config.js so it doesn't break CI. We pass
|
||||
// `ignore: false` to the programmatic ESLint instance below to override
|
||||
// that exclusion for this single test.
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { ESLint } from 'eslint';
|
||||
import { resolve } from 'node:path';
|
||||
|
||||
describe('CORE-10: src/sim/ cannot import from src/render/ or src/ui/', () => {
|
||||
it('eslint-plugin-boundaries flags a sim → render import as an error', async () => {
|
||||
const eslint = new ESLint({
|
||||
overrideConfigFile: resolve(process.cwd(), 'eslint.config.js'),
|
||||
ignore: false,
|
||||
});
|
||||
|
||||
const fixturePath = resolve(
|
||||
process.cwd(),
|
||||
'src/sim/__test_violation__/violator.ts',
|
||||
);
|
||||
const results = await eslint.lintFiles([fixturePath]);
|
||||
|
||||
expect(results).toHaveLength(1);
|
||||
|
||||
const messages = results[0].messages;
|
||||
const boundaryErrors = messages.filter(
|
||||
(m) => m.ruleId === 'boundaries/element-types' && m.severity === 2,
|
||||
);
|
||||
|
||||
expect(boundaryErrors.length).toBeGreaterThan(0);
|
||||
|
||||
// The error message should mention 'sim' (the offending element)
|
||||
// and either 'render' or 'ui' (the disallowed targets). Both terms
|
||||
// are checked separately so a regression that drops either side is
|
||||
// caught.
|
||||
const combined = boundaryErrors.map((m) => m.message).join(' | ');
|
||||
expect(combined).toMatch(/sim/i);
|
||||
expect(combined).toMatch(/render|ui/i);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,19 @@
|
||||
// DELIBERATE VIOLATION OF CORE-10 — DO NOT USE OUTSIDE THE FIREWALL TEST.
|
||||
//
|
||||
// This file lives under src/sim/__test_violation__/ and is excluded from
|
||||
// `npm run lint` via the `ignores` block in eslint.config.js. Its sole
|
||||
// purpose is to be lint-tested by lint-firewall.test.ts to prove the
|
||||
// boundaries/element-types rule actually fires (CORE-10).
|
||||
//
|
||||
// The import below targets a real file under src/render/ —
|
||||
// __firewall_target__.ts — because eslint-plugin-boundaries needs to
|
||||
// resolve the import to a real path on disk to classify the target's
|
||||
// element type. If the import path does not resolve, the plugin marks
|
||||
// the target as `isUnknown` and silently skips the check (verified
|
||||
// empirically against eslint-plugin-boundaries 6.0.2 during Plan 02
|
||||
// execution; see 01-02-SUMMARY.md "Deviations").
|
||||
|
||||
import { FIREWALL_TARGET_MARKER } from '../../render/__firewall_target__';
|
||||
|
||||
export const VIOLATION_MARKER = 'sim-imports-render';
|
||||
export const _ref = FIREWALL_TARGET_MARKER;
|
||||
Reference in New Issue
Block a user