@@ -42,6 +42,31 @@ export class AnacondaAiCli {
4242 this . assertModelResponseData ( models ) ;
4343 }
4444
45+ public async runAnacondaAiServersListCommand ( ) : Promise < ShellResult > {
46+ return await shellCommand ( cliCommands . anacondaAiServersListCmd ) ;
47+ }
48+
49+ public verifyAnacondaAiServersListCommand ( result : ShellResult ) : void {
50+ verifyShellExitCode ( result , 'anaconda ai servers --json' ) ;
51+
52+ const servers = JSON . parse ( stripAnsiSgrAndTrim ( result . output ) ) as ServerApi [ ] ;
53+ expect ( Array . isArray ( servers ) , 'servers output should be an array' ) . toBe ( true ) ;
54+ }
55+
56+ // Verifies that a server for the given model appears in the servers list with status "running"
57+ public verifyRunningServerInList ( result : ShellResult , modelName : string , modelQuantization : string ) : void {
58+ verifyShellExitCode ( result , 'anaconda ai servers --json' ) ;
59+
60+ const servers = JSON . parse ( stripAnsiSgrAndTrim ( result . output ) ) as ServerApi [ ] ;
61+ const expectedModelFile = `${ modelName } _${ modelQuantization } .gguf` ;
62+ const server = servers . find ( ( s ) => s . model === expectedModelFile ) ;
63+
64+ expect ( server , `Expected server for model "${ expectedModelFile } " to appear in servers list` ) . toBeDefined ( ) ;
65+ expect ( server ! . server_id , `Expected server_id to contain model name "${ modelName } "` ) . toContain ( modelName ) ;
66+ expect ( server ! . model , `Expected model to be "${ expectedModelFile } "` ) . toBe ( expectedModelFile ) ;
67+ expect ( server ! . status , `Expected server status to be "running"` ) . toBe ( 'running' ) ;
68+ }
69+
4570 // Executes `anaconda ai download <model>/<quant>` (positional; no --model)
4671 public async runDownloadModelCommand ( modelName : string , modelQuantization : string ) : Promise < ShellResult > {
4772 return await shellCommand ( cliCommands . downloadModelCmd ( modelName , modelQuantization ) ) ;
@@ -61,15 +86,110 @@ export class AnacondaAiCli {
6186 expect (
6287 isDownloadedNow || isAlreadyDownloaded ,
6388 'Expected the model to be either newly downloaded or already downloaded' ,
64- )
65- . toBeTruthy ( ) ;
89+ ) . toBeTruthy ( ) ;
6690 }
6791
6892 public verifyInvalidDownloadModelCommand ( result : ShellResult ) : void {
69- expect ( result . exitCode , `Expected invalid download command to fail, but got exit code ${ result . exitCode } ` , ) . not . toBe ( 0 ) ;
93+ expect (
94+ result . exitCode ,
95+ `Expected invalid download command to fail, but got exit code ${ result . exitCode } ` ,
96+ ) . not . toBe ( 0 ) ;
7097 const output = stripAnsiSgrAndTrim ( result . output ) . toLowerCase ( ) ;
7198 expect ( output , 'Expected the model to be invalid' ) . toContain ( 'error' ) ;
72- expect ( output , 'Expected the error message to be invalid' ) . toContain ( INVALID_MODEL_ERROR_MESSAGE ) ;
99+ expect ( output , 'Expected output should contain invalid model error message' ) . toContain ( INVALID_MODEL_ERROR_MESSAGE ) ;
100+ }
101+
102+ // Executes `anaconda ai launch <model>/<quant> --detach`
103+ public async runLaunchModelCommand ( modelName : string , modelQuantization : string ) : Promise < ShellResult > {
104+ return await shellCommand ( cliCommands . launchModelCmd ( modelName , modelQuantization ) ) ;
105+ }
106+
107+ // Validates that launching a model that already has a running server fails with AnacondaAIException
108+ public verifyDuplicateLaunchModelCommand ( result : ShellResult ) : void {
109+ expect ( result . exitCode , 'Expected non-zero exit code for duplicate launch' ) . not . toBe ( 0 ) ;
110+
111+ const output = stripAnsiSgrAndTrim ( result . output ) . toLowerCase ( ) ;
112+ expect (
113+ output . includes ( 'anacondaaiexception' ) || output . includes ( 'already exists' ) ,
114+ 'Expected AnacondaAIException about duplicate server' ,
115+ ) . toBeTruthy ( ) ;
116+ }
117+
118+ // Validates model server launched successfully
119+ public verifyLaunchModelCommand ( result : ShellResult , modelName : string , modelQuantization : string ) : void {
120+ verifyShellExitCode ( result , 'anaconda ai launch <model>/<quant>' ) ;
121+
122+ const output = stripAnsiSgrAndTrim ( result . output ) . toLowerCase ( ) ;
123+ const expectedModelFile = `${ modelName } _${ modelQuantization } .gguf` . toLowerCase ( ) ;
124+ expect ( output . includes ( 'running' ) , 'Expected status to be "running"' ) . toBeTruthy ( ) ;
125+ expect (
126+ output . includes ( 'inference/serve/' ) ,
127+ 'Expected "inference/serve/" is running' ,
128+ ) . toBeTruthy ( ) ;
129+ expect (
130+ output . includes ( expectedModelFile ) ,
131+ `Expected model "${ expectedModelFile } " to appear in output` ,
132+ ) . toBeTruthy ( ) ;
133+ }
134+
135+ // Executes `anaconda ai stop <modelName>_<modelQuantization>.gguf`
136+ public async runStopModelCommand ( modelName : string , modelQuantization : string ) : Promise < ShellResult > {
137+ return await shellCommand ( cliCommands . stopModelCmd ( modelName , modelQuantization ) ) ;
138+ }
139+
140+ // Validates model server stopped successfully
141+ public verifyStopModelCommand ( result : ShellResult , modelName : string , modelQuantization : string ) : void {
142+ verifyShellExitCode ( result , 'anaconda ai stop <model>.gguf' ) ;
143+
144+ const output = stripAnsiSgrAndTrim ( result . output ) . toLowerCase ( ) ;
145+ const expectedModelFile = `${ modelName } _${ modelQuantization } .gguf` . toLowerCase ( ) ;
146+ expect ( output . includes ( 'stopped' ) , 'Expected status to be "stopped"' ) . toBeTruthy ( ) ;
147+ expect ( output . includes ( 'success' ) , 'Expected "Success" in output' ) . toBeTruthy ( ) ;
148+ expect (
149+ output . includes ( expectedModelFile ) ,
150+ `Expected model "${ expectedModelFile } " to appear in output` ,
151+ ) . toBeTruthy ( ) ;
152+ }
153+
154+ // Executes `anaconda ai stop <modelName>_<modelQuantization>.gguf --rm`
155+ public async runStopAndRemoveModelCommand ( modelName : string , modelQuantization : string ) : Promise < ShellResult > {
156+ return await shellCommand ( cliCommands . stopAndRemoveModelCmd ( modelName , modelQuantization ) ) ;
157+ }
158+
159+ // Validates model server deleted successfully
160+ public verifyStopAndRemoveModelCommand ( result : ShellResult , modelName : string , modelQuantization : string ) : void {
161+ verifyShellExitCode ( result , 'anaconda ai stop <model>.gguf --rm' ) ;
162+
163+ const output = stripAnsiSgrAndTrim ( result . output ) . toLowerCase ( ) ;
164+ const expectedModelFile = `${ modelName } _${ modelQuantization } .gguf` . toLowerCase ( ) ;
165+ expect ( output . includes ( 'deleted' ) , 'Expected status to be "deleted"' ) . toBeTruthy ( ) ;
166+ expect ( output . includes ( 'success' ) , 'Expected "Success" in output' ) . toBeTruthy ( ) ;
167+ expect (
168+ output . includes ( expectedModelFile ) ,
169+ `Expected model "${ expectedModelFile } " to appear in output` ,
170+ ) . toBeTruthy ( ) ;
171+ }
172+
173+ // Validates that launching with an invalid format exits non-zero with a ValueError
174+ public verifyLaunchModelInvalidFormatCommand ( result : ShellResult ) : void {
175+ expect ( result . exitCode , 'Expected non-zero exit code for invalid format' ) . not . toBe ( 0 ) ;
176+
177+ const output = stripAnsiSgrAndTrim ( result . output ) . toLowerCase ( ) ;
178+ expect (
179+ output . includes ( 'valueerror' ) && output . includes ( 'does not look like a quantized model name' ) ,
180+ 'Expected ValueError about invalid model name format' ,
181+ ) . toBeTruthy ( ) ;
182+ }
183+
184+ // Validates that launching an unknown model exits non-zero with a ModelNotFound error
185+ public verifyLaunchModelNotFoundCommand ( result : ShellResult ) : void {
186+ expect ( result . exitCode , 'Expected non-zero exit code for unknown model' ) . not . toBe ( 0 ) ;
187+
188+ const output = stripAnsiSgrAndTrim ( result . output ) . toLowerCase ( ) ;
189+ expect (
190+ output . includes ( 'modelnotfound' ) || output . includes ( 'was not found' ) ,
191+ 'Expected ModelNotFound error for unknown model' ,
192+ ) . toBeTruthy ( ) ;
73193 }
74194
75195 private assertModelResponseData ( models : ModelApi [ ] ) : void {
@@ -89,15 +209,4 @@ export class AnacondaAiCli {
89209 } ) ;
90210 } ) ;
91211 }
92-
93- public async runAnacondaAiServersListCommand ( ) : Promise < ShellResult > {
94- return await shellCommand ( cliCommands . anacondaAiServersListCmd ) ;
95- }
96-
97- public verifyAnacondaAiServersListCommand ( result : ShellResult ) : void {
98- verifyShellExitCode ( result , 'anaconda ai servers --json' ) ;
99-
100- const servers = JSON . parse ( stripAnsiSgrAndTrim ( result . output ) ) as ServerApi [ ] ;
101- expect ( Array . isArray ( servers ) , 'servers output should be an array' ) . toBe ( true ) ;
102- }
103- }
212+ }
0 commit comments