-- Standalone Lua, no engine. Pass the fixed catalogue path as arg[1]. local target=assert(arg[1],"fauna.lua path required") local root=target:gsub("[^/\\]+$","") local L=dofile(root.."lifecycle.lua") local F=dofile(target)({lifecycle=L},{}) local passed=0 local function test(name,fn) local ok,err=pcall(fn); assert(ok,name..": "..tostring(err)) passed=passed+1; print("ok "..passed.." - "..name) end test("exact stable 23 ID catalogue and strict canonical lookup",function() local expected="hare deer fox 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" local ids={}; for _,r in ipairs(F.list()) do ids[#ids+1]=r.id; assert(F.kind(r.entity)==r.id) end assert(table.concat(ids," ")==expected and #F.catalog()==23) for _,v in ipairs({"../fox","fox.b3d","hearth:fox_extra","FOX",string.rep("a",2049)}) do assert(not F.get(v)) end assert(not F.kind({name=42}) and not F.kind(false)) end) test("species sources and lifecycle evidence are copied, not invented",function() for _,r in ipairs(F.list()) do assert(r.source_url:match("^https://en.wikipedia.org/wiki/")) assert(r.profile.id==r.id and r.profile.kind=="animal") if r.id~="deer" then assert(r.latin and #F.source(r.id)>=2) end end assert(F.get("deer").latin==false and F.get("deer").profile.confidence=="unverified_identity") local r=F.get("fox"); r.prey.deer=true; r.bounds.max[2]=999; r.profile.maturity_days={min=1,max=1} assert(not F.get("fox").prey.deer and F.get("fox").bounds.max[2]<1) local sourced=F.get("fox").profile.maturity_days assert(sourced and sourced.min==304 and sourced.max==304,"fox maturity remains the sourced real-day profile") end) test("exact authored asset convention, five clips, 24fps and measured bounds",function() for _,r in ipairs(F.list()) do assert(r.mesh=="hearth_fauna_"..r.id..".b3d" and r.texture=="hearth_fauna_"..r.id..".png") assert(r.frames==100 and r.fps==24 and r.eye_height>=.15) for i,name in ipairs({"idle","locomotion","feed","flight_swim","rest"}) do assert(r.clips[name][1]==(i-1)*20 and r.clips[name][2]==i*20-1) end assert(r.bounds.min[2]==0 and r.bounds.max[2]>0 and r.bounds.min[3]<0) end assert(F.get("deer").bounds.max[2]==1.5947 and F.get("fox").bounds.min[3]==-.8624) assert(F.get("earthworm").speed==20/3600) end) test("per-species solar rest schedules and sourced winter dormancy are explicit",function() for _,r in ipairs(F.list()) do assert(type(r.sleep)=="table" and type(r.sleep.kind)=="string",r.id) assert(({day=true,night=true,crepuscular=true,any=true})[r.activity],r.id) end assert(F.get("robin").sleep.sleep_below==-6 and F.get("barn_owl").sleep.sleep_above==6) assert(F.get("deer").sleep.active_between[1]==-8 and F.get("bank_vole").sleep.rest_hours[1]==12) assert(F.get("hedgehog").winter_dormant and F.get("common_frog").winter_dormant and F.get("smooth_newt").winter_dormant) assert(not F.get("hare").winter_dormant) end) test("calendar ages are real days, never 60-second maturity or 900-second death",function() local p=assert(F.phase("bank_vole",100,100+60)); assert(p.phase=="dependent") p=assert(F.phase("bank_vole",100,100+56*86400)); assert(p.phase=="mature" and p.age_days==56) p=assert(F.phase("bank_vole",100,100+1000*86400)); assert(not p.death_due) assert(not F.phase("hare",200,100)) assert(F.phase("earthworm",0,900).phase=="juvenile") end) test("only complete source-backed profiles authorize mature seasonal reproduction",function() local enabled={bank_vole=true,wood_mouse=true,blackbird=true,mallard=true,hare=true,fox=true} for _,r in ipairs(F.list()) do local season=r.id=="fox" and "Winter" or "Summer" assert(F.can_breed(r.id,0,1800000000,"female",season)== (enabled[r.id] or false),r.id) assert(r.profile.reproduction_enabled==(enabled[r.id] or false)) assert(not F.can_breed(r.id,1800000000-60,1800000000,"female","Summer")) end assert(not F.can_breed("missing",0,1800000000,"male")) assert(F.get("rabbit").profile.gestation_days.min==30) assert(F.get("wood_mouse").profile.gestation_days.max==26) assert(F.get("hare").profile.gestation_days.max==42 and F.get("fox").profile.gestation_days.max==53) assert(not F.can_breed("rabbit",0,1800000000,"female","Spring")) end) test("hemisphere-aware seasons not northern months gate both sexes",function() local south=dofile(target)({lifecycle=L,climate_at=function() return {season="Spring"} end},{}) assert(south.can_breed("wood_mouse",0,1794000000,"male")) -- November southern spring assert(not south.can_breed("wood_mouse",0,1794000000,"male","Winter")) assert(not south.can_breed("wood_mouse",0,1794000000,"other","Spring")) assert(not F.can_breed("wood_mouse",0,1794000000,"female")) -- no climate, fail closed assert(F.get("mallard").profile.lifespan_days==false) assert(F.can_breed("mallard",0,1794000000,"female","Spring")) end) test("actual UTC breeding windows never turn into reproductive authority",function() assert(F.breeding_window("roe_deer",7).within_window) assert(not F.breeding_window("roe_deer",11).within_window) local fox_july=F.breeding_window("fox",7) assert(fox_july.known and not fox_july.within_window, "fox breeding is sourced for winter months, not July") assert(F.breeding_window("blackbird",3).within_window) assert(not F.breeding_window("blackbird",1).within_window) end) test("food web has concrete registered prey and distinct ecological functions",function() for _,r in ipairs(F.list()) do for id in pairs(r.prey) do assert(F.get(id),id) end end assert(F.get("fox").prey.rabbit and F.get("fox").prey.hare) assert(F.get("stoat").prey.rabbit and F.get("weasel").prey.bank_vole) assert(F.get("barn_owl").prey.wood_mouse and not F.get("barn_owl").prey.rabbit) assert(F.get("otter").prey.brown_trout and F.get("badger").prey.earthworm) assert(F.get("bumblebee").foods.nectar and not F.get("bumblebee").foods.seed) assert(F.get("earthworm").foods.litter and not next(F.get("brown_trout").prey)) end) test("unknown starvation is not fabricated; explicit rabbit upper bound is days",function() for _,r in ipairs(F.list()) do assert(r.feed_seconds>=300 and r.reserve_seconds>=86400) if r.id=="rabbit" then assert(r.starvation_days==8 and r.starvation_source:match("#Diet$")) else assert(r.starvation_days==false) end end end) assert(passed==10) print("PASS "..passed.." fauna catalogue tests")