Files

47 lines
3.3 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import { buildGraph, cloneBook, makeIndex, traceIngredientClosure, validateBook } from "./core.mjs";
const book = JSON.parse(await readFile(new URL("../../data/recipe-book.json", import.meta.url), "utf8"));
const schema = JSON.parse(await readFile(new URL("../../data/recipe-book.schema.json", import.meta.url), "utf8"));
const techCsv = await readFile(new URL("../../docs/tech-tree-nodes-v0.2.csv", import.meta.url), "utf8");
const technologyIds = new Set(techCsv.split(/\r?\n/).slice(1).map((line) => line.split(",", 1)[0].trim()).filter(Boolean));
assert.equal(book.schemaVersion, 1, "配方书版本必须为 1");
assert.equal(schema.properties.schemaVersion.const, 1, "Schema 与配方书版本必须一致");
for (const required of schema.required) assert.ok(Object.hasOwn(book, required), `缺少顶层字段 ${required}`);
const issues = validateBook(book, { technologyIds });
const errors = issues.filter((entry) => entry.severity === "error");
const warnings = issues.filter((entry) => entry.severity === "warning");
assert.deepEqual(errors, [], `存在校验错误:${errors.map((entry) => entry.message).join("")}`);
assert.deepEqual(warnings, [], `存在非预期警告:${warnings.map((entry) => entry.message).join("")}`);
const index = makeIndex(book);
assert.ok(index.recipes.has("perform_first_warp"), "缺少首次折跃配方");
assert.ok(index.items.has("first_warp_record"), "缺少首次折跃记录");
assert.ok(index.recipes.has("gather_guiding_dew"), "生机循环缺少显式启动配方");
const firstWarp = traceIngredientClosure(book, "first_warp_record");
assert.deepEqual(firstWarp.missing, [], "首次折跃上游必须全部可追溯");
for (const requiredRecipe of ["perform_first_warp", "assemble_walker_construct", "harvest_life_proof", "recall_death_proof", "prove_element_cycle", "separate_dimension_proof"]) {
assert.ok(firstWarp.recipes.includes(requiredRecipe), `首次折跃追溯缺少 ${requiredRecipe}`);
}
for (const recipe of book.recipes.filter((entry) => entry.kind === "result_bag")) {
const weighted = recipe.outputs.filter((entry) => entry.weight != null);
if (weighted.length) assert.equal(weighted.reduce((sum, entry) => sum + entry.weight, 0), 100, `${recipe.id} 权重不等于 100`);
const chance = recipe.outputs.filter((entry) => entry.chance != null);
if (chance.length === recipe.outputs.length) assert.ok(Math.abs(chance.reduce((sum, entry) => sum + entry.chance, 0) - 1) < 1e-9, `${recipe.id} 概率不等于 1`);
}
const focused = buildGraph(book, { mode: "focus", depth: 2, selection: { type: "recipes", id: "perform_first_warp" } });
assert.ok(focused.nodes.some((node) => node.key === "recipes:perform_first_warp"), "聚焦图缺少选中配方");
assert.ok(focused.edges.length >= 10, "首次折跃聚焦图边数异常");
const broken = cloneBook(book);
broken.recipes[0].inputs.push({ itemId: "missing_test_item", amount: 1, mode: "consumed" });
assert.ok(validateBook(broken, { technologyIds }).some((entry) => entry.code === "missing_item" && entry.severity === "error"), "缺失引用必须被识别为错误");
console.log(`配方树测试通过:${book.items.length} 物品 / ${book.recipes.length} 配方 / ${book.machines.length} 设施 / ${firstWarp.recipes.length} 道首次折跃上游配方。`);