import { describe, it, expect, beforeEach } from 'vitest'; import { appRouter } from './index'; describe('tRPC API Layer - Character System', () => { let ctx: { userId: string }; let projectId: string; beforeEach(async () => { ctx = { userId: '123e4567-e89b-12d3-a456-426614174000' }; const project = await appRouter.project.createProject.mutate({ input: { name: 'Character System Test Project' }, ctx, }); projectId = project.id; }); describe('createCharacter', () => { it('should create a character with all profile fields', async () => { const character = await appRouter.project.createCharacter.mutate({ input: { name: 'John Doe', bio: 'A brave hero', role: 'protagonist', arc: 'Grows from coward to leader', arcType: 'positive', age: 30, gender: 'male', voice: 'Deep, commanding', traits: 'Brave, loyal, stubborn', motivation: 'Protect his family', conflict: 'Internal fear of failure', secret: 'Afraid of heights', projectId, }, ctx, }); expect(character).toMatchObject({ name: 'John Doe', bio: 'A brave hero', role: 'protagonist', arcType: 'positive', age: 30, projectId, }); expect(character.slug).toBe('john-doe'); }); it('should default role to supporting when not provided', async () => { const character = await appRouter.project.createCharacter.mutate({ input: { name: 'Jane Smith', projectId, }, ctx, }); expect(character.role).toBe('supporting'); }); }); describe('updateCharacter', () => { it('should update character profile fields', async () => { const created = await appRouter.project.createCharacter.mutate({ input: { name: 'Original', projectId }, ctx, }); const updated = await appRouter.project.updateCharacter.mutate({ input: { id: created.id, name: 'Updated Name', bio: 'New bio', role: 'antagonist', }, ctx, }); expect(updated.name).toBe('Updated Name'); expect(updated.slug).toBe('updated-name'); expect(updated.bio).toBe('New bio'); expect(updated.role).toBe('antagonist'); }); }); describe('searchCharacters', () => { it('should filter characters by query', async () => { await appRouter.project.createCharacter.mutate({ input: { name: 'Alice', bio: 'The hero', projectId }, ctx, }); await appRouter.project.createCharacter.mutate({ input: { name: 'Bob', bio: 'The villain', projectId }, ctx, }); const results = await appRouter.project.searchCharacters.query({ input: { projectId, query: 'hero' }, ctx, }); expect(results.length).toBe(1); expect(results[0].name).toBe('Alice'); }); it('should filter characters by role', async () => { await appRouter.project.createCharacter.mutate({ input: { name: 'Protag', role: 'protagonist', projectId }, ctx, }); await appRouter.project.createCharacter.mutate({ input: { name: 'Antag', role: 'antagonist', projectId }, ctx, }); const results = await appRouter.project.searchCharacters.query({ input: { projectId, role: 'protagonist' }, ctx, }); expect(results.length).toBe(1); expect(results[0].name).toBe('Protag'); }); }); describe('createRelationship', () => { it('should create a relationship between two characters', async () => { const charA = await appRouter.project.createCharacter.mutate({ input: { name: 'Character A', projectId }, ctx, }); const charB = await appRouter.project.createCharacter.mutate({ input: { name: 'Character B', projectId }, ctx, }); const rel = await appRouter.project.createRelationship.mutate({ input: { characterIdA: charA.id, characterIdB: charB.id, relationshipType: 'friendship', strength: 80, isAntagonistic: false, }, ctx, }); expect(rel.characterIdA).toBe(charA.id); expect(rel.characterIdB).toBe(charB.id); expect(rel.relationshipType).toBe('friendship'); expect(rel.strength).toBe(80); }); it('should prevent self-relationships', async () => { const charA = await appRouter.project.createCharacter.mutate({ input: { name: 'Character A', projectId }, ctx, }); await expect( appRouter.project.createRelationship.mutate({ input: { characterIdA: charA.id, characterIdB: charA.id, relationshipType: 'friendship', }, ctx, }) ).rejects.toThrow('Cannot create a relationship with the same character'); }); it('should prevent duplicate relationships', async () => { const charA = await appRouter.project.createCharacter.mutate({ input: { name: 'Character A', projectId }, ctx, }); const charB = await appRouter.project.createCharacter.mutate({ input: { name: 'Character B', projectId }, ctx, }); await appRouter.project.createRelationship.mutate({ input: { characterIdA: charA.id, characterIdB: charB.id, relationshipType: 'friendship', }, ctx, }); await expect( appRouter.project.createRelationship.mutate({ input: { characterIdA: charA.id, characterIdB: charB.id, relationshipType: 'rivalry', }, ctx, }) ).rejects.toThrow('Relationship already exists between these characters'); }); }); describe('deleteCharacter', () => { it('should remove associated relationships when deleting a character', async () => { const charA = await appRouter.project.createCharacter.mutate({ input: { name: 'Character A', projectId }, ctx, }); const charB = await appRouter.project.createCharacter.mutate({ input: { name: 'Character B', projectId }, ctx, }); await appRouter.project.createRelationship.mutate({ input: { characterIdA: charA.id, characterIdB: charB.id, relationshipType: 'friendship', }, ctx, }); await appRouter.project.deleteCharacter.mutate({ input: { id: charA.id }, ctx, }); const rels = await appRouter.project.getRelationshipsForCharacter.query({ input: { characterId: charB.id }, ctx, }); expect(rels.length).toBe(0); }); }); describe('getCharacterStats', () => { it('should return stats for a character', async () => { const charA = await appRouter.project.createCharacter.mutate({ input: { name: 'TestChar', projectId }, ctx, }); const stats = await appRouter.project.getCharacterStats.query({ input: { characterId: charA.id }, ctx, }); expect(stats.characterId).toBe(charA.id); expect(stats.sceneCount).toBe(0); expect(stats.totalDialogueLines).toBe(0); expect(stats.relationshipCount).toBe(0); }); }); });