Skip to content

Commit

Permalink
Forcing a content-type header to be specified for both request and re…
Browse files Browse the repository at this point in the history
…sponse if a body has been set
  • Loading branch information
neilcampbell committed Nov 30, 2014
1 parent 9533ba9 commit 94ab8f9
Show file tree
Hide file tree
Showing 10 changed files with 170 additions and 68 deletions.
47 changes: 47 additions & 0 deletions PactNet.Tests/Mocks/MockHttpService/MockProviderServiceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,28 @@ public void With_WithNullRequest_ThrowsArgumentException()
Assert.Throws<ArgumentException>(() => mockService.With(null));
}

[Fact]
public void With_WithRequestThatContainsABodyAndNoContentType_ThrowsArgumentException()
{
var description = "My description";
var request = new ProviderServiceRequest
{
Method = HttpVerb.Head,
Path = "/tester/testing/1",
Body = new
{
tester = 1
}
};

var mockService = GetSubject();
mockService.Start();

mockService.UponReceiving(description);

Assert.Throws<ArgumentException>(() => mockService.With(request));
}

[Fact]
public void WillRespondWith_WithNullResponse_ThrowsArgumentException()
{
Expand Down Expand Up @@ -201,6 +223,31 @@ public void WillRespondWith_WithNullRequest_ThrowsInvalidOperationException()
Assert.Throws<InvalidOperationException>(() => mockService.WillRespondWith(new ProviderServiceResponse()));
}

[Fact]
public void WillRespondWith_WithResponseThatContainsABodyAndNoContentType_ThrowsArgumentException()
{
var providerState = "My provider state";
var description = "My description";
var request = new ProviderServiceRequest();
var response = new ProviderServiceResponse
{
Status = (int)HttpStatusCode.OK,
Body = new
{
tester = 1
}
};

var mockService = GetSubject();

mockService
.Given(providerState)
.UponReceiving(description)
.With(request);

Assert.Throws<ArgumentException>(() => mockService.WillRespondWith(response));
}

[Fact]
public void WillRespondWith_WhenHostIsNull_ThrowsInvalidOperationException()
{
Expand Down
88 changes: 58 additions & 30 deletions PactNet/Mocks/MockHttpService/MockProviderService.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Text;
Expand Down Expand Up @@ -79,6 +80,11 @@ public IMockProviderService With(ProviderServiceRequest request)
throw new ArgumentException("Please supply a non null request");
}

if (!IsContentTypeSpecifiedForBody(request))
{
throw new ArgumentException("Please supply a Content-Type request header");
}

_request = request;

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

if (!IsContentTypeSpecifiedForBody(response))
{
throw new ArgumentException("Please supply a Content-Type response header");
}

_response = response;

RegisterInteraction();
Expand All @@ -101,36 +112,6 @@ public void VerifyInteractions()
SendAdminHttpRequest(HttpVerb.Get, Constants.InteractionsVerificationPath);
}

private void RegisterInteraction()
{
if (String.IsNullOrEmpty(_description))
{
throw new InvalidOperationException("description has not been set, please supply using the UponReceiving method.");
}

if (_request == null)
{
throw new InvalidOperationException("request has not been set, please supply using the With method.");
}

if (_response == null)
{
throw new InvalidOperationException("response has not been set, please supply using the WillRespondWith method.");
}

var interaction = new ProviderServiceInteraction
{
ProviderState = _providerState,
Description = _description,
Request = _request,
Response = _response
};

SendAdminHttpRequest(HttpVerb.Post, Constants.InteractionsPath, interaction);

ClearTrasientState();
}

public void Start()
{
StopRunningHost();
Expand Down Expand Up @@ -187,6 +168,36 @@ public void SendAdminHttpRequest<T>(HttpVerb method, string path, T requestConte
}
}

private void RegisterInteraction()
{
if (String.IsNullOrEmpty(_description))
{
throw new InvalidOperationException("description has not been set, please supply using the UponReceiving method.");
}

if (_request == null)
{
throw new InvalidOperationException("request has not been set, please supply using the With method.");
}

if (_response == null)
{
throw new InvalidOperationException("response has not been set, please supply using the WillRespondWith method.");
}

var interaction = new ProviderServiceInteraction
{
ProviderState = _providerState,
Description = _description,
Request = _request,
Response = _response
};

SendAdminHttpRequest(HttpVerb.Post, Constants.InteractionsPath, interaction);

ClearTrasientState();
}

private void SendAdminHttpRequest(HttpVerb method, string path)
{
SendAdminHttpRequest<object>(method, path, null);
Expand Down Expand Up @@ -215,6 +226,23 @@ private void ClearTrasientState()
_description = null;
}

private bool IsContentTypeSpecifiedForBody(IHttpMessage message)
{
//No content-type required if there is no body
if (message.Body == null)
{
return true;
}

IDictionary<string, string> headers = null;
if (message.Headers != null)
{
headers = new Dictionary<string, string>(message.Headers, StringComparer.InvariantCultureIgnoreCase);
}

return headers != null && headers.ContainsKey("Content-Type");
}

