106 lines
4.9 KiB
TypeScript
106 lines
4.9 KiB
TypeScript
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import { describe, expect, it } from "vitest";
|
|
import { checkBoundaries } from "../scripts/check-boundaries.js";
|
|
|
|
const testDirectory = path.dirname(fileURLToPath(import.meta.url));
|
|
const fixtures = path.join(testDirectory, "fixtures", "boundaries");
|
|
|
|
describe("repository dependency boundaries", () => {
|
|
it("accepts public contracts below Core", async () => {
|
|
const result = await checkBoundaries(path.join(fixtures, "legal"));
|
|
expect(result.violations).toEqual([]);
|
|
});
|
|
|
|
it("accepts generated module composition only through the generated bootstrap boundary", async () => {
|
|
const result = await checkBoundaries(path.join(fixtures, "legal-generated-bootstrap"));
|
|
expect(result.violations).toEqual([]);
|
|
});
|
|
|
|
it("honors explicit pnpm workspace exclusions", async () => {
|
|
const result = await checkBoundaries(path.join(fixtures, "workspace-exclusion"));
|
|
expect(result.violations).toEqual([]);
|
|
});
|
|
|
|
it.each([
|
|
["core-imports-module", "core-is-feature-agnostic"],
|
|
["app-imports-module", "apps-are-composition-roots"],
|
|
["module-imports-core", "modules-use-public-platform-contracts"],
|
|
["module-imports-surface", "modules-use-public-platform-contracts"],
|
|
["package-boundary-direction", "modules-use-public-platform-contracts"],
|
|
["cross-module-import", "modules-do-not-import-other-modules"],
|
|
["module-schema-imports-module", "modules-do-not-import-other-modules"],
|
|
["surface-imports-module", "surfaces-use-public-contracts"],
|
|
["cross-surface-import", "surfaces-are-independent"],
|
|
["builder-imports-module", "builder-composes-contracts"],
|
|
["contracts-import-core", "contracts-do-not-depend-upward"],
|
|
["sdk-imports-core", "sdks-do-not-depend-on-implementations"],
|
|
["shared-role-order", "sdks-do-not-depend-on-implementations"],
|
|
["package-without-exports", "workspace-imports-use-public-exports"],
|
|
["private-package-import", "workspace-imports-use-public-exports"],
|
|
["non-workspace-version", "internal-dependencies-use-workspace-protocol"],
|
|
["workspace-alias", "workspace-dependency-aliases-are-forbidden"],
|
|
["undeclared-workspace-dependency", "workspace-dependencies-are-declared"],
|
|
["duplicate-package-name", "workspace-package-names-are-unique"],
|
|
["unknown-shared-role", "shared-packages-declare-known-roles"],
|
|
["workspace-unsupported-pattern", "workspace-patterns-use-supported-syntax"],
|
|
["package-cycle", "no-circular-package-dependencies"],
|
|
["module-has-ui", "modules-do-not-own-ui"],
|
|
["module-ui-framework", "modules-do-not-own-ui"],
|
|
["module-imports-ui-package", "modules-use-public-platform-contracts"],
|
|
["cross-package-relative", "workspace-imports-use-public-exports"],
|
|
["cycle", "no-circular-source-dependencies"],
|
|
])("rejects %s", async (fixture, expectedRule) => {
|
|
const result = await checkBoundaries(path.join(fixtures, fixture));
|
|
expect(result.violations.map((violation) => violation.rule)).toContain(expectedRule);
|
|
});
|
|
|
|
it("rejects component, stylesheet, and case-insensitive view files inside a module", async () => {
|
|
const result = await checkBoundaries(path.join(fixtures, "module-has-ui"));
|
|
expect(
|
|
result.violations.filter((violation) => violation.rule === "modules-do-not-own-ui"),
|
|
).toHaveLength(3);
|
|
});
|
|
|
|
it("rejects UI framework dependencies, imports, and view formats inside a module", async () => {
|
|
const result = await checkBoundaries(path.join(fixtures, "module-ui-framework"));
|
|
expect(
|
|
result.violations.filter((violation) => violation.rule === "modules-do-not-own-ui"),
|
|
).toHaveLength(4);
|
|
});
|
|
|
|
it("requires workspace: ranges in every dependency field", async () => {
|
|
const result = await checkBoundaries(path.join(fixtures, "non-workspace-version"));
|
|
expect(
|
|
result.violations.filter(
|
|
(violation) => violation.rule === "internal-dependencies-use-workspace-protocol",
|
|
),
|
|
).toHaveLength(4);
|
|
});
|
|
|
|
it("resolves forbidden workspace aliases before applying layer rules", async () => {
|
|
const result = await checkBoundaries(path.join(fixtures, "workspace-alias"));
|
|
expect(
|
|
result.violations.filter(
|
|
(violation) => violation.rule === "workspace-dependency-aliases-are-forbidden",
|
|
),
|
|
).toHaveLength(2);
|
|
expect(result.violations.map((violation) => violation.rule)).toEqual(
|
|
expect.arrayContaining([
|
|
"workspace-dependency-aliases-are-forbidden",
|
|
"modules-use-public-platform-contracts",
|
|
]),
|
|
);
|
|
});
|
|
|
|
it("prevents contracts and SDKs from depending upward through shared packages", async () => {
|
|
const result = await checkBoundaries(path.join(fixtures, "shared-role-order"));
|
|
expect(result.violations.map((violation) => violation.rule)).toEqual(
|
|
expect.arrayContaining([
|
|
"contracts-do-not-depend-upward",
|
|
"sdks-do-not-depend-on-implementations",
|
|
]),
|
|
);
|
|
});
|
|
});
|