@ -196,6 +196,112 @@ func Test_Hedging(t *testing.T) {
}
}
type MockS3Client struct {
s3 . S3
HeadObjectFunc func ( * s3 . HeadObjectInput ) ( * s3 . HeadObjectOutput , error )
}
func ( m * MockS3Client ) HeadObject ( input * s3 . HeadObjectInput ) ( * s3 . HeadObjectOutput , error ) {
return m . HeadObjectFunc ( input )
}
func Test_RetryLogic ( t * testing . T ) {
for _ , tc := range [ ] struct {
name string
maxRetries int
exists bool
do func ( c * S3ObjectClient ) error
} {
{
"get object with retries" ,
3 ,
true ,
func ( c * S3ObjectClient ) error {
_ , _ , err := c . GetObject ( context . Background ( ) , "foo" )
return err
} ,
} ,
{
"object exists with retries" ,
3 ,
true ,
func ( c * S3ObjectClient ) error {
_ , err := c . ObjectExists ( context . Background ( ) , "foo" )
return err
} ,
} ,
{
"object doesn't exist with retries" ,
3 ,
false ,
func ( c * S3ObjectClient ) error {
_ , err := c . ObjectExists ( context . Background ( ) , "foo" )
return err
} ,
} ,
} {
t . Run ( tc . name , func ( t * testing . T ) {
callCount := atomic . NewInt32 ( 0 )
mockS3 := & MockS3Client {
HeadObjectFunc : func ( input * s3 . HeadObjectInput ) ( * s3 . HeadObjectOutput , error ) {
callNum := callCount . Inc ( )
if ! tc . exists {
rfIn := awserr . NewRequestFailure (
awserr . New ( "NotFound" , "Not Found" , nil ) , 404 , "abc" ,
)
return nil , rfIn
}
// Fail the first set of calls
if int ( callNum ) <= tc . maxRetries - 1 {
time . Sleep ( 200 * time . Millisecond ) // Simulate latency
return nil , errors . New ( "simulated error on mock call" )
}
// Succeed on the last call
return & s3 . HeadObjectOutput { } , nil
} ,
}
c , err := NewS3ObjectClient ( S3Config {
AccessKeyID : "foo" ,
SecretAccessKey : flagext . SecretWithValue ( "bar" ) ,
BackoffConfig : backoff . Config { MaxRetries : tc . maxRetries } ,
BucketNames : "foo" ,
Inject : func ( next http . RoundTripper ) http . RoundTripper {
return RoundTripperFunc ( func ( req * http . Request ) ( * http . Response , error ) {
// Increment the call counter
callNum := callCount . Inc ( )
// Fail the first set of calls
if int ( callNum ) <= tc . maxRetries - 1 {
time . Sleep ( 200 * time . Millisecond ) // Simulate latency
return nil , errors . New ( "simulated error on call" )
}
// Succeed on the last call
return & http . Response {
StatusCode : http . StatusOK ,
Body : io . NopCloser ( bytes . NewReader ( [ ] byte ( "object content" ) ) ) ,
} , nil
} )
} ,
} , hedging . Config { } )
require . NoError ( t , err )
c . S3 = mockS3
err = tc . do ( c )
if tc . exists {
require . NoError ( t , err )
require . Equal ( t , tc . maxRetries , int ( callCount . Load ( ) ) )
} else {
require . True ( t , c . IsObjectNotFoundErr ( err ) )
require . Equal ( t , 1 , int ( callCount . Load ( ) ) )
}
} )
}
}
func Test_ConfigRedactsCredentials ( t * testing . T ) {
underTest := S3Config {
AccessKeyID : "access key id" ,