import { beforeEach, describe, expect, it } from "vitest"; import { eq } from "drizzle-orm"; import { db } from "@/db"; import { fontEnum, tags, themeEnum } from "@/db/schema"; import { resolveHomeContent } from "@/lib/services/home"; import { createPage, deletePage, setPageStatus } from "@/lib/services/pages"; import { createPost } from "@/lib/services/posts"; import { getSettings, listPublicNav, saveSettings, type SettingsInput, } from "@/lib/services/settings"; import { resetDb } from "../helpers/db"; const settingsInput = (overrides: Partial = {}): SettingsInput => ({ siteTitle: "Test Site", headerText: "", footerText: "", postsPerPage: 10, excerptWords: 40, homeMode: "posts", homeTagId: null, homePageId: null, theme: "solarized-dark", font: "geist", ...overrides, }); beforeEach(resetDb); describe("home page configuration", () => { it("defaults to the post list", async () => { await saveSettings(settingsInput(), []); const home = await resolveHomeContent(await getSettings()); expect(home.kind).toBe("posts"); }); it("shows a configured tag and falls back when the tag is deleted", async () => { await createPost({ title: "Tagged", slug: "", body: "b", authorName: "T", featuredImageUrl: null, featuredImageAlt: null, status: "published", tagIds: [], newTagNames: ["Featured"], }); const [tag] = await db.select().from(tags).where(eq(tags.slug, "featured")); await saveSettings(settingsInput({ homeMode: "tag", homeTagId: tag.id }), []); const home = await resolveHomeContent(await getSettings()); expect(home).toMatchObject({ kind: "tag", tag: { slug: "featured" } }); // Deleting the tag nulls settings.home_tag_id via ON DELETE SET NULL. await db.delete(tags).where(eq(tags.id, tag.id)); const settingsAfter = await getSettings(); expect(settingsAfter.homeTagId).toBeNull(); expect((await resolveHomeContent(settingsAfter)).kind).toBe("posts"); }); it("shows a configured page and falls back when it is unpublished or deleted", async () => { const page = await createPage({ title: "Welcome", slug: "", body: "Hello!", status: "published", }); await saveSettings(settingsInput({ homeMode: "page", homePageId: page.id }), []); let home = await resolveHomeContent(await getSettings()); expect(home).toMatchObject({ kind: "page", page: { slug: "welcome" } }); // Unpublished page must not leak through the home route. await setPageStatus(page.id, "draft"); home = await resolveHomeContent(await getSettings()); expect(home.kind).toBe("posts"); await setPageStatus(page.id, "published"); await deletePage(page.id); const settingsAfter = await getSettings(); expect(settingsAfter.homePageId).toBeNull(); expect((await resolveHomeContent(settingsAfter)).kind).toBe("posts"); }); it("ignores a stale tag reference when the mode is posts", async () => { await saveSettings(settingsInput({ homeMode: "posts", homeTagId: null }), []); expect((await resolveHomeContent(await getSettings())).kind).toBe("posts"); }); }); describe("appearance settings", () => { it("defaults to solarized-dark and geist on an unseeded database", async () => { const settings = await getSettings(); expect(settings.theme).toBe("solarized-dark"); expect(settings.font).toBe("geist"); }); it("round-trips every theme in the enum", async () => { for (const theme of themeEnum.enumValues) { await saveSettings(settingsInput({ theme }), []); expect((await getSettings()).theme).toBe(theme); } }); it("round-trips every font in the enum", async () => { for (const font of fontEnum.enumValues) { await saveSettings(settingsInput({ font }), []); expect((await getSettings()).font).toBe(font); } }); }); describe("navigation", () => { it("keeps URL items, resolves page items, and hides unpublished pages", async () => { const page = await createPage({ title: "About", slug: "", body: "", status: "published", }); await saveSettings(settingsInput(), [ { label: "All posts", url: "/posts", pageId: null }, { label: "About", url: null, pageId: page.id }, { label: "Elsewhere", url: "https://example.com", pageId: null }, ]); let nav = await listPublicNav(); expect(nav).toEqual([ { label: "All posts", href: "/posts", external: false }, { label: "About", href: "/pages/about", external: false }, { label: "Elsewhere", href: "https://example.com", external: true }, ]); await setPageStatus(page.id, "draft"); nav = await listPublicNav(); expect(nav.map((l) => l.label)).toEqual(["All posts", "Elsewhere"]); // Deleting the page removes its nav item entirely (ON DELETE CASCADE). await setPageStatus(page.id, "published"); await deletePage(page.id); nav = await listPublicNav(); expect(nav.map((l) => l.label)).toEqual(["All posts", "Elsewhere"]); }); });