mirror of
https://github.com/callstack/agent-device.git
synced 2026-09-14 20:06:34 +08:00
64 lines
2.5 KiB
TypeScript
64 lines
2.5 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
import { test } from 'node:test';
|
|
import { ARCHITECTURE_OWNERSHIP, matchesDeclaredRoot } from './architecture-ownership.ts';
|
|
import { resolveImportEdges } from './model.ts';
|
|
import { workspaceSpecifierTargets } from './package-boundaries.ts';
|
|
import { listTrackedTypeScriptFiles } from './tracked-sources.ts';
|
|
|
|
const repoRoot = path.resolve(import.meta.dirname, '../..');
|
|
|
|
/**
|
|
* The `snapshot-policy` declaration owns presentation, freshness, timeout and overlay policy;
|
|
* `src/daemon/` owns the assembly that orders them (#1983, ADR 0004). The dependency runs one way
|
|
* only, so a policy stays testable without standing up a session.
|
|
*
|
|
* The real tree is clean, which is exactly why the positive control below exists: a filter that
|
|
* stopped matching would look identical to a boundary being obeyed.
|
|
*/
|
|
function daemonImportsFromSnapshotFacet(
|
|
sources: ReadonlyMap<string, string>,
|
|
workspaceTargets?: ReadonlyMap<string, string>,
|
|
): string[] {
|
|
const snapshotPolicy = ARCHITECTURE_OWNERSHIP.executablePolicies.find(
|
|
({ name }) => name === 'snapshot-policy',
|
|
);
|
|
if (!snapshotPolicy) throw new Error('snapshot-policy ownership declaration is missing');
|
|
|
|
return resolveImportEdges(sources, workspaceTargets)
|
|
.filter(
|
|
(edge) =>
|
|
snapshotPolicy.roots.some((root) => matchesDeclaredRoot(edge.file, root)) &&
|
|
snapshotPolicy.forbiddenTargetRoots.some((root) => matchesDeclaredRoot(edge.target, root)),
|
|
)
|
|
.map((edge) => `${edge.file}:${edge.line} -> ${edge.target}`);
|
|
}
|
|
|
|
test('the snapshot facet cannot import daemon assembly', () => {
|
|
const files = listTrackedTypeScriptFiles(repoRoot);
|
|
const sources = new Map(
|
|
files.map((file) => [file, fs.readFileSync(path.join(repoRoot, file), 'utf8')]),
|
|
);
|
|
|
|
assert.deepEqual(
|
|
daemonImportsFromSnapshotFacet(sources, workspaceSpecifierTargets(repoRoot)),
|
|
[],
|
|
'the host-side snapshot facet must not depend on daemon assembly',
|
|
);
|
|
});
|
|
|
|
test('the boundary reports a facet module that reaches back into the daemon', () => {
|
|
const sources = new Map([
|
|
['src/daemon/types.ts', 'export type SessionState = { id: string };\n'],
|
|
[
|
|
'src/snapshot/snapshot-freshness/android.ts',
|
|
"import type { SessionState } from '../../daemon/types.ts';\nexport type X = SessionState;\n",
|
|
],
|
|
]);
|
|
|
|
assert.deepEqual(daemonImportsFromSnapshotFacet(sources), [
|
|
'src/snapshot/snapshot-freshness/android.ts:1 -> src/daemon/types.ts',
|
|
]);
|
|
});
|