private void Dispose(IDisposable disposable)
{
if (disposable != null)
Expand Down
10 changes: 10 additions & 0 deletions PactNet/Mocks/MockHttpService/Models/IHttpMessage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System.Collections.Generic;

namespace PactNet.Mocks.MockHttpService.Models
{
public interface IHttpMessage
{
IDictionary<string, string> Headers { get; set; }
dynamic Body { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

namespace PactNet.Mocks.MockHttpService.Models
{
public class ProviderServiceRequest
public class ProviderServiceRequest : IHttpMessage
{
[JsonProperty(PropertyName = "method")]
[JsonConverter(typeof(LowercaseStringEnumConverter))]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace PactNet.Mocks.MockHttpService.Models
{
public class ProviderServiceResponse
public class ProviderServiceResponse : IHttpMessage
{
[JsonProperty(PropertyName = "status")]
public int Status { get; set; }
Expand Down
1 change: 1 addition & 0 deletions PactNet/PactNet.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@
<Compile Include="Mocks\MockHttpService\Configuration\NancyConfig.cs" />
<Compile Include="Mocks\MockHttpService\Constants.cs" />
<Compile Include="Mocks\MockHttpService\IHttpHost.cs" />
<Compile Include="Mocks\MockHttpService\Models\IHttpMessage.cs" />
<Compile Include="Mocks\MockHttpService\Nancy\NancyHttpHost.cs" />
<Compile Include="Mocks\MockHttpService\Nancy\IMockProviderAdminRequestHandler.cs" />
<Compile Include="Mocks\MockHttpService\Nancy\IMockProviderNancyRequestHandler.cs" />
Expand Down
7 changes: 6 additions & 1 deletion Samples/EventApi/Consumer.Tests/EventsApiConsumerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -122,12 +122,17 @@ public void IsAlive_WhenApiIsAlive_ReturnsTrue()
.With(new ProviderServiceRequest
{
Method = HttpVerb.Get,
Headers = new Dictionary<string, string> { { "Accept", "application/json" } },
Path = "/stats/status"
})
.WillRespondWith(new ProviderServiceResponse
{
Status = 200,
Body = "alive"
Headers = new Dictionary<string, string> { { "Content-Type", "application/json; charset=utf-8" } },
Body = new
{
alive = true
}
});

var consumer = new EventsApiClient(_mockProviderServiceBaseUri);
Expand Down
70 changes: 39 additions & 31 deletions Samples/EventApi/Consumer.Tests/pacts/consumer-event_api.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,6 @@
"name": "Consumer"
},
"interactions": [
{
"description": "A GET request to check the api status",
"request": {
"method": "get",
"path": "/stats/status"
},
"response": {
"status": 200,
"body": "alive"
}
},
{
"description": "A POST request to create a new event",
"request": {
Expand All @@ -35,6 +24,45 @@
"status": 201
}
},
{
"description": "A GET request to check the api status",
"request": {
"method": "get",
"path": "/stats/status",
"headers": {
"Accept": "application/json"
}
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json; charset=utf-8"
},
"body": {
"alive": true
}
}
},
{
"description": "A GET request to retrieve event with id '83f9262f-28f1-4703-ab1a-8cfd9e8249c9'",
"provider_state": "There is an event with id '83f9262f-28f1-4703-ab1a-8cfd9e8249c9'",
"request": {
"method": "get",
"path": "/events/83f9262f-28f1-4703-ab1a-8cfd9e8249c9",
"headers": {
"Accept": "application/json"
}
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json; charset=utf-8"
},
"body": {
"eventId": "83f9262f-28f1-4703-ab1a-8cfd9e8249c9"
}
}
},
{
"description": "A GET request to retrieve all events",
"provider_state": "There are events with ids '45D80D13-D5A2-48D7-8353-CBB4C0EAABF5', '83F9262F-28F1-4703-AB1A-8CFD9E8249C9' and '3E83A96B-2A0C-49B1-9959-26DF23F83AEB'",
Expand Down Expand Up @@ -91,26 +119,6 @@
}
]
}
},
{
"description": "A GET request to retrieve event with id '83f9262f-28f1-4703-ab1a-8cfd9e8249c9'",
"provider_state": "There is an event with id '83f9262f-28f1-4703-ab1a-8cfd9e8249c9'",
"request": {
"method": "get",
"path": "/events/83f9262f-28f1-4703-ab1a-8cfd9e8249c9",
"headers": {
"Accept": "application/json"
}
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json; charset=utf-8"
},
"body": {
"eventId": "83f9262f-28f1-4703-ab1a-8cfd9e8249c9"
}
}
}
],
"metadata": {
Expand Down
7 changes: 5 additions & 2 deletions Samples/EventApi/Consumer/EventsApiClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,18 @@ public bool IsAlive()
using (var client = HttpClient())
{
var request = new HttpRequestMessage(HttpMethod.Get, "/stats/status");
request.Headers.Add("Accept", "application/json");

var response = client.SendAsync(request);

var result = response.Result;
var content = result.Content.ReadAsStringAsync().Result;
var status = result.StatusCode;

if (status == HttpStatusCode.OK && content.Equals("alive"))
if (status == HttpStatusCode.OK)
{
return true;
var responseContent = JsonConvert.DeserializeObject<dynamic>(content, _jsonSettings);
return responseContent.alive;
}

request.Dispose();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ namespace Provider.Api.Web.Controllers
public class StatsController : ApiController
{
[Route("stats/status")]
public string GetAlive()
public dynamic GetAlive()
{
return "alive";
return new { alive = true };
}
}
}

0 comments on commit 94ab8f9

Please sign in to comment.