Skip to content

Commit

Permalink
Add concept of priority to HttpRequestInterceptionBuilder
Browse files Browse the repository at this point in the history
Add a concept of priority to HttpRequestInterceptionBuilder for use with predicate matching to allow a hierarchy of matches to be configured. The lower the value of priority, the more important the match is.
  • Loading branch information
martincostello committed Mar 4, 2018
1 parent e2b2193 commit f1ddd3f
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/HttpClientInterception/HttpClientInterceptorOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,8 @@ private static void PopulateHeaders(HttpHeaders headers, IEnumerable<KeyValuePai
private bool TryGetResponse(HttpRequestMessage request, out HttpInterceptionResponse response)
{
response = _mappings.Values
.OrderByDescending((p) => p.Priority.HasValue)
.ThenBy((p) => p.Priority)
.Where((p) => p.InternalMatcher.IsMatch(request))
.FirstOrDefault();

Expand Down
2 changes: 2 additions & 0 deletions src/HttpClientInterception/HttpInterceptionResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ internal sealed class HttpInterceptionResponse

internal HttpMethod Method { get; set; }

internal int? Priority { get; set; }

internal string ReasonPhrase { get; set; }

internal Uri RequestUri { get; set; }
Expand Down
27 changes: 27 additions & 0 deletions src/HttpClientInterception/HttpRequestInterceptionBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ public class HttpRequestInterceptionBuilder

private bool _ignoreQuery;

private int? _priority;

/// <summary>
/// Configures the builder to match any request that meets the criteria defined by the specified predicate.
/// </summary>
Expand Down Expand Up @@ -583,6 +585,30 @@ public HttpRequestInterceptionBuilder WithInterceptionCallback(Func<HttpRequestM
return this;
}

/// <summary>
/// Sets the priority for matching against HTTP requests.
/// </summary>
/// <param name="priority">The priority of the HTTP interception.</param>
/// <returns>
/// The current <see cref="HttpRequestInterceptionBuilder"/>.
/// </returns>
/// <remarks>
/// The priority is used to establish a hierarchy for matching of intercepted
/// HTTP requests, particularly when <see cref="For"/> is used. This allows
/// registered requests to establish an order of precedence when an HTTP request
/// could match against multiple predicates, where the matching predicate with
/// the lowest value for <paramref name="priority"/> dictates the HTTP response
/// that is used for the intercepted request.
/// <para />
/// By default an interception has no priority, so the first arbitrary registration
/// that matches which does not have a priority will be used to provide the response.
/// </remarks>
public HttpRequestInterceptionBuilder HavingPriority(int? priority)
{
_priority = priority;
return this;
}

internal HttpInterceptionResponse Build()
{
var response = new HttpInterceptionResponse()
Expand All @@ -594,6 +620,7 @@ internal HttpInterceptionResponse Build()
IgnoreQuery = _ignoreQuery,
Method = _method,
OnIntercepted = _onIntercepted,
Priority = _priority,
ReasonPhrase = _reasonPhrase,
RequestUri = _uriBuilder.Uri,
StatusCode = _statusCode,
Expand Down
33 changes: 33 additions & 0 deletions tests/HttpClientInterception.Tests/Examples.cs
Original file line number Diff line number Diff line change
Expand Up @@ -592,5 +592,38 @@ public static async Task Use_Custom_Request_Matching()
(await client.GetStringAsync("https://google.com/search?q=foo")).ShouldContain("Google Search");
}
}

[Fact]
public static async Task Use_Custom_Request_Matching_With_Priorities()
{
// Arrange
var options = new HttpClientInterceptorOptions()
{
ThrowOnMissingRegistration = true,
};

var builder = new HttpRequestInterceptionBuilder()
.Requests().For((request) => request.RequestUri.Host == "google.com").HavingPriority(1)
.Responds().WithContent(@"First")
.RegisterWith(options)
.Requests().For((request) => request.RequestUri.Host.Contains("google")).HavingPriority(2)
.Responds().WithContent(@"Second")
.RegisterWith(options)
.Requests().For((request) => request.RequestUri.PathAndQuery.Contains("html")).HavingPriority(3)
.Responds().WithContent(@"Third")
.RegisterWith(options)
.Requests().For((request) => true).HavingPriority(null)
.Responds().WithContent(@"Fourth")
.RegisterWith(options);

using (var client = options.CreateHttpClient())
{
// Act and Assert
(await client.GetStringAsync("https://google.com/")).ShouldBe("First");
(await client.GetStringAsync("https://google.co.uk")).ShouldContain("Second");
(await client.GetStringAsync("https://example.org/index.html")).ShouldContain("Third");
(await client.GetStringAsync("https://www.just-eat.co.uk/")).ShouldContain("Fourth");
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -993,6 +993,16 @@ public static async Task Builder_Returns_Fallback_For_Missing_Registration()
await HttpAssert.GetAsync(options, "http://bing.com/", HttpStatusCode.BadGateway);
}

[Fact]
public static void RegisterWith_Validates_Parameters()
{
// Arrange
var builder = new HttpRequestInterceptionBuilder();

// Act and Assert
Assert.Throws<ArgumentNullException>("options", () => builder.RegisterWith(null));
}

private sealed class CustomObject
{
internal enum Color
Expand Down

0 comments on commit f1ddd3f

Please sign in to comment.