import assert from 'node:assert/strict'; import test from 'node:test'; import { buildMindspaceJobExperiencePayload, formatExperienceInjectionBlock, formatExperienceInjectionLine, mapExperienceRow, normalizeExperienceRecordInput, serializeExperienceJson, } from './experience-schema.mjs'; test('normalizeExperienceRecordInput dedupes tags and defaults kind/status', () => { const normalized = normalizeExperienceRecordInput({ title: ' 标题 ', body: '正文', tags: ['a', 'a', ' b ', ''], kind: 'TASK_OUTCOME', status: 'ACTIVE', }); assert.equal(normalized.kind, 'task_outcome'); assert.equal(normalized.status, 'active'); assert.deepEqual(normalized.tags, ['a', 'b']); }); test('normalizeExperienceRecordInput clamps confidence and validates result', () => { const normalized = normalizeExperienceRecordInput({ title: 't', body: 'b', confidence: 1.5, result: 'SUCCESS', }); assert.equal(normalized.confidence, 1); assert.equal(normalized.result, 'success'); }); test('serializeExperienceJson accepts objects and valid JSON strings', () => { assert.equal(serializeExperienceJson({ a: 1 }), '{"a":1}'); assert.equal(serializeExperienceJson('{"a":1}'), '{"a":1}'); assert.equal(serializeExperienceJson(''), null); }); test('mapExperienceRow parses JSON columns', () => { const mapped = mapExperienceRow({ id: 'id-1', scope: 'global', kind: 'lesson', title: 't', body: 'b', tags_json: '["x"]', use_count: 2, environment_json: '{"runtime":"portal"}', evidence_json: '{"sources":[]}', status: 'active', created_at: 1, updated_at: 2, }); assert.deepEqual(mapped.tags, ['x']); assert.deepEqual(mapped.environment, { runtime: 'portal' }); assert.equal(mapped.useCount, 2); }); test('formatExperienceInjectionLine includes structured hints', () => { const line = formatExperienceInjectionLine({ title: '部署', body: '摘要', problem: '公网访问失败', result: 'success', confidence: 0.9, environment: { runtime: '103', components: ['caddy', 'portal'] }, }); assert.match(line, /部署: 摘要/); assert.match(line, /问题: 公网访问失败/); assert.match(line, /结果: success, conf=0.9/); assert.match(line, /环境: 103 · caddy \+ portal/); }); test('formatExperienceInjectionBlock wraps lines with header', () => { const block = formatExperienceInjectionBlock([ { title: 'A', body: 'one' }, { title: 'B', body: 'two' }, ]); assert.match(block, /^# 相关经验/); assert.match(block, /- A: one/); assert.match(block, /- B: two/); }); test('buildMindspaceJobExperiencePayload maps failed jobs to failure result', () => { const payload = buildMindspaceJobExperiencePayload({ claim: { instruction: '生成页面', userId: 'u1' }, sessionId: 's1', parsed: { title: '页面标题', summary: '完成摘要', status: 'failed' }, jobId: 'job-42', }); assert.equal(payload.kind, 'task_outcome'); assert.equal(payload.result, 'failure'); assert.equal(payload.problem, '生成页面'); assert.equal(payload.sourceSessionId, 's1'); assert.equal(payload.evidence.provenance.job_id, 'job-42'); }); test('buildMindspaceJobExperiencePayload returns null when title or summary missing', () => { assert.equal( buildMindspaceJobExperiencePayload({ claim: { instruction: 'x' }, sessionId: 's', parsed: { title: '', summary: 'ok' }, }), null, ); });