This commit is contained in:
2026-11-05 09:57:11 -05:00
parent d34388bf2c
commit 89e392cc2e
81 changed files with 12410 additions and 1135 deletions
+72 -11
View File
@@ -67,14 +67,18 @@ local function test(name, fn)
end
local planted_at = 100000
test("crop climate uses historical samples, freezing pauses growth, and catch-up is capped", function()
test("grain needs 120 real growing days and samples the whole interval with bounded work", function()
local cold_days = 4 * 86400
assert(ecology.climate_progress(planted_at, planted_at + cold_days) == 0)
climate_temperature = 15
local warm_days = 6 * 86400
assert(ecology.crop_stage(planted_at, planted_at + warm_days) == 4)
assert(ecology.crop_stage(planted_at,planted_at+119*86400)<4)
assert(ecology.crop_stage(planted_at,planted_at+120*86400)==4)
assert(ecology.climate_progress(planted_at, planted_at + 90 * 86400)
== ecology.climate_progress(planted_at, planted_at + 30 * 86400))
> ecology.climate_progress(planted_at, planted_at + 30 * 86400))
local calls=0; local climate=hearth.climate_at
hearth.climate_at=function(epoch) calls=calls+1; return climate(epoch) end
assert(ecology.crop_stage(planted_at,planted_at+10000*86400)==4 and calls==366)
hearth.climate_at=climate
assert(ecology.climate_progress("nan", planted_at + 1) == 0)
assert(ecology.climate_progress(planted_at, math.huge) == 0)
end)
@@ -83,16 +87,17 @@ test("crop stages derive from a durable planting timestamp independent of update
nodes[key({x=0,y=0,z=0})] = {name="hearth:grass"}
nodes[key(p)] = {name="hearth:crop_1"}
meta(p):set_string("planted_at", tostring(planted_at))
local stage = ecology.crop_stage(planted_at, planted_at + 2 * 86400)
local stage = ecology.crop_stage(planted_at, planted_at + 30 * 86400)
assert(stage >= 2 and stage <= 4)
local same_late_stage = ecology.crop_stage(planted_at, planted_at + 2 * 86400)
local same_late_stage = ecology.crop_stage(planted_at, planted_at + 30 * 86400)
assert(stage == same_late_stage)
now = planted_at + 30 * 86400
now = planted_at + 120 * 86400
assert(ecology.update_crop(p) == false and nodes[key(p)].name == "hearth:crop_4")
end)
test("berry regrowth is a single absolute deadline and harvest does not destroy the bush", function()
test("berry regrowth is annual, Summer gated and harvest does not destroy provenance", function()
local p = {x=2,y=1,z=0}
nodes[key(p)] = {name="hearth:berry_bush"}
meta(p):set_string("succ_natural","1")
inventory_room = false
ecology.harvest_berries(p, player)
assert(nodes[key(p)].name == "hearth:berry_bush" and delivered == 0)
@@ -100,11 +105,13 @@ test("berry regrowth is a single absolute deadline and harvest does not destroy
ecology.harvest_berries(p, player)
assert(nodes[key(p)].name == "hearth:berry_bush_bare" and delivered == 3)
local ready = tonumber(meta(p):get_string("ready_at"))
assert(ready == now + 86400)
assert(ready == now + 365.2425*86400 and meta(p):get_string("succ_natural")=="1")
ecology.harvest_berries(p, player)
assert(delivered == 3, "bare bush must not yield repeated berries")
assert(ecology.refresh_bush(p) == true and nodes[key(p)].name == "hearth:berry_bush_bare")
now = ready
season="Winter"; assert(ecology.refresh_bush(p)==true)
season="Summer"
assert(ecology.refresh_bush(p) == false and nodes[key(p)].name == "hearth:berry_bush")
end)
test("partial inventory harvest cannot duplicate crops or berries", function()
@@ -130,13 +137,15 @@ test("immature or unauthorized crops cannot be harvested", function()
assert(nodes[key(p)].name == "hearth:crop_4" and delivered == before)
permitted = true
end)
test("sapling growth waits, respects claims, and writes a bounded tree once", function()
now = 500000
test("generic sapling waits one real year, respects claims, and writes a bounded young tree once", function()
now = 2000000000
local p = {x=8,y=1,z=8}
nodes[key({x=8,y=0,z=8})] = {name="hearth:grass"}
nodes[key(p)] = {name="hearth:sapling"}
meta(p):set_string("planted_at", tostring(now - 3 * 86400))
meta(p):set_string("planter", "gardener")
assert(ecology.grow_sapling(p)==true and nodes[key(p)].name=="hearth:sapling")
now=now+365.2425*86400
permitted = false
assert(ecology.grow_sapling(p) == true and nodes[key(p)].name == "hearth:sapling")
permitted = true
@@ -188,5 +197,57 @@ test("forage availability checks the calendar even before appearance has reconci
assert(not reload.harvest_seasonal(mushrooms,player) and delivered==1)
end)
test("berry and seasonal reservations precede reentrant inventory callbacks",function()
local berry,patch={x=30,y=1,z=0},{x=31,y=1,z=0}
nodes[key(berry)]={name="hearth:berry_bush"}
nodes[key(patch)]={name="hearth:spring_greens_patch_spring"}
local original=inventory.add_item
local inside=false
inventory.add_item=function(self,list,item)
assert(not inside,"a nested harvest reached inventory")
inside=true
assert(not ecology.harvest_berries(berry,player))
assert(not ecology.harvest_seasonal(patch,player))
inside=false
return original(self,list,item)
end
season="Spring"
-- Lock the other resource so both recursive calls are unambiguously denied.
meta(patch):set_string("succ_harvest_ready_at",tostring(now+86400))
assert(ecology.harvest_berries(berry,player))
meta(patch):set_string("succ_harvest_ready_at","")
assert(ecology.harvest_seasonal(patch,player))
inventory.add_item=original
end)
test("failed delivery restores readiness and malformed fruit or patch clocks never refill",function()
local p={x=32,y=1,z=0}
local original=inventory.add_item
inventory.add_item=function(_,_,item) return make_stack(item:get_name().." "..item:get_count()) end
nodes[key(p)]={name="hearth:berry_bush"}
assert(not ecology.harvest_berries(p,player) and meta(p):get_string("ready_at")=="")
inventory.add_item=original
for _,bad in ipairs({"broken","-1","inf","nan"}) do
nodes[key(p)]={name="hearth:berry_bush"}; meta(p):set_string("ready_at",bad)
local before=delivered
assert(not ecology.harvest_berries(p,player) and delivered==before)
nodes[key(p)]={name="hearth:berry_bush_bare"}
assert(not ecology.refresh_bush(p) and nodes[key(p)].name=="hearth:berry_bush_bare")
nodes[key(p)]={name="hearth:spring_greens_patch_spring"}
meta(p):set_string("succ_harvest_ready_at",bad); season="Spring"
assert(not ecology.harvest_seasonal(p,player) and delivered==before)
end
end)
test("annual berry lock survives adapter restart, node variants and a one-day return",function()
local p={x=33,y=1,z=0}
nodes[key(p)]={name="hearth:berry_bush"}
assert(ecology.harvest_berries(p,player))
local due=meta(p):get_string("ready_at"); now=now+86400
local reload=dofile(target)(hearth,api,construction)
assert(reload.refresh_bush(p) and meta(p):get_string("ready_at")==due)
local before=delivered; reload.harvest_berries(p,player); assert(delivered==before)
-- Even a mistakenly restored full-looking node cannot reset its receipt.
nodes[key(p)]={name="hearth:berry_bush"}
assert(not reload.harvest_berries(p,player) and delivered==before)
end)
os.time = real_time
print("PASS " .. passed .. " bounded ecology tests")