Skip to content

Commit 94ab8f9

Browse files
committed
Forcing a content-type header to be specified for both request and response if a body has been set
1 parent 9533ba9 commit 94ab8f9

File tree

10 files changed

+170
-68
lines changed

10 files changed

+170
-68
lines changed

PactNet.Tests/Mocks/MockHttpService/MockProviderServiceTests.cs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,28 @@ public void With_WithNullRequest_ThrowsArgumentException()
171171
Assert.Throws<ArgumentException>(() => mockService.With(null));
172172
}
173173

174+
[Fact]
175+
public void With_WithRequestThatContainsABodyAndNoContentType_ThrowsArgumentException()
176+
{
177+
var description = "My description";
178+
var request = new ProviderServiceRequest
179+
{
180+
Method = HttpVerb.Head,
181+
Path = "/tester/testing/1",
182+
Body = new
183+
{
184+
tester = 1
185+
}
186+
};
187+
188+
var mockService = GetSubject();
189+
mockService.Start();
190+
191+
mockService.UponReceiving(description);
192+
193+
Assert.Throws<ArgumentException>(() => mockService.With(request));
194+
}
195+
174196
[Fact]
175197
public void WillRespondWith_WithNullResponse_ThrowsArgumentException()
176198
{
@@ -201,6 +223,31 @@ public void WillRespondWith_WithNullRequest_ThrowsInvalidOperationException()
201223
Assert.Throws<InvalidOperationException>(() => mockService.WillRespondWith(new ProviderServiceResponse()));
202224
}
203225

226+
[Fact]
227+
public void WillRespondWith_WithResponseThatContainsABodyAndNoContentType_ThrowsArgumentException()
228+
{
229+
var providerState = "My provider state";
230+
var description = "My description";
231+
var request = new ProviderServiceRequest();
232+
var response = new ProviderServiceResponse
233+
{
234+
Status = (int)HttpStatusCode.OK,
235+
Body = new
236+
{
237+
tester = 1
238+
}
239+
};
240+
241+
var mockService = GetSubject();
242+
243+
mockService
244+
.Given(providerState)
245+
.UponReceiving(description)
246+
.With(request);
247+
248+
Assert.Throws<ArgumentException>(() => mockService.WillRespondWith(response));
249+
}
250+
204251
[Fact]
205252
public void WillRespondWith_WhenHostIsNull_ThrowsInvalidOperationException()
206253
{

PactNet/Mocks/MockHttpService/MockProviderService.cs

Lines changed: 58 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.Net;
34
using System.Net.Http;
45
using System.Text;
@@ -79,6 +80,11 @@ public IMockProviderService With(ProviderServiceRequest request)
7980
throw new ArgumentException("Please supply a non null request");
8081
}
8182

83+
if (!IsContentTypeSpecifiedForBody(request))
84+
{
85+
throw new ArgumentException("Please supply a Content-Type request header");
86+
}
87+
8288
_request = request;
8389

8490
return this;
@@ -91,6 +97,11 @@ public void WillRespondWith(ProviderServiceResponse response)
9197
throw new ArgumentException("Please supply a non null response");
9298
}
9399

100+
if (!IsContentTypeSpecifiedForBody(response))
101+
{
102+
throw new ArgumentException("Please supply a Content-Type response header");
103+
}
104+
94105
_response = response;
95106

96107
RegisterInteraction();
@@ -101,36 +112,6 @@ public void VerifyInteractions()
101112
SendAdminHttpRequest(HttpVerb.Get, Constants.InteractionsVerificationPath);
102113
}
103114

104-
private void RegisterInteraction()
105-
{
106-
if (String.IsNullOrEmpty(_description))
107-
{
108-
throw new InvalidOperationException("description has not been set, please supply using the UponReceiving method.");
109-
}
110-
111-
if (_request == null)
112-
{
113-
throw new InvalidOperationException("request has not been set, please supply using the With method.");
114-
}
115-
116-
if (_response == null)
117-
{
118-
throw new InvalidOperationException("response has not been set, please supply using the WillRespondWith method.");
119-
}
120-
121-
var interaction = new ProviderServiceInteraction
122-
{
123-
ProviderState = _providerState,
124-
Description = _description,
125-
Request = _request,
126-
Response = _response
127-
};
128-
129-
SendAdminHttpRequest(HttpVerb.Post, Constants.InteractionsPath, interaction);
130-
131-
ClearTrasientState();
132-
}
133-
134115
public void Start()
135116
{
136117
StopRunningHost();
@@ -187,6 +168,36 @@ public void SendAdminHttpRequest<T>(HttpVerb method, string path, T requestConte
187168
}
188169
}
189170

