Files
gupi-osint-board/server/narrativeRepository.test.ts
T
gitprovandClaude Opus 4.8 ddb3a386f0 Add story-graph narrative system (campaigns, editors, runtime)
Introduce the narrative layer as a directed story-flow graph: an authored
campaign a player walks node by node, replacing the interim slot/chapter model.

Schema (migrations 015-021):
- mysteries, global NPC templates + named poses, per-user playthroughs
- story_nodes, terminals, utterances (the flow graph and dialogue trees)
- clean cutover: retire slot cutscenes/chapters/seen_dialogue

Runtime:
- New Game creates a playthrough bound to the JWT identity (dev test-user fallback)
- advance() walks the graph cutscene -> dialogue -> level -> ..., auto-skipping gates
- branching dialogue: player choices route out through node terminals

Admin authoring:
- NPC editor: upload named poses to the gupi MinIO bucket
- mystery graph editor: vertical node canvas, wiring, entrypoint, delete-by-click
- dialogue crafter: utterance tree, Tab to add child, 1/2 speaker, undo

Content authored via the manifest importer / admin panel and seeded for Glass
Harbour. MinIO added to the dev stack; dev container runs in development mode.

Also includes a folder-widget simplification (removes open/close) and a
resolveUserId auth helper.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-18 15:37:46 +02:00

22 lines
1.0 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import { resolvePoseAssetId } from './narrativeRepository.js'
describe('resolvePoseAssetId', () => {
const poses = { neutral: 'asset-neutral', concerned: 'asset-concerned', missing: null }
it('returns the requested pose asset when present', () => {
expect(resolvePoseAssetId(poses, 'concerned', 'neutral')).toBe('asset-concerned')
})
it('falls back to the NPC default pose when the requested pose is absent', () => {
expect(resolvePoseAssetId(poses, 'pointing', 'neutral')).toBe('asset-neutral')
})
it('falls back to the default when the requested pose exists but has no artwork', () => {
expect(resolvePoseAssetId(poses, 'missing', 'neutral')).toBe('asset-neutral')
})
it('returns null (no artwork) when neither requested nor default resolves', () => {
expect(resolvePoseAssetId(poses, 'pointing', 'also-missing')).toBeNull()
expect(resolvePoseAssetId(poses, null, null)).toBeNull()
expect(resolvePoseAssetId({}, 'neutral', 'neutral')).toBeNull()
})
})