Skip to content

Commit

Permalink
A few tweaks to the pact broker basic auth support
Browse files Browse the repository at this point in the history
  • Loading branch information
Neil Campbell authored and Neil Campbell committed Sep 21, 2015
1 parent db67647 commit aa7fd5f
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 113 deletions.
2 changes: 1 addition & 1 deletion PactNet.Tests/PactNet.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@
<Compile Include="Models\ProviderStateTests.cs" />
<Compile Include="PactConfigTests.cs" />
<Compile Include="PactBuilderTests.cs" />
<Compile Include="PactUriOptionsTests.cs" />
<Compile Include="ProviderServicePactFileTests.cs" />
<Compile Include="PactVerifierTests.cs" />
<Compile Include="Reporters\ReporterTests.cs" />
Expand All @@ -151,7 +152,6 @@
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
Expand Down
49 changes: 49 additions & 0 deletions PactNet.Tests/PactUriOptionsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System;
using Xunit;

namespace PactNet.Tests
{
public class PactUriOptionsTests
{
[Fact]
public void Ctor_WithEmptyUsername_ThrowsArgumentException()
{
const string username = "";
const string password = "somepassword";

Assert.Throws<ArgumentException>(() => new PactUriOptions(username, password));
}

[Fact]
public void Ctor_WithInvalidUsername_ThrowsArgumentException()
{
const string username = "some:user";
const string password = "somepassword";

Assert.Throws<ArgumentException>(() => new PactUriOptions(username, password));
}

[Fact]
public void Ctor_WithEmptyPassword_ThrowsArgumentException()
{
const string username = "someuser";
const string password = "";

Assert.Throws<ArgumentException>(() => new PactUriOptions(username, password));
}

[Fact]
public void Ctor_WithValidUsernameAndPassword_ReturnsCorrectAuthorizationSchemeAndValue()
{
const string username = "Aladdin";
const string password = "open sesame";
var expectedAuthScheme = "Basic";
var expectedAuthValue = "QWxhZGRpbjpvcGVuIHNlc2FtZQ==";

var options = new PactUriOptions(username, password);

Assert.Equal(expectedAuthScheme, options.AuthorizationScheme);
Assert.Equal(expectedAuthValue, options.AuthorizationValue);
}
}
}
67 changes: 9 additions & 58 deletions PactNet.Tests/PactVerifierTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -315,17 +315,12 @@ public void Verify_WithHttpPactFileUri_CallsHttpClientWithJsonGetRequest()
}

[Fact]
public void Verify_WithHttpsPactFileUri_AndBasicAuthUriOptions_CallsHttpClientWithJsonGetRequest()
public void Verify_WithHttpsPactFileUri_CallsHttpClientWithJsonGetRequest()
{
var serviceProvider = "Event API";
var serviceConsumer = "My client";
var pactUri = "https://yourpactserver.com/getlatestpactfile";
var pactFileJson = "{ \"provider\": { \"name\": \"Event API\" }, \"consumer\": { \"name\": \"My client\" }, \"interactions\": [{ \"description\": \"My Description\", \"provider_state\": \"My Provider State\" }, { \"description\": \"My Description 2\", \"provider_state\": \"My Provider State\" }, { \"description\": \"My Description\", \"provider_state\": \"My Provider State 2\" }], \"metadata\": { \"pactSpecificationVersion\": \"1.0.0\" } }";
var basicAuthUserName = "someuser";
var basicAuthPassword = "somepassword";
var expectedAuthHeader = Convert.ToBase64String(
Encoding.UTF8.GetBytes(
String.Format("{0}:{1}", basicAuthUserName, basicAuthPassword)));

var pactVerifier = GetSubject();

Expand All @@ -337,44 +332,22 @@ public void Verify_WithHttpsPactFileUri_AndBasicAuthUriOptions_CallsHttpClientWi
pactVerifier
.ServiceProvider(serviceProvider, new HttpClient())
.HonoursPactWith(serviceConsumer)
.PactUri(pactUri, new PactUriOptions(basicAuthUserName, basicAuthPassword));
.PactUri(pactUri);

pactVerifier.Verify();
Assert.Equal(_fakeHttpMessageHandler.RequestsRecieved.Single().Headers.Authorization.Parameter,
expectedAuthHeader);

Assert.Equal(HttpMethod.Get, _fakeHttpMessageHandler.RequestsRecieved.Single().Method);
Assert.Equal("application/json", _fakeHttpMessageHandler.RequestsRecieved.Single().Headers.Single(x => x.Key == "Accept").Value.Single());
}

[Fact]
public void PactUri_WithBasicAuthUriOptions_InvalidPassword_ThrowsApplicationException()
public void Verify_WithHttpsPactFileUriAndBasicAuthUriOptions_CallsHttpClientWithJsonGetRequestAndBasicAuthorizationHeader()
{
var serviceProvider = "Event API";
var serviceConsumer = "My client";
var pactUri = "https://yourpactserver.com/getlatestpactfile";
var basicAuthUserName = "someuser";
var basicAuthPassword = "";

var pactVerifier = GetSubject();

Assert.Throws<ApplicationException>(() => pactVerifier
.ServiceProvider(serviceProvider, new HttpClient())
.HonoursPactWith(serviceConsumer)
.PactUri(pactUri, new PactUriOptions(basicAuthUserName, basicAuthPassword)));
}

