from pathlib import Path from lupa import LuaRuntime code=(Path(__file__).resolve().parents[1]/'ale/azerothai_recovery.lua').read_text() setup=''' now=1000;os.time=function()return now end;attempts=0;expired=0;last=nil;active=true;player={x=0};ai={};captured={} function AuthDBExecute(sql) if string.find(sql,'INSERT INTO',1,true) then last=now end end function AuthDBQuery(sql) if string.find(sql,'recovery_log',1,true) then return last and now-last<1800 and {} or nil end;return active and {} or nil end function GetPlayerByName(n) return n=='Test' and player or nil end function CreateLuaEvent(fn) tick=fn end function PrintInfo(s) end function PrintError(s) error(s) end function player:GetGUIDLow()return 9 end function player:GetAccountId()return 2 end function player:IsPlayerbot()return true end function player:GetBotAI()return ai end function player:IsDead()return self.dead end function player:IsInCombat()return self.combat end function player:IsCasting()return self.casting end function player:IsTaxi()return self.taxi end function player:IsRooted()return self.root end function player:GetGroup()return self.group end function player:GetX()return self.x end function player:GetY()return 0 end function player:GetZ()return 0 end function player:GetMapId()return 0 end function player:GetLevel()return 8 end function player:GetHealth()return self.hp or 100 end function player:GetMovementType()return self.motion or 0 end function player:MoveExpire()expired=expired+1 end function player:MoveClear()end function ai:HasGameClientMaster()return player.master end function ai:IsMovementImpaired()return player.impaired end function ai:Reset(full)attempts=attempts+1 end function ai:DoSpecificAction(action,silent)assert(action=='move random');return true end function step(seconds,action) now=now+seconds AzerothAISetRecoveryIntents({{guid=9,account=2,name='Test',action=action or 'quest'}}) tick() end ''' def new(): l=LuaRuntime();l.execute(setup);l.execute(code);return l l=new();l.execute('step(0);step(599);assert(attempts==0);step(1);assert(attempts==1);step(600);assert(attempts==1);step(1200);assert(attempts==2)') print('Threshold and persistent cooldown passed') for field in ['dead','combat','casting','taxi','root','master','impaired']: l=new();l.execute(f'player.{field}=true;step(0);step(601);assert(attempts==0)') print('Death, combat, cast, taxi, root, master and impaired exclusions passed') for action in ['rest','socialize','trade']: l=new();l.execute(f'step(0,"{action}");step(601,"{action}");assert(attempts==0)') l=new();l.execute('step(0);player.x=8;step(599);step(2);assert(attempts==0)') l=new();l.execute('step(0);player.hp=99;step(599);step(2);assert(attempts==0)') l=new();l.execute('step(0);active=false;step(601);assert(attempts==0)') l=new();l.execute('step(0);now=now+601;tick();assert(attempts==0)') print('Intent, movement, health progress, active identity and stale input guards passed') l=new();l.execute('player.group={GetMembers=function()return {{IsPlayerbot=function()return false end}} end,GetMembersCount=function()return 1 end};step(0);step(601);assert(attempts==0)') l=new();l.execute('player.motion=7;step(0);step(601);assert(attempts==1 and expired==1)') print('Human group exclusion and stale flight recovery passed')