899028e20f
- RomConfig/RomCache on the kit's ConfigService/CacheStore; RomSync wires the pure domain into the kit SyncEngine (poll/watch/coalesce/fingerprint) and ProviderClient; EngineStatus assembly shared by REST + SSE - makeApi: HttpApiBuilder groups over live service values (handler runtime shares the plugin runtime's singletons) + sseRoute status feed - index.ts: definePluginKit entry (async-main boundary); headless-first (UI serve failure logged, engine continues) - cli.ts on the kit dispatcher: scan/detect/preview offline, sync/uninstall online; set-password is gone with the standalone server - dev.ts: auth-free loopback API server (:5885) — the dev:live proxy target; never bundled - verified live host-less: raw config round-trip on disk, status/platforms/ preview endpoints, soft-fail reconcile without a host - CI: workspace install, per-package typecheck, publish from plugin/ Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
57 lines
1.9 KiB
TypeScript
57 lines
1.9 KiB
TypeScript
import { describe, expect, test } from "bun:test";
|
|
import {
|
|
buildUrl,
|
|
candidateNames,
|
|
LibretroProvider,
|
|
sanitizeName,
|
|
} from "../src/art/libretro.js";
|
|
import { platformById } from "../src/domain/platforms.js";
|
|
import { parseTitle } from "../src/domain/titles.js";
|
|
|
|
const snes = platformById("snes")!;
|
|
const nswitch = platformById("switch")!;
|
|
|
|
describe("libretro name handling", () => {
|
|
test("sanitizeName replaces the forbidden characters with underscore", () => {
|
|
expect(sanitizeName("Ratchet & Clank")).toBe("Ratchet _ Clank");
|
|
expect(sanitizeName("Where in the World?")).toBe("Where in the World_");
|
|
});
|
|
|
|
test("candidateNames walks exact → region → base", () => {
|
|
const names = candidateNames(parseTitle("Chrono Trigger (USA) (Rev 1)"));
|
|
expect(names[0]).toBe("Chrono Trigger (USA) (Rev 1)"); // exact
|
|
expect(names).toContain("Chrono Trigger (USA)"); // region-kept
|
|
expect(names).toContain("Chrono Trigger (Europe)"); // region-relaxed
|
|
expect(names.at(-1)).toBe("Chrono Trigger"); // bare
|
|
});
|
|
});
|
|
|
|
describe("LibretroProvider", () => {
|
|
test("returns the first probe hit as a portrait", async () => {
|
|
const hit = buildUrl(
|
|
snes.libretroSystem!,
|
|
sanitizeName("Chrono Trigger (USA)"),
|
|
);
|
|
const provider = new LibretroProvider((url) =>
|
|
Promise.resolve(url === hit),
|
|
);
|
|
const art = await provider.resolve(
|
|
snes,
|
|
parseTitle("Chrono Trigger (USA)"),
|
|
);
|
|
expect(art).toEqual({ portrait: hit });
|
|
});
|
|
|
|
test("returns null when nothing matches", async () => {
|
|
const provider = new LibretroProvider(() => Promise.resolve(false));
|
|
expect(
|
|
await provider.resolve(snes, parseTitle("Nonexistent Game")),
|
|
).toBeNull();
|
|
});
|
|
|
|
test("returns null for a platform with no libretro system (Switch)", async () => {
|
|
const provider = new LibretroProvider(() => Promise.resolve(true));
|
|
expect(await provider.resolve(nswitch, parseTitle("Zelda"))).toBeNull();
|
|
});
|
|
});
|