[Fact]
public void Verify_WithHttpsPactFileUri_AndBasicAuthUriOptions_InUrl_CallsHttpClientWithJsonGetRequest()
{
var serviceProvider = "Event API";
var serviceConsumer = "My client";
var pactUri = "https://someuser:[email protected]/getlatestpactfile";
var pactFileJson = "{ \"provider\": { \"name\": \"Event API\" }, \"consumer\": { \"name\": \"My client\" }, \"interactions\": [{ \"description\": \"My Description\", \"provider_state\": \"My Provider State\" }, { \"description\": \"My Description 2\", \"provider_state\": \"My Provider State\" }, { \"description\": \"My Description\", \"provider_state\": \"My Provider State 2\" }], \"metadata\": { \"pactSpecificationVersion\": \"1.0.0\" } }";
var basicAuthUserName = "someuser";
var basicAuthPassword = "somepassword";
var expectedAuthHeader = Convert.ToBase64String(
Encoding.UTF8.GetBytes(
String.Format("{0}:{1}", basicAuthUserName, basicAuthPassword)));
var options = new PactUriOptions("someuser", "somepassword");

var pactVerifier = GetSubject();

Expand All @@ -386,36 +359,14 @@ public void Verify_WithHttpsPactFileUri_AndBasicAuthUriOptions_InUrl_CallsHttpCl
pactVerifier
.ServiceProvider(serviceProvider, new HttpClient())
.HonoursPactWith(serviceConsumer)
.PactUri(pactUri);
.PactUri(pactUri, options);

pactVerifier.Verify();
Assert.Equal(_fakeHttpMessageHandler.RequestsRecieved.Single().Headers.Authorization.Parameter,
expectedAuthHeader);

Assert.Equal(HttpMethod.Get, _fakeHttpMessageHandler.RequestsRecieved.Single().Method);
Assert.Equal("application/json", _fakeHttpMessageHandler.RequestsRecieved.Single().Headers.Single(x => x.Key == "Accept").Value.Single());
}

[Fact]
public void Verify_WithHttpsPactFileUri_AndBasicAuthUriOptions_InUrl_InvalidUserInfo_ThrowsException()
{
var serviceProvider = "Event API";
var serviceConsumer = "My client";
var pactUri = "https://[email protected]/getlatestpactfile";
var pactFileJson = "{ \"provider\": { \"name\": \"Event API\" }, \"consumer\": { \"name\": \"My client\" }, \"interactions\": [{ \"description\": \"My Description\", \"provider_state\": \"My Provider State\" }, { \"description\": \"My Description 2\", \"provider_state\": \"My Provider State\" }, { \"description\": \"My Description\", \"provider_state\": \"My Provider State 2\" }], \"metadata\": { \"pactSpecificationVersion\": \"1.0.0\" } }";

var pactVerifier = GetSubject();

_fakeHttpMessageHandler.Response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(pactFileJson, Encoding.UTF8, "application/json")
};

pactVerifier
.ServiceProvider(serviceProvider, new HttpClient())
.HonoursPactWith(serviceConsumer)
.PactUri(pactUri);

Assert.Throws<InvalidOperationException>(() => pactVerifier.Verify());
Assert.Equal(_fakeHttpMessageHandler.RequestsRecieved.Single().Headers.Authorization.Scheme, options.AuthorizationScheme);
Assert.Equal(_fakeHttpMessageHandler.RequestsRecieved.Single().Headers.Authorization.Parameter, options.AuthorizationValue);
}

[Fact]
Expand Down
11 changes: 0 additions & 11 deletions PactNet.Tests/app.config

This file was deleted.

