import test from 'node:test'; import assert from 'node:assert/strict'; import { buildCanonicalTranscript } from '../src/canonical-transcript.mjs'; import { ChatVoicePersistence } from '../src/chatvoice-persistence.mjs'; test('canonical 字数和哈希规则与故事素材校验一致', () => { const canonical = buildCanonicalTranscript( '第一段。 \n\n\n第二段。', [{ text: '不完整分段', speakerId: '1', startMs: 0, endMs: 1000 }], '2026-08-26T00:00:00.000Z', ); assert.equal(canonical.transcription, '第一段。\n\n第二段。'); assert.equal(canonical.transcriptionCharCount, 8); assert.equal(canonical.transcriptionSegments.length, 1); assert.equal(canonical.transcriptionSegments[0].synthetic, true); assert.match(canonical.transcriptionHash, /^[0-9a-f]{64}$/); }); test('服务端写入 ChatVoice 后必须通过 hash 和字数回读', async () => { let stored = null; const fetchImpl = async (_url, options = {}) => { if (options.method === 'PUT') { stored = JSON.parse(options.body); return { ok: true, status: 200, async json() { return { updatedAt: 'now' }; } }; } return { ok: true, status: 200, async json() { return stored || { objectId: 'voice-1' }; }, }; }; const persistence = new ChatVoicePersistence({ canonicalPersistenceEnabled: true, parseMasterKey: 'test-master-key', parseServerUrl: 'https://parse.example.test/parse', parseAppId: 'test-app', }, fetchImpl); await persistence.validateAccess('voice-1', 'Bearer session-token'); const result = await persistence.commit({ request: { chatVoiceId: 'voice-1' }, }, { text: '完整逐字稿', segments: [{ text: '完整逐字稿', speakerId: '1', startMs: 0, endMs: 1000 }], }); assert.equal(result.persisted, true); assert.equal(stored.transcriptionStatus, 'completed'); assert.equal(stored.transcriptionCompleteness, 'long_complete'); assert.equal(stored.transcriptionRevalidationStatus, 'completed'); });