import { test, expect, beforeEach, afterEach } from "bun:test"; import { mkdtemp, rm } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { addSong, readManifest, songDir } from "../../src/ref/manifest.ts"; let root = ""; beforeEach(async () => { root = await mkdtemp(join(tmpdir(), "ref-")); }); afterEach(async () => { await rm(root, { recursive: true, force: true }); }); const base = { id: "s1", title: "Song", audio: "a.flac", midi: "a.mid", capo: {} }; test("readManifest on empty root returns []", async () => { expect(await readManifest(root)).toEqual([]); }); test("addSong writes songs.json with given roles", async () => { const e = await addSong({ ...base, roles: { Gtr: "guitar", Crowd: null } }, root); expect(e.roles).toEqual({ Gtr: "guitar", Crowd: null }); expect(await Bun.file(join(root, "songs.json")).exists()).toBe(true); expect(await readManifest(root)).toEqual([e]); }); test("addSong guesses roles from the midi, keyed by track name", async () => { const ascii = (x: string) => [...x].map((c) => c.charCodeAt(0)); const trk = (n: string, prog: number) => { const body = [0, 0xff, 3, n.length, ...ascii(n), 0, 0xc0, prog, 0, 0x90, 60, 90, 0x60, 0x80, 60, 0, 0, 0xff, 0x2f, 0]; return [...ascii("MTrk"), 0, 0, 0, body.length, ...body]; }; const mid = new Uint8Array([...ascii("MThd"), 0, 0, 0, 6, 0, 1, 0, 2, 0x01, 0xe0, ...trk("Acoustic Guitar ", 25), ...trk("Brass", 61)]); const midi = join(root, "in.mid"); await Bun.write(midi, mid); const e = await addSong({ ...base, midi }, root); expect(e.roles).toEqual({ "Acoustic Guitar ": "guitar", Brass: null }); }); test("addSong rejects duplicate ids", async () => { await addSong({ ...base, roles: {} }, root); await expect(addSong({ ...base, roles: {} }, root)).rejects.toThrow(/duplicate|exists/i); expect(await readManifest(root)).toHaveLength(1); }); test("rejects unsafe ids", async () => { await expect(addSong({ ...base, id: "../x", roles: {} }, root)).rejects.toThrow(); }); test("songDir", () => { expect(songDir("s1", root)).toBe(join(root, "s1")); });