import { describe, expect, it } from 'vitest';
import { checkBoundary, importSpecs } from '../boundary.mjs';
/** Пути в test-параметрах пишутся со слэшем, приводим к системным сепараторам. */
const sep = process.platform === 'win32' ? '\\' : '/';
const files = (entries) => new Map(entries.map(([f, src]) => [f.replaceAll('/', sep), src]));
describe('importSpecs', () => {
it('вытаскивает статические, side-effect, re-export и динамические импорты', () => {
const src = [
"import { a } from './a';",
"import './b';",
'import * as ns from "../c/d";',
"const m = await import('./dynamic');",
"export { x } from './reexport';"
].join('\n');
expect(importSpecs(src)).toEqual(['./a', './b', '../c/d', './dynamic', './reexport']);
});
});
describe('checkBoundary: движок', () => {
it('импорт из игры — error', () => {
const v = checkBoundary(files([
['packages/engine/src/core/x.ts', "import { hero } from '../../../apps/game/src/data/npcs';"]
]));
expect(v).toEqual([
expect.objectContaining({ severity: 'error', rule: 'engine-no-game' })
]);
});
it('относительный импорт мимо пакета — error', () => {
const v = checkBoundary(files([
['packages/engine/src/core/x.ts', "import { t } from '../../../CLAUDE.md';"]
]));
expect(v[0]).toMatchObject({ rule: 'engine-self-contained', severity: 'error' });
});
it('npm-импорты и внутренние относительные — чисто', () => {
const v = checkBoundary(files([
['packages/engine/src/render/y.ts', "import { Container } from 'pixi.js';\nimport { a } from '../core/a';"]
]));
expect(v).toEqual([]);
});
});
describe('checkBoundary: игра', () => {
it('под-путь движка мимо index.ts — error', () => {
const v = checkBoundary(files([
['apps/game/src/systems/z.ts', "import { iso } from '@rpg/engine/math/iso';"]
]));
expect(v[0]).toMatchObject({ rule: 'game-engine-public-api', severity: 'error', spec: '@rpg/engine/math/iso' });
});
it('публичное API разрешено: сам пакет и assets/*', () => {
const v = checkBoundary(files([
['apps/game/src/main.ts', "import { Game } from '@rpg/engine';\nimport fontUrl from '@rpg/engine/assets/fonts/VT323-Regular.ttf?url';"]
]));
expect(v).toEqual([]);
});
it('относительный импорт в движок мимо API — error', () => {
const v = checkBoundary(files([
['apps/game/src/data/map.ts', "import { rleDecode } from '../../../packages/engine/src/map/mapFormat';"]
]));
expect(v[0]).toMatchObject({ rule: 'game-self-contained', severity: 'error' });
});
it('npm-импорты и внутренние относительные — чисто', () => {
const v = checkBoundary(files([
['apps/game/src/scenes/s.ts', "import * as PIXI from 'pixi.js';\nimport { AREAS } from '../data/locations';"]
]));
expect(v).toEqual([]);
});
});