test: add comprehensive automated test coverage
- 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
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import {
|
||||
DEFAULT_SITE_NAME,
|
||||
MAINTENANCE_MODE_KEY,
|
||||
SITE_NAME_KEY,
|
||||
SITE_SETTINGS_ENTITY_ID,
|
||||
SITE_SETTINGS_ENTITY_TYPE,
|
||||
SITE_SETTINGS_FAVICON_FIELD_KEY,
|
||||
SITE_SETTINGS_LOGO_LIGHT_FIELD_KEY,
|
||||
buildDefaultMailSettings,
|
||||
buildDefaultMarqueeSettings,
|
||||
getMailSettings,
|
||||
getMaintenanceMode,
|
||||
getMarqueeSettings,
|
||||
getSiteSettings,
|
||||
getSiteSettingsMediaBindings,
|
||||
setMaintenanceMode,
|
||||
updateMailSettings,
|
||||
updateMarqueeSettings,
|
||||
updateSiteSettings,
|
||||
} from "@/lib/app-config";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { createMediaAsset } from "@/tests/helpers/factories";
|
||||
|
||||
describe("maintenance mode", () => {
|
||||
it("defaults to false when unset", async () => {
|
||||
expect(await getMaintenanceMode()).toBe(false);
|
||||
});
|
||||
|
||||
it("persists and reads back the enabled flag", async () => {
|
||||
await setMaintenanceMode(true);
|
||||
expect(await getMaintenanceMode()).toBe(true);
|
||||
const row = await prisma.appConfig.findUnique({ where: { key: MAINTENANCE_MODE_KEY } });
|
||||
expect(row?.value).toBe("true");
|
||||
await setMaintenanceMode(false);
|
||||
expect(await getMaintenanceMode()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("site settings", () => {
|
||||
it("returns defaults (with fallback name) when nothing stored", async () => {
|
||||
const settings = await getSiteSettings();
|
||||
expect(settings.defaultLocale).toBe("de");
|
||||
expect(settings.locales.en.siteName).toBe(DEFAULT_SITE_NAME);
|
||||
});
|
||||
|
||||
it("uses the stored siteName key as the fallback name", async () => {
|
||||
await prisma.appConfig.create({ data: { key: SITE_NAME_KEY, value: "My Studio" } });
|
||||
const settings = await getSiteSettings();
|
||||
expect(settings.locales.ar.siteName).toBe("My Studio");
|
||||
});
|
||||
|
||||
it("round-trips an updated settings object", async () => {
|
||||
const next = await getSiteSettings();
|
||||
next.defaultLocale = "ar";
|
||||
next.brand.primaryColor = "#123456";
|
||||
next.locales.en.siteName = "Updated EN";
|
||||
await updateSiteSettings(next);
|
||||
|
||||
const reloaded = await getSiteSettings();
|
||||
expect(reloaded.defaultLocale).toBe("ar");
|
||||
expect(reloaded.brand.primaryColor).toBe("#123456");
|
||||
expect(reloaded.locales.en.siteName).toBe("Updated EN");
|
||||
});
|
||||
});
|
||||
|
||||
describe("mail settings", () => {
|
||||
it("returns defaults when unset", async () => {
|
||||
expect(await getMailSettings()).toEqual(buildDefaultMailSettings());
|
||||
});
|
||||
|
||||
it("round-trips stored mail settings", async () => {
|
||||
const next = buildDefaultMailSettings();
|
||||
next.smtp.host = "smtp.test";
|
||||
next.smtp.port = 465;
|
||||
next.sender.email = "from@test";
|
||||
next.recipients.contact = "c@test";
|
||||
await updateMailSettings(next);
|
||||
|
||||
const reloaded = await getMailSettings();
|
||||
expect(reloaded.smtp.host).toBe("smtp.test");
|
||||
expect(reloaded.smtp.port).toBe(465);
|
||||
expect(reloaded.recipients.contact).toBe("c@test");
|
||||
});
|
||||
});
|
||||
|
||||
describe("marquee settings", () => {
|
||||
it("returns defaults when unset", async () => {
|
||||
const settings = await getMarqueeSettings();
|
||||
expect(settings.locales.de.row1).toBe(buildDefaultMarqueeSettings().locales.de.row1);
|
||||
});
|
||||
|
||||
it("stores german-synced values", async () => {
|
||||
const next = buildDefaultMarqueeSettings();
|
||||
next.locales.de.row1 = "GERMAN ROW";
|
||||
next.locales.en.row1 = "will be overwritten";
|
||||
await updateMarqueeSettings(next);
|
||||
|
||||
const reloaded = await getMarqueeSettings();
|
||||
expect(reloaded.locales.de.row1).toBe("GERMAN ROW");
|
||||
expect(reloaded.locales.en.row1).toBe("GERMAN ROW");
|
||||
expect(reloaded.locales.ar.row1).toBe("GERMAN ROW");
|
||||
});
|
||||
});
|
||||
|
||||
describe("getSiteSettingsMediaBindings", () => {
|
||||
it("returns nulls when there are no usages", async () => {
|
||||
const bindings = await getSiteSettingsMediaBindings();
|
||||
expect(bindings).toEqual({ siteLogoLight: null, siteLogoDark: null, favicon: null, defaultOgImage: null });
|
||||
});
|
||||
|
||||
it("maps media usages to their field bindings", async () => {
|
||||
const logo = await createMediaAsset({ url: "https://cdn/logo.png" });
|
||||
const favicon = await createMediaAsset({ url: "https://cdn/favicon.svg" });
|
||||
await prisma.mediaUsage.create({
|
||||
data: {
|
||||
assetId: logo.id,
|
||||
usageType: "GENERIC",
|
||||
entityType: SITE_SETTINGS_ENTITY_TYPE,
|
||||
entityId: SITE_SETTINGS_ENTITY_ID,
|
||||
fieldKey: SITE_SETTINGS_LOGO_LIGHT_FIELD_KEY,
|
||||
},
|
||||
});
|
||||
await prisma.mediaUsage.create({
|
||||
data: {
|
||||
assetId: favicon.id,
|
||||
usageType: "GENERIC",
|
||||
entityType: SITE_SETTINGS_ENTITY_TYPE,
|
||||
entityId: SITE_SETTINGS_ENTITY_ID,
|
||||
fieldKey: SITE_SETTINGS_FAVICON_FIELD_KEY,
|
||||
},
|
||||
});
|
||||
|
||||
const bindings = await getSiteSettingsMediaBindings();
|
||||
expect(bindings.siteLogoLight?.assetId).toBe(logo.id);
|
||||
expect(bindings.siteLogoLight?.url).toBe("https://cdn/logo.png");
|
||||
expect(bindings.favicon?.assetId).toBe(favicon.id);
|
||||
expect(bindings.favicon?.version).toMatch(/\d{4}-\d{2}-\d{2}T/); // updatedAt ISO string
|
||||
expect(bindings.siteLogoDark).toBeNull();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user