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>
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
import { describe, it, expect, afterEach, vi, beforeEach } from 'vitest'
|
||||
import { randomUUID } from 'crypto'
|
||||
import { DatabaseSync } from 'node:sqlite'
|
||||
import { mkdtempSync, rmSync } from 'fs'
|
||||
import { join } from 'path'
|
||||
import { tmpdir } from 'os'
|
||||
import { WritingLogRepository } from '../../src/main/db/repositories/writing-log.repo'
|
||||
import { WritingSessionRepository } from '../../src/main/db/repositories/writing-session.repo'
|
||||
import { WritingSessionService } from '../../src/main/services/writing-session.service'
|
||||
|
||||
describe('WritingSessionRepository', () => {
|
||||
let userDir: string
|
||||
let logRepo: WritingLogRepository
|
||||
let repo: WritingSessionRepository
|
||||
|
||||
afterEach(() => {
|
||||
logRepo?.close()
|
||||
if (userDir) rmSync(userDir, { recursive: true, force: true })
|
||||
})
|
||||
|
||||
it('UT-SESSION-03: insert and updateActivity accumulates word_delta', () => {
|
||||
userDir = mkdtempSync(join(tmpdir(), 'bilin-session-'))
|
||||
logRepo = new WritingLogRepository(userDir)
|
||||
repo = new WritingSessionRepository(logRepo.getDb())
|
||||
const id = randomUUID()
|
||||
repo.insert({
|
||||
id,
|
||||
bookId: 'b1',
|
||||
chapterId: 'c1',
|
||||
startedAt: '2026-07-08T10:00:00.000Z',
|
||||
endedAt: '2026-07-08T10:05:00.000Z',
|
||||
wordDelta: 50,
|
||||
date: '2026-07-08'
|
||||
})
|
||||
repo.updateActivity(id, '2026-07-08T10:10:00.000Z', 30)
|
||||
expect(repo.getById(id)!.wordDelta).toBe(80)
|
||||
})
|
||||
})
|
||||
|
||||
describe('WritingSessionService', () => {
|
||||
let userDir: string
|
||||
let logRepo: WritingLogRepository
|
||||
let repo: WritingSessionRepository
|
||||
let service: WritingSessionService
|
||||
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers()
|
||||
vi.setSystemTime(new Date('2026-07-08T10:00:00.000Z'))
|
||||
userDir = mkdtempSync(join(tmpdir(), 'bilin-session-svc-'))
|
||||
logRepo = new WritingLogRepository(userDir)
|
||||
repo = new WritingSessionRepository(logRepo.getDb())
|
||||
service = new WritingSessionService(repo)
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers()
|
||||
logRepo?.close()
|
||||
if (userDir) rmSync(userDir, { recursive: true, force: true })
|
||||
})
|
||||
|
||||
it('UT-SESSION-01: within 5min merges into one session', () => {
|
||||
service.recordActivity('b1', 'c1', 50)
|
||||
vi.advanceTimersByTime(2 * 60 * 1000)
|
||||
service.recordActivity('b1', 'c1', 30)
|
||||
const rows = repo.listForBookDate('b1', '2026-07-08')
|
||||
expect(rows).toHaveLength(1)
|
||||
expect(rows[0].wordDelta).toBe(80)
|
||||
})
|
||||
|
||||
it('UT-SESSION-02: after 6min creates two sessions', () => {
|
||||
service.recordActivity('b1', 'c1', 50)
|
||||
vi.advanceTimersByTime(6 * 60 * 1000)
|
||||
service.recordActivity('b1', 'c1', 30)
|
||||
const rows = repo.listForBookDate('b1', '2026-07-08')
|
||||
expect(rows).toHaveLength(2)
|
||||
expect(rows[0].wordDelta).toBe(50)
|
||||
expect(rows[1].wordDelta).toBe(30)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user