Files
youle_cocos/cocoscreator_projects/framework-tests/architecture/import-boundaries.test.mjs
T

152 lines
6.6 KiB
JavaScript

import { afterEach, test } from 'node:test';
import assert from 'node:assert/strict';
import { mkdtemp, mkdir, rm, writeFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { dirname, join } from 'node:path';
import { scanImportBoundaries } from '../../scripts/lib/import-boundaries.mjs';
const roots = [];
afterEach(async () => {
await Promise.all(roots.splice(0).map((root) => rm(root, { recursive: true, force: true })));
});
async function createFixtureRoot() {
const root = await mkdtemp(join(tmpdir(), 'youle-import-boundaries-'));
roots.push(root);
return root;
}
async function writeFixture(root, relativePath, source) {
const file = join(root, relativePath);
await mkdir(dirname(file), { recursive: true });
await writeFile(file, source, 'utf8');
}
function scan(root) {
return scanImportBoundaries({
frameworkDir: join(root, 'framework'),
gamesDir: join(root, 'games'),
});
}
test('scanner rejects sdk contracts importing a framework platform path', async () => {
const root = await createFixtureRoot();
await writeFixture(root, 'framework/sdk/contracts/bad.ts', "import '../platform/session.ts'\n");
assert.match(scan(root)[0].message, /sdk.*platform/);
});
test('scanner rejects game imports of framework net internals', async () => {
const root = await createFixtureRoot();
await writeFixture(root, 'games/a/assets/game/bad.ts', "import '../../../framework/net/net-client.ts'\n");
assert.match(scan(root)[0].message, /game.*framework\/net/);
});
test('scanner rejects multiline game imports of framework net internals', async () => {
const root = await createFixtureRoot();
await writeFixture(root, 'games/a/assets/game/bad.ts', "import {\n NetClient,\n} from '../../../../framework/net/net-client.ts'\n");
const violations = scan(root);
assert.equal(violations.length, 1);
assert.match(violations[0].message, /game.*framework\/net/);
});
test('scanner rejects multiline sdk contract imports outside their sibling directory', async () => {
const root = await createFixtureRoot();
await writeFixture(root, 'framework/sdk/contracts/bad.ts', "import {\n PlatformSession,\n} from '../../platform/session.ts'\n");
const violations = scan(root);
assert.equal(violations.length, 1);
assert.match(violations[0].message, /sdk\/contracts.*sibling contracts/);
});
test('scanner permits a game import of the public sdk barrel', async () => {
const root = await createFixtureRoot();
await writeFixture(root, 'games/a/assets/game/allowed.ts', "import '../../../../framework/sdk/index.ts'\n");
assert.deepEqual(scan(root), []);
});
test('scanner rejects games importing migration-only sdk declarations', async () => {
const root = await createFixtureRoot();
await writeFixture(root, 'games/a/assets/game/bad.ts', "import type { GameContext } from '../../../../framework/sdk/index.ts'\n");
assert.match(scan(root)[0].message, /game.*GameContext.*migration-only/);
});
test('scanner rejects cc imports in sdk contracts', async () => {
const root = await createFixtureRoot();
await writeFixture(root, 'framework/sdk/contracts/bad.ts', "import { Node } from 'cc'\n");
assert.match(scan(root)[0].message, /sdk\/contracts.*cc/);
});
test('scanner rejects non-relative imports in sdk contracts', async () => {
const root = await createFixtureRoot();
await writeFixture(root, 'framework/sdk/contracts/bad.ts', "import 'node:fs'\n");
const violations = scan(root);
assert.equal(violations.length, 1);
assert.match(violations[0].message, /sdk\/contracts.*non-relative/);
});
test('scanner rejects import-type leaks from sdk contracts', async () => {
const root = await createFixtureRoot();
await writeFixture(root, 'framework/sdk/contracts/bad.ts', "type ReadonlyRoom = import('../../platform/readonly.ts').ReadonlyRoomStore\n");
const violations = scan(root);
assert.equal(violations.length, 1);
assert.match(violations[0].message, /sdk\/contracts.*sibling contracts/);
});
test('scanner rejects export-star leaks from sdk contracts', async () => {
const root = await createFixtureRoot();
await writeFixture(root, 'framework/sdk/contracts/index.ts', "export * from '../../platform/readonly.ts'\n");
const violations = scan(root);
assert.equal(violations.length, 1);
assert.match(violations[0].message, /sdk\/contracts.*sibling contracts/);
});
test('scanner rejects named re-export leaks from sdk contracts', async () => {
const root = await createFixtureRoot();
await writeFixture(root, 'framework/sdk/contracts/index.ts', "export { ReadonlyRoomStore } from '../../platform/readonly.ts'\n");
const violations = scan(root);
assert.equal(violations.length, 1);
assert.match(violations[0].message, /sdk\/contracts.*sibling contracts/);
});
test('scanner permits cc imports in game code', async () => {
const root = await createFixtureRoot();
await writeFixture(root, 'games/a/assets/game/allowed.ts', "import { Node } from 'cc'\n");
assert.deepEqual(scan(root), []);
});
test('scanner rejects framework imports resolving under games', async () => {
const root = await createFixtureRoot();
await writeFixture(root, 'framework/ui/bad.ts', "import '../../games/a/assets/game/entry.ts'\n");
assert.match(scan(root)[0].message, /framework.*games/);
});
test('scanner rejects framework production imports of migration-only sdk declarations', async () => {
const root = await createFixtureRoot();
await writeFixture(root, 'framework/application/bad.ts', "import type { GameContext } from '../sdk/index.ts'\n");
const violations = scan(root);
assert.equal(violations.length, 1);
assert.match(violations[0].message, /GameContext.*migration-only/);
});
test('scanner rejects alias imports of migration-only sdk declarations', async () => {
const root = await createFixtureRoot();
await writeFixture(root, 'framework/application/bad.ts', "import type { IGameModule } from '@internal/sdk'\n");
const violations = scan(root);
assert.equal(violations.length, 1);
assert.match(violations[0].message, /IGameModule.*migration-only/);
});
test('scanner rejects new production imports of a quarantined legacy runtime', async () => {
const root = await createFixtureRoot();
await writeFixture(root, 'framework/application/bad.ts', "import '../net/net-client.ts'\n");
const violations = scan(root);
assert.equal(violations.length, 1);
assert.match(violations[0].message, /new production code.*legacy runtime/);
});
test('scanner rejects string-literal dynamic imports that cross a boundary', async () => {
const root = await createFixtureRoot();
await writeFixture(root, 'games/a/assets/game/bad.ts', "await import('../../../framework/net/net-client.ts')\n");
assert.match(scan(root)[0].message, /game.*framework\/net/);
});