@@ -419,7 +419,6 @@ describe('useLocalApiServer', () => {
419419 expect ( result . current . serverHost ) . toBe ( '127.0.0.1' ) // Should remain default
420420 expect ( result . current . apiPrefix ) . toBe ( '/v1' ) // Should remain default
421421
422-
423422 act ( ( ) => {
424423 result . current . addTrustedHost ( 'example.com' )
425424 } )
@@ -428,4 +427,240 @@ describe('useLocalApiServer', () => {
428427 expect ( result . current . serverPort ) . toBe ( 9000 ) // Should remain changed
429428 } )
430429 } )
430+
431+ describe ( 'error handling scenarios' , ( ) => {
432+ it ( 'should provide correct configuration for port conflict error messages' , ( ) => {
433+ const { result } = renderHook ( ( ) => useLocalApiServer ( ) )
434+
435+ // Test common conflicting ports and verify they're stored correctly
436+ // These values will be used in error messages when port conflicts occur
437+ const conflictPorts = [
438+ { port : 80 , expectedMessage : 'Port 80 is already in use' } ,
439+ { port : 443 , expectedMessage : 'Port 443 is already in use' } ,
440+ { port : 3000 , expectedMessage : 'Port 3000 is already in use' } ,
441+ { port : 8080 , expectedMessage : 'Port 8080 is already in use' } ,
442+ { port : 11434 , expectedMessage : 'Port 11434 is already in use' }
443+ ]
444+
445+ conflictPorts . forEach ( ( { port, expectedMessage } ) => {
446+ act ( ( ) => {
447+ result . current . setServerPort ( port )
448+ } )
449+
450+ expect ( result . current . serverPort ) . toBe ( port )
451+ // Verify the port value that would be used in error message construction
452+ expect ( `Port ${ result . current . serverPort } is already in use` ) . toBe ( expectedMessage )
453+ } )
454+ } )
455+
456+ it ( 'should validate API key requirements for error prevention' , ( ) => {
457+ const { result } = renderHook ( ( ) => useLocalApiServer ( ) )
458+
459+ // Test empty API key - should trigger validation error
460+ act ( ( ) => {
461+ result . current . setApiKey ( '' )
462+ } )
463+ expect ( result . current . apiKey ) . toBe ( '' )
464+ expect ( result . current . apiKey . trim ( ) . length === 0 ) . toBe ( true ) // Would fail validation
465+
466+ // Test whitespace only API key - should trigger validation error
467+ act ( ( ) => {
468+ result . current . setApiKey ( ' ' )
469+ } )
470+ expect ( result . current . apiKey ) . toBe ( ' ' )
471+ expect ( result . current . apiKey . toString ( ) . trim ( ) . length === 0 ) . toBe ( true ) // Would fail validation
472+
473+ // Test valid API key - should pass validation
474+ act ( ( ) => {
475+ result . current . setApiKey ( 'sk-valid-api-key-123' )
476+ } )
477+ expect ( result . current . apiKey ) . toBe ( 'sk-valid-api-key-123' )
478+ expect ( result . current . apiKey . toString ( ) . trim ( ) . length > 0 ) . toBe ( true ) // Would pass validation
479+ } )
480+
481+ it ( 'should configure trusted hosts for CORS error handling' , ( ) => {
482+ const { result } = renderHook ( ( ) => useLocalApiServer ( ) )
483+
484+ // Add hosts that are commonly involved in CORS errors
485+ const corsRelatedHosts = [ 'localhost' , '127.0.0.1' , '0.0.0.0' , 'example.com' ]
486+
487+ corsRelatedHosts . forEach ( ( host ) => {
488+ act ( ( ) => {
489+ result . current . addTrustedHost ( host )
490+ } )
491+ } )
492+
493+ expect ( result . current . trustedHosts ) . toEqual ( corsRelatedHosts )
494+ expect ( result . current . trustedHosts . length ) . toBe ( 4 ) // Verify count for error context
495+
496+ // Test removing a critical host that might cause access errors
497+ act ( ( ) => {
498+ result . current . removeTrustedHost ( '127.0.0.1' )
499+ } )
500+
501+ expect ( result . current . trustedHosts ) . toEqual ( [ 'localhost' , '0.0.0.0' , 'example.com' ] )
502+ expect ( result . current . trustedHosts . includes ( '127.0.0.1' ) ) . toBe ( false ) // Might cause localhost access errors
503+ } )
504+
505+ it ( 'should configure timeout values that prevent timeout errors' , ( ) => {
506+ const { result } = renderHook ( ( ) => useLocalApiServer ( ) )
507+
508+ // Test very short timeout - likely to cause timeout errors
509+ act ( ( ) => {
510+ result . current . setProxyTimeout ( 1 )
511+ } )
512+ expect ( result . current . proxyTimeout ) . toBe ( 1 )
513+ expect ( result . current . proxyTimeout < 60 ) . toBe ( true ) // Likely to timeout
514+
515+ // Test reasonable timeout - should prevent timeout errors
516+ act ( ( ) => {
517+ result . current . setProxyTimeout ( 600 )
518+ } )
519+ expect ( result . current . proxyTimeout ) . toBe ( 600 )
520+ expect ( result . current . proxyTimeout >= 600 ) . toBe ( true ) // Should be sufficient
521+
522+ // Test very long timeout - prevents timeout but might cause UX issues
523+ act ( ( ) => {
524+ result . current . setProxyTimeout ( 3600 )
525+ } )
526+ expect ( result . current . proxyTimeout ) . toBe ( 3600 )
527+ expect ( result . current . proxyTimeout > 1800 ) . toBe ( true ) // Very long timeout
528+ } )
529+
530+ it ( 'should configure server host to prevent binding errors' , ( ) => {
531+ const { result } = renderHook ( ( ) => useLocalApiServer ( ) )
532+
533+ // Test localhost binding - generally safe
534+ act ( ( ) => {
535+ result . current . setServerHost ( '127.0.0.1' )
536+ } )
537+ expect ( result . current . serverHost ) . toBe ( '127.0.0.1' )
538+ expect ( result . current . serverHost === '127.0.0.1' ) . toBe ( true ) // Localhost binding
539+
540+ // Test all interfaces binding - might cause permission errors on some systems
541+ act ( ( ) => {
542+ result . current . setServerHost ( '0.0.0.0' )
543+ } )
544+ expect ( result . current . serverHost ) . toBe ( '0.0.0.0' )
545+ expect ( result . current . serverHost === '0.0.0.0' ) . toBe ( true ) // All interfaces binding (potential permission issues)
546+
547+ // Verify host format for error message construction
548+ expect ( result . current . serverHost . includes ( '.' ) ) . toBe ( true ) // Valid IP format
549+ } )
550+ } )
551+
552+ describe ( 'integration error scenarios' , ( ) => {
553+ it ( 'should provide configuration data that matches error message patterns' , ( ) => {
554+ const { result } = renderHook ( ( ) => useLocalApiServer ( ) )
555+
556+ // Set up configuration that would be used in actual error messages
557+ act ( ( ) => {
558+ result . current . setServerHost ( '127.0.0.1' )
559+ result . current . setServerPort ( 8080 )
560+ result . current . setApiKey ( 'test-key' )
561+ } )
562+
563+ // Verify values match what error handling expects
564+ const config = {
565+ host : result . current . serverHost ,
566+ port : result . current . serverPort ,
567+ apiKey : result . current . apiKey
568+ }
569+
570+ expect ( config . host ) . toBe ( '127.0.0.1' )
571+ expect ( config . port ) . toBe ( 8080 )
572+ expect ( config . apiKey ) . toBe ( 'test-key' )
573+
574+ // These values would be used in error messages like:
575+ // "Failed to bind to 127.0.0.1:8080: Address already in use"
576+ // "Port 8080 is already in use. Please try a different port."
577+ const expectedErrorContext = `${ config . host } :${ config . port } `
578+ expect ( expectedErrorContext ) . toBe ( '127.0.0.1:8080' )
579+ } )
580+
581+ it ( 'should detect invalid configurations that would cause startup errors' , ( ) => {
582+ const { result } = renderHook ( ( ) => useLocalApiServer ( ) )
583+
584+ // Test configuration that would prevent server startup
585+ act ( ( ) => {
586+ result . current . setApiKey ( '' ) // Invalid - empty API key
587+ result . current . setServerPort ( 0 ) // Invalid - port 0
588+ } )
589+
590+ // Verify conditions that would trigger validation errors
591+ const hasValidApiKey = ! ! ( result . current . apiKey && result . current . apiKey . toString ( ) . trim ( ) . length > 0 )
592+ const hasValidPort = result . current . serverPort > 0 && result . current . serverPort <= 65535
593+
594+ expect ( hasValidApiKey ) . toBe ( false ) // Would trigger "Missing API key" error
595+ expect ( hasValidPort ) . toBe ( false ) // Would trigger "Invalid port" error
596+
597+ // Fix configuration
598+ act ( ( ) => {
599+ result . current . setApiKey ( 'valid-key' )
600+ result . current . setServerPort ( 3000 )
601+ } )
602+
603+ const hasValidApiKeyFixed = ! ! ( result . current . apiKey && result . current . apiKey . toString ( ) . trim ( ) . length > 0 )
604+ const hasValidPortFixed = result . current . serverPort > 0 && result . current . serverPort <= 65535
605+
606+ expect ( hasValidApiKeyFixed ) . toBe ( true ) // Should pass validation
607+ expect ( hasValidPortFixed ) . toBe ( true ) // Should pass validation
608+ } )
609+ } )
610+
611+ describe ( 'configuration validation' , ( ) => {
612+ it ( 'should maintain consistent state for server configuration' , ( ) => {
613+ const { result } = renderHook ( ( ) => useLocalApiServer ( ) )
614+
615+ // Set up a complete server configuration
616+ act ( ( ) => {
617+ result . current . setServerHost ( '127.0.0.1' )
618+ result . current . setServerPort ( 8080 )
619+ result . current . setApiPrefix ( '/api/v1' )
620+ result . current . setApiKey ( 'test-key-123' )
621+ result . current . setTrustedHosts ( [ 'localhost' , '127.0.0.1' ] )
622+ result . current . setProxyTimeout ( 300 )
623+ result . current . setCorsEnabled ( true )
624+ result . current . setVerboseLogs ( false )
625+ } )
626+
627+ // Verify all settings are consistent
628+ expect ( result . current . serverHost ) . toBe ( '127.0.0.1' )
629+ expect ( result . current . serverPort ) . toBe ( 8080 )
630+ expect ( result . current . apiPrefix ) . toBe ( '/api/v1' )
631+ expect ( result . current . apiKey ) . toBe ( 'test-key-123' )
632+ expect ( result . current . trustedHosts ) . toEqual ( [ 'localhost' , '127.0.0.1' ] )
633+ expect ( result . current . proxyTimeout ) . toBe ( 300 )
634+ expect ( result . current . corsEnabled ) . toBe ( true )
635+ expect ( result . current . verboseLogs ) . toBe ( false )
636+ } )
637+
638+ it ( 'should handle edge cases in configuration values' , ( ) => {
639+ const { result } = renderHook ( ( ) => useLocalApiServer ( ) )
640+
641+ // Test edge case: empty API prefix
642+ act ( ( ) => {
643+ result . current . setApiPrefix ( '' )
644+ } )
645+ expect ( result . current . apiPrefix ) . toBe ( '' )
646+
647+ // Test edge case: API prefix without leading slash
648+ act ( ( ) => {
649+ result . current . setApiPrefix ( 'v1' )
650+ } )
651+ expect ( result . current . apiPrefix ) . toBe ( 'v1' )
652+
653+ // Test edge case: minimum port number
654+ act ( ( ) => {
655+ result . current . setServerPort ( 1 )
656+ } )
657+ expect ( result . current . serverPort ) . toBe ( 1 )
658+
659+ // Test edge case: maximum valid port number
660+ act ( ( ) => {
661+ result . current . setServerPort ( 65535 )
662+ } )
663+ expect ( result . current . serverPort ) . toBe ( 65535 )
664+ } )
665+ } )
431666} )
0 commit comments