Files
lingxi-web/server/test/business.test.js
T

112 lines
4.4 KiB
JavaScript

import test from 'node:test'
import assert from 'node:assert/strict'
import {
countBy,
dateKey,
normalizeBusinessRelationship,
percent,
telegramAttachment,
} from '../src/business.js'
import { decryptEmailPassword, encryptEmailPassword } from '../src/email.js'
import {
HUMAN_REPLY_SYSTEM_PROMPT,
buildHumanReplyUserPrompt,
replySuggestionIssues,
replyToneInstruction,
} from '../src/reply-suggestions.js'
test('Telegram 图片、文档、语音和贴纸映射为可下载文件', () => {
assert.deepEqual(
telegramAttachment({
'@type': 'messagePhoto',
photo: { sizes: [{ photo: { id: 11, size: 2048 } }] },
}),
{
telegramFileId: 11,
attachmentName: 'telegram-photo-11.jpg',
attachmentMimeType: 'image/jpeg',
attachmentSize: 2048,
},
)
assert.equal(telegramAttachment({
'@type': 'messageDocument',
document: { file_name: 'quote.pdf', mime_type: 'application/pdf', document: { id: 12, size: 4096 } },
}).attachmentMimeType, 'application/pdf')
assert.equal(telegramAttachment({
'@type': 'messageVoiceNote',
voice_note: { duration: 8, voice: { id: 13, expected_size: 1024 } },
}).duration, 8)
assert.equal(telegramAttachment({
'@type': 'messageSticker',
sticker: { emoji: '👍', format: { '@type': 'stickerFormatWebp' }, sticker: { id: 14, size: 512 } },
}).attachmentMimeType, 'image/webp')
})
test('业务指标工具处理百分比、分组和无效日期', () => {
assert.equal(percent(3, 8), 37.5)
assert.equal(percent(1, 0), 0)
assert.equal(dateKey('invalid'), '')
assert.equal(dateKey('2026-07-27T12:30:00Z'), '2026-07-27')
assert.deepEqual(
Object.fromEntries(countBy([{ stage: 'new' }, { stage: 'new' }, { stage: 'won' }], 'stage')),
{ new: 2, won: 1 },
)
})
test('业务关系分类使用受控类型和中文标签', () => {
assert.deepEqual(normalizeBusinessRelationship('supplier'), { type: 'supplier', label: '上游供应商' })
assert.deepEqual(normalizeBusinessRelationship('unsupported'), { type: 'unknown', label: '待确认' })
})
test('Email 渠道凭据使用认证加密且可正确解密', () => {
process.env.EMAIL_CREDENTIAL_KEY = 'lingxi-email-test-key-with-more-than-32-bytes'
const encrypted = encryptEmailPassword('app-password-123')
assert.notEqual(encrypted, 'app-password-123')
assert.match(encrypted, /^v1:/)
assert.equal(decryptEmailPassword(encrypted), 'app-password-123')
const tampered = encrypted.split(':')
tampered[2] = `${tampered[2][0] === 'A' ? 'B' : 'A'}${tampered[2].slice(1)}`
assert.throws(() => decryptEmailPassword(tampered.join(':')))
})
test('回复建议提示词突出最新消息、避免重复并遵循企业语气', () => {
const prompt = buildHumanReplyUserPrompt({
targetInstruction: '使用西班牙语',
tone: 'friendly',
latestCustomerMessage: '¿Cuándo pueden enviarlo?',
latestAgentMessage: 'I will check it.',
conversation: '客户: ¿Cuándo pueden enviarlo?',
conversationMemory: '客户关注交期',
knowledgeContext: [],
})
assert.match(HUMAN_REPLY_SYSTEM_PROMPT, /不是写客服范文/)
assert.match(HUMAN_REPLY_SYSTEM_PROMPT, /不要重新问候/)
assert.match(prompt, /¿Cuándo pueden enviarlo\?/)
assert.match(prompt, /坐席上一条回复(避免重复)/)
assert.match(prompt, /长期合作伙伴/)
assert.match(replyToneInstruction('concise'), /简洁直接/)
})
test('回复建议质量检查拦截模板话术和同质草稿', () => {
const valid = [
{ style: 'stable', content: 'I can confirm the dispatch date with our warehouse and update you once I have it.' },
{ style: 'active', content: "Let me check the warehouse schedule and come back to you with the exact date." },
{ style: 'brief', content: "I'll verify the dispatch date and get back to you." },
]
assert.deepEqual(replySuggestionIssues(valid), [])
const invalid = valid.map((item) => ({ ...item }))
invalid[0].content = 'Dear customer, please rest assured that we value your feedback.'
invalid[2] = { style: 'active', content: invalid[1].content }
const issues = replySuggestionIssues(invalid)
assert.ok(issues.some((item) => item.includes('模板化')))
assert.ok(issues.some((item) => item.includes('缺少 brief')))
assert.ok(issues.some((item) => item.includes('过于相似')))
assert.ok(replySuggestionIssues([
...valid.slice(0, 2),
{ style: 'brief', content: "I'll confirm it today." },
]).some((item) => item.includes('时间承诺')))
})