From a92761dc130bf7cf7314d62e0024486e511fdfce Mon Sep 17 00:00:00 2001 From: Myles Resnick Date: Thu, 30 Jul 2026 10:46:34 -0400 Subject: [PATCH] Make the Pokewalker suite CI-runnable (fixture dataset) + pay save.loaded debt T4 auto-runs shipped-mod suites ROM-free: the test now uses T.fixtures.fresh() (FIXMON_A) with the level assertion derived from the growth curve, and the now-covered event:save.loaded DEBT entry is removed as gate_meta_coverage instructs. All four CI jobs reproduced green on a ROM-free checkout. --- mods/pokewalker/tests/pokewalker_test.lua | 25 +++++++++++++++-------- tests/engine/gate_meta_coverage.lua | 1 - 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/mods/pokewalker/tests/pokewalker_test.lua b/mods/pokewalker/tests/pokewalker_test.lua index 392da4a4..b53aaeba 100644 --- a/mods/pokewalker/tests/pokewalker_test.lua +++ b/mods/pokewalker/tests/pokewalker_test.lua @@ -1,14 +1,16 @@ -- Standalone: luajit mods/pokewalker/tests/pokewalker_test.lua -- Exercises the stated effect: opt-in gating, the native-bridge seam, and -- steps converting to EXP through the engine's own growth math. +-- +-- Shipped-mod suites are auto-run by the ROM-free T4 tier, so this runs +-- against the committed fixture dataset (T.fixtures), never data/generated. package.path = "./?.lua;./?/init.lua;" .. package.path local T = require("tests.modkit") -local Data = require("src.core.Data") -Data:load() +local Data = T.fixtures.fresh() --- The native Health bridge only exists inside the iOS app; stand it in so --- the mod sees the same surface it does on device. +-- The native step bridge only exists inside the iOS/Android apps; stand it +-- in so the mod sees the same surface it does on device. local syncCalls = 0 love.system = love.system or {} love.system.syncHealthSteps = function() @@ -20,25 +22,30 @@ local run = T.sdk.loadMod("mods/pokewalker", { data = Data }) T.eq(#run.errors, 0, "loads clean (" .. tostring(run.errors[1]) .. ")") local events = run.loader.events +local Growth = require("src.pokemon.Growth") local Pokemon = require("src.pokemon.Pokemon") -local mon = Pokemon.new(Data, "PIDGEY", 5) +local mon = Pokemon.new(Data, "FIXMON_A", 5) local game = { data = Data, save = { party = { mon } } } -- Dormant until opted in: seeded steps survive every event untouched and -- the native bridge is never poked (no permission prompt without consent). -love.filesystem.write("steps_pending.json", '{"steps": 4000}') +love.filesystem.write("steps_pending.json", '{"steps": 20000}') events:emit("game.ready", { game = game }) events:emit("map.entered", {}) T.check(love.filesystem.read("steps_pending.json") ~= nil, "opt-out leaves pending steps untouched") T.eq(syncCalls, 0, "opt-out never calls the native bridge") --- Opted in: 4000 steps at the default 20 steps/EXP credit the lead mon. +-- Opted in: 20000 steps at the default 20 steps/EXP credit the lead mon. run.loader.modOptions.pokewalker = { enabled = true } +local def = Data.pokemon[mon.species] local expBefore = mon.exp events:emit("save.loaded", {}) -T.eq(mon.exp, expBefore + 200, "4000 steps at 20 steps/EXP = +200 EXP") -T.eq(mon.level, 8, "level-ups ride the engine growth curve (5 -> 8)") +T.eq(mon.exp, expBefore + 1000, "20000 steps at 20 steps/EXP = +1000 EXP") +local expectedLevel = Growth.levelForExp(def.growthRate, mon.exp, 100, + Data.growth_rates) +T.check(mon.level > 5, "enough EXP to actually level (fixture curve)") +T.eq(mon.level, expectedLevel, "level matches the engine growth curve") T.check(mon.stats.hp > 0 and mon.hp <= mon.stats.hp, "stat recalc keeps HP within the new maximum") T.check(love.filesystem.read("steps_pending.json") == nil, diff --git a/tests/engine/gate_meta_coverage.lua b/tests/engine/gate_meta_coverage.lua index 2c29d47c..108237c9 100644 --- a/tests/engine/gate_meta_coverage.lua +++ b/tests/engine/gate_meta_coverage.lua @@ -121,7 +121,6 @@ local DEBT = { ["event:pokemon.evolved"] = "M7", ["event:pokemon.level_up"] = "M7", ["event:pokemon.move_learned"] = "M7", - ["event:save.loaded"] = "M11", ["event:save.loading"] = "M11", ["event:save.writing"] = "M11", ["event:trade.completed"] = "M12",