import { mkdir, mkdtemp, rm } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { describe, expect, it } from "vitest"; import { PROJECT_YAML_NAME, projectYamlSchema, readProjectEnabledMods, readProjectYaml, writeProjectYaml, } from "../src/project-yaml.js"; async function tmpDir(): Promise { return mkdtemp(join(tmpdir(), "editor-yaml-")); } describe("projectYamlSchema", () => { it("accepts empty and partial metadata", () => { expect(projectYamlSchema.safeParse({}).success).toBe(true); expect(projectYamlSchema.safeParse({ name: "Game" }).success).toBe(true); expect( projectYamlSchema.safeParse({ name: "Game", icon: "icon.png", pack: { installer: true, splitAssets: true } }) .success, ).toBe(true); }); it("accepts the F1 mods field (absent / empty / enabled list)", () => { expect(projectYamlSchema.safeParse({ mods: {} }).success).toBe(true); expect(projectYamlSchema.safeParse({ mods: { enabled: [] } }).success).toBe(true); expect(projectYamlSchema.safeParse({ mods: { enabled: ["a-mod", "b-mod"] } }).success).toBe(true); expect(projectYamlSchema.safeParse({ name: "G", mods: { enabled: ["a-mod"] } }).success).toBe(true); }); it("rejects unknown keys and wrong types", () => { expect(projectYamlSchema.safeParse({ nmae: "Game" }).success).toBe(false); expect(projectYamlSchema.safeParse({ name: 42 }).success).toBe(false); expect(projectYamlSchema.safeParse({ pack: { installer: "yes" } }).success).toBe(false); expect(projectYamlSchema.safeParse({ pack: { unknown: true } }).success).toBe(false); expect(projectYamlSchema.safeParse({ pack: "installed" }).success).toBe(false); expect(projectYamlSchema.safeParse({ mods: { enabled: [1] } }).success).toBe(false); expect(projectYamlSchema.safeParse({ mods: { enabled: "" } }).success).toBe(false); expect(projectYamlSchema.safeParse({ mods: { unknown: true } }).success).toBe(false); expect(projectYamlSchema.safeParse({ mods: "all" }).success).toBe(false); }); }); describe("readProjectYaml / writeProjectYaml", () => { it("returns null when project.yaml is absent", async () => { const dir = await tmpDir(); try { expect(await readProjectYaml(dir)).toBeNull(); } finally { await rm(dir, { recursive: true, force: true }); } }); it("roundtrips a written project.yaml", async () => { const dir = await tmpDir(); try { const data = { name: "Pinefield", icon: "icon.png", pack: { installer: true, mods: true, splitAssets: true } }; await writeProjectYaml(dir, data); expect(await readProjectYaml(dir)).toEqual(data); const { readFile } = await import("node:fs/promises"); const raw = await readFile(join(dir, PROJECT_YAML_NAME), "utf8"); expect(raw).toContain("name: Pinefield"); } finally { await rm(dir, { recursive: true, force: true }); } }); it("roundtrips the mods field losslessly with other fields preserved", async () => { const dir = await tmpDir(); try { const data = { name: "Pinefield", pack: { mods: true }, mods: { enabled: ["fishing-mod", "weather-mod"] }, }; await writeProjectYaml(dir, data); expect(await readProjectYaml(dir)).toEqual(data); } finally { await rm(dir, { recursive: true, force: true }); } }); it("roundtrips an empty enabled list (all disabled) as distinct from absent", async () => { const dir = await tmpDir(); try { await writeProjectYaml(dir, { mods: { enabled: [] } }); const read = await readProjectYaml(dir); expect(read).toEqual({ mods: { enabled: [] } }); expect(read?.mods?.enabled).toEqual([]); } finally { await rm(dir, { recursive: true, force: true }); } }); it("readProjectEnabledMods returns undefined when absent or malformed", async () => { const dir = await tmpDir(); try { expect(await readProjectEnabledMods(dir)).toBeUndefined(); await writeProjectYaml(dir, { mods: { enabled: ["a-mod"] } }); expect(await readProjectEnabledMods(dir)).toEqual(["a-mod"]); await writeProjectYaml(dir, {}); expect(await readProjectEnabledMods(dir)).toBeUndefined(); const { writeFile } = await import("node:fs/promises"); await writeFile(join(dir, PROJECT_YAML_NAME), "name: [broken\n", "utf8"); expect(await readProjectEnabledMods(dir)).toBeUndefined(); } finally { await rm(dir, { recursive: true, force: true }); } }); it("throws on malformed yaml and invalid data", async () => { const dir = await tmpDir(); try { await mkdir(dir, { recursive: true }); const { writeFile } = await import("node:fs/promises"); await writeFile(join(dir, PROJECT_YAML_NAME), "name: [unclosed\n", "utf8"); await expect(readProjectYaml(dir)).rejects.toThrow(/invalid project.yaml/); await expect(writeProjectYaml(dir, { name: 42 } as never)).rejects.toThrow(/invalid project.yaml/); } finally { await rm(dir, { recursive: true, force: true }); } }); });