diff --git a/README.md b/README.md
index f2a7add..2b51aae 100644
--- a/README.md
+++ b/README.md
@@ -372,6 +372,24 @@ await httpClient.PostAsync("/api/infos", new StringContent("test", Encoding.ASCI
the response returned from the handler will be `415 Unsupported Media Type`
+
+### Handle requests only if they match the authorization header
+
+For some tests you might want to configure the handler to return when the correct authorization token is provided.
+
+To do this you can configure the handler like so:
+
+```csharp
+handler
+ .RespondTo()
+ .Post()
+ .ForUrl("/search")
+ .AndAuthorization(new AuthenticationHeaderValue("Scheme", "Value"))
+ .With(HttpStatusCode.OK);
+```
+
+This allows you to configure multiple responses to a url depending on the authorization of a request.
+
### Handle requests only if they match the request body
For some tests you might want to configure the handler to return certain responses based on the request content.
diff --git a/src/Codenizer.HttpClient.Testable/IRequestBuilder.cs b/src/Codenizer.HttpClient.Testable/IRequestBuilder.cs
index da20b88..6f2e87e 100644
--- a/src/Codenizer.HttpClient.Testable/IRequestBuilder.cs
+++ b/src/Codenizer.HttpClient.Testable/IRequestBuilder.cs
@@ -1,5 +1,6 @@
using System;
using System.Net;
+using System.Net.Http.Headers;
namespace Codenizer.HttpClient.Testable
{
@@ -87,6 +88,13 @@ public interface IRequestBuilder
/// A MIME content type (for example text/plain)
/// The current instance
IRequestBuilder AndContentType(string contentType);
+
+ ///
+ /// Respond to a request that matches the authorization header
+ ///
+ /// A authorization header value
+ /// The current instance
+ IRequestBuilder AndAuthorization(AuthenticationHeaderValue authenticationHeader);
///
/// Respond to a request that matches the accept header
diff --git a/src/Codenizer.HttpClient.Testable/RequestBuilder.cs b/src/Codenizer.HttpClient.Testable/RequestBuilder.cs
index 4a72861..10264d9 100644
--- a/src/Codenizer.HttpClient.Testable/RequestBuilder.cs
+++ b/src/Codenizer.HttpClient.Testable/RequestBuilder.cs
@@ -3,6 +3,7 @@
using System.Linq;
using System.Net;
using System.Net.Http;
+using System.Net.Http.Headers;
using System.Threading.Tasks;
using Newtonsoft.Json;
@@ -118,6 +119,12 @@ internal RequestBuilder(HttpMethod method, string pathAndQuery, string? contentT
/// Optional. The value of the Accept header to match.
///
public string? Accept { get; private set; }
+
+
+ ///
+ /// Optional. The value of the Accept header to match.
+ ///
+ public AuthenticationHeaderValue? AuthorizationHeader { get; private set; }
///
/// Optional. The expected content of the request.
@@ -245,6 +252,11 @@ public IRequestBuilder AndContentType(string contentType)
return this;
}
+ public IRequestBuilder AndAuthorization(AuthenticationHeaderValue authenticationHeader) {
+ AuthorizationHeader = authenticationHeader;
+ return this;
+ }
+
///
public IRequestBuilder Accepting(string mimeType)
{
@@ -396,6 +408,11 @@ internal Dictionary BuildRequestHeaders()
{
var headers = new Dictionary();
+ if (AuthorizationHeader is not null)
+ {
+ headers.Add("Authorization", AuthorizationHeader.ToString());
+ }
+
if (!string.IsNullOrEmpty(Accept))
{
headers.Add("Accept", Accept!);
diff --git a/test/Codenizer.HttpClient.Testable.Tests.Unit/WhenMatchingRoutes.cs b/test/Codenizer.HttpClient.Testable.Tests.Unit/WhenMatchingRoutes.cs
index b0b0d56..af13720 100644
--- a/test/Codenizer.HttpClient.Testable.Tests.Unit/WhenMatchingRoutes.cs
+++ b/test/Codenizer.HttpClient.Testable.Tests.Unit/WhenMatchingRoutes.cs
@@ -1,6 +1,10 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
+using System.Runtime.CompilerServices;
using FluentAssertions;
using Xunit;
@@ -9,7 +13,7 @@ namespace Codenizer.HttpClient.Testable.Tests.Unit
public static class ConfiguredRequestsExtensions
{
internal static RequestBuilder? Match(this ConfiguredRequests configuredRequests, HttpMethod method, string uri,
- string? accept)
+ string? accept, AuthenticationHeaderValue? authorization)
{
var requestMessage = new HttpRequestMessage(method, uri);
if (!string.IsNullOrEmpty(accept))
@@ -17,6 +21,10 @@ public static class ConfiguredRequestsExtensions
requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(accept));
}
+ if (authorization != null) {
+ requestMessage.Headers.Authorization = authorization;
+ }
+
return configuredRequests.Match(requestMessage);
}
}
@@ -39,7 +47,7 @@ public void GivenPathWithQueryParameters_ReturnedRequestBuilderMatches()
.Match(
HttpMethod.Get,
"/api/foo/bar?blah=blurb",
- null)
+ null, null)
.Should()
.Be(requestBuilder);
}
@@ -60,10 +68,57 @@ public void GivenNonConfigured_NoResultIsReturned()
.Match(
HttpMethod.Get,
"/api/baz",
- null)
+ null, null)
.Should()
.BeNull();
}
+
+
+ [Fact]
+ public void GivenRouteWithAuthorizationHeader_RequestBuilderIsReturned()
+ {
+ var requestBuilder = new RequestBuilder(HttpMethod.Get, "/api/foos/{id}", null);
+ requestBuilder.AndAuthorization(new AuthenticationHeaderValue("Bearer"));
+
+ var routes = new List
+ {
+ requestBuilder
+ };
+
+ var dictionary = ConfiguredRequests.FromRequestBuilders(routes);
+
+ dictionary
+ .Match(
+ HttpMethod.Get,
+ "/api/foos/1234",
+ null,
+ new AuthenticationHeaderValue("Bearer"))
+ .Should()
+ .Be(requestBuilder);
+ }
+
+
+ [Fact]
+ public void GivenRouteAuthorizationHeaderAndEmptyRequestAuthorization_RequestBuilderIsNotReturned()
+ {
+ var requestBuilder = new RequestBuilder(HttpMethod.Get, "/api/foos/{id}", null);
+ requestBuilder.AndAuthorization(new AuthenticationHeaderValue("Bearer"));
+
+ var routes = new List
+ {
+ requestBuilder
+ };
+
+ var dictionary = ConfiguredRequests.FromRequestBuilders(routes);
+
+ dictionary
+ .Match(
+ HttpMethod.Get,
+ "/api/foos/1234",
+ null, null)
+ .Should()
+ .Be(null);
+ }
[Fact]
public void GivenRouteWithParameter_RequestBuilderIsReturned()
@@ -81,7 +136,7 @@ public void GivenRouteWithParameter_RequestBuilderIsReturned()
.Match(
HttpMethod.Get,
"/api/foos/1234",
- null)
+ null, null)
.Should()
.Be(requestBuilder);
}
@@ -102,7 +157,7 @@ public void GivenRouteWithParameterAndQueryString_RequestBuilderIsReturned()
.Match(
HttpMethod.Get,
"/api/foos/1234/?blah=baz",
- null)
+ null, null)
.Should()
.Be(requestBuilder);
}
@@ -123,7 +178,7 @@ public void GivenRouteWithParameterAndQueryStringWithoutSeparator_RequestBuilder
.Match(
HttpMethod.Get,
"/api/foos/1234?blah=baz",
- null)
+ null, null)
.Should()
.Be(requestBuilder);
}
@@ -143,7 +198,7 @@ public void GivenRouteWithParameterAndQueryStringWithoutSeparatorX_RequestBuilde
.Match(
HttpMethod.Get,
"/api/foos/1234?blah=qux",
- null)
+ null, null)
.Should()
.Be(routes[1]);
}
@@ -165,7 +220,7 @@ public void GivenResponseWithAcceptHeaderAndNoAcceptHeaderInRequest_RequestDoesN
.Match(
HttpMethod.Get,
"/api/foo",
- null)
+ null, null)
.Should()
.BeNull();
}
@@ -187,7 +242,7 @@ public void GivenResponseWithAcceptHeaderAndAcceptHeaderInRequestMatches_Respons
.Match(
HttpMethod.Get,
"/api/foo",
- "foo/bar")
+ "foo/bar", null)
.Should()
.NotBeNull();
}
@@ -209,7 +264,7 @@ public void GivenResponseWithAcceptHeaderAndAcceptHeaderInRequestDoesNotMatch_Re
.Match(
HttpMethod.Get,
"/api/foo",
- "derp/derp")
+ "derp/derp", null)
.Should()
.BeNull();
}
@@ -234,7 +289,7 @@ public void GivenTwoResponsesWithDifferentAcceptHeaderAndAcceptHeaderInRequestMa
.Match(
HttpMethod.Get,
"/api/foo",
- "baz/quux")
+ "baz/quux", null)
.Should()
.BeOfType()
.Which
@@ -263,7 +318,7 @@ public void GivenTwoResponsesWithDifferentAcceptHeaderAndAcceptHeaderInRequestMa
.Match(
HttpMethod.Get,
"/api/foo?bar=baz",
- "baz/quux")
+ "baz/quux", null)
.Should()
.BeOfType()
.Which
@@ -272,6 +327,41 @@ public void GivenTwoResponsesWithDifferentAcceptHeaderAndAcceptHeaderInRequestMa
.Be("baz/quux");
}
+ [Fact]
+ public void GivenTwoResponsesWithDifferentAuthorizationInRequest_ResponseBuilderIsReturned() {
+ var requestBuilderOne = new RequestBuilder(HttpMethod.Get, "/api/foo?bar=baz", null)
+ .AndAuthorization(new AuthenticationHeaderValue("BEARER", "Value"));
+ var requestBuilderTwo = new RequestBuilder(HttpMethod.Get, "/api/foo?bar=baz", null)
+ .AndAuthorization(new AuthenticationHeaderValue("BEARER"));
+
+ var routes = new List
+ {
+ (RequestBuilder)requestBuilderOne,
+ (RequestBuilder)requestBuilderTwo
+ };
+
+ var dictionary = ConfiguredRequests.FromRequestBuilders(routes);
+
+ dictionary
+ .Match(
+ HttpMethod.Get,
+ "/api/foo?bar=baz",
+ "baz/quux",
+ new AuthenticationHeaderValue("BEARER", "Value"))
+ .Should()
+ .Be(requestBuilderOne);
+
+
+ dictionary
+ .Match(
+ HttpMethod.Get,
+ "/api/foo?bar=baz",
+ "baz/quux",
+ new AuthenticationHeaderValue("BEARER"))
+ .Should()
+ .Be(requestBuilderTwo);
+ }
+
[Fact]
public void GivenRouteHasExtraPartInPath_ShouldNotReturnAMatch()
{
@@ -282,7 +372,7 @@ public void GivenRouteHasExtraPartInPath_ShouldNotReturnAMatch()
var dictionary = ConfiguredRequests.FromRequestBuilders(routes);
- dictionary.Match(HttpMethod.Post, "/api/v2/foos/1/bla-bla", "application/json")
+ dictionary.Match(HttpMethod.Post, "/api/v2/foos/1/bla-bla", "application/json", null)
.Should()
.BeNull();
}