Ruined
This commit is contained in:
Executable
+269
@@ -0,0 +1,269 @@
|
||||
-- Standalone Lua 5.1+; policy only, never starts Luanti.
|
||||
local target=assert(arg[1],"lifecycle.lua path required")
|
||||
local L=dofile(target)
|
||||
local passed=0
|
||||
local function test(label,fn)
|
||||
local ok,err=pcall(fn); assert(ok,label..": "..tostring(err))
|
||||
passed=passed+1; print("ok "..passed.." - "..label)
|
||||
end
|
||||
local function eq(a,b) assert(a==b,tostring(a).." ~= "..tostring(b)) end
|
||||
local function url(s) return type(s)=="string" and s:match("^https://[^/]+/.+") end
|
||||
local DAY,YEAR=86400,365.2425
|
||||
local function at(p,days) return assert(L.phase(p,0,days*DAY)) end
|
||||
local new_plants={
|
||||
dandelion="Taraxacum officinale",daisy="Bellis perennis",red_clover="Trifolium pratense",
|
||||
birdsfoot_trefoil="Lotus corniculatus",ribwort_plantain="Plantago lanceolata",selfheal="Prunella vulgaris",
|
||||
knapweed="Centaurea nigra",oxeye_daisy="Leucanthemum vulgare",field_scabious="Knautia arvensis",
|
||||
teasel="Dipsacus fullonum",cow_parsley="Anthriscus sylvestris",garlic_mustard="Alliaria petiolata",
|
||||
honeysuckle="Lonicera periclymenum",ivy="Hedera helix",gorse="Ulex europaeus",broom="Cytisus scoparius",
|
||||
crab_apple="Malus sylvestris",wild_cherry="Prunus avium",field_maple="Acer campestre",holly_fern="Polystichum setiferum",
|
||||
}
|
||||
local new_animals="rabbit red_squirrel bank_vole wood_mouse hedgehog badger roe_deer stoat weasel otter robin blackbird blue_tit barn_owl mallard common_frog smooth_newt brown_trout bumblebee earthworm"
|
||||
test("module constants retain unaccelerated real seconds",function()
|
||||
eq(L.VERSION,1); eq(L.SECONDS_PER_DAY,DAY); eq(L.DAYS_PER_YEAR,YEAR)
|
||||
eq(at(L.plant("oak"),1).age_seconds,86400)
|
||||
end)
|
||||
test("all hundred actual catalogue records receive group baselines",function()
|
||||
local path=target:gsub("[^/\\]+$","botany.lua")
|
||||
local B=dofile(path)({},{}); local records=B.list(); eq(#records,100)
|
||||
for _,r in ipairs(records) do
|
||||
local p=assert(L.plant(r.id,r.group)); eq(p.group,r.group); eq(p.pending,false)
|
||||
assert(p.germination_days~=nil and p.juvenile_days~=nil and p.maturity_days~=nil and p.lifespan_days~=nil)
|
||||
assert(L.phase(p,0,0),r.id); assert(L.resolve(p,731104),r.id)
|
||||
end
|
||||
end)
|
||||
test("twenty registered plant IDs preserve exact botanical contract",function()
|
||||
local count=0
|
||||
for id,latin in pairs(new_plants) do
|
||||
local p=assert(L.plant(id)); eq(p.latin,latin); eq(p.pending,false); count=count+1
|
||||
end
|
||||
eq(count,20); eq(#L.ids("plant"),100)
|
||||
end)
|
||||
test("twenty registered animals plus three legacy identity slots",function()
|
||||
local count=0
|
||||
for id in new_animals:gmatch("%S+") do
|
||||
local p=assert(L.animal(id)); eq(p.id,id); eq(p.pending,false)
|
||||
assert(type(p.latin)=="string" and url(p.source_url)); count=count+1
|
||||
end
|
||||
eq(count,20); eq(#L.ids("animal"),23)
|
||||
for _,id in ipairs({"hare","fox","deer"}) do eq(L.animal(id).pending,false) end
|
||||
end)
|
||||
test("ten trees expose species evidence without laundering fallback timings",function()
|
||||
local count=0
|
||||
for id in ("oak birch pine willow alder beech aspen rowan hazel spruce"):gmatch("%S+") do
|
||||
local p=L.plant(id,"trees"); assert(p.latin and url(p.source_url))
|
||||
assert(p.germination_days.confidence=="policy_fallback")
|
||||
assert(p.juvenile_days.confidence=="policy_fallback")
|
||||
assert(p.maturity_days.min>=3*YEAR); assert(p.lifespan_days.min>=60*YEAR)
|
||||
count=count+1
|
||||
end
|
||||
eq(count,10); eq(L.plant("birch").maturity_days.confidence,"policy_fallback")
|
||||
eq(L.plant("alder").maturity_days.confidence,"policy_fallback")
|
||||
end)
|
||||
test("verified oak onset is forty real years not forty seconds",function()
|
||||
local p=L.plant("oak"); eq(p.maturity_days.min,40*YEAR)
|
||||
assert(url(p.maturity_days.source_url)); eq(p.maturity_days.confidence,"source_reported")
|
||||
eq(at(p,30).phase,"seed"); eq(at(p,40*YEAR-1).phase,"juvenile")
|
||||
eq(at(p,40*YEAR).phase,"mature"); eq(at(p,40*YEAR).reproductive_ready,false)
|
||||
end)
|
||||
test("verified rowan and beech distinguish onset and heavy crops",function()
|
||||
eq(L.plant("rowan").maturity_days.max,10*YEAR)
|
||||
local p=L.plant("beech"); eq(p.maturity_days.min,10*YEAR); eq(p.maturity_days.max,30*YEAR)
|
||||
eq(p.maturity_days.statistic,"onset_to_heavy_crop")
|
||||
end)
|
||||
test("alder composite longevity is not a universal hundred-year constant",function()
|
||||
local p=L.plant("alder"); eq(p.lifespan_days.min,60*YEAR); eq(p.lifespan_days.max,160*YEAR)
|
||||
eq(p.lifespan_days.statistic,"composite_envelope"); assert(url(p.evidence.lifespan_context.source_url))
|
||||
end)
|
||||
test("all source-reported numerical claims carry field-level references",function()
|
||||
local fields={"germination_days","juvenile_days","maturity_days","lifespan_days","gestation_days","incubation_days","larval_days","birth_count"}
|
||||
local claims=0
|
||||
for _,kind in ipairs({"plant","animal"}) do for _,id in ipairs(L.ids(kind)) do
|
||||
local p=kind=="plant" and L.plant(id) or L.animal(id)
|
||||
for _,name in ipairs(fields) do
|
||||
local r=p[name]
|
||||
if r then
|
||||
assert(r.min>=0 and r.max>=r.min and r.max<math.huge)
|
||||
assert(type(r.note)=="string" and #r.note>0)
|
||||
if r.confidence=="source_reported" then
|
||||
assert(url(r.source_url)); assert(url(p.evidence[name].source_url)); claims=claims+1
|
||||
else eq(r.confidence,"policy_fallback") end
|
||||
end
|
||||
end
|
||||
for _,e in pairs(p.evidence) do assert(url(e.source_url) and #e.note>0); eq(e.reviewed,"2026-11-03") end
|
||||
end end
|
||||
assert(claims>=35)
|
||||
end)
|
||||
test("verified thirty-day rabbit value is gestation not maturity",function()
|
||||
local p=L.animal("rabbit"); eq(p.gestation_days.min,30); eq(p.gestation_days.max,37)
|
||||
eq(p.maturity_days,false); eq(at(p,30).phase,"dependent")
|
||||
assert(p.evidence.maturity_context.note:find("Conflicting",1,true))
|
||||
eq(at(p,30).reproductive_ready,false)
|
||||
end)
|
||||
test("bank vole maturity weeks differ from weaning days",function()
|
||||
local p=L.animal("bank_vole"); eq(p.juvenile_days.max,25)
|
||||
eq(p.maturity_days.min,42); eq(p.maturity_days.max,56)
|
||||
eq(at(p,24).phase,"dependent"); eq(at(p,30).phase,"juvenile"); eq(at(p,56).phase,"mature")
|
||||
end)
|
||||
test("wood mouse and squirrel retain independently verified gestation",function()
|
||||
eq(L.animal("wood_mouse").gestation_days.min,21); eq(L.animal("wood_mouse").gestation_days.max,26)
|
||||
eq(L.animal("red_squirrel").gestation_days.min,38); eq(L.animal("red_squirrel").gestation_days.max,39)
|
||||
eq(at(L.animal("wood_mouse"),30).phase,"juvenile")
|
||||
end)
|
||||
test("frogs are not sexually mature at metamorphosis",function()
|
||||
local p=L.animal("common_frog"); eq(p.larval_days.max,98); eq(p.maturity_days.max,3*YEAR)
|
||||
eq(p.incubation_days.min,14); eq(at(p,98).phase,"juvenile"); eq(at(p,98).reproductive_ready,false)
|
||||
end)
|
||||
test("earthworm natural lifespan stays unknown and maturity takes years",function()
|
||||
local p=L.animal("earthworm"); eq(p.lifespan_days,false)
|
||||
eq(p.maturity_days.min,YEAR); eq(p.maturity_days.max,2*YEAR)
|
||||
eq(at(p,30).phase,"juvenile"); eq(p.reproduction,"paired_hermaphrodite_cocoon")
|
||||
end)
|
||||
test("generic deer cannot inherit a roe deer life history",function()
|
||||
local p=L.animal("deer"); eq(p.latin,false); eq(p.source_url,false); eq(p.maturity_days,false)
|
||||
eq(p.confidence,"unverified_identity"); assert(L.animal("roe_deer").latin)
|
||||
end)
|
||||
test("winter appearance never declares death including annual representation",function()
|
||||
for _,id in ipairs({"dandelion","daisy","garlic_mustard","foxglove"}) do
|
||||
local p=L.plant(id); local s=L.seasonal(p,"Winter"); eq(s.dormant,true); eq(s.death_due,false)
|
||||
end
|
||||
local p=L.plant("daisy"); p.life_cycle="annual"
|
||||
eq(L.seasonal(p,"Winter").death_due,false)
|
||||
eq(L.seasonal(L.plant("pine"),"Winter").dormant,false)
|
||||
eq(L.seasonal(L.plant("daisy"),"Spring").dormant,false)
|
||||
end)
|
||||
test("biennial leaves survive their first winter",function()
|
||||
local p=L.plant("garlic_mustard"); eq(p.life_cycle,"biennial")
|
||||
eq(p.life_cycle_confidence,"source_reported"); eq(at(p,200).death_due,false)
|
||||
eq(L.seasonal(p,"Winter").death_due,false)
|
||||
eq(L.plant("teasel").life_cycle_confidence,"policy_fallback")
|
||||
end)
|
||||
test("averages and record ages are never mandatory death deadlines",function()
|
||||
for _,p in ipairs({L.animal("rabbit"),L.plant("oak"),L.animal("brown_trout")}) do
|
||||
local s=at(p,p.lifespan_days.max+1); eq(s.beyond_lifespan_range,true); eq(s.death_due,false)
|
||||
end
|
||||
end)
|
||||
test("age retains unloaded calendar gaps while work is bounded separately",function()
|
||||
local gap=40*YEAR*DAY; local e=L.elapsed(100,100+gap,8)
|
||||
eq(e.elapsed_seconds,gap); eq(e.work_seconds,8); eq(e.deferred_seconds,gap-8)
|
||||
eq(L.phase(L.plant("oak"),100,100+gap).phase,"mature")
|
||||
eq(L.elapsed(100,100,0).elapsed_seconds,0)
|
||||
end)
|
||||
test("clock reversal is explicit and never silently rebaselines age",function()
|
||||
local p=L.plant("oak"); local value,reason=L.phase(p,100,99)
|
||||
eq(value,nil); eq(reason,"clock_reversed")
|
||||
value,reason=L.elapsed(100,99,8); eq(value,nil); eq(reason,"clock_reversed")
|
||||
eq(L.phase(p,100,100).age_seconds,0)
|
||||
end)
|
||||
test("NaN infinities negatives strings and beyond-safe clocks fail closed",function()
|
||||
local p=L.plant("oak")
|
||||
for _,v in ipairs({0/0,math.huge,-math.huge,-1,"100",L.MAX_CLOCK+2,1e300}) do
|
||||
eq(L.phase(p,v,100),nil); eq(L.phase(p,0,v),nil)
|
||||
eq(L.elapsed(0,v,8),nil); eq(L.elapsed(0,100,v),nil)
|
||||
end
|
||||
eq(L.phase(p,nil,100),nil); eq(L.elapsed(0,100,nil),nil)
|
||||
end)
|
||||
test("largest valid age is constant-work and is not truncated",function()
|
||||
local s=assert(L.phase(L.plant("oak"),0,L.MAX_CLOCK))
|
||||
eq(s.age_seconds,L.MAX_CLOCK); assert(s.age_days>1000000000); eq(s.death_due,false)
|
||||
local e=assert(L.elapsed(0,L.MAX_CLOCK,1)); eq(e.work_seconds,1); eq(e.elapsed_seconds,L.MAX_CLOCK)
|
||||
end)
|
||||
test("duration selection is deterministic bounded and salt dependent",function()
|
||||
local r={min=10,max=40}
|
||||
local a=L.duration(r,731104,"oak:maturity_days"); eq(a,L.duration(r,731104,"oak:maturity_days"))
|
||||
assert(a>=10 and a<=40); assert(a~=L.duration(r,731105,"oak:maturity_days"))
|
||||
assert(a~=L.duration(r,731104,"birch:maturity_days")); eq(L.duration({min=30,max=30},0,""),30)
|
||||
end)
|
||||
test("invalid duration ranges and seeds are rejected",function()
|
||||
for _,r in ipairs({{min=-1,max=2},{min=3,max=2},{min=0,max=math.huge},{min=0,max=1e300},{min=0/0,max=2},{}}) do
|
||||
eq(L.duration(r,0,"x"),nil)
|
||||
end
|
||||
for _,seed in ipairs({-1,.5,2147483647,math.huge,0/0,"1"}) do
|
||||
eq(L.duration({min=1,max=2},seed,"x"),nil); eq(L.resolve(L.animal("deer"),seed),nil)
|
||||
end
|
||||
eq(L.duration({min=1,max=2},0,string.rep("x",65)),nil)
|
||||
end)
|
||||
test("resolved profiles preserve range provenance and ordered stages",function()
|
||||
for _,kind in ipairs({"plant","animal"}) do for _,id in ipairs(L.ids(kind)) do
|
||||
local p=kind=="plant" and L.plant(id) or L.animal(id)
|
||||
for _,seed in ipairs({0,1,731104,2147483646}) do
|
||||
local r=assert(L.resolve(p,seed)); eq(r.selection_seed,seed)
|
||||
local previous=0
|
||||
for _,name in ipairs({"germination_days","juvenile_days","maturity_days","lifespan_days"}) do
|
||||
local n=r.selected_days[name]
|
||||
if n then assert(n>=p[name].min and n<=p[name].max and n>=previous); previous=n end
|
||||
end
|
||||
assert(L.phase(r,0,100)); eq(p.selected_days,nil)
|
||||
end
|
||||
end end
|
||||
end)
|
||||
test("profiles nested evidence lists and IDs are fresh defensive copies",function()
|
||||
local p=L.plant("oak"); p.maturity_days.min=1; p.evidence.maturity_days.note="changed"
|
||||
eq(L.plant("oak").maturity_days.min,40*YEAR)
|
||||
assert(L.plant("oak").evidence.maturity_days.note~="changed")
|
||||
local a=L.animal("roe_deer"); a.breeding_months[1]=1; eq(L.animal("roe_deer").breeding_months[1],7)
|
||||
local ids=L.ids("plant"); ids[1]="changed"; assert(L.ids("plant")[1]~="changed")
|
||||
end)
|
||||
test("identity and group validation never invent profiles",function()
|
||||
for _,id in ipairs({"hearth:oak","../oak","Oak","oak_mature","unknown",string.rep("a",65),true,1}) do
|
||||
eq(L.plant(id,"trees"),nil); eq(L.animal(id),nil)
|
||||
end
|
||||
eq(L.plant("oak","herbs"),nil); eq(L.ids("other"),nil)
|
||||
end)
|
||||
test("malformed profile values and stage ordering fail safely",function()
|
||||
local p=L.plant("oak"); p.maturity_days={min=1,max=2}; eq(L.phase(p,0,0),nil)
|
||||
p=L.plant("oak"); p.selected_days=true; eq(L.phase(p,0,0),nil)
|
||||
p=L.plant("oak"); p.selected_days={maturity_days=0/0}; eq(L.phase(p,0,0),nil)
|
||||
p=L.plant("oak"); p.maturity_days={min=1,max=math.huge}; eq(L.phase(p,0,0),nil)
|
||||
eq(L.phase({},0,0),nil); eq(L.seasonal(L.animal("rabbit"),"Winter"),nil)
|
||||
eq(L.seasonal(L.plant("oak"),"winter"),nil)
|
||||
end)
|
||||
test("breeding windows describe mating or nesting not immediate births",function()
|
||||
local p=L.animal("roe_deer"); eq(p.reproduction,"delayed_implantation"); eq(p.gestation_days,false)
|
||||
eq(L.breeding_window(p,7).within_window,true); eq(L.breeding_window(p,1).within_window,false)
|
||||
eq(L.breeding_window(p,7).eligible,false)
|
||||
eq(L.breeding_window(L.animal("blue_tit"),4).within_window,true)
|
||||
eq(L.breeding_window(L.animal("blue_tit"),12).within_window,false)
|
||||
eq(L.breeding_window(L.animal("barn_owl"),4).known,false)
|
||||
eq(L.breeding_window(p,0),nil); eq(L.breeding_window(p,13),nil); eq(L.breeding_window(p,math.huge),nil)
|
||||
end)
|
||||
test("six verified basic profiles enable reproduction without claiming all taxa complete",function()
|
||||
local enabled={bank_vole=true,wood_mouse=true,blackbird=true,mallard=true,hare=true,fox=true}; local count=0
|
||||
for _,id in ipairs(L.ids("animal")) do
|
||||
local p=L.animal(id); eq(p.reproduction_enabled,enabled[id] or false)
|
||||
if enabled[id] then
|
||||
count=count+1; assert(p.maturity_days and p.birth_count)
|
||||
assert(p.gestation_days or p.incubation_days); assert(p.postpartum_days>=21)
|
||||
end
|
||||
eq(p.feeding_interval_seconds,nil); eq(p.movement_speed,nil)
|
||||
end
|
||||
eq(count,6); eq(L.animal("bank_vole").gestation_days.max,24)
|
||||
eq(L.animal("blue_tit").incubation_days.max,14); eq(L.animal("blue_tit").maturity_days,false)
|
||||
eq(L.animal("bumblebee").reproduction,"colony_caste_dependent")
|
||||
local hare,fox=L.animal("hare"),L.animal("fox")
|
||||
eq(hare.maturity_days.min,236); eq(hare.gestation_days.max,42); eq(hare.juvenile_days.max,30)
|
||||
eq(hare.birth_count.max,8); eq(hare.reproduction_enabled,true); eq(hare.postpartum_days,30)
|
||||
eq(fox.maturity_days.min,304); eq(fox.gestation_days.max,53); eq(fox.juvenile_days.min,56)
|
||||
eq(fox.birth_count.max,9); eq(fox.reproduction_enabled,true); eq(fox.breeding_seasons.Winter,true)
|
||||
end)
|
||||
test("hedgehog and amphibian winter dormancy is explicit and never a death rule",function()
|
||||
for _,id in ipairs({"hedgehog","common_frog","smooth_newt"}) do
|
||||
local p=L.animal(id); eq(p.winter_dormant,true)
|
||||
assert(L.phase(p,0,L.MAX_CLOCK).death_due==false)
|
||||
end
|
||||
eq(L.animal("hare").winter_dormant,nil)
|
||||
end)
|
||||
test("loading and calling policy does not pollute globals or read the clock",function()
|
||||
local before={}; for k,v in pairs(_G) do before[k]=v end
|
||||
local saved_time,saved_random=os.time,math.random
|
||||
os.time=function() error("unexpected clock read") end; math.random=function() error("unexpected random call") end
|
||||
local ok,err=pcall(function()
|
||||
local fresh=dofile(target); fresh.resolve(fresh.plant("oak"),123)
|
||||
fresh.phase(fresh.animal("wood_mouse"),0,100); fresh.elapsed(0,100,8)
|
||||
end)
|
||||
os.time=saved_time; math.random=saved_random; assert(ok,err)
|
||||
for k,v in pairs(_G) do eq(v,before[k]) end
|
||||
for k,v in pairs(before) do eq(_G[k],v) end
|
||||
end)
|
||||
eq(passed,32)
|
||||
print("PASS "..passed.." lifecycle checks")
|
||||
Reference in New Issue
Block a user