Files
bilin/tests/main/character-graph.test.ts
T
bing 4adafa1936 feat: ship v1.0.0 with character graph and writing analytics
Add P7 Cytoscape relationship graph with setting CRUD, P9 cockpit analytics driven by writing_sessions, chapter POV selection, and full test coverage.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-08 10:48:31 +08:00

74 lines
2.8 KiB
TypeScript

import { describe, it, expect, beforeEach, afterEach } from 'vitest'
import { mkdtempSync, rmSync } from 'fs'
import { join } from 'path'
import { tmpdir } from 'os'
import { randomUUID } from 'crypto'
import { BookRegistryService } from '../../src/main/services/book-registry'
import { CharacterGraphService } from '../../src/main/services/character-graph.service'
import { SettingRepository } from '../../src/main/db/repositories/setting.repo'
import { closeAllBookDbs } from '../../src/main/db/connection'
describe('CharacterGraphService', () => {
let dir: string
let registry: BookRegistryService
let bookId: string
let charA: string
let charB: string
let charC: string
beforeEach(() => {
dir = mkdtempSync(join(tmpdir(), 'bilin-graph-'))
registry = new BookRegistryService(dir)
const meta = registry.create({ name: '图谱书', category: '测试', createSampleChapter: false })
bookId = meta.id
const repo = new SettingRepository(registry.getDb(bookId))
charA = repo.create('character', '角色A').id
charB = repo.create('character', '角色B').id
charC = repo.create('character', '角色C').id
})
afterEach(() => {
closeAllBookDbs()
rmSync(dir, { recursive: true, force: true })
})
it('UT-GRAPH-01: getData returns nodes and edges', () => {
const svc = new CharacterGraphService(registry)
svc.upsertRelationship(bookId, charA, { targetId: charB, label: '朋友', intimacy: 3 })
const data = svc.getData(bookId)
expect(data.nodes.length).toBe(3)
expect(data.edges).toHaveLength(1)
expect(data.edges[0].label).toBe('朋友')
})
it('UT-GRAPH-02: bidirectional edges dedupe to max intimacy', () => {
const svc = new CharacterGraphService(registry)
svc.upsertRelationship(bookId, charA, { targetId: charB, label: '同事', intimacy: 2 })
svc.upsertRelationship(bookId, charB, { targetId: charA, label: '挚友', intimacy: 4 })
const data = svc.getData(bookId)
expect(data.edges).toHaveLength(1)
expect(data.edges[0].intimacy).toBe(4)
})
it('UT-GRAPH-03: connected filter hides isolated nodes', () => {
const svc = new CharacterGraphService(registry)
svc.upsertRelationship(bookId, charA, { targetId: charB, label: '师徒', intimacy: 5 })
const data = svc.getData(bookId, 'connected')
expect(data.nodes).toHaveLength(2)
expect(data.nodes.some((n) => n.id === charC)).toBe(false)
})
it('IT-GRAPH-01: upsert and delete relationship', () => {
const svc = new CharacterGraphService(registry)
const rel = svc.upsertRelationship(bookId, charA, {
id: randomUUID(),
targetId: charB,
label: '恋人',
intimacy: 5
})
expect(svc.getData(bookId).edges).toHaveLength(1)
svc.deleteRelationship(bookId, charA, rel.id)
expect(svc.getData(bookId).edges).toHaveLength(0)
})
})