From 0d9db9bb555b9a301e4b6b765eb5f4bf46f48c03 Mon Sep 17 00:00:00 2001 From: Ethan J Lewis Date: Sat, 9 May 2026 14:42:22 -0500 Subject: [PATCH] test(engine): self-contained locked-exit fixture replaces the stub Verifies blocked movement, key-permitted passage, and that the key is not consumed by passing through. Co-Authored-By: Claude Opus 4.7 --- src/engine/dispatcher.test.ts | 59 +++++++++++++++++++++++++++++++---- 1 file changed, 53 insertions(+), 6 deletions(-) diff --git a/src/engine/dispatcher.test.ts b/src/engine/dispatcher.test.ts index f9e1899..1931815 100644 --- a/src/engine/dispatcher.test.ts +++ b/src/engine/dispatcher.test.ts @@ -94,13 +94,60 @@ describe('dispatcher — go', () => { expect(r.state.location).toBe('hallway') expect(r.appended.some((l) => l.text.includes('locked'))).toBe(true) }) +}) - it('opens a locked exit when required item is in inventory', () => { - // Locked-exit-with-key happy path is covered by the playthrough integration - // test in Task 8. The sample world above doesn't have an unlocked path to - // pick up the brass key without first traversing the locked door, so this - // test is intentionally a placeholder. - expect(true).toBe(true) +describe('locked exits', () => { + function makeWorld(): World { + return { + startingRoom: 'antechamber', + startingInventory: [], + rooms: { + antechamber: { + id: 'antechamber', + title: '[ Antechamber ]', + descriptions: { firstVisit: '.', revisit: '.', examined: '.' }, + exits: { n: 'vault' }, + lockedExits: { n: { requires: 'rusted-key', lockedNarration: 'The door is locked.' } }, + items: ['rusted-key'], + }, + vault: { + id: 'vault', + title: '[ Vault ]', + descriptions: { firstVisit: 'You are inside.', revisit: '.', examined: '.' }, + exits: {}, + items: [], + }, + }, + items: { + 'rusted-key': { id: 'rusted-key', names: ['rusted key', 'key'], short: 'a rusted key', long: '.', initialState: {}, takeable: true }, + }, + encounters: {}, + endings: { true: { whenFlags: {}, narration: '' }, wrong: { whenFlags: {}, narration: '' }, bad: { whenFlags: {}, narration: '' } }, + } + } + + it('blocks movement without the key', () => { + const world = makeWorld() + const state = initialStateFor(world) + const result = dispatch(state, { kind: 'go', direction: 'n' }, world) + expect(result.appended.at(-1)?.text).toBe('The door is locked.') + expect(result.state.location).toBe('antechamber') + }) + + it('permits movement once the key is in inventory', () => { + const world = makeWorld() + let state = initialStateFor(world) + state = { ...state, inventory: [{ id: 'rusted-key', state: {} }] } + const result = dispatch(state, { kind: 'go', direction: 'n' }, world) + expect(result.state.location).toBe('vault') + }) + + it('does not consume the key on passage', () => { + const world = makeWorld() + let state = initialStateFor(world) + state = { ...state, inventory: [{ id: 'rusted-key', state: {} }] } + const result = dispatch(state, { kind: 'go', direction: 'n' }, world) + expect(result.state.inventory.find((i) => i.id === 'rusted-key')).toBeDefined() }) })