feat(api): retire CharacterCombatStatsService; characters/me returns effective stats

This commit is contained in:
Bastian Wagner
2026-08-20 16:26:33 +02:00
parent 67c29e783f
commit e5746dec5c
5 changed files with 37 additions and 61 deletions

View File

@@ -2,11 +2,27 @@ import { NotFoundException } from '@nestjs/common';
import { Repository } from 'typeorm';
import { DEMO_CHARACTER_ID } from '../demo/demo-character.constants';
import { SOUTH_GATE_ID } from '../database/seeds/vertical-slice.constants';
import { CharacterStatsService } from './character-stats.service';
import { Character } from './entities/character.entity';
import { CharactersService } from './characters.service';
function fakeCharacterStats(
overrides: Partial<{ maxHp: number; attack: number }> = {},
): CharacterStatsService {
return {
calculate: jest.fn().mockResolvedValue({
maxHp: overrides.maxHp ?? 100,
currentHp: 100,
attack: overrides.attack ?? 6,
weaponDamage: 8,
armor: 0,
combatPower: 0,
}),
} as unknown as CharacterStatsService;
}
describe('CharactersService', () => {
it('returns the demo character with its current location summary', async () => {
it('returns the demo character with effective attack/HP and its location summary', async () => {
const repository = {
findOne: jest.fn().mockResolvedValue({
id: DEMO_CHARACTER_ID,
@@ -20,11 +36,12 @@ describe('CharactersService', () => {
currentLocation: {
id: SOUTH_GATE_ID,
key: 'south-gate',
name: 'S\u00fcdtor von Graufurt',
name: 'Südtor von Graufurt',
},
}),
} as unknown as Repository<Character>;
const service = new CharactersService(repository);
const characterStats = fakeCharacterStats({ maxHp: 115, attack: 7 });
const service = new CharactersService(repository, characterStats);
await expect(service.getDemoCharacter()).resolves.toEqual({
id: DEMO_CHARACTER_ID,
@@ -33,18 +50,17 @@ describe('CharactersService', () => {
experience: 0,
silver: 0,
currentHp: 100,
maxHp: 100,
attack: 6,
maxHp: 115,
attack: 7,
currentLocation: {
id: SOUTH_GATE_ID,
key: 'south-gate',
name: 'S\u00fcdtor von Graufurt',
name: 'Südtor von Graufurt',
},
});
expect(repository.findOne).toHaveBeenCalledWith({
where: { id: DEMO_CHARACTER_ID },
relations: { currentLocation: true },
});
expect(characterStats.calculate).toHaveBeenCalledWith(
expect.objectContaining({ id: DEMO_CHARACTER_ID }),
);
});
it('exposes the persisted silver so the HUD never has to guess', async () => {
@@ -65,7 +81,7 @@ describe('CharactersService', () => {
},
}),
} as unknown as Repository<Character>;
const service = new CharactersService(repository);
const service = new CharactersService(repository, fakeCharacterStats());
await expect(service.getDemoCharacter()).resolves.toEqual(
expect.objectContaining({ experience: 24, silver: 18 }),
@@ -76,7 +92,7 @@ describe('CharactersService', () => {
const repository = {
findOne: jest.fn().mockResolvedValue(null),
} as unknown as Repository<Character>;
const service = new CharactersService(repository);
const service = new CharactersService(repository, fakeCharacterStats());
await expect(service.getDemoCharacter()).rejects.toBeInstanceOf(
NotFoundException,