Release Healer Man 0.1.46
This commit is contained in:
1 parent
f82daf1c13
commit
9ff574ac0b
73 files changed
+33486
-174
No files matched your search
+100
-1
@@ -1,11 +1,13 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { mkdtempSync, rmSync } from "node:fs";
|
||||
import { mkdtempSync, readFileSync, rmSync } from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import test from "node:test";
|
||||
import { DatabaseSync } from "node:sqlite";
|
||||
import { GameDatabaseError, openGameDatabase } from "./database.mjs";
|
||||
|
||||
const routeCatalog = JSON.parse(readFileSync(new URL("../src/game/generated/dungeonRouteCatalog.generated.json", import.meta.url), "utf8"));
|
||||
|
||||
function withDatabase(run) {
|
||||
const directory = mkdtempSync(path.join(os.tmpdir(), "healer-man-db-test-"));
|
||||
const database = openGameDatabase({ dataDir: directory, sessionTtlDays: 1 });
|
||||
@@ -137,6 +139,103 @@ test("persists revisioned Manastorm GM configuration and rejects stale or unauth
|
||||
}), (error) => error instanceof GameDatabaseError && error.status === 400);
|
||||
}));
|
||||
|
||||
test("stores GM simulation replays and gates exact-hash route approval", () => withDatabase(async (database) => {
|
||||
const gmAuth = await database.register("Phenom", "storm-admin");
|
||||
const playerAuth = await database.register("RoutePlayer", "route-player");
|
||||
const gm = database.authenticate(gmAuth.token);
|
||||
const player = database.authenticate(playerAuth.token);
|
||||
const routeHash = routeCatalog.routes.find((route) => route.dungeonId === "deadmines").hash;
|
||||
const replay = {
|
||||
schemaVersion: 1,
|
||||
dungeonId: "deadmines",
|
||||
routeId: "primary",
|
||||
routeHash,
|
||||
mode: "navigation-only",
|
||||
seed: "deadmines:primary",
|
||||
result: "completed",
|
||||
startedAt: 1_700_000_000_000,
|
||||
durationMs: 42_000,
|
||||
report: { schemaVersion: 1, findings: [] },
|
||||
frames: [{ atMs: 0, actors: [] }],
|
||||
events: [],
|
||||
};
|
||||
|
||||
assert.throws(() => database.saveDungeonSimulationReplay(player, replay), (error) => (
|
||||
error instanceof GameDatabaseError && error.status === 403
|
||||
));
|
||||
assert.throws(() => database.putDungeonRouteReview(gm, "deadmines", "primary", {
|
||||
routeHash, status: "Approved", expectedRevision: 0,
|
||||
}), (error) => error instanceof GameDatabaseError && error.code === "route_run_required");
|
||||
|
||||
const saved = database.saveDungeonSimulationReplay(gm, replay);
|
||||
assert.equal(saved.criticalFindings, 0);
|
||||
assert.equal(database.getDungeonSimulationReplay(gm, saved.id).frames.length, 1);
|
||||
assert.equal(database.getDungeonSimulationReport(gm, saved.id).schemaVersion, 1);
|
||||
|
||||
const review = database.putDungeonRouteReview(gm, "deadmines", "primary", {
|
||||
routeHash, status: "Approved", expectedRevision: 0, note: "Clean navigation-only pass.",
|
||||
});
|
||||
assert.equal(review.revision, 1);
|
||||
assert.deepEqual(database.listDungeonRouteApprovals().approvals.map(({ reviewedAt: _reviewedAt, ...entry }) => entry), [{
|
||||
dungeonId: "deadmines", routeId: "primary", routeHash,
|
||||
}]);
|
||||
assert.throws(() => database.putDungeonRouteReview(gm, "deadmines", "primary", {
|
||||
routeHash, status: "NeedsWork", expectedRevision: 0,
|
||||
}), (error) => error instanceof GameDatabaseError && error.status === 409);
|
||||
assert.equal(database.deleteDungeonSimulationReplay(gm, saved.id).ok, true);
|
||||
}));
|
||||
|
||||
test("requires an override note when the latest exact-hash route run has critical findings", () => withDatabase(async (database) => {
|
||||
const gmAuth = await database.register("Phenom", "storm-admin");
|
||||
const gm = database.authenticate(gmAuth.token);
|
||||
const routeHash = routeCatalog.routes.find((route) => route.dungeonId === "blackfathom-deeps").hash;
|
||||
database.saveDungeonSimulationReplay(gm, {
|
||||
schemaVersion: 1,
|
||||
dungeonId: "blackfathom-deeps",
|
||||
routeId: "primary",
|
||||
routeHash,
|
||||
mode: "navigation-only",
|
||||
seed: "route-seed",
|
||||
result: "completed",
|
||||
startedAt: 1_700_000_000_000,
|
||||
durationMs: 10_000,
|
||||
report: { schemaVersion: 1, findings: [{ severity: "critical", code: "no-path" }] },
|
||||
frames: [],
|
||||
events: [],
|
||||
});
|
||||
assert.throws(() => database.putDungeonRouteReview(gm, "blackfathom-deeps", "primary", {
|
||||
routeHash, status: "Approved", expectedRevision: 0,
|
||||
}), (error) => error instanceof GameDatabaseError && error.code === "route_override_required");
|
||||
const review = database.putDungeonRouteReview(gm, "blackfathom-deeps", "primary", {
|
||||
routeHash, status: "Approved", expectedRevision: 0, criticalOverrideNote: "Known scripted lift gap.",
|
||||
});
|
||||
assert.equal(review.status, "Approved");
|
||||
assert.equal(review.criticalOverrideNote, "Known scripted lift gap.");
|
||||
}));
|
||||
|
||||
test("requires the latest exact-hash route run to be completed before approval", () => withDatabase(async (database) => {
|
||||
const gmAuth = await database.register("Phenom", "storm-admin");
|
||||
const gm = database.authenticate(gmAuth.token);
|
||||
const routeHash = routeCatalog.routes.find((route) => route.dungeonId === "razorfen-downs").hash;
|
||||
database.saveDungeonSimulationReplay(gm, {
|
||||
schemaVersion: 1,
|
||||
dungeonId: "razorfen-downs",
|
||||
routeId: "primary",
|
||||
routeHash,
|
||||
mode: "full-combat",
|
||||
seed: "incomplete-route-seed",
|
||||
result: "stopped",
|
||||
startedAt: 1_700_000_000_000,
|
||||
durationMs: 10_000,
|
||||
report: { schemaVersion: 1, findings: [] },
|
||||
frames: [],
|
||||
events: [],
|
||||
});
|
||||
assert.throws(() => database.putDungeonRouteReview(gm, "razorfen-downs", "primary", {
|
||||
routeHash, status: "Approved", expectedRevision: 0,
|
||||
}), (error) => error instanceof GameDatabaseError && error.code === "route_completed_run_required");
|
||||
}));
|
||||
|
||||
test("creates online groups through invitations and transfers leadership when the leader leaves", () => withDatabase(async (database) => {
|
||||
const leaderAuth = await database.register("GroupLeader", "group-pass");
|
||||
const healerAuth = await database.register("GroupHealer", "healer-pass");
|
||||
|
||||
Reference in new issue
Block a user