import { mkdtempSync, readFileSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { DEFAULT_CATALOG_SYNC_FLAGS, describeActions, parseCatalogSyncArgs, writeGeneratedSql, } from "./catalog-sync"; describe("catalog-sync argument parsing", () => { it("defaults to a read-only audit with no filters", () => { expect(parseCatalogSyncArgs([])).toEqual({ ...DEFAULT_CATALOG_SYNC_FLAGS, sourceIds: [], }); expect(describeActions(parseCatalogSyncArgs([]))).toBe("audit"); }); it("accepts comma-separated source ids", () => { const flags = parseCatalogSyncArgs([ "--sources=default-habbo-com,default-wibbo", ]); expect(flags.sourceIds).toEqual(["default-habbo-com", "default-wibbo"]); }); it("accepts display names, including ones with spaces", () => { const flags = parseCatalogSyncArgs(["--sources=Habbo (COM), Wibbo"]); expect(flags.sourceIds).toEqual(["Habbo (COM)", "Wibbo"]); }); it("trims and drops empty source entries", () => { expect(parseCatalogSyncArgs(["--sources= a , ,b,"]).sourceIds).toEqual([ "a", "b", ]); }); it("turns --all into every repair step", () => { const flags = parseCatalogSyncArgs(["--all"]); expect(flags.repairIcons).toBe(true); expect(flags.repairNitros).toBe(true); expect(flags.repairFurniData).toBe(true); expect(flags.repairStructure).toBe(true); expect(flags.checkFurniDataIds).toBe(true); // Generating/applying SQL is never implied by a repair. expect(flags.generateSql).toBe(false); expect(flags.applySql).toBe(false); }); it("treats applying SQL as implying generation", () => { expect(parseCatalogSyncArgs(["--apply-sql"]).generateSql).toBe(true); expect(parseCatalogSyncArgs(["--organize-sql"]).generateSql).toBe(true); expect(describeActions(parseCatalogSyncArgs(["--apply-sql"]))).toBe( "audit + apply-sql", ); }); it("accepts the alias spellings of the repair flags", () => { const flags = parseCatalogSyncArgs([ "--repair-icons", "--repair-nitros", "--repair-furnidata", "--repair-structure", ]); expect(flags.repairIcons).toBe(true); expect(flags.repairNitros).toBe(true); expect(flags.repairFurniData).toBe(true); expect(flags.repairStructure).toBe(true); }); it("ignores a bare -- separator", () => { expect(parseCatalogSyncArgs(["--", "--icons"]).repairIcons).toBe(true); }); it("rejects an unknown flag", () => { expect(() => parseCatalogSyncArgs(["--nope"])).toThrow(/Unknown flag/); }); it("rejects --sources without a value", () => { expect(() => parseCatalogSyncArgs(["--sources"])).toThrow(/needs a value/); }); }); describe("generated catalog SQL output", () => { let cwd = ""; beforeEach(() => { cwd = process.cwd(); }); afterEach(() => { vi.restoreAllMocks(); process.chdir(cwd); }); it("writes the SQL under storage/catalog-sync with a trailing newline", async () => { let target = ""; try { const dir = mkdtempSync(join(tmpdir(), "catalog-sync-sql-")); target = dir; process.chdir(dir); const file = await writeGeneratedSql("INSERT INTO `catalog_items` (1);"); expect(file.startsWith("storage/catalog-sync/")).toBe(true); expect(file.endsWith(".sql")).toBe(true); expect(readFileSync(join(dir, file), "utf8")).toBe( "INSERT INTO `catalog_items` (1);\n", ); } finally { if (target) rmSync(target, { recursive: true, force: true }); } }); });