Persist fatal encounters and audit resident readiness with party outcome metrics
Core tests / core (push) Successful in 42s
Core tests / core (push) Successful in 42s
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
from pathlib import Path
|
||||
from lupa import LuaRuntime
|
||||
source=(Path(__file__).resolve().parents[1]/'ale/azerothai_experience.lua').read_text()
|
||||
l=LuaRuntime();l.execute('''
|
||||
now=100000;os.time=function()return now end;events={};writes={};ddl=false
|
||||
function CreateLuaEvent()end
|
||||
function RegisterPlayerEvent(n,f)events[n]=f end
|
||||
function AuthDBExecute(s)table.insert(writes,s)end
|
||||
function AuthDBQuery(s)
|
||||
if s:find('CREATE TABLE',1,true) then ddl=true;return nil end
|
||||
if s:find('azerothai_lifecycle',1,true) then return {GetString=function()return 'A' end,GetUInt32=function()return 12 end} end
|
||||
assert(ddl)
|
||||
return {GetUInt32=function(_,i)return ({[0]=7,[1]=99990,[3]=50,[4]=0,[7]=5})[i] end,GetString=function()return 'Wolf' end,GetFloat=function()return 10 end,NextRow=function()return false end}
|
||||
end
|
||||
p={level=5,account=12};function p:GetGUIDLow()return 1 end;function p:GetAccountId()return self.account end;function p:GetName()return 'A' end
|
||||
function p:IsPlayerbot()return true end;function p:GetBotAI()return {HasGameClientMaster=function()return p.controlled end} end
|
||||
function p:GetMapId()return 0 end;function p:GetLevel()return self.level end;function p:GetX()return 10 end;function p:GetY()return 10 end
|
||||
enemy={x=10};function enemy:ToCreature()return self end;function enemy:GetEntry()return 50 end;function enemy:GetX()return self.x end;function enemy:GetY()return 10 end;function enemy:GetName()return 'Wolf' end
|
||||
''');l.execute(source)
|
||||
l.execute('''
|
||||
assert(AzerothAILearnedDanger(p,enemy));enemy.x=500;assert(not AzerothAILearnedDanger(p,enemy));enemy.x=10
|
||||
p.level=8;assert(not AzerothAILearnedDanger(p,enemy));p.level=5
|
||||
p.account=99;events[8](8,enemy,p);assert(#writes==0);p.account=12
|
||||
p.controlled=true;events[8](8,enemy,p);assert(#writes==0);p.controlled=false
|
||||
events[8](8,enemy,p);assert(#writes==1);events[8](8,enemy,p);assert(#writes==1)
|
||||
''')
|
||||
# Reload the whole module: durable SQL memory must still affect target selection.
|
||||
l.execute(source);l.execute("assert(AzerothAILearnedDanger(p,enemy));now=400000;assert(not AzerothAILearnedDanger(p,enemy))")
|
||||
print('Death memory: identity, human control, duplicate event, radius, level reassessment, expiry and reload persistence passed')
|
||||
@@ -0,0 +1,27 @@
|
||||
from pathlib import Path
|
||||
from lupa import LuaRuntime
|
||||
source=(Path(__file__).resolve().parents[1]/'ale/azerothai_readiness.lua').read_text()
|
||||
setup='''
|
||||
now=1000;os.time=function()return now end;calls={}
|
||||
function WorldDBQuery()return {GetUInt32=function(_,i)return i==0 and 10 or 11 end,NextRow=function()return false end}end
|
||||
function AuthDBQuery()return {GetUInt32=function(_,i)return i==0 and 1 or 12 end,GetString=function()return 'A' end,NextRow=function()return false end}end
|
||||
function CreateLuaEvent(fn)tick=fn end;function PrintError(s)error(s)end
|
||||
p={ai={}};function GetPlayersInWorld()return {p}end
|
||||
function p:GetGUIDLow()return 1 end;function p:GetAccountId()return 12 end;function p:GetName()return 'A' end
|
||||
function p:IsPlayerbot()return true end;function p:GetBotAI()return self.ai end;function p.ai:HasGameClientMaster()return p.human end
|
||||
function p:IsDead()return p.dead end;function p:IsInCombat()return p.combat end;function p:IsCasting()return false end;function p:IsTaxi()return false end
|
||||
function p:GetGroup()return p.group end;function p:GetFreeTalentPoints()return 1 end
|
||||
function p:GetItemByPos(_,slot)if slot==15 then return {IsBroken=function()return true end,GetUInt32Value=function(_,i)return i==0x003D and 50 or 0 end}end end
|
||||
function p.ai:GetInventoryItems()return {{GetEntry=function()return 10 end,GetCount=function()return 3 end}}end
|
||||
function p:CanUseItem()return true end;function p:GetPowerType()return 0 end
|
||||
function p.ai:DoSpecificAction(s)table.insert(calls,s)end;function p.ai:ChangeStrategy(s)table.insert(calls,s)end;function p.ai:HasCheat()return p.goldCheat end
|
||||
function AzerothAISurvivalOverride()return p.recovering end
|
||||
'''
|
||||
def new():
|
||||
l=LuaRuntime();l.execute(setup);l.execute(source);return l
|
||||
l=new();l.execute("tick();local r=AzerothAIReadinessSnapshot(p);assert(r.foodCount==3 and r.drinkCount==0 and r.brokenItems==1 and r.freeTalents==1);assert(#calls==4);tick();assert(#calls==4)")
|
||||
for condition in ['human','combat','dead','recovering']:
|
||||
l=new();l.execute(f'p.{condition}=true;tick();assert(#calls==0)')
|
||||
l=new();l.execute("p.group={IsRaidGroup=function()return false end,GetMembersCount=function()return 2 end,GetMembers=function()return {p} end};tick();assert(#calls==0)")
|
||||
l=new();l.execute("p.goldCheat=true;tick();assert(#calls==2,'No repair or purchase under gold cheat')")
|
||||
print('Readiness: inventory counts, broken gear, cooldown, combat/death/recovery/human/group guards and normal-cost actions passed')
|
||||
@@ -10,3 +10,21 @@ test('activity distinguishes stale data and guarded actions from inferred stalls
|
||||
|
||||
test('pending social events count once after native confirmation',()=>{const s=state();s.community.feed=[{id:'social-1:reply',kind:'conversation',status:'pending',at:new Date(now*1000).toISOString(),names:['A','B']}];const a=observeInsights(s,undefined,now);s.community.feed[0].status='confirmed';const b=observeInsights(s,a.cursor,now+60);assert.equal(b.events.length,1);assert.equal(observeInsights(s,b.cursor,now+120).events.length,0);});
|
||||
test('enabling failure tracking does not backfill old failed party actions',()=>{const s=state();const a=observeInsights(s,undefined,now);s.community.feed=[{id:'party-old',kind:'party',status:'failed',at:new Date((now-600)*1000).toISOString(),names:['A','B']},{id:'party-new',kind:'party',status:'failed',at:new Date((now+20)*1000).toISOString(),names:['A','B']}];const b=observeInsights(s,a.cursor,now+60);assert.deepEqual(b.events.map(e=>e.id),['social:party-new']);});
|
||||
|
||||
test('party time requires fresh consecutive matching living-party observations',()=>{
|
||||
const s=state();for(const b of s.bots)Object.assign(b.world,{groupId:'g',groupNames:['A','B'],nearby:['A','B']});
|
||||
let a=observeInsights(s,undefined,now);assert.equal(a.events.length,0);
|
||||
for(const b of s.bots)b.world.at=now+60;
|
||||
let c=observeInsights(s,a.cursor,now+60);assert.equal(c.events.filter(e=>e.kind==='nearPartySeconds').reduce((n,e)=>n+e.amount,0),120);
|
||||
assert.equal(observeInsights(s,c.cursor,now+90).events.length,0,'same snapshot is not more evidence');
|
||||
for(const b of s.bots)b.world.at=now+500;
|
||||
assert.equal(observeInsights(s,c.cursor,now+500).events.length,0,'long collection gap excluded');
|
||||
s.bots[1].world.isDead=true;s.bots[0].world.at=now+90;
|
||||
assert.equal(observeInsights(s,c.cursor,now+90).events.filter(e=>e.kind==='nearPartySeconds').length,0);
|
||||
});
|
||||
test('monitor distinguishes mana recovery, named leader and ghost state',()=>{
|
||||
const b=bot();Object.assign(b.world,{manaPct:20,survivalAction:'recovering readiness',survivalAt:now,survivalSince:now-70});
|
||||
assert.equal(activityReason(b,now).label,'Recovering mana');assert.match(activityReason(b,now).detail,/1m 10s/);
|
||||
b.world.survivalAction='regroup requested';b.world.leaderName='Zaku';assert.equal(activityReason(b,now).label,'Catching up to Zaku');
|
||||
b.world.isGhost=true;assert.equal(activityReason(b,now).label,'Recovering as a ghost');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user