2026-05-01 20:39:09 +08:00
import { describe , expect , it } from 'vitest'
2026-04-28 22:07:21 +08:00
2026-05-01 20:39:09 +08:00
import { __yuanbaoTestUtils } from './yuanbao'
2026-04-28 22:07:21 +08:00
const {
2026-04-30 17:12:18 +08:00
appendUniqueSourceItems ,
buildOrderedDomSourceItems ,
2026-04-28 22:07:21 +08:00
buildSourceItem ,
extractCitationMarkerIndexes ,
2026-04-30 17:12:18 +08:00
extractQuestionText ,
findUnresolvedCitationIndexes ,
2026-04-28 22:07:21 +08:00
mergeText ,
normalizeSourceUrl ,
2026-04-30 17:12:18 +08:00
parseYuanbaoCaptures ,
2026-04-28 22:07:21 +08:00
resolveCitationsFromMarkers ,
2026-05-01 20:39:09 +08:00
} = __yuanbaoTestUtils
describe ( 'yuanbao adapter helpers' , ( ) = > {
it ( 'unwraps redirect URLs and reads Yuanbao document URL fields' , ( ) = > {
expect (
normalizeSourceUrl (
'https://yuanbao.tencent.com/link?target=https%3A%2F%2Fexample.com%2Fa%23ref' ,
) ,
) . toBe ( 'https://example.com/a' )
expect (
buildSourceItem ( {
docUrl : 'https://example.com/doc?from=yuanbao#source' ,
title : '引用文章' ,
siteName : 'Example' ,
} ) ,
) . toMatchObject ( {
url : 'https://example.com/doc?from=yuanbao' ,
normalized_url : 'https://example.com/doc?from=yuanbao' ,
title : '引用文章' ,
site_name : 'Example' ,
host : 'example.com' ,
} )
} )
it ( 'rejects browser and build diagnostics as monitoring questions' , ( ) = > {
expect ( extractQuestionText ( { question_text : '合肥全屋定制推荐' } ) ) . toBe ( '合肥全屋定制推荐' )
expect ( ( ) = >
extractQuestionText ( {
question_text :
"The resource http://localhost:8000/static/js/runtime.c632b2fe.js was preloaded using link preload but not used within a few seconds from the window's load event." ,
} ) ,
) . toThrow ( 'yuanbao_question_text_runtime_diagnostic' )
expect ( ( ) = >
extractQuestionText ( {
question_text :
'failed to solve: process "/bin/sh -c go build" did not complete successfully: exit code: 1' ,
} ) ,
) . toThrow ( 'yuanbao_question_text_runtime_diagnostic' )
} )
it ( 'merges streaming answer fragments without duplicating a repeated prefix' , ( ) = > {
const first =
'通过三轮搜索,我已经掌握了合肥全屋定制市场的高性价比品牌。一定要合同中的定标准确和责任。'
const repeated =
'通过三轮搜索,我已经掌握了合肥全屋定制市场的高性价比品牌。结合你的预算,我建议优先看本土老牌。'
expect ( mergeText ( first , repeated ) ) . toBe ( repeated )
} )
it ( 'resolves citation markers against the best available source pool' , ( ) = > {
2026-04-28 22:07:21 +08:00
const sources = [
2026-05-01 20:39:09 +08:00
{ url : 'https://example.com/1' , normalized_url : 'https://example.com/1' } ,
{ url : 'https://example.com/2' , normalized_url : 'https://example.com/2' } ,
{ url : 'https://example.com/3' , normalized_url : 'https://example.com/3' } ,
]
expect ( extractCitationMarkerIndexes ( '正文 [citation:1] 和 [citation:3]' ) ) . toEqual ( [ 1 , 3 ] )
expect (
resolveCitationsFromMarkers ( '正文 [citation:1] 和 [citation:3]' , [ [ ] , sources ] ) . map (
( item ) = > item . url ,
) ,
) . toEqual ( [ 'https://example.com/1' , 'https://example.com/3' ] )
} )
it ( 'preserves indexed DOM citation slots even when URLs repeat' , ( ) = > {
2026-04-30 17:12:18 +08:00
const domSources = buildOrderedDomSourceItems ( [
{
2026-05-01 20:39:09 +08:00
url : 'https://example.com/repeated' ,
title : '来源 1' ,
2026-04-30 17:12:18 +08:00
text : null ,
2026-05-01 20:39:09 +08:00
siteName : 'Example' ,
2026-04-30 17:12:18 +08:00
sourceIndex : 1 ,
} ,
{
2026-05-01 20:39:09 +08:00
url : 'https://example.com/repeated' ,
title : '来源 2' ,
2026-04-30 17:12:18 +08:00
text : null ,
2026-05-01 20:39:09 +08:00
siteName : 'Example' ,
2026-04-30 17:12:18 +08:00
sourceIndex : 2 ,
} ,
{
2026-05-01 20:39:09 +08:00
url : 'https://example.com/third' ,
title : '来源 3' ,
2026-04-30 17:12:18 +08:00
text : null ,
2026-05-01 20:39:09 +08:00
siteName : 'Example' ,
2026-04-30 17:12:18 +08:00
sourceIndex : 3 ,
} ,
2026-05-01 20:39:09 +08:00
] )
2026-04-30 17:12:18 +08:00
2026-05-01 20:39:09 +08:00
expect ( domSources ) . toHaveLength ( 3 )
expect ( domSources . map ( ( item ) = > item . title ) ) . toEqual ( [ '来源 1' , '来源 2' , '来源 3' ] )
expect ( findUnresolvedCitationIndexes ( '正文 [citation:2] [citation:3]' , domSources ) ) . toEqual ( [ ] )
} )
2026-04-30 17:12:18 +08:00
2026-05-01 20:39:09 +08:00
it ( 'keeps primary DOM citation order when appending parsed extras' , ( ) = > {
2026-04-30 17:12:18 +08:00
const primary = [
2026-05-01 20:39:09 +08:00
{ url : 'https://example.com/1' , normalized_url : 'https://example.com/1' } ,
{ url : 'https://example.com/2' , normalized_url : 'https://example.com/2' } ,
]
2026-04-30 17:12:18 +08:00
const appended = appendUniqueSourceItems ( primary , [
2026-05-01 20:39:09 +08:00
{ url : 'https://example.com/2' , normalized_url : 'https://example.com/2' , title : 'duplicate' } ,
{ url : 'https://example.com/3' , normalized_url : 'https://example.com/3' } ,
] )
2026-04-30 17:12:18 +08:00
expect ( appended . map ( ( item ) = > item . url ) ) . toEqual ( [
2026-05-01 20:39:09 +08:00
'https://example.com/1' ,
'https://example.com/2' ,
'https://example.com/3' ,
] )
} )
2026-04-30 17:12:18 +08:00
2026-05-01 20:39:09 +08:00
it ( 'does not report incomplete citations when the answer has no markers' , ( ) = > {
expect ( findUnresolvedCitationIndexes ( '正文没有引用小图标' , [ ] ) ) . toEqual ( [ ] )
} )
2026-04-30 17:12:18 +08:00
2026-05-01 20:39:09 +08:00
it ( 'collects source candidates from deep search thinking fields' , ( ) = > {
2026-04-30 17:12:18 +08:00
const summary = parseYuanbaoCaptures ( [
{
2026-05-01 20:39:09 +08:00
url : 'https://yuanbao.tencent.com/api/chat' ,
2026-04-30 17:12:18 +08:00
status : 200 ,
2026-05-01 20:39:09 +08:00
contentType : 'text/event-stream' ,
2026-04-30 17:12:18 +08:00
body : [
2026-05-01 20:39:09 +08:00
'data: {"type":"deepSearch","contents":[{"type":"step","webPages":[{"targetUrl":"https://example.com/a#ref","title":"来源 A","siteName":"Example"}],"references":[{"sourceUrl":"https://example.com/b","title":"来源 B"}]}]}' ,
'data: {"type":"text","msg":"正文 [citation:1] [citation:2]"}' ,
] . join ( '\n\n' ) ,
2026-04-30 17:12:18 +08:00
} ,
2026-05-01 20:39:09 +08:00
] )
expect ( summary . searchResults . map ( ( item ) = > item . url ) ) . toContain ( 'https://example.com/a' )
expect ( summary . citations . map ( ( item ) = > item . url ) ) . toContain ( 'https://example.com/b' )
expect (
resolveCitationsFromMarkers ( summary . answer , [
summary . citations ,
summary . searchResults ,
[ . . . summary . citations , . . . summary . searchResults ] ,
] ) . map ( ( item ) = > item . url ) ,
) . toEqual ( [ 'https://example.com/b' , 'https://example.com/a' ] )
} )
} )