1- import { describe , expect , test } from "vite-plus/test" ;
2- import { isDashScopeE2EReady , parseStdoutJson , runCommandHelp , runCommandE2e } from "./helpers.ts" ;
1+ import { afterEach , beforeEach , describe , expect , test , vi } from "vite-plus/test" ;
2+ import * as core from "bailian-cli-core" ;
3+ import {
4+ isDashScopeE2EReady ,
5+ parseStdoutJson ,
6+ runCommandHelp ,
7+ runCommandE2e ,
8+ runCommandInProcess ,
9+ } from "./helpers.ts" ;
10+ import { E2E_HARNESS_IDENTITY } from "./harness/identity.ts" ;
311import { ADVISOR_ROUTES } from "./topic-routes.ts" ;
412
513describe ( "e2e: advisor recommend" , ( ) => {
@@ -14,6 +22,112 @@ describe("e2e: advisor recommend", () => {
1422 } ) ;
1523} ) ;
1624
25+ describe ( "e2e: advisor recommend 传输(离线)" , ( ) => {
26+ beforeEach ( ( ) => {
27+ // 隔离目录、同步与原生网络,回归不读取真实目录或调用付费模型。
28+ vi . spyOn ( core , "maybeSyncWikiData" ) . mockResolvedValue ( false ) ;
29+ vi . spyOn ( core , "loadCatalog" ) . mockResolvedValue ( [ ] ) ;
30+ vi . stubGlobal ( "fetch" , vi . fn ( ) . mockRejectedValue ( new Error ( "禁止原生网络请求" ) ) ) ;
31+ } ) ;
32+ afterEach ( ( ) => {
33+ vi . restoreAllMocks ( ) ;
34+ vi . unstubAllGlobals ( ) ;
35+ } ) ;
36+
37+ test . each ( [ true , false ] ) (
38+ "真实命令注入统一传输且默认关闭 thinking,verbose=%s" ,
39+ async ( verbose ) => {
40+ const endpoint = "https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions" ;
41+ const requests : RequestInit [ ] = [ ] ;
42+ vi . spyOn ( core . httpTransport , "fetch" ) . mockImplementation ( async ( input , init ) => {
43+ expect ( input ) . toBe ( endpoint ) ;
44+ requests . push ( init ?? { } ) ;
45+ const body = JSON . parse ( init ?. body as string ) ;
46+ const content = JSON . stringify (
47+ requests . length === 1
48+ ? {
49+ taskSummary : "文本生成" ,
50+ inputModality : [ "Text" ] ,
51+ outputModality : [ "Text" ] ,
52+ requiredCapabilities : [ "TG" ] ,
53+ matchedScenes : [ ] ,
54+ sceneConfidence : 1 ,
55+ }
56+ : { scores : [ ] } ,
57+ ) ;
58+ const headers = { "x-request-id" : "advisor-success-request" } ;
59+ return body . stream
60+ ? new Response (
61+ `data: ${ JSON . stringify ( { choices : [ { delta : { content } } ] } ) } \n\ndata: [DONE]\n\n` ,
62+ { headers } ,
63+ )
64+ : Response . json ( { choices : [ { message : { content } } ] } , { headers } ) ;
65+ } ) ;
66+ const { stdout, stderr, exitCode } = await runCommandInProcess (
67+ ADVISOR_ROUTES ,
68+ [
69+ "advisor" ,
70+ "recommend" ,
71+ "--message" ,
72+ "文本生成" ,
73+ "--no-agent" ,
74+ ...( verbose ? [ "--verbose" ] : [ "--no-thinking" ] ) ,
75+ ] ,
76+ {
77+ DASHSCOPE_API_KEY : "test-advisor-secret-key" ,
78+ DASHSCOPE_BASE_URL : "https://dashscope.aliyuncs.com" ,
79+ DO_NOT_TRACK : "1" ,
80+ NODE_OPTIONS : "" ,
81+ HTTP_PROXY : "" ,
82+ HTTPS_PROXY : "" ,
83+ http_proxy : "" ,
84+ https_proxy : "" ,
85+ } ,
86+ ) ;
87+ expect ( exitCode , stderr ) . toBe ( 0 ) ;
88+ expect ( requests ) . toHaveLength ( 2 ) ;
89+ for ( const request of requests ) {
90+ const headers = new Headers ( request . headers ) ;
91+ expect ( headers . get ( "authorization" ) ) . toBe ( "Bearer test-advisor-secret-key" ) ;
92+ expect ( headers . get ( "user-agent" ) ) . toBe (
93+ `${ E2E_HARNESS_IDENTITY . clientName } /${ E2E_HARNESS_IDENTITY . version } ` ,
94+ ) ;
95+ expect ( headers . get ( "x-dashscope-source-config" ) ) . toBe (
96+ core . sourceConfig ( E2E_HARNESS_IDENTITY ) ,
97+ ) ;
98+ expect ( headers . get ( "x-dashscope-openapisource" ) ) . toBe ( "BailianCLI" ) ;
99+ expect ( headers . get ( "x-dashscope-service" ) ) . toBe ( "bailian-cli" ) ;
100+ expect ( JSON . parse ( request . body as string ) . model ) . toBe ( core . DEFAULT_LLM_MODEL ) ;
101+ }
102+ expect ( JSON . parse ( requests [ 0 ] . body as string ) . enable_thinking ) . toBeUndefined ( ) ;
103+ expect ( JSON . parse ( requests [ 0 ] . body as string ) . stream ) . toBeUndefined ( ) ;
104+ expect ( JSON . parse ( requests [ 1 ] . body as string ) . stream ) . toBeUndefined ( ) ;
105+ expect ( parseStdoutJson < { intent : { taskSummary : string } } > ( stdout ) . intent . taskSummary ) . toBe (
106+ "文本生成" ,
107+ ) ;
108+ expect ( stdout ) . not . toContain ( "request_id:" ) ;
109+ expect ( stderr ) . not . toContain ( "test-advisor-secret-key" ) ;
110+ if ( verbose ) {
111+ const debugLines = stderr
112+ . trim ( )
113+ . split ( "\n" )
114+ . filter ( ( line ) => line . startsWith ( '{"debug":' ) ) ;
115+ expect ( debugLines . length ) . toBeGreaterThanOrEqual ( 4 ) ;
116+ const requestDebug = JSON . parse ( debugLines [ 0 ] ! ) . debug ;
117+ const responseDebug = JSON . parse ( debugLines [ 1 ] ! ) . debug ;
118+ expect ( requestDebug . details ) . toMatchObject ( { method : "POST" , url : endpoint } ) ;
119+ expect ( responseDebug . details ) . toMatchObject ( {
120+ status : 200 ,
121+ request_id : "advisor-success-request" ,
122+ } ) ;
123+ } else {
124+ expect ( stderr ) . not . toContain ( '{"debug":' ) ;
125+ expect ( stderr ) . not . toContain ( "request_id:" ) ;
126+ }
127+ } ,
128+ ) ;
129+ } ) ;
130+
17131describe . skipIf ( ! isDashScopeE2EReady ( ) ) ( "e2e: advisor recommend (DashScope)" , ( ) => {
18132 test ( "advisor recommend without --message errors as usage error (2)" , async ( ) => {
19133 const { stdout, stderr, exitCode } = await runCommandE2e ( ADVISOR_ROUTES , [
0 commit comments