chore: initialize repository baseline
Import the existing Electron + React + TypeScript app as the version-control baseline before the scanner rework (C# input/capture sidecar, resolution-anchored layout profiles, OCR preprocessing, eval harness, rescan-merge, GOOD interop). Housekeeping in this commit: - Remove orphaned temp_inputhelper_block.ts (duplicate of the input-helper script). - Ignore .claude/scheduled_tasks.lock local session state. - Add .gitattributes to normalize line endings (LF in repo). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { createDemoArtifacts, createDemoCharacters, getPresets } from "./demoData";
|
||||
import { recommendArtifacts, suggestBuilds } from "./scoring";
|
||||
import type { Artifact, ArtifactSlot, Character, CharacterPreset } from "../types/domain";
|
||||
|
||||
describe("recommendation engine", () => {
|
||||
it("flags low-confidence artifacts for review", () => {
|
||||
const artifacts = createDemoArtifacts();
|
||||
const recommendations = recommendArtifacts(artifacts, createDemoCharacters(), getPresets());
|
||||
const reviewed = recommendations.find((entry) => entry.artifactId === "art-007");
|
||||
|
||||
expect(reviewed?.verdict).toBe("needs_review");
|
||||
});
|
||||
|
||||
it("suggests build candidates for owned characters", () => {
|
||||
const builds = suggestBuilds(createDemoArtifacts(), createDemoCharacters(), getPresets());
|
||||
|
||||
expect(builds.length).toBeGreaterThan(0);
|
||||
expect(builds[0].artifactIds.length).toBe(5);
|
||||
});
|
||||
|
||||
it("suggests partial builds when the scanned inventory has missing slots", () => {
|
||||
const partialArtifacts = createDemoArtifacts().filter((artifact) => artifact.slot === "sands" || artifact.slot === "goblet");
|
||||
const builds = suggestBuilds(partialArtifacts, createDemoCharacters(), getPresets());
|
||||
|
||||
expect(builds.length).toBeGreaterThan(0);
|
||||
expect(builds[0].artifactIds.length).toBeLessThan(5);
|
||||
expect(builds[0].warnings.join(" ")).toContain("Missing");
|
||||
});
|
||||
|
||||
it("warns when a suggested build wants an artifact equipped by another character", () => {
|
||||
const artifacts = createDemoArtifacts().map((artifact) =>
|
||||
artifact.slot === "flower" ? { ...artifact, equipped: "Furina" } : artifact,
|
||||
);
|
||||
const characters = createDemoCharacters().map((character) =>
|
||||
character.id === "furina" ? { ...character, owned: false } : character,
|
||||
);
|
||||
const builds = suggestBuilds(artifacts, characters, getPresets());
|
||||
const conflictBuild = builds.find((build) => build.warnings.some((warning) => warning.includes("Conflicts")));
|
||||
|
||||
expect(conflictBuild?.warnings.join(" ")).toContain("Furina");
|
||||
});
|
||||
|
||||
it("allows a preferred 4-piece build with one off-piece", () => {
|
||||
const preset = testPreset(["preferred"], ["fallback"]);
|
||||
const builds = suggestBuilds([
|
||||
artifact("a", "flower", "preferred", "HP"),
|
||||
artifact("b", "plume", "preferred", "ATK"),
|
||||
artifact("c", "sands", "preferred", "ATK%"),
|
||||
artifact("d", "goblet", "preferred", "ATK%"),
|
||||
artifact("e", "circlet", "off_piece", "CRIT Rate"),
|
||||
], [testCharacter()], [preset]);
|
||||
|
||||
const recommended = builds.find((build) => build.quality === "recommended_set");
|
||||
expect(recommended?.artifactIds).toEqual(expect.arrayContaining(["a", "b", "c", "d", "e"]));
|
||||
});
|
||||
|
||||
it("creates a practical 2pc+2pc fallback build", () => {
|
||||
const preset = testPreset(["preferred"], ["fallback"]);
|
||||
const builds = suggestBuilds([
|
||||
artifact("a", "flower", "preferred", "HP"),
|
||||
artifact("b", "plume", "preferred", "ATK"),
|
||||
artifact("c", "sands", "fallback", "ATK%"),
|
||||
artifact("d", "goblet", "fallback", "ATK%"),
|
||||
artifact("e", "circlet", "off_piece", "CRIT Rate"),
|
||||
], [testCharacter()], [preset]);
|
||||
|
||||
const fallback = builds.find((build) => build.quality === "alternative_set");
|
||||
expect(fallback?.explanation).toContain("2pc+2pc");
|
||||
});
|
||||
});
|
||||
|
||||
function testCharacter(): Character {
|
||||
return { id: "tester", name: "Tester", owned: true, level: 90, constellation: 0, rolePreference: "main_dps", confidence: 1 };
|
||||
}
|
||||
|
||||
function testPreset(recommendedSets: string[], alternativeSets: string[]): CharacterPreset {
|
||||
return {
|
||||
characterId: "tester",
|
||||
role: "main_dps",
|
||||
recommendedSets,
|
||||
alternativeSets,
|
||||
mainStats: {
|
||||
sands: ["ATK%"],
|
||||
goblet: ["ATK%"],
|
||||
circlet: ["CRIT Rate"],
|
||||
},
|
||||
substatWeights: { "CRIT Rate": 1, "CRIT DMG": 1, "ATK%": 0.8 },
|
||||
explanation: "Test preset",
|
||||
};
|
||||
}
|
||||
|
||||
function artifact(id: string, slot: ArtifactSlot, setKey: string, mainStat: string): Artifact {
|
||||
return {
|
||||
id,
|
||||
setKey,
|
||||
setName: setKey,
|
||||
slot,
|
||||
rarity: 5,
|
||||
level: 20,
|
||||
mainStat,
|
||||
substats: [
|
||||
{ key: "CRIT Rate", value: 7, unit: "%" },
|
||||
{ key: "CRIT DMG", value: 14, unit: "%" },
|
||||
],
|
||||
locked: false,
|
||||
source: "mock",
|
||||
confidence: 0.99,
|
||||
lastSeenAt: "2026-07-04T00:00:00.000Z",
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user