test(turn-in): cover the unknown-character rejection path
TurnInService locks and validates the character row before touching inventory, silver, or reputation, but no test exercised that branch. Both sibling services in this slice (renown, reputation) have the equivalent test; this closes the gap. `characterNotFound` is shared from travel.errors and so is not a TurnInDomainError -- assert on the wire contract (code + status) the way reputation.service.spec.ts does for the same shared error. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { HttpException } from '@nestjs/common';
|
||||
import { DataSource, EntityManager, EntityTarget } from 'typeorm';
|
||||
import { Character } from '../characters/entities/character.entity';
|
||||
import { CharacterItem } from '../items/entities/character-item.entity';
|
||||
@@ -168,6 +169,30 @@ async function expectTurnInDomainError(promise: Promise<unknown>, code: string):
|
||||
expect(error.code).toBe(code);
|
||||
}
|
||||
|
||||
/**
|
||||
* `characterNotFound` is shared from `travel.errors`, so it is not a
|
||||
* TurnInDomainError. Assert on the wire contract -- code plus status --
|
||||
* the way `reputation.service.spec.ts` does for the same shared error.
|
||||
*/
|
||||
async function expectHttpErrorWithCode(
|
||||
promise: Promise<unknown>,
|
||||
code: string,
|
||||
status: number,
|
||||
): Promise<void> {
|
||||
let error: unknown;
|
||||
try {
|
||||
await promise;
|
||||
} catch (cause) {
|
||||
error = cause;
|
||||
}
|
||||
expect(error).toBeInstanceOf(HttpException);
|
||||
if (!(error instanceof HttpException)) {
|
||||
throw new Error('Expected HttpException');
|
||||
}
|
||||
expect(error.getStatus()).toBe(status);
|
||||
expect(error.getResponse()).toMatchObject({ code });
|
||||
}
|
||||
|
||||
describe('TurnInService', () => {
|
||||
describe('turnIn', () => {
|
||||
it('consumes the exact quantity, grants silver and reputation atomically', async () => {
|
||||
@@ -232,6 +257,19 @@ describe('TurnInService', () => {
|
||||
expect(state.characters[0].silver).toBe(10);
|
||||
});
|
||||
|
||||
it('rejects an unknown character, mutating nothing', async () => {
|
||||
const { service, state } = createService(createState());
|
||||
|
||||
await expectHttpErrorWithCode(
|
||||
service.turnIn('unknown-character', 'ash-pelt-border-guard', 1),
|
||||
'CHARACTER_NOT_FOUND',
|
||||
404,
|
||||
);
|
||||
expect(state.characterItems[0].quantity).toBe(5);
|
||||
expect(state.characters[0].silver).toBe(10);
|
||||
expect(state.characterReputation).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('rejects a disabled turn-in', async () => {
|
||||
const state = createState({ turnIns: [turnInDefinition({ enabled: false })] });
|
||||
const { service } = createService(state);
|
||||
|
||||
Reference in New Issue
Block a user