* refactor(draw-image): add tests/fixtures/cover.svg and loadFixture helper * feat(draw-image): add --template-dir flag to cli for test isolation * refactor(draw-image): switch tests from templates/cover to fixtures * chore(draw-image): simplify production cover.svg now decoupled from tests * docs(handoff): add handoff + ADR for issue #196 * docs(handoff): set PR number * docs(project-map): update after structural changes --------- Co-authored-by: opencode-agent <agent@opencode.local>
54 lines
No EOL
2.1 KiB
TypeScript
54 lines
No EOL
2.1 KiB
TypeScript
import { describe, test, expect, beforeAll, afterAll } from "vitest"
|
|
import { existsSync, readFileSync, rmSync, mkdirSync } from "node:fs"
|
|
import path from "node:path"
|
|
import os from "node:os"
|
|
import { fileURLToPath } from "node:url"
|
|
import { spawnSync } from "node:child_process"
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
const DRAW_IMAGE_DIR = path.resolve(__dirname, "..")
|
|
const TMP = path.join(os.tmpdir(), `draw-image-idempotency-${process.pid}`)
|
|
|
|
beforeAll(() => {
|
|
if (existsSync(TMP)) rmSync(TMP, { recursive: true, force: true })
|
|
mkdirSync(TMP, { recursive: true })
|
|
})
|
|
|
|
afterAll(() => {
|
|
if (existsSync(TMP)) rmSync(TMP, { recursive: true, force: true })
|
|
})
|
|
|
|
function renderCli(template: string, title: string, out: string, slots?: string): { status: number; stdout: string; stderr: string } {
|
|
const args = ["--experimental-strip-types", path.join(DRAW_IMAGE_DIR, "cli.ts"), "render", template, "--title", title, "--out", out, "--template-dir", "tests/fixtures"]
|
|
if (slots) args.push("--slots", slots)
|
|
return spawnSync("node", args, { encoding: "utf-8", cwd: DRAW_IMAGE_DIR })
|
|
}
|
|
|
|
describe("integration — idempotency", () => {
|
|
test("re-render with same args skips", () => {
|
|
const out = path.join(TMP, "cover.png")
|
|
const r1 = renderCli("cover", "Same Title", out)
|
|
expect(r1.status).toBe(0)
|
|
const result1 = JSON.parse(r1.stdout)
|
|
expect(result1.status).toBe("rendered")
|
|
|
|
const r2 = renderCli("cover", "Same Title", out)
|
|
expect(r2.status).toBe(0)
|
|
const result2 = JSON.parse(r2.stdout)
|
|
expect(result2.status).toBe("skipped")
|
|
})
|
|
|
|
test("changed title re-renders", () => {
|
|
const out = path.join(TMP, "cover2.png")
|
|
const r1 = renderCli("cover", "Title A", out)
|
|
const result1 = JSON.parse(r1.stdout)
|
|
expect(result1.status).toBe("rendered")
|
|
|
|
const r2 = renderCli("cover", "Title B", out)
|
|
const result2 = JSON.parse(r2.stdout)
|
|
expect(result2.status).toBe("rendered")
|
|
|
|
const meta = JSON.parse(readFileSync(out.replace(/\.png$/, ".meta.json"), "utf-8"))
|
|
expect(meta.hash).toHaveLength(64)
|
|
})
|
|
}) |