99 lines
4.3 KiB
JavaScript
99 lines
4.3 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import fs from "node:fs";
|
|
import test from "node:test";
|
|
import vm from "node:vm";
|
|
|
|
// Evaluate the compiler's actual pure contracts and validator without its
|
|
// filesystem-heavy catalog generation. Source DBCs stay read-only.
|
|
const source = fs.readFileSync(new URL("./compile-campaign-catalog.mjs", import.meta.url), "utf8");
|
|
function section(startMarker, endMarker) {
|
|
const start = source.indexOf(startMarker);
|
|
const end = source.indexOf(endMarker, start);
|
|
assert(start >= 0 && end > start, `Missing compiler section ${startMarker}`);
|
|
return source.slice(start, end);
|
|
}
|
|
const { contracts, validate } = vm.runInNewContext([
|
|
section("const pinnedEffect = ", "const AUXILIARY_NON_BOSS_CASTS = "),
|
|
section("function pinnedSourceSpellFor(spell)", "function basicAttack("),
|
|
"({ contracts: PINNED_SOURCE_SPELLS, validate: pinnedSourceSpellFor })",
|
|
].join("\n"));
|
|
|
|
function spellRow(id, sourceProfile) {
|
|
const signature = contracts.get(id).signatures[sourceProfile];
|
|
return { id, sourceProfile, ...JSON.parse(JSON.stringify(signature)) };
|
|
}
|
|
|
|
test("rejects attribute drift in both pinned Dual Wield profiles and Thrash", () => {
|
|
for (const [id, ascension, epoch] of [[674, 336, 80], [29651, 80, 80], [3391, 262_160, 262_160]]) {
|
|
for (const [profile, attributes0] of [["ascension", ascension], ["epoch", epoch]]) {
|
|
const row = spellRow(id, profile);
|
|
assert.equal(row.attributes0, attributes0);
|
|
assert.equal(validate(row), contracts.get(id));
|
|
assert.throws(() => validate({ ...row, attributes0: attributes0 ^ 1 }), /contract drift/);
|
|
}
|
|
}
|
|
});
|
|
|
|
test("rejects a changed Thrash die roll instead of silently changing its two-swing contract", () => {
|
|
for (const profile of ["ascension", "epoch"]) {
|
|
const row = spellRow(3391, profile);
|
|
assert.equal(row.effects[0].basePoints, 2);
|
|
assert.equal(row.effects[0].dieSides, 1);
|
|
assert.equal(contracts.get(3391).extraAttackCount, 2);
|
|
row.effects[0].dieSides = 0;
|
|
assert.throws(() => validate(row), /contract drift/);
|
|
}
|
|
});
|
|
|
|
test("fails closed for an unknown source profile on a pinned weapon utility", () => {
|
|
assert.throws(() => validate({ ...spellRow(674, "ascension"), sourceProfile: "unknown" }), /no unknown source profile/);
|
|
});
|
|
|
|
test("rejects changed shout trigger links, child effects, and shared area caps", () => {
|
|
for (const profile of ["ascension", "epoch"]) {
|
|
for (const [parent, child, maximum] of [[5246, 20511, 5], [19134, 29544, 0]]) {
|
|
const row = spellRow(parent, profile);
|
|
assert.equal(row.effects[0].triggerSpellId, child);
|
|
assert.equal(row.maxAffectedTargets, maximum);
|
|
assert.equal(validate(row), contracts.get(parent));
|
|
assert.throws(() => validate({ ...row, maxAffectedTargets: maximum + 1 }), /contract drift/);
|
|
row.effects[0].triggerSpellId = child + 1;
|
|
assert.throws(() => validate(row), /contract drift/);
|
|
const childRow = spellRow(child, profile);
|
|
assert.equal(validate(childRow), contracts.get(child));
|
|
childRow.effects[0].auraType += 1;
|
|
assert.throws(() => validate(childRow), /contract drift/);
|
|
}
|
|
}
|
|
});
|
|
|
|
test("rejects shout spell-mechanic and any raw attribute drift", () => {
|
|
for (const profile of ["ascension", "epoch"]) {
|
|
for (const id of [5246, 20511, 19134, 29544]) {
|
|
const row = spellRow(id, profile);
|
|
assert.equal(row.spellMechanic, 5);
|
|
assert.throws(() => validate({ ...row, spellMechanic: 0 }), /contract drift/);
|
|
for (let word = 0; word < 8; word += 1) {
|
|
const field = `attributes${word}`;
|
|
assert.equal(typeof row[field], "number");
|
|
assert.throws(() => validate({ ...row, [field]: row[field] ^ 1 }), /contract drift/);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
test("pins complete Critical Block ranks including aura107 siblings without assigning a creature", () => {
|
|
for (const profile of ["ascension", "epoch"]) {
|
|
for (const id of [47294, 47295, 47296]) {
|
|
const row = spellRow(id, profile);
|
|
assert.equal(validate(row), contracts.get(id));
|
|
assert.equal(row.durationIndex, 0);
|
|
assert.equal(row.effects[0].auraType, 253);
|
|
assert.equal(row.effects[1].auraType, 107);
|
|
const changed = structuredClone(row);
|
|
changed.effects[1].basePoints += 1;
|
|
assert.throws(() => validate(changed), /contract drift/);
|
|
}
|
|
}
|
|
});
|