171+
private void RegisterInteraction()
172+
{
173+
if (String.IsNullOrEmpty(_description))
174+
{
175+
throw new InvalidOperationException("description has not been set, please supply using the UponReceiving method.");
176+
}
177+
178+
if (_request == null)
179+
{
180+
throw new InvalidOperationException("request has not been set, please supply using the With method.");
181+
}
182+
183+
if (_response == null)
184+
{
185+
throw new InvalidOperationException("response has not been set, please supply using the WillRespondWith method.");
186+
}
187+
188+
var interaction = new ProviderServiceInteraction
189+
{
190+
ProviderState = _providerState,
191+
Description = _description,
192+
Request = _request,
193+
Response = _response
194+
};
195+
196+
SendAdminHttpRequest(HttpVerb.Post, Constants.InteractionsPath, interaction);
197+
198+
ClearTrasientState();
199+
}
200+
190201
private void SendAdminHttpRequest(HttpVerb method, string path)
191202
{
192203
SendAdminHttpRequest<object>(method, path, null);
@@ -215,6 +226,23 @@ private void ClearTrasientState()
215226
_description = null;
216227
}
217228

229+
private bool IsContentTypeSpecifiedForBody(IHttpMessage message)
230+
{
231+
//No content-type required if there is no body
232+
if (message.Body == null)
233+
{
234+
return true;
235+
}
236+
237+
IDictionary<string, string> headers = null;
238+
if (message.Headers != null)
239+
{
240+
headers = new Dictionary<string, string>(message.Headers, StringComparer.InvariantCultureIgnoreCase);
241+
}
242+
243+
return headers != null && headers.ContainsKey("Content-Type");
244+
}
245+
218246
private void Dispose(IDisposable disposable)
219247
{
220248
if (disposable != null)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using System.Collections.Generic;
2+
3+
namespace PactNet.Mocks.MockHttpService.Models
4+
{
5+
public interface IHttpMessage
6+
{
7+
IDictionary<string, string> Headers { get; set; }
8+
dynamic Body { get; set; }
9+
}
10+
}

PactNet/Mocks/MockHttpService/Models/ProviderServiceRequest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
namespace PactNet.Mocks.MockHttpService.Models
77
{
8-
public class ProviderServiceRequest
8+
public class ProviderServiceRequest : IHttpMessage
99
{
1010
[JsonProperty(PropertyName = "method")]
1111
[JsonConverter(typeof(LowercaseStringEnumConverter))]

PactNet/Mocks/MockHttpService/Models/ProviderServiceResponse.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
namespace PactNet.Mocks.MockHttpService.Models
55
{
6-
public class ProviderServiceResponse
6+
public class ProviderServiceResponse : IHttpMessage
77
{
88
[JsonProperty(PropertyName = "status")]
99
public int Status { get; set; }

PactNet/PactNet.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@
105105
<Compile Include="Mocks\MockHttpService\Configuration\NancyConfig.cs" />
106106
<Compile Include="Mocks\MockHttpService\Constants.cs" />
107107
<Compile Include="Mocks\MockHttpService\IHttpHost.cs" />
108+
<Compile Include="Mocks\MockHttpService\Models\IHttpMessage.cs" />
108109
<Compile Include="Mocks\MockHttpService\Nancy\NancyHttpHost.cs" />
109110
<Compile Include="Mocks\MockHttpService\Nancy\IMockProviderAdminRequestHandler.cs" />
110111
<Compile Include="Mocks\MockHttpService\Nancy\IMockProviderNancyRequestHandler.cs" />

Samples/EventApi/Consumer.Tests/EventsApiConsumerTests.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,12 +122,17 @@ public void IsAlive_WhenApiIsAlive_ReturnsTrue()
122122
.With(new ProviderServiceRequest
123123
{
124124
Method = HttpVerb.Get,
125+
Headers = new Dictionary<string, string> { { "Accept", "application/json" } },
125126
Path = "/stats/status"
126127
})
127128
.WillRespondWith(new ProviderServiceResponse
128129
{
129130
Status = 200,
130-
Body = "alive"
131+
Headers = new Dictionary<string, string> { { "Content-Type", "application/json; charset=utf-8" } },
132+
Body = new
133+
{
134+
alive = true
135+
}
131136
});
132137

133138
var consumer = new EventsApiClient(_mockProviderServiceBaseUri);

Samples/EventApi/Consumer.Tests/pacts/consumer-event_api.json

Lines changed: 39 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,6 @@
66
"name": "Consumer"
77
},
88
"interactions": [
9-
{
10-
"description": "A GET request to check the api status",
11-
"request": {
12-
"method": "get",
13-
"path": "/stats/status"
14-
},
15-
"response": {
16-
"status": 200,
17-
"body": "alive"
18-
}
19-
},
209
{
2110
"description": "A POST request to create a new event",
2211
"request": {
@@ -35,6 +24,45 @@
3524
"status": 201
3625
}
3726
},
27+
{
28+
"description": "A GET request to check the api status",
29+
"request": {
30+
"method": "get",
31+
"path": "/stats/status",
32+
"headers": {
33+
"Accept": "application/json"
34+
}
35+
},
36+
"response": {
37+
"status": 200,
38+
"headers": {
39+
"Content-Type": "application/json; charset=utf-8"
40+
},
41+
"body": {
42+
"alive": true
43+
}
44+
}
45+
},
46+
{
47+
"description": "A GET request to retrieve event with id '83f9262f-28f1-4703-ab1a-8cfd9e8249c9'",
48+
"provider_state": "There is an event with id '83f9262f-28f1-4703-ab1a-8cfd9e8249c9'",
49+
"request": {
50+
"method": "get",
51+
"path": "/events/83f9262f-28f1-4703-ab1a-8cfd9e8249c9",
52+
"headers": {
53+
"Accept": "application/json"
54+
}
55+
},
56+
"response": {
57+
"status": 200,
58+
"headers": {
59+
"Content-Type": "application/json; charset=utf-8"
60+
},
61+
"body": {
62+
"eventId": "83f9262f-28f1-4703-ab1a-8cfd9e8249c9"
63+
}
64+
}
65+
},
3866
{
3967
"description": "A GET request to retrieve all events",
4068
"provider_state": "There are events with ids '45D80D13-D5A2-48D7-8353-CBB4C0EAABF5', '83F9262F-28F1-4703-AB1A-8CFD9E8249C9' and '3E83A96B-2A0C-49B1-9959-26DF23F83AEB'",
@@ -91,26 +119,6 @@
91119
}
92120
]
93121
}
94-
},
95-
{
96-
"description": "A GET request to retrieve event with id '83f9262f-28f1-4703-ab1a-8cfd9e8249c9'",
97-
"provider_state": "There is an event with id '83f9262f-28f1-4703-ab1a-8cfd9e8249c9'",
98-
"request": {
99-
"method": "get",
100-
"path": "/events/83f9262f-28f1-4703-ab1a-8cfd9e8249c9",
101-
"headers": {
102-
"Accept": "application/json"
103-
}
104-
},
105-
"response": {
106-
"status": 200,
107-
"headers": {
108-
"Content-Type": "application/json; charset=utf-8"
109-
},
110-
"body": {
111-
"eventId": "83f9262f-28f1-4703-ab1a-8cfd9e8249c9"
112-
}
113-
}
114122
}
115123
],
116124
"metadata": {

Samples/EventApi/Consumer/EventsApiClient.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,18 @@ public bool IsAlive()
2929
using (var client = HttpClient())
3030
{
3131
var request = new HttpRequestMessage(HttpMethod.Get, "/stats/status");
32+
request.Headers.Add("Accept", "application/json");
33+
3234
var response = client.SendAsync(request);
3335

3436
var result = response.Result;
3537
var content = result.Content.ReadAsStringAsync().Result;
3638
var status = result.StatusCode;
3739

38-
if (status == HttpStatusCode.OK && content.Equals("alive"))
40+
if (status == HttpStatusCode.OK)
3941
{
40-
return true;
42+
var responseContent = JsonConvert.DeserializeObject<dynamic>(content, _jsonSettings);
43+
return responseContent.alive;
4144
}
4245

4346
request.Dispose();

Samples/EventApi/Provider.Api.Web/Controllers/StatsController.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ namespace Provider.Api.Web.Controllers
55
public class StatsController : ApiController
66
{
77
[Route("stats/status")]
8-
public string GetAlive()
8+
public dynamic GetAlive()
99
{
10-
return "alive";
10+
return new { alive = true };
1111
}
1212
}
1313
}

0 commit comments

Comments
 (0)