add testing suite

This commit is contained in:
2026-02-21 22:19:21 -05:00
parent 07128c3529
commit 83eee20b2c
25 changed files with 5317 additions and 1628 deletions

View File

@@ -0,0 +1,92 @@
import { describe, it, expect, beforeAll } from "vitest";
import { generateDungeon } from "../../src/dungeonGenerator.js";
import { generatePDF } from "../../src/generatePDF.js";
import fs from "fs/promises";
import path from "path";
const hasOllama = !!process.env.OLLAMA_API_URL;
describe.skipIf(!hasOllama)("Dungeon generation (Ollama)", () => {
let dungeonData;
beforeAll(async () => {
dungeonData = await generateDungeon();
}, 120000);
it("generates dungeon data", () => {
expect(dungeonData).toBeDefined();
});
it("title is 2-4 words, no colons", () => {
expect(dungeonData.title).toBeTruthy();
const words = dungeonData.title.split(/\s+/);
expect(words.length).toBeGreaterThanOrEqual(2);
expect(words.length).toBeLessThanOrEqual(4);
expect(dungeonData.title).not.toContain(":");
});
it("flavor text is ≤60 words", () => {
expect(dungeonData.flavor).toBeTruthy();
const words = dungeonData.flavor.split(/\s+/);
expect(words.length).toBeLessThanOrEqual(60);
});
it("hooks have no title prefixes", () => {
expect(dungeonData.hooksRumors).toBeDefined();
dungeonData.hooksRumors.forEach((hook) => {
expect(hook).not.toMatch(/^[^:]+:\s/);
});
});
it("has exactly 6 random events", () => {
expect(dungeonData.randomEvents).toBeDefined();
expect(dungeonData.randomEvents.length).toBe(6);
});
it("encounter details do not start with encounter name", () => {
expect(dungeonData.encounters).toBeDefined();
dungeonData.encounters.forEach((encounter) => {
if (encounter.details) {
const detailsLower = encounter.details.toLowerCase();
const nameLower = encounter.name.toLowerCase();
expect(detailsLower.startsWith(nameLower)).toBe(false);
}
});
});
it("treasure descriptions do not start with 'description'", () => {
expect(dungeonData.treasure).toBeDefined();
dungeonData.treasure.forEach((item) => {
if (typeof item === "object" && item.description) {
expect(item.description.toLowerCase().startsWith("description")).toBe(false);
}
});
});
it("NPC traits do not start with 'description'", () => {
expect(dungeonData.npcs).toBeDefined();
dungeonData.npcs.forEach((npc) => {
if (npc.trait) {
expect(npc.trait.toLowerCase().startsWith("description")).toBe(false);
}
});
});
it("PDF fits on one page", async () => {
const testPdfPath = path.join(process.cwd(), "test-output.pdf");
try {
await generatePDF(dungeonData, testPdfPath);
const pdfBuffer = await fs.readFile(testPdfPath);
const pdfText = pdfBuffer.toString("binary");
const pageCount = (pdfText.match(/\/Type\s*\/Page[^s]/g) || []).length;
const expectedPages = dungeonData.map ? 2 : 1;
expect(pageCount).toBeLessThanOrEqual(expectedPages);
} finally {
try {
await fs.unlink(testPdfPath);
} catch {
// Ignore cleanup errors
}
}
}, 60000);
});