154 lines
4.7 KiB
TypeScript
154 lines
4.7 KiB
TypeScript
import { readdirSync, readFileSync, statSync } from "node:fs";
|
|
import { join } from "node:path";
|
|
import { describe, expect, it } from "@suaveplan/testing/runner";
|
|
|
|
const PKG_ROOT = new URL("..", import.meta.url).pathname;
|
|
const PKG_JSON = JSON.parse(
|
|
readFileSync(join(PKG_ROOT, "package.json"), "utf8")
|
|
) as {
|
|
readonly dependencies: Record<string, string>;
|
|
readonly exports: Record<string, unknown>;
|
|
};
|
|
|
|
const RUNTIME_ALLOWLIST = new Set([
|
|
"@suaveplan/api-types",
|
|
"@suaveplan/error",
|
|
"tslib",
|
|
"zod",
|
|
]);
|
|
|
|
const SRC_ROOT = join(PKG_ROOT, "src");
|
|
|
|
function walk(dir: string): string[] {
|
|
const out: string[] = [];
|
|
for (const entry of readdirSync(dir)) {
|
|
const p = join(dir, entry);
|
|
const st = statSync(p);
|
|
if (st.isDirectory()) out.push(...walk(p));
|
|
else out.push(p);
|
|
}
|
|
return out;
|
|
}
|
|
|
|
const FEATURE_MODULES = [
|
|
"registry",
|
|
"resolver",
|
|
"loader",
|
|
"subscription",
|
|
"federation",
|
|
"directives",
|
|
"engine",
|
|
"capability-id",
|
|
"error-codes",
|
|
"testing",
|
|
"validators",
|
|
];
|
|
|
|
describe("dep-graph", () => {
|
|
it("dependencies match the contract-layer allowlist", {
|
|
timeout: 5000,
|
|
}, () => {
|
|
for (const dep of Object.keys(PKG_JSON.dependencies)) {
|
|
expect(RUNTIME_ALLOWLIST.has(dep)).toBe(true);
|
|
}
|
|
});
|
|
|
|
it("subpath exports cover every feature module", { timeout: 5000 }, () => {
|
|
const exports = PKG_JSON.exports;
|
|
for (const m of FEATURE_MODULES) {
|
|
expect(exports[`./${m}`]).toBeDefined();
|
|
}
|
|
expect(exports["."]).toBeDefined();
|
|
});
|
|
|
|
it("no biome-ignore suppressions in src or tests", {
|
|
timeout: 5000,
|
|
}, () => {
|
|
const files = walk(SRC_ROOT)
|
|
.concat(walk(join(PKG_ROOT, "tests")))
|
|
.filter((f) => f.endsWith(".ts"));
|
|
const needle = ["biome", "ignore"].join("-");
|
|
for (const f of files) {
|
|
if (f.endsWith("dep-graph.test.ts")) continue;
|
|
const text = readFileSync(f, "utf8");
|
|
expect({ file: f, has: text.includes(needle) }).toEqual({
|
|
file: f,
|
|
has: false,
|
|
});
|
|
}
|
|
});
|
|
|
|
it("no direct vitest/bun:test imports", { timeout: 5000 }, () => {
|
|
const files = walk(SRC_ROOT)
|
|
.concat(walk(join(PKG_ROOT, "tests")))
|
|
.filter((f) => f.endsWith(".ts"));
|
|
const banned =
|
|
/from\s+['"](vitest|bun:test)['"]|require\(\s*['"](vitest|bun:test)['"]\s*\)/;
|
|
for (const f of files) {
|
|
const text = readFileSync(f, "utf8");
|
|
if (banned.test(text)) {
|
|
throw new Error(`${f} imports vitest or bun:test directly`);
|
|
}
|
|
}
|
|
expect(true).toBe(true);
|
|
});
|
|
|
|
it("no `export *` in feature barrels", { timeout: 5000 }, () => {
|
|
for (const m of FEATURE_MODULES) {
|
|
const idx = join(SRC_ROOT, m, "index.ts");
|
|
const text = readFileSync(idx, "utf8");
|
|
expect(text.includes("export *")).toBe(false);
|
|
}
|
|
});
|
|
|
|
it("every src .ts uses default-import zod", { timeout: 5000 }, () => {
|
|
const files = walk(SRC_ROOT).filter(
|
|
(f) => f.endsWith(".ts") && !f.endsWith(".d.ts")
|
|
);
|
|
const named = /from\s+["']zod["']/;
|
|
const importLine =
|
|
/^\s*import\s+\{[^}]*\bz\b[^}]*\}\s+from\s+["']zod["']/m;
|
|
for (const f of files) {
|
|
const text = readFileSync(f, "utf8");
|
|
if (!named.test(text)) continue;
|
|
// If file imports zod, it must use `import z from "zod"` form.
|
|
const hasDefault = /^\s*import\s+z\s+from\s+["']zod["']/m.test(
|
|
text
|
|
);
|
|
const hasNamedZ = importLine.test(text);
|
|
expect({ file: f, hasDefault, hasNamedZ }).toEqual({
|
|
file: f,
|
|
hasDefault: true,
|
|
hasNamedZ: false,
|
|
});
|
|
}
|
|
});
|
|
|
|
it("every implementation .ts has a sibling .md", {
|
|
timeout: 5000,
|
|
}, () => {
|
|
const files = walk(SRC_ROOT).filter(
|
|
(f) =>
|
|
f.endsWith(".ts") &&
|
|
!f.endsWith(".test.ts") &&
|
|
!f.endsWith(".types.ts") &&
|
|
!f.endsWith(".spec-conformance.test.ts") &&
|
|
!/[/\\]index\.ts$/.test(f)
|
|
);
|
|
for (const f of files) {
|
|
const md = f.replace(/\.ts$/, ".md");
|
|
let has = false;
|
|
try {
|
|
statSync(md);
|
|
has = true;
|
|
} catch {
|
|
has = false;
|
|
}
|
|
expect({ file: f, hasMd: has }).toEqual({
|
|
file: f,
|
|
hasMd: true,
|
|
});
|
|
}
|
|
});
|
|
});
|