From 40ff950390079cd58097a210fbec1055f8322624 Mon Sep 17 00:00:00 2001 From: Bastian Wagner Date: Fri, 21 Aug 2026 14:27:20 +0200 Subject: [PATCH] test(combat): stop asserting the deleted experience reward contract combat.service.spec.ts mocked CombatRewardService, so it never noticed that design R8 removed CombatRewardDto.experience. Its mocks returned { experience, silver, items } and one assertion required experience: 8 back out -- encoding a contract the production DTO no longer has. A mock lying about the real shape is worse than no test: it would keep passing if the real DTO drifted further. The rollback test deliberately wrote two independent fields so its assertions could not pass vacuously ("rolled back" vs "never written"). Swapped the abolished experience for renown rather than dropping to a single field, preserving that intent. Monster level is untouched -- it is a monster stat this slice keeps, not the abolished character level. Co-Authored-By: Claude Opus 5 --- apps/api/src/combat/combat.service.spec.ts | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/apps/api/src/combat/combat.service.spec.ts b/apps/api/src/combat/combat.service.spec.ts index 0750b88..ecd749c 100644 --- a/apps/api/src/combat/combat.service.spec.ts +++ b/apps/api/src/combat/combat.service.spec.ts @@ -173,8 +173,7 @@ function character(overrides: Partial = {}): Character { return { id: CHARACTER_ID, name: 'Aric Duskwalker', - level: 1, - experience: 0, + renown: 1, silver: 0, baseHp: 100, baseAttack: 6, @@ -271,7 +270,7 @@ function fakeRewardService( return { grantVictoryRewards: overrides.grantVictoryRewards ?? - jest.fn().mockResolvedValue({ experience: 8, silver: 6, items: [] }), + jest.fn().mockResolvedValue({ silver: 6, items: [] }), loadRewards: overrides.loadRewards ?? jest.fn().mockResolvedValue(null), } as unknown as CombatRewardService; } @@ -836,7 +835,6 @@ describe('CombatService', () => { ); const rewards = fakeRewardService({ grantVictoryRewards: jest.fn().mockResolvedValue({ - experience: 8, silver: 6, items: [], }), @@ -856,7 +854,7 @@ describe('CombatService', () => { ); expect(result.status).toBe(CombatStatus.WON); - expect(result.rewards).toEqual({ experience: 8, silver: 6, items: [] }); + expect(result.rewards).toEqual({ silver: 6, items: [] }); expect(rewards.grantVictoryRewards).toHaveBeenCalledTimes(1); // The reward service must receive the transaction manager, not the data // source: `expect.anything()` would pass even if the code handed over @@ -914,7 +912,6 @@ describe('CombatService', () => { it('replays the persisted reward when a finished combat is read again', async () => { const persisted = { - experience: 16, silver: 12, items: [ { @@ -997,10 +994,12 @@ describe('CombatService', () => { new CombatEngineService(), fakeCharacterStats(), fakeRewardService({ - // Genuinely write XP/silver through the transaction's manager + // Genuinely write renown/silver through the transaction's manager // before failing, so the assertions below prove the rollback // discards those writes rather than passing vacuously because - // nothing was ever written. + // nothing was ever written. Two independent fields, deliberately: + // one field alone could not distinguish "rolled back" from + // "never written". grantVictoryRewards: jest .fn() .mockImplementation(async (manager: EntityManager) => { @@ -1009,7 +1008,7 @@ describe('CombatService', () => { id: CHARACTER_ID, }); if (combatCharacter) { - combatCharacter.experience += 8; + combatCharacter.renown += 1; combatCharacter.silver += 6; await characters.save(combatCharacter); } @@ -1027,7 +1026,7 @@ describe('CombatService', () => { expect(dataSource.state.combats[0].status).toBe(CombatStatus.ACTIVE); expect(dataSource.state.combats[0].monsterCurrentHp).toBe(1); expect(dataSource.state.combatEvents).toHaveLength(0); - expect(dataSource.state.characters[0].experience).toBe(0); + expect(dataSource.state.characters[0].renown).toBe(1); expect(dataSource.state.characters[0].silver).toBe(0); }); });