Files
MineGeo/tests/difficulty-presets.test.ts
pavel444-byte a7085e4e80
Some checks failed
Deploy MineGeo to Dokploy / deploy (push) Failing after 2m37s
Improve MineGeo UI and application flows
2026-10-18 11:44:04 +05:00

141 lines
3.9 KiB
TypeScript

import assert from "node:assert/strict";
import { test } from "node:test";
import { customDifficultySchema, scoreGuess } from "../shared/protocol";
import { mergeDifficultyPresets } from "../shared/difficulty-presets";
const rules = { seconds: 90, zoom: true };
test("expanded difficulty rules preserve defaults and validate controls", () => {
const preset = customDifficultySchema.parse({ name: "Classic", ...rules });
assert.deepEqual(
{
pan: preset.pan,
compass: preset.compass,
coordinates: preset.coordinates,
mapZoom: preset.mapZoom,
scoring: preset.scoring,
},
{
pan: true,
compass: true,
coordinates: true,
mapZoom: true,
scoring: "normal",
},
);
for (const key of ["pan", "compass", "coordinates", "mapZoom"])
assert.equal(
customDifficultySchema.safeParse({
name: "Fixture",
...rules,
[key]: "false",
}).success,
false,
);
assert.equal(
customDifficultySchema.safeParse({
name: "Fixture",
...rules,
scoring: "extreme",
}).success,
false,
);
assert.equal(
customDifficultySchema.safeParse({ name: "Fixture", ...rules, rounds: 10 })
.success,
false,
);
});
test("scoring strictness changes distance decay while perfect guesses remain 5000", () => {
const bounds = { minX: 0, minZ: 0, maxX: 100, maxZ: 100 };
const actual = { x: 10, z: 10 },
guess = { x: 40, z: 40 };
const scores = ["strict", "normal", "gentle"].map(
(mode) =>
scoreGuess(actual, guess, bounds, mode as "strict" | "normal" | "gentle")
.score,
);
assert.ok(scores[0] < scores[1] && scores[1] < scores[2]);
for (const mode of ["strict", "normal", "gentle"] as const)
assert.equal(scoreGuess(actual, actual, bounds, mode).score, 5000);
});
test("difficulty names accept normal text and count Unicode characters consistently with PostgreSQL", () => {
for (const name of [
"Normal",
"Speed 90",
"Тест",
"中文",
"😎😎",
"😎".repeat(40),
"a".repeat(40),
"a\\b",
])
assert.equal(
customDifficultySchema.safeParse({ name, ...rules }).success,
true,
name,
);
assert.equal(
customDifficultySchema.parse({ name: " My challenge ", ...rules }).name,
"My challenge",
);
for (const name of [
"",
"a",
"😎",
"a".repeat(41),
"😎".repeat(41),
"x\nname",
"x\u0085name",
"x\u202ename",
"x\u2066name",
])
assert.equal(
customDifficultySchema.safeParse({ name, ...rules }).success,
false,
);
});
test("difficulty rules enforce their supported range and strict request shape", () => {
for (const seconds of [15, 90, 600])
assert.equal(
customDifficultySchema.safeParse({ name: "Fixture", ...rules, seconds })
.success,
true,
);
for (const seconds of [0, 14, 16, 601, 15.5, NaN, Infinity, "90"])
assert.equal(
customDifficultySchema.safeParse({ name: "Fixture", ...rules, seconds })
.success,
false,
);
assert.equal(
customDifficultySchema.safeParse({
name: "Fixture",
...rules,
owner: "forged",
}).success,
false,
);
});
test("a delayed preset listing cannot erase a newly saved preset or duplicate it", async () => {
const old = { id: "old", name: "Old", ...rules };
const saved = { id: "saved", name: "New", ...rules };
let release!: (presets: (typeof old)[]) => void;
const response = new Promise<(typeof old)[]>((resolve) => {
release = resolve;
});
let presets = [old];
const loading = response.then((snapshot) => {
presets = mergeDifficultyPresets(presets, snapshot);
});
presets = mergeDifficultyPresets(presets, [saved]);
release([old]);
await loading;
assert.deepEqual(
presets.map((p) => p.id),
["saved", "old"],
);
assert.equal(mergeDifficultyPresets(presets, [saved]).length, 2);
});