44 changes: 35 additions & 9 deletions PactNet/PactUriOptions.cs
Original file line number Diff line number Diff line change
@@ -1,22 +1,48 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace PactNet
{
public class PactUriOptions
{
public PactUriOptions(string basicAuthUserName, string basicAuthPassword)
private const string AuthScheme = "Basic";
private readonly string _username;
private readonly string _password;

internal string AuthorizationScheme
{
if (String.IsNullOrEmpty(basicAuthUserName) || String.IsNullOrEmpty(basicAuthPassword))
throw new ApplicationException("Invalid username or password");
get { return AuthScheme; }
}

this.BasicAuthUserName = basicAuthUserName;
this.BasicAuthPassword = basicAuthPassword;
internal string AuthorizationValue
{
get
{
return Convert.ToBase64String(
Encoding.UTF8.GetBytes(
String.Format("{0}:{1}", _username, _password)));
}
}

public string BasicAuthUserName { get; private set; }
public string BasicAuthPassword { get; private set; }
public PactUriOptions(string username, string password)
{
if (String.IsNullOrEmpty(username))
{
throw new ArgumentException("username is null or empty.");
}

if (username.Contains(":"))
{
throw new ArgumentException("username contains a ':' character, which is not allowed.");
}

if (String.IsNullOrEmpty(password))
{
throw new ArgumentException("password is null or empty.");
}

_username = username;
_password = password;
}
}
}
37 changes: 5 additions & 32 deletions PactNet/PactVerifier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -181,37 +181,13 @@ public void Verify(string description = null, string providerState = null)
if (IsWebUri(PactFileUri))
{
var request = new HttpRequestMessage(HttpMethod.Get, PactFileUri);
request.Headers.Add("Accept", "application/json");

if (PactUriOptions != null)
{
if (String.IsNullOrEmpty(PactUriOptions.BasicAuthUserName) ||
String.IsNullOrEmpty(PactUriOptions.BasicAuthPassword))
{
throw new ApplicationException("Invalid basic-auth username or password");
}
}
else
{
var uriObj = new Uri(PactFileUri);
if (!String.IsNullOrEmpty(uriObj.UserInfo))
{
if(!uriObj.UserInfo.Contains(":"))
throw new ApplicationException("Invalid basic-auth username or password in the url");

PactUriOptions = new PactUriOptions(uriObj.UserInfo.Split(':')[0], uriObj.UserInfo.Split(':')[1]);
}
request.Headers.Add("Authorization", String.Format("{0} {1}", PactUriOptions.AuthorizationScheme, PactUriOptions.AuthorizationValue));
}

if (PactUriOptions != null)
request.Headers.Add("Authorization", "Basic " +
Convert.ToBase64String(
Encoding.UTF8.GetBytes(
String.Format("{0}:{1}",
PactUriOptions.BasicAuthUserName,
PactUriOptions.BasicAuthPassword))));


request.Headers.Add("Accept", "application/json");
var response = _httpClient.SendAsync(request).Result;

try
Expand All @@ -234,8 +210,7 @@ public void Verify(string description = null, string providerState = null)
}
catch (Exception ex)
{
throw new InvalidOperationException(
String.Format("Json Pact file could not be retrieved using uri \'{0}\'.", PactFileUri), ex);
throw new InvalidOperationException(String.Format("Json Pact file could not be retrieved using uri \'{0}\'.", PactFileUri), ex);
}

//Filter interactions
Expand All @@ -252,12 +227,10 @@ public void Verify(string description = null, string providerState = null)
if ((description != null || providerState != null) &&
(pactFile.Interactions == null || !pactFile.Interactions.Any()))
{
throw new ArgumentException(
"The specified description and/or providerState filter yielded no interactions.");
throw new ArgumentException("The specified description and/or providerState filter yielded no interactions.");
}

var loggerName = LogProvider.CurrentLogProvider.AddLogger(_config.LogDir, ProviderName.ToLowerSnakeCase(),
"{0}_verifier.log");
var loggerName = LogProvider.CurrentLogProvider.AddLogger(_config.LogDir, ProviderName.ToLowerSnakeCase(), "{0}_verifier.log");
_config.LoggerName = loggerName;

try
Expand Down
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,11 @@ public class SomethingApiTests
pactVerifier
.ServiceProvider("Something API", testServer.HttpClient)
.HonoursPactWith("Consumer")
.PactUri("../../../Consumer.Tests/pacts/consumer-something_api.json") //NOTE: You can alternatively specify a http or https uri. e.g. http://pact-broker/pacts/provider/Something%20Api/consumer/Consumer/version/latest
.PactUri("../../../Consumer.Tests/pacts/consumer-something_api.json")
//or
.PactUri("http://pact-broker/pacts/provider/Something%20Api/consumer/Consumer/version/latest") //You can specify a http or https uri
//or
.PactUri("http://pact-broker/pacts/provider/Something%20Api/consumer/Consumer/version/latest", new PactUriOptions("someuser", "somepassword")) //You can also specify http/https basic auth details
.Verify(); //NOTE: Optionally you can control what interactions are verified by specifying a providerDescription and/or providerState
}
}
Expand Down Expand Up @@ -253,7 +257,11 @@ public class SomethingApiTests
pactVerifier
.ServiceProvider("Something API", client) //NOTE: You can also use your own client by using the ServiceProvider method which takes a Func<ProviderServiceRequest, ProviderServiceResponse>. You are then responsible for mapping and performing the actual provider verification HTTP request within that Func.
.HonoursPactWith("Consumer")
.PactUri("../../../Consumer.Tests/pacts/consumer-something_api.json") //NOTE: You can alternatively specify a http or https uri. e.g. http://pact-broker/pacts/provider/Something%20Api/consumer/Consumer/version/latest
.PactUri("../../../Consumer.Tests/pacts/consumer-something_api.json")
//or
.PactUri("http://pact-broker/pacts/provider/Something%20Api/consumer/Consumer/version/latest") //You can specify a http or https uri
//or
.PactUri("http://pact-broker/pacts/provider/Something%20Api/consumer/Consumer/version/latest", new PactUriOptions("someuser", "somepassword")) //You can also specify http/https basic auth details
.Verify(); //NOTE: Optionally you can control what interactions are verified by specifying a providerDescription and/or providerState
}
}
Expand Down

0 comments on commit aa7fd5f

Please sign in to comment.