@@ -2,7 +2,9 @@ package docker
22
33import (
44 "errors"
5+ "fmt"
56 "math"
7+ "net"
68 "net/http"
79 "testing"
810 "time"
@@ -12,6 +14,62 @@ import (
1214 "github.com/stretchr/testify/require"
1315)
1416
17+ // timeoutValueError implements net.Error with a configurable Timeout() return value.
18+ type timeoutValueError struct {
19+ isTimeout bool
20+ }
21+
22+ func (e * timeoutValueError ) Error () string { return "network error" }
23+ func (e * timeoutValueError ) Timeout () bool { return e .isTimeout }
24+ func (e * timeoutValueError ) Temporary () bool { return false }
25+
26+ func TestIsRetryableNetworkError (t * testing.T ) {
27+ for _ , c := range []struct {
28+ name string
29+ err error
30+ expected bool
31+ }{
32+ {
33+ // A direct timeout error from the network layer should be retryable.
34+ name : "net.Error with Timeout() true" ,
35+ err : & timeoutValueError {isTimeout : true },
36+ expected : true ,
37+ },
38+ {
39+ // Timeout errors wrapped by fmt.Errorf should still be detected
40+ // via errors.As unwrapping.
41+ name : "wrapped net.Error with Timeout() true" ,
42+ err : fmt .Errorf ("read failed: %w" , & timeoutValueError {isTimeout : true }),
43+ expected : true ,
44+ },
45+ {
46+ // A net.Error that is not a timeout (e.g. a connection refused)
47+ // should not be retryable.
48+ name : "net.Error with Timeout() false" ,
49+ err : & timeoutValueError {isTimeout : false },
50+ expected : false ,
51+ },
52+ {
53+ // A plain error with no net.Error in the chain should not be retryable.
54+ name : "plain error" ,
55+ err : errors .New ("something broke" ),
56+ expected : false ,
57+ },
58+ {
59+ // net.OpError is the concrete type returned by real socket operations;
60+ // verify that errors.As can unwrap through it to find the timeout.
61+ name : "net.OpError wrapping timeout" ,
62+ err : & net.OpError {Op : "read" , Err : & timeoutValueError {isTimeout : true }},
63+ expected : true ,
64+ },
65+ } {
66+ t .Run (c .name , func (t * testing.T ) {
67+ result := isRetryableNetworkError (c .err )
68+ assert .Equal (t , c .expected , result )
69+ })
70+ }
71+ }
72+
1573func TestParseDecimalInString (t * testing.T ) {
1674 for _ , prefix := range []string {"" , "text" , "0" } {
1775 for _ , suffix := range []string {"" , "text" } {
0 commit comments