77 lines
2.0 KiB
JavaScript
77 lines
2.0 KiB
JavaScript
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
|
|
const postsDir = path.resolve("src/posts");
|
|
|
|
test("Content Contract: All posts in src/posts/ satisfy frontmatter schema", () => {
|
|
if (!fs.existsSync(postsDir)) {
|
|
fs.mkdirSync(postsDir, { recursive: true });
|
|
}
|
|
const postFiles = fs.readdirSync(postsDir).filter((f) => f.endsWith(".md"));
|
|
|
|
if (postFiles.length === 0) {
|
|
// When posts are decoupled and mounted externally or ingested via API
|
|
return;
|
|
}
|
|
|
|
for (const file of postFiles) {
|
|
const filePath = path.join(postsDir, file);
|
|
const content = fs.readFileSync(filePath, "utf-8");
|
|
|
|
// 1. Filename Convention: YYYY-MM-DD-slug.md
|
|
assert.match(
|
|
file,
|
|
/^\d{4}-\d{2}-\d{2}-[a-z0-9-]+\.md$/,
|
|
`Post filename '${file}' must follow 'YYYY-MM-DD-slug.md' naming standard`
|
|
);
|
|
|
|
// 2. YAML Frontmatter Boundary check
|
|
assert.match(
|
|
content,
|
|
/^---\n([\s\S]*?)\n---/,
|
|
`Post '${file}' must start with valid YAML frontmatter delimiters`
|
|
);
|
|
|
|
// Extract frontmatter block
|
|
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
const fmText = fmMatch[1];
|
|
|
|
// 3. Title Invariant
|
|
assert.match(
|
|
fmText,
|
|
/title:\s*"[^"]{5,}"/,
|
|
`Post '${file}' must contain a valid title of at least 5 characters`
|
|
);
|
|
|
|
// 4. Date Invariant (ISO Format)
|
|
assert.match(
|
|
fmText,
|
|
/date:\s*\d{4}-\d{2}-\d{2}/,
|
|
`Post '${file}' must define an ISO date (YYYY-MM-DD)`
|
|
);
|
|
|
|
// 5. Layout Invariant
|
|
assert.match(
|
|
fmText,
|
|
/layout:\s*layouts\/post\.njk/,
|
|
`Post '${file}' must specify 'layout: layouts/post.njk'`
|
|
);
|
|
|
|
// 6. Tags Invariant
|
|
assert.match(
|
|
fmText,
|
|
/tags:\s*\n(\s+-\s+[a-z0-9-]+\n)+/,
|
|
`Post '${file}' must specify a non-empty YAML tags list`
|
|
);
|
|
|
|
// 7. Excerpt Invariant
|
|
assert.match(
|
|
fmText,
|
|
/excerpt:\s*"[^"]{20,}"/,
|
|
`Post '${file}' must provide a substantive excerpt of at least 20 characters`
|
|
);
|
|
}
|
|
});
|