Skip to content

Commit 2eef4af

Browse files
committed
chore: Add tests to cover error catches
1 parent ecae0f5 commit 2eef4af

File tree

1 file changed

+236
-1
lines changed

1 file changed

+236
-1
lines changed

web-app/src/hooks/__tests__/useLocalApiServer.test.ts

Lines changed: 236 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)