From f68d0299e32f66ef12729a00813e25fabc193a19 Mon Sep 17 00:00:00 2001 From: DualFroz Date: Fri, 11 Sep 2026 20:34:09 +0200 Subject: [PATCH] test: cover tests.leaderboardRecordKeepsTheBoardSortedBestScoreFirst --- tests/run.lua | 173 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 173 insertions(+) diff --git a/tests/run.lua b/tests/run.lua index a10690c..0941769 100644 --- a/tests/run.lua +++ b/tests/run.lua @@ -9817,6 +9817,179 @@ function tests.leaderboardRecordOnAnEmptyBoardLandsAtRankOne() eq(#board, 1, "the board now holds exactly one entry") end +function tests.leaderboardRecordKeepsTheBoardSortedBestScoreFirst() + local board = Leaderboard.new() + Leaderboard.record(board, { score = 50, tag = "a" }) + Leaderboard.record(board, { score = 90, tag = "b" }) + Leaderboard.record(board, { score = 70, tag = "c" }) + eq(board[1].tag, "b", "the highest score sorts to the very top") + eq(board[2].tag, "c", "the middling score lands second") + eq(board[3].tag, "a", "the lowest score lands last") + local rank = Leaderboard.record(board, { score = 80, tag = "d" }) + eq(rank, 2, "a fresh entry ranks by where it actually lands, not where it was inserted") + eq(board[2].tag, "d", "the new entry displaces the weaker one down a slot") + eq(board[3].tag, "c", "everything below the insertion point shifts down by one") +end + +function tests.leaderboardRecordDropsAnEntryThatDoesNotCrackTheCap() + local board = Leaderboard.new() + for i = 1, Leaderboard.CAPACITY do + Leaderboard.record(board, { score = 1000 - i }) + end + eq(#board, Leaderboard.CAPACITY, "the board fills to exactly its capacity") + local rank = Leaderboard.record(board, { score = 1 }) + eq(rank, nil, "a score worse than everything already on the board does not crack it") + eq(#board, Leaderboard.CAPACITY, "the board stays at capacity rather than growing") + eq(board[Leaderboard.CAPACITY].score, 1000 - Leaderboard.CAPACITY, "the weakest surviving entry is unchanged") +end + +function tests.leaderboardRecordBumpsTheWeakestEntryOffOnceFull() + local board = Leaderboard.new() + for i = 1, Leaderboard.CAPACITY do + Leaderboard.record(board, { score = 1000 - i }) + end + local worst = board[Leaderboard.CAPACITY].score + local rank = Leaderboard.record(board, { score = 1500 }) + ok(rank ~= nil, "a score that beats the current worst entry cracks the board") + eq(#board, Leaderboard.CAPACITY, "the board stays at capacity after bumping someone off") + for _, entry in ipairs(board) do + ok(entry.score ~= worst, "the previous weakest entry no longer appears on the board") + end +end + +function tests.leaderboardTopReturnsAtMostCountEntriesInOrder() + local board = Leaderboard.new() + Leaderboard.record(board, { score = 30 }) + Leaderboard.record(board, { score = 10 }) + Leaderboard.record(board, { score = 20 }) + local top2 = Leaderboard.top(board, 2) + eq(#top2, 2, "top(2) returns exactly two entries") + eq(top2[1].score, 30, "the first slot is the best score") + eq(top2[2].score, 20, "the second slot is the next best score") +end + +function tests.leaderboardTopDefaultsToTheFullCapacityWhenNoCountIsGiven() + local board = Leaderboard.new() + for i = 1, Leaderboard.CAPACITY + 3 do + Leaderboard.record(board, { score = i }) + end + local top = Leaderboard.top(board) + eq(#top, Leaderboard.CAPACITY, "top() with no count returns the board's full capacity, not everything ever recorded") +end + +function tests.leaderboardBestScoreReadsTheTopEntryOrZeroWhenEmpty() + eq(Leaderboard.bestScore(Leaderboard.new()), 0, "an empty board's best score is zero") + local board = Leaderboard.new() + Leaderboard.record(board, { score = 42 }) + eq(Leaderboard.bestScore(board), 42, "the best score matches the single recorded entry") +end + +function tests.metaLeaderboardFieldDefaultsToEmptyAndSurvivesASaveLoadRoundTrip() + local fresh = Meta.load("/tmp/warren_meta_does_not_exist_55511.sav") + eq(#fresh.leaderboard, 0, "a freshly defaulted meta state has an empty leaderboard") + local meta = Meta.new() + Leaderboard.record(meta.leaderboard, { score = 250, className = "mage", depth = 4, turn = 88, won = false, seed = 7 }) + local path = os.tmpname() + Meta.save(meta, path) + local restored = Meta.load(path) + os.remove(path) + eq(#restored.leaderboard, 1, "a recorded leaderboard entry survives a save/load round trip") + eq(restored.leaderboard[1].score, 250, "a restored entry keeps its own score") + eq(restored.leaderboard[1].className, "mage", "a restored entry keeps its own class") +end + +function tests.replayRunIsDeterministicAndMatchesAFreshGamesOwnReport() + local seed, steps = 55, 60 + local game = Game.new(seed) + for _ = 1, steps do + if not game.player:isAlive() then break end + game:autoStep() + end + local expected = Morgue.build(game) + local replayed = Replay.run(seed, nil, steps) + local matches, mismatchField = Replay.matches(expected, replayed) + ok(matches, "replaying the same seed and step count reproduces every compared field (first mismatch: " + .. tostring(mismatchField) .. ")") + eq(replayed.turn, expected.turn, "the replay reaches the exact same turn") + eq(replayed.deepestDepth, expected.deepestDepth, "the replay reaches the exact same depth") +end + +function tests.replayVerifyConfirmsAGenuineReportAndReturnsItsScore() + local seed, steps = 55, 60 + local game = Game.new(seed) + for _ = 1, steps do + if not game.player:isAlive() then break end + game:autoStep() + end + local report = Morgue.build(game) + local matches, mismatchField, score = Replay.verify(seed, nil, steps, report) + ok(matches, "verify confirms a report that genuinely came from this seed and step count") + eq(mismatchField, nil, "a matching verify names no mismatched field") + eq(score, Morgue.score(report), "verify's returned score matches the report's own score") +end + +function tests.replayVerifyFlagsATamperedReportAndStillReturnsTheRealScore() + local seed, steps = 55, 60 + local game = Game.new(seed) + for _ = 1, steps do + if not game.player:isAlive() then break end + game:autoStep() + end + local report = Morgue.build(game) + local tampered = {} + for k, v in pairs(report) do tampered[k] = v end + tampered.gold = report.gold + 9999 + local matches, mismatchField, score = Replay.verify(seed, nil, steps, tampered) + eq(matches, false, "a tampered gold total fails verification") + eq(mismatchField, "gold", "verify names gold as the field that diverged") + eq(score, Morgue.score(report), "the replay still reports the real, untampered score") +end + +function tests.replayComparedFieldsCoversEveryFieldMorgueScoreActuallyReads() + local weighed = { + "deepestDepth", "kills", "bossKills", "questsCompleted", "artifactsFound", + "vaultsOpened", "setPiecesFound", "chestsOpened", "playerLevel", "gold", "won", + } + for _, field in ipairs(weighed) do + local found = false + for _, compared in ipairs(Replay.COMPARED_FIELDS) do + if compared == field then found = true end + end + ok(found, "Replay.COMPARED_FIELDS includes '" .. field .. "', every field Morgue.score weighs") + end +end + +function tests.replayMatchesReportsTheFirstDivergingFieldInComparedFieldsOrder() + local base = { + won = false, alive = true, deepestDepth = 3, turn = 100, gold = 10, essence = 2, + kills = 1, bossKills = 0, questsCompleted = 0, artifactsFound = 0, + vaultsOpened = 0, setPiecesFound = 0, chestsOpened = 0, playerLevel = 1, + } + local altered = {} + for k, v in pairs(base) do altered[k] = v end + altered.turn = 999 + local matches, field = Replay.matches(base, altered) + eq(matches, false, "a divergent turn count fails the match") + eq(field, "turn", "the mismatch is reported against the turn field specifically") + eq(Replay.matches(base, base), true, "an identical report matches itself") +end + +function tests.challengesForDaySameDayAlwaysReturnsTheSamePresetAndSeed() + local presetA, seedA = Challenges.forDay(7) + local presetB, seedB = Challenges.forDay(7) + eq(presetA, presetB, "the same day index always names the identical preset table") + eq(seedA, seedB, "the same day index always yields the identical seed") +end + +function tests.challengesForDayCyclesThroughTheCatalogInOrder() + for i = 0, #Challenges.CATALOG - 1 do + local preset = Challenges.forDay(i) + eq(preset, Challenges.CATALOG[i + 1], "day " .. i .. " maps to catalog slot " .. (i + 1)) + end + local wrapped = Challenges.forDay(#Challenges.CATALOG) + eq(wrapped, Challenges.CATALOG[1], "the cycle wraps back to the first preset after one full lap") +end + end local order = {