import { describe, it, expect } from 'bun:test'; import { World } from '@common/rpg/core/world'; import { Reputation, Factions } from '@common/rpg/components/faction'; import { resolveVariables } from '@common/rpg/utils/variables'; function world() { return new World(); } describe('FactionMember', () => { it('join() adds membership, isMember() returns true', () => { const w = world(); const e = w.createEntity(); Factions.join(e, 'guards'); expect(Factions.isMember(e, 'guards')).toBeTrue(); }); it('leave() removes membership, isMember() returns false', () => { const w = world(); const e = w.createEntity(); Factions.join(e, 'guards'); Factions.leave(e, 'guards'); expect(Factions.isMember(e, 'guards')).toBeFalse(); }); it('isMember() returns false when entity has no FactionMember', () => { const w = world(); const e = w.createEntity(); expect(Factions.isMember(e, 'guards')).toBeFalse(); }); it('getFactions() returns all factionIds', () => { const w = world(); const e = w.createEntity(); Factions.join(e, 'guards'); Factions.join(e, 'merchants'); expect(Factions.getFactions(e).sort()).toEqual(['guards', 'merchants']); }); it('member variable resolves on world', () => { const w = world(); const e = w.createEntity('player'); Factions.join(e, 'guards'); expect(resolveVariables(w)['player.FactionMember.guards']).toBeTrue(); }); }); describe('Reputation', () => { it('getReputation() returns 0 when no component', () => { const w = world(); const e = w.createEntity(); expect(Factions.getReputation(e, 'guards')).toBe(0); }); it('setReputation() creates component on first call', () => { const w = world(); const e = w.createEntity(); Factions.setReputation(e, 'guards', 50); expect(Factions.getReputation(e, 'guards')).toBe(50); }); it('setReputation() updates score on second call without duplicate component', () => { const w = world(); const e = w.createEntity(); Factions.setReputation(e, 'guards', 50); Factions.setReputation(e, 'guards', 75); expect(Factions.getReputation(e, 'guards')).toBe(75); expect(e.getAll(Reputation).length).toBe(1); }); it('adjustReputation() adds delta from zero default', () => { const w = world(); const e = w.createEntity(); Factions.adjustReputation(e, 'guards', 20); expect(Factions.getReputation(e, 'guards')).toBe(20); }); it('adjustReputation() accumulates correctly', () => { const w = world(); const e = w.createEntity(); Factions.adjustReputation(e, 'guards', 20); Factions.adjustReputation(e, 'guards', -5); expect(Factions.getReputation(e, 'guards')).toBe(15); }); it('Reputation component adjust() mutates score', () => { const w = world(); const e = w.createEntity(); Factions.setReputation(e, 'guards', 10); const comp = e.get(Reputation, (c) => c.factionId == 'guards')!; comp.adjust(30); expect(comp.score).toBe(40); }); it('score variable resolves on world', () => { const w = world(); const e = w.createEntity('player'); Factions.setReputation(e, 'guards', 50); expect(resolveVariables(w)['player.Reputation.guards']).toBe(50); }); }); describe('Factions.getReputationBetween', () => { it('returns null when target has no factions', () => { const w = world(); const observer = w.createEntity(); const target = w.createEntity(); expect(Factions.getReputationBetween(observer, target)).toBeNull(); }); it("returns observer's rep with target's sole faction", () => { const w = world(); const observer = w.createEntity(); const target = w.createEntity(); Factions.join(target, 'guards'); Factions.setReputation(observer, 'guards', 40); expect(Factions.getReputationBetween(observer, target)).toBe(40); }); it('returns the minimum when target belongs to multiple factions', () => { const w = world(); const observer = w.createEntity(); const target = w.createEntity(); Factions.join(target, 'guards'); Factions.join(target, 'bandits'); Factions.setReputation(observer, 'guards', 60); Factions.setReputation(observer, 'bandits', -30); expect(Factions.getReputationBetween(observer, target)).toBe(-30); }); });