import { describe, expect, it } from "vitest"; import { getActivePortfolioCategories, getActivePortfolioCategoryBySlug, getAdminPortfolioCategories, getAdminPortfolioProjectById, getAdminPortfolioProjects, getPublishedPortfolioProjectBySlug, getPublishedPortfolioProjects, } from "@/lib/portfolio"; import { eq } from "drizzle-orm"; import { db } from "@/lib/db"; import { category, mediaUsage, portfolioAsset, portfolioProject, portfolioSection } from "@/lib/db/schema"; import { createAsset, createCategory, createMediaAsset, createProject, createSection, } from "@/tests/helpers/factories"; describe("categories", () => { it("lists admin categories with project counts, ordered", async () => { const a = await createCategory({ slug: "a", sortOrder: 2 }); await createCategory({ slug: "b", sortOrder: 1 }); await createProject({ categoryId: a.id }); const categories = await getAdminPortfolioCategories(); expect(categories.map((c) => c.slug)).toEqual(["b", "a"]); // sortOrder asc expect(categories.find((c) => c.slug === "a")?.projectCount).toBe(1); expect(categories.find((c) => c.slug === "b")?.projectCount).toBe(0); }); it("returns only active categories publicly", async () => { await createCategory({ slug: "on", isActive: true }); await createCategory({ slug: "off", isActive: false }); const active = await getActivePortfolioCategories(); expect(active.map((c) => c.slug)).toEqual(["on"]); }); it("finds an active category by slug and ignores inactive ones", async () => { await createCategory({ slug: "visible", isActive: true }); await createCategory({ slug: "hidden", isActive: false }); expect((await getActivePortfolioCategoryBySlug("visible"))?.slug).toBe("visible"); expect(await getActivePortfolioCategoryBySlug("hidden")).toBeNull(); }); }); describe("admin projects", () => { it("filters by status and category", async () => { const cat = await createCategory(); await createProject({ categoryId: cat.id, slug: "pub", isPublished: true }); await createProject({ categoryId: cat.id, slug: "draft", isPublished: false }); const published = await getAdminPortfolioProjects({ status: "published" }); expect(published.map((p) => p.slug)).toEqual(["pub"]); const drafts = await getAdminPortfolioProjects({ status: "draft" }); expect(drafts.map((p) => p.slug)).toEqual(["draft"]); const byCategory = await getAdminPortfolioProjects({ categoryId: cat.id }); expect(byCategory.length).toBe(2); }); it("maps localized content and nested sections/assets", async () => { const project = await createProject({ slug: "mapped" }); await createSection(project.id, { titleEn: "Intro" }); await createAsset(project.id, { altEn: "Cover" }); const detail = await getAdminPortfolioProjectById(project.id); expect(detail?.title.en).toBe("Title"); expect(detail?.sections[0].title.en).toBe("Intro"); expect(detail?.assets[0].alt.en).toBe("Cover"); }); it("attaches media bindings to a project fetched by id", async () => { const project = await createProject(); const cover = await createMediaAsset(); await db.insert(mediaUsage).values({ assetId: cover.id, usageType: "PORTFOLIO_COVER", entityType: "portfolio-project", entityId: project.id, fieldKey: "cover", }); const detail = await getAdminPortfolioProjectById(project.id); expect(detail?.coverMediaAssetId).toBe(cover.id); }); it("returns null for a missing project id", async () => { expect(await getAdminPortfolioProjectById("missing")).toBeNull(); }); }); describe("published projects", () => { it("returns only published projects in active categories", async () => { const activeCat = await createCategory({ isActive: true }); const inactiveCat = await createCategory({ isActive: false }); await createProject({ categoryId: activeCat.id, slug: "shown", isPublished: true }); await createProject({ categoryId: activeCat.id, slug: "hidden-draft", isPublished: false }); await createProject({ categoryId: inactiveCat.id, slug: "hidden-cat", isPublished: true }); const projects = await getPublishedPortfolioProjects(); expect(projects.map((p) => p.slug)).toEqual(["shown"]); }); it("filters published projects by category slug", async () => { const catA = await createCategory({ slug: "cat-a", isActive: true }); const catB = await createCategory({ slug: "cat-b", isActive: true }); await createProject({ categoryId: catA.id, slug: "in-a", isPublished: true }); await createProject({ categoryId: catB.id, slug: "in-b", isPublished: true }); const projects = await getPublishedPortfolioProjects({ categorySlug: "cat-a" }); expect(projects.map((p) => p.slug)).toEqual(["in-a"]); }); it("finds a published project by slug and hides drafts", async () => { await createProject({ slug: "live", isPublished: true }); await createProject({ slug: "wip", isPublished: false }); expect((await getPublishedPortfolioProjectBySlug("live"))?.slug).toBe("live"); expect(await getPublishedPortfolioProjectBySlug("wip")).toBeNull(); }); }); describe("referential integrity", () => { it("restricts deleting a category that still has projects", async () => { const cat = await createCategory(); await createProject({ categoryId: cat.id }); await expect(db.delete(category).where(eq(category.id, cat.id))).rejects.toThrow(); }); it("cascades section and asset deletion when a project is removed", async () => { const project = await createProject(); await createSection(project.id); await createAsset(project.id); await db.delete(portfolioProject).where(eq(portfolioProject.id, project.id)); expect(await db.$count(portfolioSection)).toBe(0); expect(await db.$count(portfolioAsset)).toBe(0); }); });