- Vitest multi-project setup (unit / integration / component) - Real-Postgres integration harness via in-process PGlite (TEST_DATABASE_URL override), migrations applied per worker; production code untouched - Unit: routing, locale, validation/Zod schemas, metadata, mail, site-theme, marquee, media, portfolio helpers, plus architecture-rule tests - Integration: Prisma data layer, API routes, and all server actions - Component: UI primitives and form components (jsdom + Testing Library) - 371 tests passing
138 lines
4.7 KiB
TypeScript
138 lines
4.7 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
|
|
import {
|
|
countMediaUsageReferences,
|
|
createMediaAsset,
|
|
deleteEntityMediaUsages,
|
|
getAdminMediaAssets,
|
|
getMediaAssetById,
|
|
getMediaOptions,
|
|
getPortfolioMediaBindings,
|
|
replaceEntityMediaUsages,
|
|
} from "@/lib/media";
|
|
import { prisma } from "@/lib/prisma";
|
|
import { createMediaAsset as seedAsset } from "@/tests/helpers/factories";
|
|
|
|
describe("createMediaAsset / getMediaAssetById", () => {
|
|
it("creates and reads back an asset with usages", async () => {
|
|
const created = await createMediaAsset({
|
|
source: "EXTERNAL",
|
|
kind: "IMAGE",
|
|
url: "https://cdn/x.png",
|
|
fileName: "x.png",
|
|
label: "X",
|
|
});
|
|
const found = await getMediaAssetById(created.id);
|
|
expect(found?.url).toBe("https://cdn/x.png");
|
|
expect(found?.usages).toEqual([]);
|
|
});
|
|
|
|
it("returns null for a missing asset", async () => {
|
|
expect(await getMediaAssetById("nope")).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe("getMediaOptions", () => {
|
|
it("filters by kind", async () => {
|
|
await seedAsset({ kind: "IMAGE" });
|
|
await seedAsset({ kind: "DOCUMENT" });
|
|
const images = await getMediaOptions({ kind: "IMAGE" });
|
|
expect(images.every((a) => a.kind === "IMAGE")).toBe(true);
|
|
const all = await getMediaOptions();
|
|
expect(all.length).toBe(2);
|
|
});
|
|
});
|
|
|
|
describe("getAdminMediaAssets", () => {
|
|
it("returns newest first with usage details", async () => {
|
|
const a = await seedAsset();
|
|
await createMediaUsageFor(a.id);
|
|
const list = await getAdminMediaAssets();
|
|
expect(list.length).toBe(1);
|
|
expect(list[0].usages.length).toBe(1);
|
|
});
|
|
});
|
|
|
|
describe("replaceEntityMediaUsages", () => {
|
|
it("replaces existing usages transactionally", async () => {
|
|
const a1 = await seedAsset();
|
|
const a2 = await seedAsset();
|
|
|
|
await replaceEntityMediaUsages({
|
|
entityType: "portfolio-project",
|
|
entityId: "p1",
|
|
usages: [{ assetId: a1.id, usageType: "PORTFOLIO_COVER", fieldKey: "cover" }],
|
|
});
|
|
expect(await countMediaUsageReferences(a1.id)).toBe(1);
|
|
|
|
await replaceEntityMediaUsages({
|
|
entityType: "portfolio-project",
|
|
entityId: "p1",
|
|
usages: [{ assetId: a2.id, usageType: "PORTFOLIO_COVER", fieldKey: "cover" }],
|
|
});
|
|
expect(await countMediaUsageReferences(a1.id)).toBe(0);
|
|
expect(await countMediaUsageReferences(a2.id)).toBe(1);
|
|
});
|
|
|
|
it("clears usages when given an empty list", async () => {
|
|
const a1 = await seedAsset();
|
|
await replaceEntityMediaUsages({
|
|
entityType: "portfolio-project",
|
|
entityId: "p2",
|
|
usages: [{ assetId: a1.id, usageType: "PORTFOLIO_ASSET", fieldKey: "a" }],
|
|
});
|
|
await replaceEntityMediaUsages({ entityType: "portfolio-project", entityId: "p2", usages: [] });
|
|
expect(await countMediaUsageReferences(a1.id)).toBe(0);
|
|
});
|
|
});
|
|
|
|
describe("deleteEntityMediaUsages", () => {
|
|
it("removes only the target entity's usages", async () => {
|
|
const a1 = await seedAsset();
|
|
await replaceEntityMediaUsages({
|
|
entityType: "portfolio-project",
|
|
entityId: "keep",
|
|
usages: [{ assetId: a1.id, usageType: "PORTFOLIO_ASSET", fieldKey: "a" }],
|
|
});
|
|
await replaceEntityMediaUsages({
|
|
entityType: "portfolio-project",
|
|
entityId: "drop",
|
|
usages: [{ assetId: a1.id, usageType: "PORTFOLIO_ASSET", fieldKey: "b" }],
|
|
});
|
|
await deleteEntityMediaUsages("portfolio-project", "drop");
|
|
expect(await countMediaUsageReferences(a1.id)).toBe(1);
|
|
});
|
|
});
|
|
|
|
describe("getPortfolioMediaBindings", () => {
|
|
it("routes usages into cover / section / asset buckets", async () => {
|
|
const cover = await seedAsset();
|
|
const section = await seedAsset();
|
|
const asset = await seedAsset();
|
|
|
|
await prisma.mediaUsage.createMany({
|
|
data: [
|
|
{ assetId: cover.id, usageType: "PORTFOLIO_COVER", entityType: "portfolio-project", entityId: "proj", fieldKey: "cover" },
|
|
{ assetId: section.id, usageType: "PORTFOLIO_SECTION", entityType: "portfolio-project", entityId: "proj", fieldKey: "sec_1" },
|
|
{ assetId: asset.id, usageType: "PORTFOLIO_ASSET", entityType: "portfolio-project", entityId: "proj", fieldKey: "ast_1" },
|
|
],
|
|
});
|
|
|
|
const bindings = await getPortfolioMediaBindings("proj");
|
|
expect(bindings.coverAssetId).toBe(cover.id);
|
|
expect(bindings.sectionAssetIds.sec_1).toBe(section.id);
|
|
expect(bindings.assetIds.ast_1).toBe(asset.id);
|
|
});
|
|
|
|
it("returns empty bindings for an unknown project", async () => {
|
|
const bindings = await getPortfolioMediaBindings("missing");
|
|
expect(bindings).toEqual({ coverAssetId: null, sectionAssetIds: {}, assetIds: {} });
|
|
});
|
|
});
|
|
|
|
async function createMediaUsageFor(assetId: string) {
|
|
await prisma.mediaUsage.create({
|
|
data: { assetId, usageType: "GENERIC", entityType: "e", entityId: "1", fieldKey: "f" },
|
|
});
|
|
}
|