Initial Commit
This commit is contained in:
@@ -0,0 +1,177 @@
|
||||
import { promises as fs } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { describe, expect, it } from "@suaveplan/testing/runner";
|
||||
|
||||
const PKG_ROOT = path.resolve(__dirname, "..");
|
||||
const SRC_ROOT = path.join(PKG_ROOT, "src");
|
||||
|
||||
async function walk(dir: string): Promise<string[]> {
|
||||
const out: string[] = [];
|
||||
const entries = await fs.readdir(dir, { withFileTypes: true });
|
||||
for (const e of entries) {
|
||||
const p = path.join(dir, e.name);
|
||||
if (e.isDirectory()) {
|
||||
out.push(...(await walk(p)));
|
||||
} else {
|
||||
out.push(p);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function isExempt(filePath: string): boolean {
|
||||
const base = path.basename(filePath);
|
||||
return (
|
||||
base.endsWith(".test.ts") ||
|
||||
base.endsWith(".types.ts") ||
|
||||
base === "index.ts"
|
||||
);
|
||||
}
|
||||
|
||||
describe("dep-graph: package shape", () => {
|
||||
it("every implementation .ts has a sibling .md", {
|
||||
timeout: 5000,
|
||||
}, async () => {
|
||||
const files = await walk(SRC_ROOT);
|
||||
const tsFiles = files.filter((f) => f.endsWith(".ts") && !isExempt(f));
|
||||
const missing: string[] = [];
|
||||
for (const ts of tsFiles) {
|
||||
const md = ts.replace(/\.ts$/, ".md");
|
||||
try {
|
||||
await fs.stat(md);
|
||||
} catch {
|
||||
missing.push(path.relative(PKG_ROOT, ts));
|
||||
}
|
||||
}
|
||||
expect(
|
||||
missing,
|
||||
`missing sibling .md for: ${missing.join(", ")}`
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
it("no source file uses `export *` (feature-barrel rule)", {
|
||||
timeout: 5000,
|
||||
}, async () => {
|
||||
const files = await walk(SRC_ROOT);
|
||||
const tsFiles = files.filter((f) => f.endsWith(".ts"));
|
||||
const offending: string[] = [];
|
||||
for (const f of tsFiles) {
|
||||
const content = await fs.readFile(f, "utf-8");
|
||||
if (/^\s*export\s*\*/m.test(content)) {
|
||||
offending.push(path.relative(PKG_ROOT, f));
|
||||
}
|
||||
}
|
||||
expect(offending).toEqual([]);
|
||||
});
|
||||
|
||||
it("no test file imports runner primitives directly from vitest or bun:test", {
|
||||
timeout: 5000,
|
||||
}, async () => {
|
||||
const files = await walk(SRC_ROOT);
|
||||
const tests = files.filter((f) => f.endsWith(".test.ts"));
|
||||
const offending: string[] = [];
|
||||
for (const f of tests) {
|
||||
const content = await fs.readFile(f, "utf-8");
|
||||
if (/from\s+["'](?:vitest|bun:test)["']/.test(content)) {
|
||||
offending.push(path.relative(PKG_ROOT, f));
|
||||
}
|
||||
}
|
||||
expect(offending).toEqual([]);
|
||||
});
|
||||
|
||||
it("no source file contains banned patterns", {
|
||||
timeout: 5000,
|
||||
}, async () => {
|
||||
const files = await walk(SRC_ROOT);
|
||||
const tsFiles = files.filter(
|
||||
(f) => f.endsWith(".ts") && !f.endsWith(".test.ts")
|
||||
);
|
||||
const banned = [
|
||||
"@deprecated",
|
||||
"TODO",
|
||||
"FIXME",
|
||||
"ts-ignore",
|
||||
"ts-expect-error",
|
||||
".skip",
|
||||
".only",
|
||||
"console.log",
|
||||
"biome-ignore",
|
||||
];
|
||||
const hits: string[] = [];
|
||||
for (const f of tsFiles) {
|
||||
const content = await fs.readFile(f, "utf-8");
|
||||
for (const b of banned) {
|
||||
if (content.includes(b)) {
|
||||
hits.push(`${path.relative(PKG_ROOT, f)}: ${b}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
expect(hits).toEqual([]);
|
||||
});
|
||||
|
||||
it("package.json declares subpath exports for every command + prompt + run-function + otel + error-codes module", {
|
||||
timeout: 5000,
|
||||
}, async () => {
|
||||
const pkg = JSON.parse(
|
||||
await fs.readFile(path.join(PKG_ROOT, "package.json"), "utf-8")
|
||||
) as { exports: Record<string, unknown> };
|
||||
const required = [
|
||||
".",
|
||||
"./error-codes/error-codes",
|
||||
"./otel/instruments",
|
||||
"./run-function",
|
||||
"./prompt/prompt",
|
||||
"./commands/keys/keys",
|
||||
"./commands/files/files",
|
||||
"./commands/files/serve-file",
|
||||
"./commands/license/license",
|
||||
];
|
||||
for (const r of required) {
|
||||
expect(
|
||||
pkg.exports[r],
|
||||
`missing subpath export: ${r}`
|
||||
).toBeDefined();
|
||||
}
|
||||
});
|
||||
|
||||
it("package.json declares all 18 bin entries", {
|
||||
timeout: 5000,
|
||||
}, async () => {
|
||||
const pkg = JSON.parse(
|
||||
await fs.readFile(path.join(PKG_ROOT, "package.json"), "utf-8")
|
||||
) as { bin: Record<string, string> };
|
||||
const required = [
|
||||
"suaveplan",
|
||||
"license",
|
||||
"create-root-keys",
|
||||
"create-server-keys",
|
||||
"roll-server-keys",
|
||||
"create-root-keys-chain",
|
||||
"create-server-keys-chain",
|
||||
"roll-server-keys-chain",
|
||||
"verify-key-chain",
|
||||
"view-keys",
|
||||
"view-vault",
|
||||
"view-public-key-file",
|
||||
"view-chain-public",
|
||||
"prepare-file",
|
||||
"serve-file",
|
||||
"validate-file",
|
||||
"get-file-stats",
|
||||
"validate-packet",
|
||||
];
|
||||
for (const r of required) {
|
||||
expect(pkg.bin[r], `missing bin: ${r}`).toBeDefined();
|
||||
}
|
||||
expect(Object.keys(pkg.bin)).toHaveLength(18);
|
||||
// Each declared bin must have a matching .ts file in src/bin.
|
||||
for (const r of required) {
|
||||
const expected = path.join(
|
||||
SRC_ROOT,
|
||||
"bin",
|
||||
r === "suaveplan" ? "index.ts" : `${r}.ts`
|
||||
);
|
||||
await expect(fs.stat(expected)).resolves.toBeDefined();
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user