@@ -1063,27 +1063,150 @@ public async Task GetNonSuccessAsync_Logs_The_GitHub_Request_Id_Header_Value()
10631063 }
10641064
10651065 [ Fact ]
1066- public async Task GetNonSuccessAsync_Retries_On_Non_Success ( )
1066+ public async Task GetNonSuccessAsync_Does_Not_Retry_On_Expected_Status ( )
10671067 {
10681068 // Arrange
10691069 var handlerMock = new Mock < HttpMessageHandler > ( ) ;
10701070 handlerMock
10711071 . Protected ( )
1072- . SetupSequence < Task < HttpResponseMessage > > (
1072+ . Setup < Task < HttpResponseMessage > > (
10731073 "SendAsync" ,
10741074 ItExpr . Is < HttpRequestMessage > ( req => req . Method == HttpMethod . Get ) ,
10751075 ItExpr . IsAny < CancellationToken > ( ) )
1076- . ReturnsAsync ( CreateHttpResponseFactory ( statusCode : HttpStatusCode . InternalServerError ) )
1077- . ReturnsAsync ( CreateHttpResponseFactory ( statusCode : HttpStatusCode . Found , content : EXPECTED_RESPONSE_CONTENT ) ) ;
1076+ . ReturnsAsync ( CreateHttpResponseFactory ( statusCode : HttpStatusCode . NotFound , content : "Not Found" ) ) ;
10781077
10791078 using var httpClient = new HttpClient ( handlerMock . Object ) ;
10801079 var githubClient = new GithubClient ( _mockOctoLogger . Object , httpClient , null , _retryPolicy , _dateTimeProvider . Object , PERSONAL_ACCESS_TOKEN ) ;
10811080
10821081 // Act
1083- var result = await githubClient . GetNonSuccessAsync ( URL , HttpStatusCode . Found ) ;
1082+ var result = await githubClient . GetNonSuccessAsync ( URL , HttpStatusCode . NotFound ) ;
1083+
1084+ // Assert
1085+ result . Should ( ) . Be ( "Not Found" ) ;
1086+ handlerMock . Protected ( ) . Verify (
1087+ "SendAsync" ,
1088+ Times . Once ( ) ,
1089+ ItExpr . Is < HttpRequestMessage > ( req => req . Method == HttpMethod . Get ) ,
1090+ ItExpr . IsAny < CancellationToken > ( ) ) ;
1091+ }
1092+
1093+ [ Fact ]
1094+ public async Task GetNonSuccessAsync_Does_Not_Retry_On_Unexpected_Success_Status ( )
1095+ {
1096+ // Arrange
1097+ var handlerMock = new Mock < HttpMessageHandler > ( ) ;
1098+ handlerMock
1099+ . Protected ( )
1100+ . Setup < Task < HttpResponseMessage > > (
1101+ "SendAsync" ,
1102+ ItExpr . Is < HttpRequestMessage > ( req => req . Method == HttpMethod . Get ) ,
1103+ ItExpr . IsAny < CancellationToken > ( ) )
1104+ . ReturnsAsync ( CreateHttpResponseFactory ( statusCode : HttpStatusCode . OK , content : "Success" ) ) ;
1105+
1106+ using var httpClient = new HttpClient ( handlerMock . Object ) ;
1107+ var githubClient = new GithubClient ( _mockOctoLogger . Object , httpClient , null , _retryPolicy , _dateTimeProvider . Object , PERSONAL_ACCESS_TOKEN ) ;
1108+
1109+ // Act & Assert - should throw immediately without retry
1110+ await FluentActions
1111+ . Invoking ( async ( ) => await githubClient . GetNonSuccessAsync ( URL , HttpStatusCode . NotFound ) )
1112+ . Should ( )
1113+ . ThrowExactlyAsync < HttpRequestException > ( )
1114+ . WithMessage ( "*OK*" ) ;
1115+
1116+ handlerMock . Protected ( ) . Verify (
1117+ "SendAsync" ,
1118+ Times . Once ( ) ,
1119+ ItExpr . Is < HttpRequestMessage > ( req => req . Method == HttpMethod . Get ) ,
1120+ ItExpr . IsAny < CancellationToken > ( ) ) ;
1121+ }
1122+
1123+ [ Fact ]
1124+ public async Task GetNonSuccessAsync_Retries_On_Server_Error ( )
1125+ {
1126+ // Arrange
1127+ var handlerMock = new Mock < HttpMessageHandler > ( ) ;
1128+ handlerMock
1129+ . Protected ( )
1130+ . SetupSequence < Task < HttpResponseMessage > > (
1131+ "SendAsync" ,
1132+ ItExpr . Is < HttpRequestMessage > ( req => req . Method == HttpMethod . Get ) ,
1133+ ItExpr . IsAny < CancellationToken > ( ) )
1134+ . ReturnsAsync ( CreateHttpResponseFactory ( statusCode : HttpStatusCode . InternalServerError , content : "Server Error" ) )
1135+ . ReturnsAsync ( CreateHttpResponseFactory ( statusCode : HttpStatusCode . NotFound , content : "Not Found" ) ) ;
1136+
1137+ using var httpClient = new HttpClient ( handlerMock . Object ) ;
1138+ var githubClient = new GithubClient ( _mockOctoLogger . Object , httpClient , null , _retryPolicy , _dateTimeProvider . Object , PERSONAL_ACCESS_TOKEN ) ;
1139+
1140+ // Act - should retry on 5xx and eventually succeed
1141+ var result = await githubClient . GetNonSuccessAsync ( URL , HttpStatusCode . NotFound ) ;
10841142
10851143 // Assert
1086- result . Should ( ) . Be ( EXPECTED_RESPONSE_CONTENT ) ;
1144+ result . Should ( ) . Be ( "Not Found" ) ;
1145+ handlerMock . Protected ( ) . Verify (
1146+ "SendAsync" ,
1147+ Times . Exactly ( 2 ) ,
1148+ ItExpr . Is < HttpRequestMessage > ( req => req . Method == HttpMethod . Get ) ,
1149+ ItExpr . IsAny < CancellationToken > ( ) ) ;
1150+ }
1151+
1152+ [ Fact ]
1153+ public async Task GetNonSuccessAsync_Does_Not_Retry_On_Client_Error ( )
1154+ {
1155+ // Arrange
1156+ var handlerMock = new Mock < HttpMessageHandler > ( ) ;
1157+ handlerMock
1158+ . Protected ( )
1159+ . Setup < Task < HttpResponseMessage > > (
1160+ "SendAsync" ,
1161+ ItExpr . Is < HttpRequestMessage > ( req => req . Method == HttpMethod . Get ) ,
1162+ ItExpr . IsAny < CancellationToken > ( ) )
1163+ . ReturnsAsync ( CreateHttpResponseFactory ( statusCode : HttpStatusCode . BadRequest , content : "Bad Request" ) ) ;
1164+
1165+ using var httpClient = new HttpClient ( handlerMock . Object ) ;
1166+ var githubClient = new GithubClient ( _mockOctoLogger . Object , httpClient , null , _retryPolicy , _dateTimeProvider . Object , PERSONAL_ACCESS_TOKEN ) ;
1167+
1168+ // Act & Assert - should throw immediately without retry on 4xx
1169+ await FluentActions
1170+ . Invoking ( async ( ) => await githubClient . GetNonSuccessAsync ( URL , HttpStatusCode . NotFound ) )
1171+ . Should ( )
1172+ . ThrowExactlyAsync < HttpRequestException > ( )
1173+ . WithMessage ( "*BadRequest*" ) ;
1174+
1175+ handlerMock . Protected ( ) . Verify (
1176+ "SendAsync" ,
1177+ Times . Once ( ) ,
1178+ ItExpr . Is < HttpRequestMessage > ( req => req . Method == HttpMethod . Get ) ,
1179+ ItExpr . IsAny < CancellationToken > ( ) ) ;
1180+ }
1181+
1182+ [ Fact ]
1183+ public async Task GetNonSuccessAsync_Does_Not_Retry_On_Redirect_Status ( )
1184+ {
1185+ // Arrange
1186+ var handlerMock = new Mock < HttpMessageHandler > ( ) ;
1187+ handlerMock
1188+ . Protected ( )
1189+ . Setup < Task < HttpResponseMessage > > (
1190+ "SendAsync" ,
1191+ ItExpr . Is < HttpRequestMessage > ( req => req . Method == HttpMethod . Get ) ,
1192+ ItExpr . IsAny < CancellationToken > ( ) )
1193+ . ReturnsAsync ( CreateHttpResponseFactory ( statusCode : HttpStatusCode . MovedPermanently , content : "Moved" ) ) ;
1194+
1195+ using var httpClient = new HttpClient ( handlerMock . Object ) ;
1196+ var githubClient = new GithubClient ( _mockOctoLogger . Object , httpClient , null , _retryPolicy , _dateTimeProvider . Object , PERSONAL_ACCESS_TOKEN ) ;
1197+
1198+ // Act & Assert - should throw immediately without retry on redirect
1199+ await FluentActions
1200+ . Invoking ( async ( ) => await githubClient . GetNonSuccessAsync ( URL , HttpStatusCode . NotFound ) )
1201+ . Should ( )
1202+ . ThrowExactlyAsync < HttpRequestException > ( )
1203+ . WithMessage ( "*MovedPermanently*" ) ;
1204+
1205+ handlerMock . Protected ( ) . Verify (
1206+ "SendAsync" ,
1207+ Times . Once ( ) ,
1208+ ItExpr . Is < HttpRequestMessage > ( req => req . Method == HttpMethod . Get ) ,
1209+ ItExpr . IsAny < CancellationToken > ( ) ) ;
10871210 }
10881211
10891212 [ Fact ]
0 commit comments