67 lines
4.8 KiB
Python
67 lines
4.8 KiB
Python
from pathlib import Path
|
|
from lupa import LuaRuntime
|
|
setup='''
|
|
now=1000;os.time=function()return now end;timers={};receipts={};audit={};people={};rows={{1,'A',11,'active'},{2,'B',12,'active'}};roster={{1,'A',11},{2,'B',12}};removed=0;joins=0
|
|
function query(data)local i=1;return {GetUInt32=function(_,n)return data[i][n+1]end,GetString=function(_,n)return data[i][n+1]end,NextRow=function()i=i+1;return i<=#data end}end
|
|
function AuthDBQuery()return query(rows)end
|
|
function CharDBQuery()return query(roster)end
|
|
function AuthDBExecute(s)table.insert(audit,s)end
|
|
function GetGUIDLow(g)return g end
|
|
function GetPlayersInWorld()return people end
|
|
function GetPlayerByName(n)for _,p in pairs(people)do if p.name==n then return p end end end
|
|
function CreateLuaEvent(fn,ms)timers[ms]=fn end
|
|
function PrintInfo()end;function PrintError(s)error(s)end
|
|
function AzerothAIComplete(id,ok,result)receipts[id]={ok,result}end
|
|
function AzerothAIEmitSnapshots()end
|
|
function make(id,name)
|
|
local p={id=id,name=name,dist=20}
|
|
function p:GetName()return self.name end;function p:GetGUIDLow()return self.id end;function p:GetAccountId()return 10+self.id end
|
|
function p:IsPlayerbot()return not self.human end;function p:IsDead()return self.dead end;function p:IsInCombat()return self.combat end
|
|
function p:IsCasting()return false end;function p:IsTaxi()return false end
|
|
function p:GetBotAI()return {HasGameClientMaster=function()return self.controlled end}end
|
|
function p:GetTeam()return self.team or 0 end;function p:GetLevel()return 5 end;function p:GetMapId()return 0 end;function p:GetPhaseMask()return 1 end
|
|
function p:GetDistance()return self.dist end;function p:GetGroup()return self.group end
|
|
function p:GroupCreate(b)joins=joins+1;local g=group({self,b},2);self.group=g;b.group=g;return g end
|
|
return p
|
|
end
|
|
function group(members,count)
|
|
local g={members=members,count=count}
|
|
function g:GetGUID()return 99 end;function g:IsRaidGroup()return false end;function g:GetMembers()return self.members end;function g:GetMembersCount()return self.count end
|
|
function g:GetMemberGUID(name)return name=='A' and 1 or 2 end
|
|
function g:RemoveMember(id)removed=removed+1;for _,p in pairs(self.members)do p.group=nil end;return true end
|
|
function g:AddMember(p)joins=joins+1;p.group=self;table.insert(self.members,p);self.count=self.count+1;return true end
|
|
return g
|
|
end
|
|
A=make(1,'A');B=make(2,'B');people={A,B}
|
|
function invite(id,expires)return AzerothAIQueueParty(id or 'x',{name='A',guid=1,account=11},{name='B',guid=2,account=12},expires or now+45)end
|
|
function pulse(dt)now=now+dt;timers[2000]()end
|
|
function orphan(state)
|
|
rows[2][4]=state;people={A};A.group=group({A},2)
|
|
timers[10000]()
|
|
end
|
|
'''
|
|
def new():
|
|
l=LuaRuntime();l.execute(setup);l.execute((Path(__file__).resolve().parents[1]/'ale/azerothai_parties.lua').read_text());return l
|
|
l=new();l.execute("A.combat=true;assert(invite().azerothaiDeferred);pulse(2);assert(not receipts.x and joins==0);A.combat=false;pulse(2);assert(receipts.x[1] and joins==1)")
|
|
l=new();l.execute("A.dist=100;invite();pulse(2);assert(not receipts.x);A.dist=40;pulse(2);assert(receipts.x[1])")
|
|
l=new();l.execute("A.dist=100;invite();pulse(2);A.dist=20;pulse(44);assert(not receipts.x[1] and joins==0)")
|
|
l=new();l.execute("invite();B.controlled=true;pulse(2);assert(not receipts.x[1] and joins==0)")
|
|
l=new();l.execute("invite();B.team=1;pulse(2);assert(not receipts.x[1] and joins==0)")
|
|
l=new();l.execute("invite();assert(invite().azerothaiDeferred);pulse(2);assert(joins==1)")
|
|
l=new();l.execute("orphan('dead');assert(removed==1)")
|
|
l=new();l.execute("orphan('active');assert(removed==0)")
|
|
l=new();l.execute("rows[2][2]='Unknown';orphan('dead');assert(removed==0)")
|
|
l=new();l.execute("A.controlled=true;orphan('dead');assert(removed==0)")
|
|
print('Native invitation retries, expiration, idempotence, faction/human guards and verified-death cleanup passed')
|
|
# Exercise the actual TypeScript wrapper template, including deferred receipt handling.
|
|
import re
|
|
source=(Path(__file__).resolve().parents[1]/'core/ale.ts').read_text()
|
|
wrapper=re.search(r'return `(local __command_id=.*?)`;',source,re.S).group(1)
|
|
l=new();l.execute(wrapper.replace('${qid}',"'wrapped'").replace('${source}',"return AzerothAIQueueParty(__command_id,{name='A',guid=1,account=11},{name='B',guid=2,account=12},1045)"))
|
|
l.execute("assert(not receipts.wrapped);pulse(2);assert(receipts.wrapped[1])")
|
|
l=new();l.execute(wrapper.replace('${qid}',"'ordinary'").replace('${source}','return 7'));l.execute("assert(receipts.ordinary[1] and receipts.ordinary[2]=='7')")
|
|
print('Real bridge wrapper preserves ordinary receipts and waits for deferred native completion')
|
|
|
|
l=new();l.execute("A.group=group({A},2);invite();pulse(2);assert(not receipts.x and joins==0);pulse(44);assert(not receipts.x[1] and joins==0)")
|
|
print('Invitations preserve incomplete living parties while waiting, then expire')
|