refactor: workspace restructure — contract package + pure domain ported, tests green

- bun workspaces: contract/ (Schemas = source of truth: config raw↔resolved,
  domain DTOs, HttpApi contract, API errors) + plugin/ + ui/
- pure domain moved src/{engine,art}→plugin/src/{domain,art}, types now
  imported from @rom-manager/contract and @punktfunk/plugin-kit/wire (the
  hand-copied wire.ts dies), loggers injected instead of module-global
- ui.* and devEntry dropped from the config shape (standalone server is
  gone; stale keys tolerated on decode, dropped by the next save)
- all 48 domain tests ported and green BEFORE any Effect wiring

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-20 18:54:38 +02:00
parent e78df91925
commit d6cf5f3d36
30 changed files with 1938 additions and 181 deletions
+67
View File
@@ -0,0 +1,67 @@
import { describe, expect, test } from "bun:test";
import {
dedupeKey,
parseTitle,
pickBestRelease,
} from "../src/domain/titles.js";
describe("parseTitle", () => {
test("strips No-Intro tags and keeps region + revision metadata", () => {
const p = parseTitle("Chrono Trigger (USA) (Rev 1) [!]");
expect(p.displayTitle).toBe("Chrono Trigger");
expect(p.noIntroName).toBe("Chrono Trigger (USA) (Rev 1) [!]");
expect(p.region).toBe("USA");
expect(p.revision).toBe("Rev 1");
});
test("normalises a trailing article", () => {
expect(parseTitle("Legend of Zelda, The (USA)").displayTitle).toBe(
"The Legend of Zelda",
);
});
test("recognises multi-region tags", () => {
const p = parseTitle("Sonic (USA, Europe)");
expect(p.regions).toEqual(["USA", "Europe"]);
expect(p.region).toBe("USA");
});
test("converts underscores and collapses whitespace", () => {
expect(parseTitle("Super_Mario__World").displayTitle).toBe(
"Super Mario World",
);
});
test("a tagless name is its own title", () => {
const p = parseTitle("Tetris");
expect(p.displayTitle).toBe("Tetris");
expect(p.region).toBeUndefined();
});
});
describe("region dedupe", () => {
test("groups by base title", () => {
expect(dedupeKey(parseTitle("Sonic (USA)"))).toBe(
dedupeKey(parseTitle("Sonic (Europe)")),
);
});
test("pickBestRelease honors region priority, then shortest name", () => {
const items = [
{ parsed: parseTitle("Sonic (Europe)") },
{ parsed: parseTitle("Sonic (USA)") },
{ parsed: parseTitle("Sonic (Japan)") },
];
const best = pickBestRelease(items, ["USA", "World", "Europe", "Japan"]);
expect(best.parsed.region).toBe("USA");
});
test("falls back to shortest name when no region matches priority", () => {
const items = [
{ parsed: parseTitle("Game (Korea) (Beta)") },
{ parsed: parseTitle("Game (Korea)") },
];
const best = pickBestRelease(items, ["USA"]);
expect(best.parsed.noIntroName).toBe("Game (Korea)");
});
});