@@ -2,6 +2,7 @@ package easyssh
22
33import (
44 "context"
5+ "errors"
56 "os"
67 "os/user"
78 "path"
@@ -512,3 +513,117 @@ func TestCommandTimeout(t *testing.T) {
512513 assert .NotNil (t , err )
513514 assert .Equal (t , "Run Command Timeout: " + context .DeadlineExceeded .Error (), err .Error ())
514515}
516+
517+ // TestProxyTimeoutHandling tests that timeout is properly respected when using proxy connections
518+ // This test uses a non-existent proxy server to force a timeout during proxy connection
519+ func TestProxyTimeoutHandling (t * testing.T ) {
520+ ssh := & MakeConfig {
521+ Server : "example.com" ,
522+ User : "testuser" ,
523+ Port : "22" ,
524+ KeyPath : "./tests/.ssh/id_rsa" ,
525+ Timeout : 1 * time .Second , // Short timeout for testing
526+ Proxy : DefaultConfig {
527+ User : "testuser" ,
528+ Server : "10.255.255.1" , // Non-routable IP that should timeout
529+ Port : "22" ,
530+ KeyPath : "./tests/.ssh/id_rsa" ,
531+ Timeout : 1 * time .Second ,
532+ },
533+ }
534+
535+ // Test Connect() method directly to test proxy connection timeout
536+ start := time .Now ()
537+ session , client , err := ssh .Connect ()
538+ elapsed := time .Since (start )
539+
540+ // Should timeout within reasonable bounds
541+ assert .True (t , elapsed < 3 * time .Second , "Connection should timeout within 3 seconds, took %v" , elapsed )
542+ assert .True (t , elapsed >= 1 * time .Second , "Connection should take at least 1 second (timeout value), took %v" , elapsed )
543+
544+ // Should return nil session and client
545+ assert .Nil (t , session )
546+ assert .Nil (t , client )
547+
548+ // Should have error
549+ assert .NotNil (t , err )
550+ }
551+
552+ // TestProxyDialTimeout tests the specific scenario described in issue #93
553+ // where proxy dial timeout should be respected and properly detected
554+ func TestProxyDialTimeout (t * testing.T ) {
555+ ssh := & MakeConfig {
556+ Server : "10.255.255.1" , // Non-routable IP that should timeout
557+ User : "testuser" ,
558+ Port : "22" ,
559+ KeyPath : "./tests/.ssh/id_rsa" ,
560+ Timeout : 2 * time .Second , // Short timeout for testing
561+ Proxy : DefaultConfig {
562+ User : "testuser" ,
563+ Server : "10.255.255.2" , // Another non-routable IP for proxy
564+ Port : "22" ,
565+ KeyPath : "./tests/.ssh/id_rsa" ,
566+ Timeout : 2 * time .Second ,
567+ },
568+ }
569+
570+ // Test Connect() method directly to avoid SSH server dependency
571+ start := time .Now ()
572+ session , client , err := ssh .Connect ()
573+ elapsed := time .Since (start )
574+
575+ // Should timeout within reasonable bounds
576+ assert .True (t , elapsed < 5 * time .Second , "Connection should timeout within 5 seconds, took %v" , elapsed )
577+ assert .True (t , elapsed >= 2 * time .Second , "Connection should take at least 2 seconds (timeout value), took %v" , elapsed )
578+
579+ // Should return nil session and client
580+ assert .Nil (t , session )
581+ assert .Nil (t , client )
582+
583+ // Should have error
584+ assert .NotNil (t , err )
585+ // Note: This will timeout at the proxy connection level, not at proxy dial level
586+ // so it won't be ErrProxyDialTimeout, but we can still verify the timeout behavior
587+ }
588+
589+ // TestProxyDialTimeoutInRun tests timeout detection in Run method
590+ func TestProxyDialTimeoutInRun (t * testing.T ) {
591+ ssh := & MakeConfig {
592+ Server : "example.com" ,
593+ User : "testuser" ,
594+ Port : "22" ,
595+ KeyPath : "./tests/.ssh/id_rsa" ,
596+ Timeout : 2 * time .Second ,
597+ Proxy : DefaultConfig {
598+ User : "testuser" ,
599+ Server : "127.0.0.1" , // Assume localhost SSH exists
600+ Port : "22" ,
601+ KeyPath : "./tests/.ssh/id_rsa" ,
602+ Timeout : 2 * time .Second ,
603+ },
604+ }
605+
606+ // Mock a scenario where Connect() returns ErrProxyDialTimeout
607+ // by temporarily changing the target to a non-routable address
608+ ssh .Server = "10.255.255.1"
609+
610+ start := time .Now ()
611+ outStr , errStr , isTimeout , err := ssh .Run ("whoami" )
612+ elapsed := time .Since (start )
613+
614+ // Should timeout within reasonable bounds
615+ assert .True (t , elapsed < 5 * time .Second , "Should timeout within 5 seconds, took %v" , elapsed )
616+
617+ // Should return empty output
618+ assert .Equal (t , "" , outStr )
619+ assert .Equal (t , "" , errStr )
620+
621+ // Should have error
622+ assert .NotNil (t , err )
623+
624+ // If it's specifically a proxy dial timeout, isTimeout should be true
625+ if errors .Is (err , ErrProxyDialTimeout ) {
626+ assert .True (t , isTimeout , "isTimeout should be true for proxy dial timeout" )
627+ }
628+ }
629+
0 commit comments