This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
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.each([
|
||||
["core-imports-module", "core-is-feature-agnostic"],
|
||||
["module-imports-surface", "modules-use-public-platform-contracts"],
|
||||
["cross-module-import", "modules-do-not-import-other-modules"],
|
||||
["module-has-ui", "modules-do-not-own-ui"],
|
||||
["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 both component and stylesheet 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(2);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,3 @@
|
||||
import { calendar } from "../../modules/calendar/src/index.js";
|
||||
|
||||
export const invalidCoreFeature = calendar;
|
||||
@@ -0,0 +1 @@
|
||||
export const calendar = "calendar";
|
||||
@@ -0,0 +1,3 @@
|
||||
import { media } from "../../../modules/media/src/index.js";
|
||||
|
||||
export const invalidModuleDependency = media;
|
||||
@@ -0,0 +1 @@
|
||||
export const media = "media";
|
||||
@@ -0,0 +1,3 @@
|
||||
import { b } from "./b.js";
|
||||
|
||||
export const a = b;
|
||||
@@ -0,0 +1,3 @@
|
||||
import { a } from "./a.js";
|
||||
|
||||
export const b = a;
|
||||
@@ -0,0 +1,3 @@
|
||||
import type { HealthContract } from "@ekklesia/contracts";
|
||||
|
||||
export const health: HealthContract = { status: "ok" };
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "@ekklesia/contracts",
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"ekklesia": {
|
||||
"layer": "contracts"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface HealthContract {
|
||||
readonly status: "ok";
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
.calendar {
|
||||
display: grid;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export function Calendar(): string {
|
||||
return "calendar";
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
import { CalendarView } from "../../../surfaces/dashboard/src/calendar-view.js";
|
||||
|
||||
export const invalidModuleUi = CalendarView;
|
||||
+1
@@ -0,0 +1 @@
|
||||
export const CalendarView = "calendar-view";
|
||||
@@ -0,0 +1,61 @@
|
||||
import { readFile } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { parse } from "yaml";
|
||||
|
||||
interface ComposeService {
|
||||
healthcheck?: unknown;
|
||||
image?: string;
|
||||
ports?: string[];
|
||||
volumes?: string[];
|
||||
}
|
||||
|
||||
interface ComposeDocument {
|
||||
services?: Record<string, ComposeService>;
|
||||
volumes?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
const repositoryRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
||||
|
||||
describe("development infrastructure", () => {
|
||||
it("has valid, local-only, version-pinned Compose services", async () => {
|
||||
const source = await readFile(
|
||||
path.join(repositoryRoot, "infrastructure", "compose.yaml"),
|
||||
"utf8",
|
||||
);
|
||||
const compose = parse(source) as ComposeDocument;
|
||||
const postgres = compose.services?.postgres;
|
||||
const objectStorage = compose.services?.["object-storage"];
|
||||
|
||||
expect(postgres?.image).toBe("postgres:18.4-alpine3.24");
|
||||
expect(objectStorage?.image).toBe("chrislusf/seaweedfs:4.28");
|
||||
expect(postgres?.healthcheck).toBeDefined();
|
||||
expect(objectStorage?.healthcheck).toBeDefined();
|
||||
|
||||
for (const service of [postgres, objectStorage]) {
|
||||
expect(service).toBeDefined();
|
||||
expect(service?.image).not.toMatch(/:latest$/u);
|
||||
expect(service?.ports?.every((port) => port.startsWith("127.0.0.1:"))).toBe(true);
|
||||
expect(service?.volumes?.length).toBeGreaterThan(0);
|
||||
}
|
||||
|
||||
expect(compose.volumes).toHaveProperty("postgres-data");
|
||||
expect(compose.volumes).toHaveProperty("object-storage-data");
|
||||
});
|
||||
|
||||
it("has a parseable CI workflow with a single repository gate", async () => {
|
||||
const source = await readFile(
|
||||
path.join(repositoryRoot, ".github", "workflows", "ci.yml"),
|
||||
"utf8",
|
||||
);
|
||||
const workflow = parse(source) as {
|
||||
jobs?: { verify?: { steps?: Array<{ run?: string }> } };
|
||||
};
|
||||
const commands = workflow.jobs?.verify?.steps?.flatMap((step) => step.run ?? []) ?? [];
|
||||
|
||||
expect(workflow.jobs?.verify).toBeDefined();
|
||||
expect(commands).toContain("pnpm install --frozen-lockfile");
|
||||
expect(commands).toContain("pnpm ci");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user