@@ -91,6 +91,20 @@ class TestEnvironment {
9191 return this . server . findRequestsTo ( '/v0/tts/stream/json' ) ;
9292 }
9393
94+ /**
95+ * Get the list voices requests
96+ */
97+ getListVoicesRequests ( ) {
98+ return this . server . findRequestsTo ( '/v0/tts/voices' ) . filter ( ( req ) => req . method === 'GET' ) ;
99+ }
100+
101+ /**
102+ * Get the delete voice requests
103+ */
104+ getDeleteVoiceRequests ( ) {
105+ return this . server . findRequestsTo ( '/v0/tts/voices' ) . filter ( ( req ) => req . method === 'DELETE' ) ;
106+ }
107+
94108 /**
95109 * Run a CLI command for testing
96110 */
@@ -327,9 +341,9 @@ class MockHumeServer {
327341 this . setupDefaultTtsStreamHandler ( ) ;
328342 }
329343
330- // Default TTS response handler
344+ // Default handlers for all endpoints
331345 setupDefaultTtsStreamHandler ( ) {
332- // Handle TTS API requests - actual path used by the client
346+ // TTS stream API endpoint
333347 this . addHandler ( '/v0/tts/stream/json' , async ( req ) => {
334348 try {
335349 const body = await req . json ( ) ;
@@ -372,6 +386,53 @@ class MockHumeServer {
372386 } ) ;
373387 }
374388 } ) ;
389+
390+ // Add handler for voices endpoint
391+ this . addHandler ( '/v0/tts/voices' , async ( req ) => {
392+ // Check if it's a GET or DELETE request
393+ if ( req . method === 'GET' ) {
394+ // For listing voices
395+ const url = new URL ( req . url ) ;
396+ const provider = url . searchParams . get ( 'provider' ) || 'CUSTOM_VOICE' ;
397+
398+ let voices = [ ] ;
399+ if ( provider === 'CUSTOM_VOICE' ) {
400+ voices = [
401+ { id : 'custom1' , name : 'my-narrator' , createdAt : '2023-01-01T00:00:00Z' } ,
402+ { id : 'custom2' , name : 'my-assistant' , createdAt : '2023-01-02T00:00:00Z' } ,
403+ ] ;
404+ } else {
405+ voices = [
406+ { id : 'shared1' , name : 'hume-narrator' , createdAt : '2023-01-01T00:00:00Z' } ,
407+ { id : 'shared2' , name : 'hume-assistant' , createdAt : '2023-01-02T00:00:00Z' } ,
408+ { id : 'shared3' , name : 'hume-podcaster' , createdAt : '2023-01-03T00:00:00Z' } ,
409+ ] ;
410+ }
411+
412+ return Response . json ( { data : voices } ) ;
413+ } else if ( req . method === 'DELETE' ) {
414+ // For deleting a voice
415+ const url = new URL ( req . url ) ;
416+ const name = url . searchParams . get ( 'name' ) ;
417+
418+ if ( ! name ) {
419+ return new Response ( JSON . stringify ( { error : 'Missing name parameter' } ) , { status : 400 } ) ;
420+ }
421+
422+ return Response . json ( { success : true } ) ;
423+ } else if ( req . method === 'POST' ) {
424+ // For saving a voice (already implemented in the original code)
425+ const body = await req . json ( ) ;
426+ return Response . json ( {
427+ id : 'new-voice-123' ,
428+ name : body . name ,
429+ createdAt : new Date ( ) . toISOString ( ) ,
430+ } ) ;
431+ }
432+
433+ // Fallback
434+ return new Response ( 'Method not supported' , { status : 405 } ) ;
435+ } ) ;
375436 }
376437}
377438
@@ -758,4 +819,43 @@ describe('CLI End-to-End Tests', () => {
758819 expect ( continuationRequests [ 0 ] . body . context ?. generation_id ) . toBe ( 'config_test_gen_2' ) ; // Should use the second generation
759820 expect ( continuationRequests [ 0 ] . body . format ?. type ) . toBe ( 'mp3' ) ; // Should still use mp3 from config
760821 } ) ;
822+
823+ // Voice management command tests
824+ test ( 'Voice list command structure' , async ( ) => {
825+ // We're only checking the command structure, not the actual API call
826+ const result = await testEnv . runCliCommand ( [ 'voices' , 'list' , '--help' ] ) ;
827+ expect ( result . exitCode ) . toBe ( 0 ) ;
828+ expect ( result . stdout ) . toContain ( 'List available voices' ) ;
829+ expect ( result . stdout ) . toContain ( '--provider' ) ;
830+ } ) ;
831+
832+ test ( 'Voice list with provider option' , async ( ) => {
833+ // Test that the provider option is recognized
834+ const result = await testEnv . runCliCommand ( [
835+ 'voices' ,
836+ 'list' ,
837+ '--provider' ,
838+ 'HUME_AI' ,
839+ '--help' ,
840+ ] ) ;
841+ expect ( result . exitCode ) . toBe ( 0 ) ;
842+ expect ( result . stdout ) . toContain ( 'List available voices' ) ;
843+ expect ( result . stdout ) . toContain ( '--provider' ) ;
844+ } ) ;
845+
846+ test ( 'Voice delete command structure' , async ( ) => {
847+ // We're only checking the command structure, not the actual API call
848+ const result = await testEnv . runCliCommand ( [ 'voices' , 'delete' , '--help' ] ) ;
849+ expect ( result . exitCode ) . toBe ( 0 ) ;
850+ expect ( result . stdout ) . toContain ( 'Delete a saved voice' ) ;
851+ expect ( result . stdout ) . toContain ( '--name' ) ;
852+ } ) ;
853+
854+ test ( 'Error when deleting a voice without name' , async ( ) => {
855+ const result = await testEnv . runCliCommand ( [ 'voices' , 'delete' ] ) ;
856+ expect ( result . exitCode ) . not . toBe ( 0 ) ;
857+ // Test that we get an error, but don't be specific about the message
858+ // since the error format might vary
859+ expect ( result . stderr . length ) . toBeGreaterThan ( 0 ) ;
860+ } ) ;
761861} ) ;
0 commit comments