Commit f5d33eed by PLN (Algolia)

blog: a folder with neither meta.md nor a language file is not a post —…

blog: a folder with neither meta.md nor a language file is not a post — seed-note folders mint no slug or route; tests own their single-language fixtures
parent c2311673
......@@ -28,7 +28,16 @@ export const BLOG_LANGS = ['fr', 'en'];
function blogSlugs() {
return fs
.readdirSync(BLOG_ROOT)
.filter((name) => fs.statSync(path.join(BLOG_ROOT, name)).isDirectory());
.filter((name) => fs.statSync(path.join(BLOG_ROOT, name)).isDirectory())
// A folder with neither meta.md nor a language file is not a post —
// notes, scratch, seeds-in-progress. It mints no slug and no route.
.filter((name) => {
const dir = path.join(BLOG_ROOT, name);
return (
fs.existsSync(path.join(dir, 'meta.md')) ||
BLOG_LANGS.some((lang) => fs.existsSync(path.join(dir, `${lang}.md`)))
);
});
}
function langFile(slug, lang) {
......
......@@ -43,10 +43,18 @@ async function withProdEnv(fn) {
}
// Live roster from disk — the owner edits fixtures freely; the accessors
// are what this suite pins, not any particular folder name.
// are what this suite pins, not any particular folder name. Post-ness
// mirrors the loader: a folder with neither meta.md nor a language file
// (seed notes, scratch) is not a post.
const BLOG_ROOT = path.join(process.cwd(), "content", "blog");
const ALL_BLOG_SLUGS = fs
.readdirSync(path.join(process.cwd(), "content", "blog"))
.filter((f) => !f.startsWith("_") && fs.statSync(path.join(process.cwd(), "content", "blog", f)).isDirectory());
.readdirSync(BLOG_ROOT)
.filter((f) => !f.startsWith("_") && fs.statSync(path.join(BLOG_ROOT, f)).isDirectory())
.filter(
(f) =>
fs.existsSync(path.join(BLOG_ROOT, f, "meta.md")) ||
BLOG_LANGS.some((lang) => fs.existsSync(path.join(BLOG_ROOT, f, `${lang}.md`)))
);
/** Whether a live folder is currently a draft (owner edits freely). */
function readMetaDraft(slug) {
......@@ -74,6 +82,11 @@ const DRAFT_DIR = path.join(process.cwd(), "content", "blog", DRAFT_SLUG);
const EN_SLUG = "_fixture-en-only";
const EN_DIR = path.join(process.cwd(), "content", "blog", EN_SLUG);
// Synthetic fr-only post — the fallback/banner tests need a single-language
// post they own; real posts gain translations as the owner writes them.
const FR_SLUG = "_fixture-fr-only";
const FR_DIR = path.join(process.cwd(), "content", "blog", FR_SLUG);
beforeAll(() => {
fs.rmSync(FIXTURE_DIR, { recursive: true, force: true });
fs.mkdirSync(FIXTURE_DIR, { recursive: true });
......@@ -105,12 +118,19 @@ beforeAll(() => {
path.join(EN_DIR, "en.md"),
"---\ntitle: EN Fixture\n---\n\nEnglish only body.\n",
);
fs.rmSync(FR_DIR, { recursive: true, force: true });
fs.mkdirSync(FR_DIR, { recursive: true });
fs.writeFileSync(
path.join(FR_DIR, "fr.md"),
"---\ntitle: Sujet seul\n---\n\nCorps français seul.\n",
);
});
afterAll(() => {
fs.rmSync(FIXTURE_DIR, { recursive: true, force: true });
fs.rmSync(DRAFT_DIR, { recursive: true, force: true });
fs.rmSync(EN_DIR, { recursive: true, force: true });
fs.rmSync(FR_DIR, { recursive: true, force: true });
});
describe("blog public accessors (prod rule untouched)", () => {
......@@ -121,6 +141,17 @@ describe("blog public accessors (prod rule untouched)", () => {
);
});
it("treats a folder with no meta.md and no language file as not-a-post", () => {
const scratch = path.join(BLOG_ROOT, "_notes-not-a-post");
fs.mkdirSync(scratch, { recursive: true });
fs.writeFileSync(path.join(scratch, "ideas.md"), "# seeds\n");
try {
expect(getAllBlogSlugs()).not.toContain("_notes-not-a-post");
} finally {
fs.rmSync(scratch, { recursive: true, force: true });
}
});
it("filters drafts out in prod — /blog/[slug] never renders them", async () => {
await withProdEnv(() => {
// The synthetic fixture is a published post, so it stays; every real
......@@ -151,13 +182,11 @@ describe("blog public accessors (prod rule untouched)", () => {
const posts = getBlogPosts();
const dates = posts.filter((p) => ALL_BLOG_SLUGS.includes(p.slug)).map((p) => p.date);
expect(dates).toEqual([...dates].sort().reverse());
const again = posts.find((p) => p.slug === "hello-world-again");
const again = posts.find((p) => p.slug === FR_SLUG);
// The localized title (fr.md frontmatter) wins over meta.md's — compare
// against the fixture's own fr.md so hand edits to the fixture don't
// falsify the accessor contract.
const frTitle = matter(
fs.readFileSync(path.join("content/blog/hello-world-again/fr.md"), "utf8"),
).data.title;
const frTitle = matter(fs.readFileSync(path.join(FR_DIR, "fr.md"), "utf8")).data.title;
expect(again.title).toBe(frTitle);
expect(again.languages).toEqual(["fr"]);
});
......@@ -170,7 +199,7 @@ describe("blog public accessors (prod rule untouched)", () => {
describe("bilingual accessors", () => {
it("reports available languages from the folder contents", () => {
expect(getBlogLanguages("hello-world-again")).toEqual(["fr"]);
expect(getBlogLanguages(FR_SLUG)).toEqual(["fr"]);
expect(getBlogLanguages(EN_SLUG)).toEqual(["en"]);
expect(getBlogLanguages(FIXTURE_SLUG)).toEqual(["fr", "en"]);
});
......@@ -194,12 +223,12 @@ describe("bilingual accessors", () => {
it("falls back to the available language and flags the banner", async () => {
// fr requested on an fr-only post: no banner.
const fr = await getBlogPost("hello-world-again");
const fr = await getBlogPost(FR_SLUG);
expect(fr.lang).toBe("fr");
expect(fr.untranslated).toBeNull();
// en requested on an fr-only post: fr renders, banner data present.
const en = await getBlogPost("hello-world-again", "en");
const en = await getBlogPost(FR_SLUG, "en");
expect(en.lang).toBe("fr");
expect(en.untranslated).toBe("en");
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment