feat(combat): validate potions, persist telegraph state, and expose both on the combat DTO

Also updates the pre-existing exact-equality playerState assertion in
combat-equipment-integration.spec.ts, which broke from the new
potionsRemaining field but wasn't listed in the task brief's file scope.
This commit is contained in:
Bastian Wagner
2026-08-20 21:48:51 +02:00
parent 43d2085d2c
commit c7b9601eb4
3 changed files with 65 additions and 3 deletions

View File

@@ -325,6 +325,8 @@ describe('CombatService', () => {
name: 'Aric Duskwalker',
maxHp: 100,
currentHp: 100,
potionsRemaining: 2,
potionsMax: 2,
});
expect(combat.monster).toEqual({
key: 'ash-rat',
@@ -333,6 +335,7 @@ describe('CombatService', () => {
maxHp: 45,
currentHp: 45,
artworkPath: '/images/monsters/ash-rat.png',
pendingIntent: null,
});
expect(combat.events).toEqual([]);
expect(dataSource.state.combats).toHaveLength(1);
@@ -653,6 +656,43 @@ describe('CombatService', () => {
expect.arrayContaining([{ target: Combat, mode: 'pessimistic_write' }]),
);
});
it('resolves POTION, heals the player, and persists the reduced potion count', async () => {
const { service, combatId } = await startedCombat();
await service.performAction(CHARACTER_ID, combatId, CombatAction.ATTACK);
const result = await service.performAction(CHARACTER_ID, combatId, CombatAction.POTION);
expect(result.player.potionsRemaining).toBe(1);
expect(result.player.currentHp).toBe(95);
const reloaded = await service.getCombat(CHARACTER_ID, combatId);
expect(reloaded.player.potionsRemaining).toBe(1);
});
it('rejects POTION once both potions have been used', async () => {
const { service, combatId } = await startedCombat();
await service.performAction(CHARACTER_ID, combatId, CombatAction.POTION);
await service.performAction(CHARACTER_ID, combatId, CombatAction.POTION);
await expectCombatDomainError(
service.performAction(CHARACTER_ID, combatId, CombatAction.POTION),
'COMBAT_NO_POTIONS_REMAINING',
);
});
it('persists the telegraphed Heavy Attack across a reload', async () => {
const { service, combatId } = await startedCombat();
await service.performAction(CHARACTER_ID, combatId, CombatAction.ATTACK);
await service.performAction(CHARACTER_ID, combatId, CombatAction.ATTACK);
const telegraphed = await service.performAction(CHARACTER_ID, combatId, CombatAction.ATTACK);
expect(telegraphed.monster.pendingIntent).toBe('HEAVY_ATTACK');
const reloaded = await service.getCombat(CHARACTER_ID, combatId);
expect(reloaded.monster.pendingIntent).toBe('HEAVY_ATTACK');
});
});
describe('getCombat', () => {