/// <summary> /// Creates a tester. /// </summary> /// <param name="tests">The conformance tests.</param> /// <param name="api">The API interface to test.</param> /// <param name="httpClient">The optional HTTP client for HTTP tests.</param> public ConformanceApiTester(IReadOnlyList <ConformanceTestInfo> tests, IConformanceApi api, HttpClient?httpClient) { m_tests = tests ?? throw new ArgumentNullException(nameof(tests)); m_api = api ?? throw new ArgumentNullException(nameof(api)); m_httpClient = httpClient; var sameNameTests = m_tests.GroupBy(x => x.Test).FirstOrDefault(x => x.Count() != 1); if (sameNameTests != null) { throw new ArgumentException($"Multiple tests have the name {sameNameTests.First().Test}"); } foreach (var testsPerMethod in m_tests.GroupBy(x => x.Method).Select(x => x.ToList())) { for (var i = 0; i < testsPerMethod.Count; i++) { for (var j = i + 1; j < testsPerMethod.Count; j++) { if (JToken.DeepEquals(testsPerMethod[i].Request, testsPerMethod[j].Request)) { throw new ArgumentException($"Tests must not have the same method name and request data, e.g. {testsPerMethod[i].Test} and {testsPerMethod[j].Test}."); } } } } }
/// <summary> /// Creates the handler. /// </summary> public ConformanceApiHttpHandler(IConformanceApi service, ServiceHttpHandlerSettings settings) : base(settings) { if (service == null) { throw new ArgumentNullException("service"); } m_service = service; }
public ConformanceApiController(IConformanceApi api) { m_api = api; }
private async Task HostAsync(HttpContext httpContext, IConformanceApi service, HttpContentSerializer contentSerializer) { var httpRequest = httpContext.Request; var requestUrl = httpRequest.GetEncodedUrl(); var apiHandler = new ConformanceApiHttpHandler(service, new ServiceHttpHandlerSettings { ContentSerializer = contentSerializer }); var requestMessage = new HttpRequestMessage(new HttpMethod(httpRequest.Method), requestUrl) { Content = new StreamContent(httpRequest.Body), }; foreach (var header in httpRequest.Headers) { // Every header should be able to fit into one of the two header collections. // Try message.Headers first since that accepts more of them. if (!requestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value.AsEnumerable())) { requestMessage.Content.Headers.TryAddWithoutValidation(header.Key, header.Value.AsEnumerable()); } } HttpResponseMessage?responseMessage = null; ServiceErrorDto? error = null; try { responseMessage = await apiHandler.TryHandleHttpRequestAsync(requestMessage, httpContext.RequestAborted).ConfigureAwait(false); if (responseMessage == null) { error = ServiceErrors.CreateInvalidRequest($"Test not found for {httpRequest.Method} {requestUrl}"); } } catch (Exception exception) { error = ServiceErrorUtility.CreateInternalErrorForException(exception); } if (error != null) { var statusCode = HttpServiceErrors.TryGetHttpStatusCode(error.Code) ?? HttpStatusCode.InternalServerError; responseMessage = new HttpResponseMessage(statusCode) { Content = contentSerializer.CreateHttpContent(error) }; } if (responseMessage != null) { using (responseMessage) { var response = httpContext.Response; response.StatusCode = (int)responseMessage.StatusCode; var responseHeaders = responseMessage.Headers; // Ignore the Transfer-Encoding header if it is just "chunked". // We let the host decide about whether the response should be chunked or not. if (responseHeaders.TransferEncodingChunked == true && responseHeaders.TransferEncoding.Count == 1) { responseHeaders.TransferEncoding.Clear(); } foreach (var header in responseHeaders) { response.Headers.Append(header.Key, header.Value.ToArray()); } // ReSharper disable once ConditionIsAlwaysTrueOrFalse if (responseMessage.Content != null) { var contentHeaders = responseMessage.Content.Headers; // Copy the response content headers only after ensuring they are complete. // We ask for Content-Length first because HttpContent lazily computes this // and only afterwards writes the value into the content headers. _ = contentHeaders.ContentLength; foreach (var header in contentHeaders) { response.Headers.Append(header.Key, header.Value.ToArray()); } await responseMessage.Content.CopyToAsync(response.Body).ConfigureAwait(false); } } } }
public ConformanceApiHttpHandler(IConformanceApi service, ServiceHttpHandlerSettings?settings = null) : base(settings, s_defaults) { m_service = service ?? throw new ArgumentNullException(nameof(service)); }
/// <summary> /// Creates a tester. /// </summary> /// <param name="tests">The conformance tests.</param> /// <param name="api">The API interface to test.</param> public ConformanceApiTester(IReadOnlyList <ConformanceTestInfo> tests, IConformanceApi api) : this(tests, api, httpClient : null) { }
public ConformanceApiTester(IReadOnlyList <ConformanceTestInfo> tests, IConformanceApi api, HttpClient?httpClient) : this(new ConformanceApiTesterSettings { Tests = tests, Api = api, JsonSerializer = NewtonsoftJsonServiceSerializer.Instance, HttpClient = httpClient }) { }