-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathGetMovieUnhappyTests.cs
185 lines (155 loc) · 8.82 KB
/
GetMovieUnhappyTests.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
using FluentAssertions;
using FluentAssertions.Execution;
using MovieProject.Web;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using SystemTestingTools;
using Xunit;
namespace IsolatedTests.ComponentTestings
{
[Collection("SharedServer collection")]
[Trait("Project", "MovieProject Component Tests (Unhappy)")]
public class GetMovieUnhappyTests
{
private readonly TestServerFixture Fixture;
private static string MovieUrl = "http://www.omdbapi.com/?apikey=863d6589&type=movie";
private static string MatrixMovieUrl = $"{MovieUrl}&t=matrix";
public GetMovieUnhappyTests(TestServerFixture fixture)
{
Fixture = fixture;
}
[Fact]
public async Task When_CallThrowsException_Then_LogError_And_Return_Downstream_Error()
{
// arrange
var client = Fixture.Server.CreateClient();
client.CreateSession();
var exception = new HttpRequestException("weird network error");
// add exception twice because we configured a retry
client.AppendHttpCallStub(HttpMethod.Get, new System.Uri(MatrixMovieUrl), exception, counter: 2);
// act
var httpResponse = await client.GetAsync("/api/movie/matrix");
using (new AssertionScope())
{
// assert logs
var logs = client.GetSessionLogs();
logs.Count.Should().Be(1);
logs[0].ToString().Should().StartWith($"Critical: GET {MatrixMovieUrl} threw exception [weird network error]");
// assert outgoing
var outgoingRequests = client.GetSessionOutgoingRequests();
outgoingRequests.Count.Should().Be(2); // because of retry
AssertMatrixEndpointCalled(outgoingRequests[0]);
AssertMatrixEndpointCalled(outgoingRequests[1]);
// assert return
httpResponse.StatusCode.Should().Be(HttpStatusCode.InternalServerError);
var message = await httpResponse.ReadBody();
message.Should().Be(Constants.DownstreamErrorMessage);
}
}
[InlineData(HttpStatusCode.TooManyRequests, "Fake_Responses/Unhappy/429_TooManyRequests_ProperlyFormatted.txt", "Too many requests with your api key")]
[InlineData(HttpStatusCode.TooManyRequests, "Fake_Responses/Unhappy/429_TooManyRequests_RawFormatted.txt", "Too many requests with your api key")]
[InlineData(HttpStatusCode.InternalServerError, "Fake_Responses/Unhappy/500_InternalServerError.txt", "<title>Internal Server Error</title>")]
[InlineData(HttpStatusCode.ServiceUnavailable, "Fake_Responses/Unhappy/503_ServiceUnailable.txt", "The service is temporarily unavailable")]
[InlineData(HttpStatusCode.OK, "Real_Responses/Unhappy/200_SomethingWentWrong_WhenSendingEmptyMovieName.txt", "Something went wrong")]
[InlineData(HttpStatusCode.Unauthorized, "Real_Responses/Unhappy/401_InvalidKey.txt", "Invalid API key!")]
[InlineData(HttpStatusCode.Unauthorized, "Real_Responses/Unhappy/401_LimitReached.txt", "Request limit reached")]
[InlineData(HttpStatusCode.RequestTimeout, "Fake_Responses/Unhappy/408_Timeout.txt", " ")]
[Theory]
public async Task When_DownstreamSystemReturnsError_Then_LogError_And_ReturnDefaultErrorMessage(HttpStatusCode httpStatus, string fileName, string logContent)
{
// arrange
// errors that are worth retrying
bool isTransientDownstreamError = (int)httpStatus >= 500 || httpStatus == HttpStatusCode.RequestTimeout;
var client = Fixture.Server.CreateClient();
client.CreateSession();
var response = ResponseFactory.FromFiddlerLikeResponseFile($"{Fixture.StubsFolder}/OmdbApi/{fileName}");
// we add it twice to account for the recall attempt
client.AppendHttpCallStub(HttpMethod.Get, new System.Uri(MatrixMovieUrl), response);
if(isTransientDownstreamError) client.AppendHttpCallStub(HttpMethod.Get, new System.Uri(MatrixMovieUrl), response);
// act
var httpResponse = await client.GetAsync("/api/movie/matrix");
using (new AssertionScope())
{
// assert logs
var logs = client.GetSessionLogs();
logs.Count.Should().Be(1);
// we check that we log downstream errors specifically with extra details so we can easily debug, the format should be
// Critical: URL returned invalid response: http status=XXX and body [FULL RESPONSE BODY HERE]
logs[0].ToString().Should().StartWith($"Critical: GET {MatrixMovieUrl} had exception");
logs[0].Message.Should().Contain(logContent);
logs[0].Message.Should().Contain($" response HttpStatus={(int)httpStatus} and body=[");
// assert outgoing
var outgoingRequests = client.GetSessionOutgoingRequests();
outgoingRequests.Count.Should().Be(isTransientDownstreamError ? 2 : 1); // 2 calls because we configured a retry
AssertMatrixEndpointCalled(outgoingRequests[0]);
if (isTransientDownstreamError)
{
AssertMatrixEndpointCalled(outgoingRequests[1]);
}
// assert return
httpResponse.StatusCode.Should().Be(HttpStatusCode.InternalServerError);
var message = await httpResponse.ReadBody();
message.Should().Be(Constants.DownstreamErrorMessage);
}
}
[Fact]
public async Task When_DownstreamSystemReturnsInvalidJson_Then_LogError_And_ReturnDefaultErrorMessage()
{
// arrange
var client = Fixture.Server.CreateClient();
client.CreateSession();
var response = ResponseFactory.FromFiddlerLikeResponseFile($"{Fixture.StubsFolder}/OmdbApi/Fake_Responses/Unhappy/200_Unexpected_Json.txt");
// we add it twice to account for the recall attempt
client.AppendHttpCallStub(HttpMethod.Get, new System.Uri(MatrixMovieUrl), response);
// act
var httpResponse = await client.GetAsync("/api/movie/matrix");
using (new AssertionScope())
{
// assert logs
var logs = client.GetSessionLogs();
logs.Count.Should().Be(1);
// we check that we log downstream errors specifically with extra details so we can easily debug, the format should be
// Critical: URL returned invalid response: http status=XXX and body [FULL RESPONSE BODY HERE]
logs[0].ToString().Should().StartWith($"Critical: GET {MatrixMovieUrl} had exception [DTO is invalid] while [processing response], response HttpStatus=200 and body=[");
logs[0].Message.Should().Contain(@"""weirdRoot"":");
// assert outgoing
var outgoingRequests = client.GetSessionOutgoingRequests();
AssertMatrixEndpointCalled(outgoingRequests[0]);
// assert return
httpResponse.StatusCode.Should().Be(HttpStatusCode.InternalServerError);
var message = await httpResponse.ReadBody();
message.Should().Be(Constants.DownstreamErrorMessage);
}
}
[InlineData("movie/ a", "name has too few characters", "a")]
[InlineData("movie/1 ", "name has too few characters", "1")]
[Theory]
public async Task When_UserDoesntSendNecessaryInput_Then_LogWarning_And_ReturnBadRequestMessage(string route, string correctMessage, string culprit)
{
// arrange
var client = Fixture.Server.CreateClient();
client.CreateSession();
// act
var httpResponse = await client.GetAsync($"/api/{route}");
// assert logs
using (new AssertionScope())
{
var logs = client.GetSessionLogs();
logs.Count.Should().Be(1);
logs[0].ToString().Should().Be($"Warning: Bad request={correctMessage} = [{culprit}]");
// assert outgoing
client.GetSessionOutgoingRequests().Count.Should().Be(0);
// assert return
httpResponse.StatusCode.Should().Be(HttpStatusCode.BadRequest);
var message = await httpResponse.ReadBody();
message.Should().Be($"{correctMessage} = [{culprit}]");
}
}
private void AssertMatrixEndpointCalled(HttpRequestMessage request)
{
request.GetEndpoint().Should().Be($"GET {MatrixMovieUrl}");
request.GetHeaderValue("Referer").Should().Be(MovieProject.Logic.Constants.Website);
}
}
}