107 lines
5.4 KiB
JavaScript
107 lines
5.4 KiB
JavaScript
const {test}=require('node:test');
|
|
const assert=require('node:assert/strict');
|
|
const {execute}=require('../.test-build/service.js');
|
|
const {seed}=require('../.test-build/seed.js');
|
|
const {connectors}=require('../.test-build/connectors/index.js');
|
|
const {applyAuthoritativeAleEvent}=require('../.test-build/service.js');
|
|
|
|
class Store {
|
|
constructor(){this.state=seed();}
|
|
async load(){return structuredClone(this.state);}
|
|
async save(s,r){assert.equal(this.state.revision,r);s.revision=r+1;this.state=structuredClone(s);}
|
|
}
|
|
|
|
test('game lifecycle death closes identity once and preserves Zaku',async()=>{
|
|
const store=new Store();
|
|
const bot=store.state.bots.find(b=>b.id==='bot-6');
|
|
const payload={name:bot.name,isBot:true,characterGuid:99,accountId:2,deathId:'hardcore-99'};
|
|
await applyAuthoritativeAleEvent(store,'player.permanent-death',payload);
|
|
assert.equal(store.state.bots.find(b=>b.id===bot.id).status,'dead');
|
|
await applyAuthoritativeAleEvent(store,'player.permanent-death',payload);
|
|
assert.equal(store.state.events.filter(e=>e.kind==='death').length,1);
|
|
await applyAuthoritativeAleEvent(store,'player.permanent-death',{name:'Zaku',isBot:true,characterGuid:9,accountId:1});
|
|
assert.notEqual(store.state.bots.find(b=>b.name==='Zaku').status,'dead');
|
|
});
|
|
|
|
test('Zaku is a mortal persistent hero and hardcore starts enabled',()=>{
|
|
const state=seed();
|
|
const zaku=state.bots.find(b=>b.id==='hero-zaku');
|
|
assert.ok(zaku);
|
|
assert.equal(zaku.tier,'hero');
|
|
assert.equal(zaku.mortal,true);
|
|
assert.equal(zaku.role,'Warlock');
|
|
assert.equal(zaku.race,'Undead');
|
|
assert.equal(state.settings.hardcore.enabled,true);
|
|
assert.equal(state.settings.hardcore.requireVerifiedDeath,true);
|
|
});
|
|
|
|
test('confirmed hardcore death permanently closes an identity',async()=>{
|
|
const store=new Store();
|
|
await execute(store,{type:'recordDeath',botId:'bot-6',cause:'Killed in combat',killer:'Defias Pillager',zone:'Westfall',confirmed:true});
|
|
const bot=store.state.bots.find(b=>b.id==='bot-6');
|
|
assert.equal(bot.status,'dead');
|
|
assert.equal(bot.death.verified,true);
|
|
assert.equal(store.state.events.at(-1).kind,'death');
|
|
await assert.rejects(execute(store,{type:'lifecycle',botId:'bot-6',action:'spawn'}),/permanently dead/);
|
|
});
|
|
|
|
test('verified bridge death ends life before another AI decision',async()=>{
|
|
const store=new Store();
|
|
const old=connectors.simulation;
|
|
connectors.simulation=()=>({
|
|
health:async()=>({ok:true,detail:'test'}),
|
|
lifecycle:async()=>{},
|
|
decide:async()=>{throw new Error('intent should not execute after death')},
|
|
observe:async()=>({zone:'Duskwood',level:20,summary:'fatal combat',availableActions:['quest'],alive:false,death:{id:'death-1',at:new Date().toISOString(),cause:'Killed in combat',killer:'Stitches',zone:'Duskwood',verified:true}})
|
|
});
|
|
try{
|
|
await execute(store,{type:'think',botId:'bot-1'});
|
|
const bot=store.state.bots.find(b=>b.id==='bot-1');
|
|
assert.equal(bot.status,'dead');
|
|
assert.equal(bot.decisions,0);
|
|
assert.equal(bot.death.id,'death-1');
|
|
}finally{connectors.simulation=old;}
|
|
});
|
|
|
|
test('unverified bridge death fails safe without erasing identity',async()=>{
|
|
const store=new Store();
|
|
const old=connectors.simulation;
|
|
connectors.simulation=()=>({
|
|
health:async()=>({ok:true,detail:'test'}),
|
|
lifecycle:async()=>{},
|
|
decide:async()=>({summary:'unused'}),
|
|
observe:async()=>({zone:'Duskwood',level:20,summary:'ambiguous state',availableActions:['quest'],alive:false})
|
|
});
|
|
try{
|
|
await assert.rejects(execute(store,{type:'think',botId:'bot-1'}),/verified death event/);
|
|
const bot=store.state.bots.find(b=>b.id==='bot-1');
|
|
assert.equal(bot.status,'online');
|
|
assert.equal(bot.death,undefined);
|
|
}finally{connectors.simulation=old;}
|
|
});
|
|
|
|
test('complete roster clears stale presence and historical events cannot restore it',async()=>{
|
|
const store=new Store();
|
|
const payload={name:'Rosterbot',isBot:true,characterGuid:123,accountId:2,className:'Death Knight',raceName:'Orc',level:55};
|
|
await applyAuthoritativeAleEvent(store,'player.snapshot',payload);
|
|
await applyAuthoritativeAleEvent(store,'player.snapshot',{roster:true,observedAt:100,characterGuids:[123]});
|
|
assert.equal(store.state.bots.filter(b=>b.status==='online').length,1);
|
|
await applyAuthoritativeAleEvent(store,'player.snapshot',{roster:true,observedAt:101,characterGuids:[]});
|
|
for(const kind of ['player.login','player.snapshot','player.zone'])await applyAuthoritativeAleEvent(store,kind,payload);
|
|
assert.equal(store.state.bots.filter(b=>b.status==='online').length,0);
|
|
await applyAuthoritativeAleEvent(store,'player.snapshot',{roster:true,observedAt:100,characterGuids:[123]});
|
|
assert.equal(store.state.bots.filter(b=>b.status==='online').length,0);
|
|
});
|
|
|
|
test('normal resurrection mode preserves memories and identity across death and later snapshot',async()=>{
|
|
const store=new Store();store.state.settings.hardcore.enabled=false;
|
|
const bot=store.state.bots.find(b=>b.id==='bot-6');
|
|
bot.memories.push({id:'keep',text:'My companion',importance:8,source:'observation',at:new Date().toISOString()});
|
|
const payload={name:bot.name,isBot:true,characterGuid:99,accountId:2,healthPct:0};
|
|
await applyAuthoritativeAleEvent(store,'player.killed-by-creature',payload);
|
|
assert.notEqual(store.state.bots.find(b=>b.id===bot.id).status,'dead');
|
|
await applyAuthoritativeAleEvent(store,'player.snapshot',{...payload,healthPct:100});
|
|
const revived=store.state.bots.find(b=>b.id===bot.id);
|
|
assert.equal(revived.death,undefined);assert.ok(revived.memories.some(m=>m.id==='keep'));
|
|
});
|