const {test}=require('node:test'); const assert=require('node:assert/strict'); const {partyDecision,bondEvidence,community,compileCommunityAction,observePresence,applyCooperation}=require('../.test-build/community.js'); const now=()=>Date.now()/1000; function bot(id){return {id,name:id,status:'online',level:5,autonomy:{managed:true,characterGuid:id==='A'?1:2,accountId:id==='A'?11:12},personality:{sociability:60,activities:['quest']},relationships:[],memories:[],world:{at:now(),team:1,nearby:id==='A'?['B']:['A'],inCombat:false,groupNames:[],groupId:'',guildId:3,guildName:'Guild',guildRank:0,mapId:0,x:1,y:1}};} test('party acceptance respects faction, level, hostility, combat, and observation freshness',()=>{ const a=bot('A'),b=bot('B');assert.equal(partyDecision(a,b).accepted,true); b.world.team=0;assert.equal(partyDecision(a,b).accepted,false);b.world.team=1; b.level=55;assert.equal(partyDecision(a,b).accepted,false);b.level=5; b.relationships=[{targetId:'A',affinity:-10}];assert.equal(partyDecision(a,b).accepted,false);b.relationships=[]; b.world.at=now()-150;assert.equal(partyDecision(a,b).accepted,false);b.world.at=now(); b.world.inCombat=true;assert.equal(partyDecision(a,b).accepted,false); }); test('relationship evidence is reciprocal, replay safe, bounded, and excludes deaths',()=>{ const a=bot('A'),b=bot('B'),s={bots:[a,b]}; bondEvidence(s,'x',a,b,'Helped',3);bondEvidence(s,'x',a,b,'Helped',3); assert.equal(a.relationships[0].affinity,3);assert.equal(b.relationships[0].affinity,3); applyCooperation(s,{observedAt:now(),evidenceId:'q',characterGuid:1,peerGuid:2,cooperation:'quest',questId:5}); assert.equal(a.relationships[0].affinity,6);assert.equal(a.memories.length,1); applyCooperation(s,{observedAt:now(),evidenceId:'q',characterGuid:1,peerGuid:2,cooperation:'quest',questId:5});assert.equal(a.memories.length,1); b.status='dead';applyCooperation(s,{observedAt:now(),evidenceId:'q2',characterGuid:1,peerGuid:2,cooperation:'quest'});assert.equal(a.memories.length,1); }); test('native commands protect human parties, cap party size, verify identities and enforce guild authority',()=>{ const a=bot('A'),b=bot('B'); const p=compileCommunityAction('party',a,b);assert.match(p,/AzerothAIQueueParty/);assert.match(p,/guid=2,account=12/);assert.match(p,/__command_id/); const g=compileCommunityAction('recruit',a,b);assert.match(g,/GetGuildRank\(\)~=0/);assert.match(g,/Already belongs to a guild/); assert.match(compileCommunityAction('leave',a),/Party changed/); }); test('fresh movement snapshots update guild registry and stale snapshots cannot create stuck alerts',()=>{ const a=bot('A'),s={bots:[a]};a.world.at=now()-30; observePresence(s,a,{observedAt:now(),team:1,x:10,y:20,mapId:0,guildId:3,guildName:'Guild',groupId:'',nearbyBots:'B'}); assert.equal(community(s).guilds[0].name,'Guild');const at=a.world.at; observePresence(s,a,{observedAt:now()-500,team:1,x:2,y:2});assert.equal(a.world.at,at); }); test('offline party members are protected when their identity cannot be checked live',()=>{ assert.match(compileCommunityAction('party',bot('A'),bot('B')),/AzerothAIQueueParty/); assert.match(compileCommunityAction('leave',bot('A')),/Party has offline members/); }); test('recent danger encourages shy companions without overriding hostility or human control',()=>{ const {partyDecision}=require('../.test-build/community.js');const a=bot('A'),b=bot('B');a.personality.sociability=0;b.personality.sociability=0; assert.equal(partyDecision(a,b).accepted,false);a.world.dangerAt=now();assert.equal(partyDecision(a,b).accepted,true); b.relationships=[{targetId:'A',affinity:-1}];assert.equal(partyDecision(a,b).accepted,false);b.relationships=[]; b.world.humanControlled=true;assert.equal(partyDecision(a,b).accepted,false);b.world.humanControlled=false; a.world.dangerAt=now()-601;assert.equal(partyDecision(a,b).accepted,false); }); test('companion ranking prefers complementary native roles and shared active quests',()=>{ const {companionScore}=require('../.test-build/community.js');const a=bot('A'),b=bot('B'),c=bot('C');a.world.combatRole='tank';b.world.combatRole='healer';c.world.combatRole='damage';assert.ok(companionScore(a,b)>companionScore(a,c)); b.world.combatRole='damage';a.world.quests=[1,2];c.world.quests=[1,2];assert.ok(companionScore(a,c)>companionScore(a,b)); });