#nullable enable using System.Net; namespace Bit.Test.Common.MockedHttpClient; public class HttpRequestMatcher : IHttpRequestMatcher { private readonly Func _matcher; private HttpRequestMatcher? _childMatcher; private MockedHttpResponse _mockedResponse = new(HttpStatusCode.OK); private bool _responseSpecified = false; public int NumberOfMatches { get; private set; } /// /// Returns whether or not the provided request can be handled by this matcher chain. /// /// /// public bool Matches(HttpRequestMessage request) => _matcher(request) && (_childMatcher == null || _childMatcher.Matches(request)); public HttpRequestMatcher(HttpMethod method) { _matcher = request => request.Method == method; } public HttpRequestMatcher(string uri) { _matcher = request => request.RequestUri == new Uri(uri); } public HttpRequestMatcher(Uri uri) { _matcher = request => request.RequestUri == uri; } public HttpRequestMatcher(HttpMethod method, string uri) { _matcher = request => request.Method == method && request.RequestUri == new Uri(uri); } public HttpRequestMatcher(Func matcher) { _matcher = matcher; } public HttpRequestMatcher WithHeader(string name, string value) { return AddChild(request => request.Headers.TryGetValues(name, out var values) && values.Contains(value)); } public HttpRequestMatcher WithQueryParameters(Dictionary requiredQueryParameters) => WithQueryParameters(requiredQueryParameters.Select(x => $"{x.Key}={x.Value}").ToArray()); public HttpRequestMatcher WithQueryParameters(string name, string value) => WithQueryParameters($"{name}={value}"); public HttpRequestMatcher WithQueryParameters(params string[] queryKeyValues) { bool matcher(HttpRequestMessage request) { var query = request.RequestUri?.Query; if (query == null) { return false; } return queryKeyValues.All(queryKeyValue => query.Contains(queryKeyValue)); } return AddChild(matcher); } /// /// Configure how this matcher should respond to matching HttpRequestMessages. /// Note, after specifying a response, you can no longer further specify match criteria. /// /// /// public MockedHttpResponse RespondWith(HttpStatusCode statusCode) { _responseSpecified = true; _mockedResponse = new MockedHttpResponse(statusCode); return _mockedResponse; } /// /// Called to produce an HttpResponseMessage for the given request. This is probably something you want to leave alone /// /// public async Task RespondToAsync(HttpRequestMessage request) { NumberOfMatches++; return await (_childMatcher == null ? _mockedResponse.RespondToAsync(request) : _childMatcher.RespondToAsync(request)); } private HttpRequestMatcher AddChild(Func matcher) { if (_responseSpecified) { throw new Exception("Cannot continue to configure a matcher after a response has been specified"); } _childMatcher = new HttpRequestMatcher(matcher); return _childMatcher; } }