11import { fileURLToPath } from "node:url" ;
2- import { describe , expect , test } from "vite-plus/test" ;
2+ import { httpTransport } from "bailian-cli-core" ;
3+ import { describe , expect , test , vi } from "vite-plus/test" ;
34import {
45 isDashScopeE2EReady ,
56 parseStdoutJson ,
@@ -22,14 +23,120 @@ const require = createRequire(${JSON.stringify(fileURLToPath(new URL("../../../c
2223require("undici").fetch = globalThis.fetch = async () => { throw new Error("Network disabled for deploy query E2E"); };` ) } `,
2324} ;
2425
26+ const IN_PROCESS_ENV : NodeJS . ProcessEnv = {
27+ ...READ_ONLY_ENV ,
28+ NODE_OPTIONS : "" ,
29+ NODE_USE_ENV_PROXY : "0" ,
30+ HTTP_PROXY : "" ,
31+ HTTPS_PROXY : "" ,
32+ ALL_PROXY : "" ,
33+ http_proxy : "" ,
34+ https_proxy : "" ,
35+ all_proxy : "" ,
36+ DASHSCOPE_API_KEY : "" ,
37+ BAILIAN_API_KEY : "" ,
38+ DASHSCOPE_BASE_URL : "" ,
39+ BAILIAN_BASE_URL : "" ,
40+ BAILIAN_WORKSPACE_ID : "" ,
41+ DASHSCOPE_OUTPUT : "" ,
42+ DASHSCOPE_QUIET : "0" ,
43+ DASHSCOPE_VERBOSE : "0" ,
44+ } ;
45+
46+ async function runOfflineDeploy ( args : string [ ] ) {
47+ // Keep the no-network contract without NODE_OPTIONS forcing every matrix case
48+ // into a new process. Fail even if command code catches a forbidden request.
49+ const transportFetch = vi
50+ . spyOn ( httpTransport , "fetch" )
51+ . mockRejectedValue ( new Error ( "Network disabled for deploy query E2E" ) ) ;
52+ const nativeFetch = vi
53+ . spyOn ( globalThis , "fetch" )
54+ . mockRejectedValue ( new Error ( "Network disabled for deploy query E2E" ) ) ;
55+ try {
56+ const result = await runCommandInProcess ( DEPLOY_ROUTES , args , IN_PROCESS_ENV ) ;
57+ expect ( transportFetch ) . not . toHaveBeenCalled ( ) ;
58+ expect ( nativeFetch ) . not . toHaveBeenCalled ( ) ;
59+ return result ;
60+ } finally {
61+ transportFetch . mockRestore ( ) ;
62+ nativeFetch . mockRestore ( ) ;
63+ }
64+ }
65+
2566function runReadOnlyDeploy ( args : string [ ] ) {
26- return runCommandInProcess (
27- DEPLOY_ROUTES ,
28- [ "deploy" , ...args , "--api-key" , "e2e-dummy-key" , "--dry-run" , "--output" , "json" , "--quiet" ] ,
29- READ_ONLY_ENV ,
30- ) ;
67+ return runOfflineDeploy ( [
68+ "deploy" ,
69+ ...args ,
70+ "--api-key" ,
71+ "e2e-dummy-key" ,
72+ "--dry-run" ,
73+ "--output" ,
74+ "json" ,
75+ "--quiet" ,
76+ ] ) ;
3177}
3278
79+ test . each ( [
80+ {
81+ label : "read-only query preview" ,
82+ args : [
83+ "deploy" ,
84+ "capacity" ,
85+ "list" ,
86+ "--deployed-model" ,
87+ "dep-test" ,
88+ "--dry-run" ,
89+ "--api-key" ,
90+ "e2e-dummy-key" ,
91+ ] ,
92+ exitCode : 0 ,
93+ } ,
94+ {
95+ label : "missing required query flag" ,
96+ args : [
97+ "deploy" ,
98+ "capacity" ,
99+ "get" ,
100+ "--deployed-model" ,
101+ "dep-test" ,
102+ "--dry-run" ,
103+ "--api-key" ,
104+ "e2e-dummy-key" ,
105+ ] ,
106+ exitCode : 2 ,
107+ } ,
108+ {
109+ label : "risky write confirmation" ,
110+ args : [
111+ "deploy" ,
112+ "capacity" ,
113+ "delete" ,
114+ "--deployed-model" ,
115+ "dep-test" ,
116+ "--instance-id" ,
117+ "instance-test" ,
118+ "--api-key" ,
119+ "e2e-dummy-key" ,
120+ ] ,
121+ exitCode : 7 ,
122+ } ,
123+ {
124+ label : "refund link without credentials" ,
125+ args : [ "deploy" , "capacity" , "unsubscribe" , "--instance-id" , "a b&c" ] ,
126+ exitCode : 0 ,
127+ } ,
128+ ] ) ( "$label matches the network-blocked subprocess exactly" , async ( { args, exitCode } ) => {
129+ const commandArgs = [ ...args , "--output" , "json" ] ;
130+ const inProcess = await runOfflineDeploy ( commandArgs ) ;
131+ // Preserve real startup/preload coverage and block networking in that process.
132+ const subprocess = await runCommandE2e ( DEPLOY_ROUTES , commandArgs , {
133+ ...IN_PROCESS_ENV ,
134+ NODE_OPTIONS : READ_ONLY_ENV . NODE_OPTIONS ,
135+ } ) ;
136+ expect ( subprocess . exitCode , subprocess . stderr ) . toBe ( exitCode ) ;
137+ expect ( inProcess ) . toEqual ( subprocess ) ;
138+ } ) ;
139+
33140// Deliberately ungated: these checks must also run on machines without credentials.
34141describe ( "e2e: deploy read-only queries (offline, no credentials)" , ( ) => {
35142 test . each ( [
@@ -204,11 +311,7 @@ describe("e2e: deploy capacity unsubscribe (offline, no credentials)", () => {
204311 const REFUND_URL = `https://billing-cost.console.aliyun.com/refund/refund?instanceId=${ INSTANCE_ID } ` ;
205312
206313 function runUnsubscribe ( args : string [ ] ) {
207- return runCommandE2e (
208- DEPLOY_ROUTES ,
209- [ "deploy" , "capacity" , "unsubscribe" , ...args , "--output" , "json" ] ,
210- READ_ONLY_ENV ,
211- ) ;
314+ return runOfflineDeploy ( [ "deploy" , "capacity" , "unsubscribe" , ...args , "--output" , "json" ] ) ;
212315 }
213316
214317 test ( "--help exposes --instance-id, no --yes and no --api-key requirement" , async ( ) => {
@@ -239,11 +342,14 @@ describe("e2e: deploy capacity unsubscribe (offline, no credentials)", () => {
239342 } ) ;
240343
241344 test ( "--quiet retains the complete refund-link result" , async ( ) => {
242- const { stdout, stderr, exitCode } = await runCommandE2e (
243- DEPLOY_ROUTES ,
244- [ "deploy" , "capacity" , "unsubscribe" , "--instance-id" , INSTANCE_ID , "--quiet" ] ,
245- READ_ONLY_ENV ,
246- ) ;
345+ const { stdout, stderr, exitCode } = await runOfflineDeploy ( [
346+ "deploy" ,
347+ "capacity" ,
348+ "unsubscribe" ,
349+ "--instance-id" ,
350+ INSTANCE_ID ,
351+ "--quiet" ,
352+ ] ) ;
247353 expect ( exitCode , stderr ) . toBe ( 0 ) ;
248354 expect ( JSON . parse ( stdout ) ) . toEqual ( {
249355 action : "deploy.capacity.unsubscribe" ,
@@ -798,11 +904,15 @@ describe("e2e: deploy capacity writes (offline, no credentials)", () => {
798904 } ,
799905 { path : [ "overflow" ] , args : [ "--deployed-model" , "dep-test" , "--strategy" , "disable" ] } ,
800906 ] ) ( "deploy $path without --yes requires confirmation (7)" , async ( { path, args } ) => {
801- const { stdout, stderr, exitCode } = await runCommandE2e (
802- DEPLOY_ROUTES ,
803- [ "deploy" , ...path , ...args , "--api-key" , "e2e-dummy-key" , "--output" , "json" ] ,
804- READ_ONLY_ENV ,
805- ) ;
907+ const { stdout, stderr, exitCode } = await runOfflineDeploy ( [
908+ "deploy" ,
909+ ...path ,
910+ ...args ,
911+ "--api-key" ,
912+ "e2e-dummy-key" ,
913+ "--output" ,
914+ "json" ,
915+ ] ) ;
806916 expect ( exitCode , stderr ) . toBe ( 7 ) ;
807917 expect ( stdout . trim ( ) ) . toBe ( "" ) ;
808918 expect ( JSON . parse ( stderr ) ) . toMatchObject ( {
0 commit comments