| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | using Google.Apis.Discovery; |
| | using Google.Apis.Requests; |
| | using Google.Apis.Services; |
| | using Google.Apis.Tests.Mocks; |
| | using Google.Apis.Util; |
| | using Newtonsoft.Json; |
| | using System; |
| | using System.Collections.Generic; |
| | using System.IO; |
| | using System.Linq; |
| | using System.Net; |
| | using System.Net.Http; |
| | using System.Net.Http.Headers; |
| | using System.Text; |
| | using System.Text.RegularExpressions; |
| | using System.Threading; |
| | using System.Threading.Tasks; |
| | using Xunit; |
| |
|
| | namespace Google.Apis.Tests.Apis.Requests |
| | { |
| | |
| | public class BatchRequestTest |
| | { |
| | private static string ExpectedContentMessage = NormalizeLineEndings(@"--BOUNDARY |
| | Content-Type: application/http |
| | |
| | POST http://sample.com/5?q=20 |
| | If-Match: ""100"" |
| | Content-Type: application/json; charset=utf-8 |
| | Content-Length: 41 |
| | |
| | {""etag_key"":""\""100\"""",""name_key"":""Name1""} |
| | |
| | --BOUNDARY |
| | Content-Type: application/http |
| | |
| | POST http://sample.com/5?q=20 |
| | If-Match: ""200"" |
| | Content-Type: application/json; charset=utf-8 |
| | Content-Length: 43 |
| | |
| | {""etag_key"":""\""200\"""",""name_key"":""Name1-1""} |
| | |
| | --BOUNDARY-- |
| | "); |
| | |
| | class MockResponse : IDirectResponseSchema |
| | { |
| | [Newtonsoft.Json.JsonPropertyAttribute("etag_key")] |
| | public string ETag { get; set; } |
| |
|
| | [Newtonsoft.Json.JsonPropertyAttribute("id_key")] |
| | public Nullable<int> Id { get; set; } |
| |
|
| | public override bool Equals(object obj) |
| | { |
| | var other = obj as MockResponse; |
| | return other != null && other.ETag == ETag && other.Id == Id; |
| | } |
| |
|
| | public override int GetHashCode() |
| | { |
| | return (ETag ?? string.Empty).GetHashCode(); |
| | } |
| | } |
| |
|
| | |
| | class MockRequest : IDirectResponseSchema |
| | { |
| | [Newtonsoft.Json.JsonPropertyAttribute("etag_key")] |
| | public string ETag { get; set; } |
| |
|
| | [Newtonsoft.Json.JsonPropertyAttribute("name_key")] |
| | public string Name { get; set; } |
| |
|
| | public override bool Equals(object obj) |
| | { |
| | var other = obj as MockRequest; |
| | return (other != null && other.ETag == ETag && other.Name == Name); |
| | } |
| |
|
| | public override int GetHashCode() |
| | { |
| | return (ETag ?? string.Empty).GetHashCode() ^ (Name ?? string.Empty).GetHashCode(); |
| | } |
| | } |
| |
|
| | |
| | class TestClientServiceRequest : ClientServiceRequest<MockResponse> |
| | { |
| | private MockRequest body; |
| |
|
| | [RequestParameterAttribute("id", Google.Apis.Util.RequestParameterType.Path)] |
| | public int Id { get { return 5; } } |
| |
|
| | [RequestParameterAttribute("q", Google.Apis.Util.RequestParameterType.Query)] |
| | public int Q { get { return 20; } } |
| |
|
| | public TestClientServiceRequest(IClientService service, MockRequest body) |
| | : base(service) |
| | { |
| | this.body = body; |
| | InitParameters(); |
| | } |
| |
|
| | public override string MethodName |
| | { |
| | get { return "POST"; } |
| | } |
| |
|
| | public override string RestPath |
| | { |
| | get { return "{id}"; } |
| | } |
| |
|
| | public override string HttpMethod |
| | { |
| | get { return "POST"; } |
| | } |
| |
|
| | protected override object GetBody() |
| | { |
| | return body; |
| | } |
| |
|
| | protected override void InitParameters() |
| | { |
| | base.InitParameters(); |
| | RequestParameters.Add("id", new Parameter { Name = "id", ParameterType = "path" }); |
| | RequestParameters.Add("q", new Parameter { Name = "q", ParameterType = "query" }); |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | class BatchMessageHandler : CountableMessageHandler |
| | { |
| | private static string ResponseContent = NormalizeLineEndings(@"--BOUNDARY |
| | Content-Type: application/http |
| | |
| | HTTP/1.1 200 OK |
| | ETag: ""\""10011\"""" |
| | Content-Type: application/json; charset=UTF-8 |
| | Content-Length: 505 |
| | Vary: Accept-Encoding |
| | Vary: Referer |
| | |
| | { |
| | ""etag_key"": ""\""10011\"""", |
| | ""id_key"": 1 |
| | } |
| | |
| | --BOUNDARY |
| | Content-Type: application/http |
| | |
| | SECOND_RESPONSE |
| | |
| | --BOUNDARY--"); |
| |
|
| | private static string SuccessfulResponse = NormalizeLineEndings( |
| | @"HTTP/1.1 200 OK |
| | ETag: ""234"" |
| | Content-Type: application/json; charset=UTF-8 |
| | Content-Length: 202 |
| | |
| | { |
| | ""id_key"": 2 |
| | }"); |
| |
|
| | private static string UnsuccessfulResponse = NormalizeLineEndings( |
| | @"HTTP/1.1 404 Not Found |
| | Content-Type: application/json; charset=UTF-8 |
| | Date: Thu, 14 Nov 2013 22:03:08 GMT |
| | Expires: Thu, 14 Nov 2013 22:03:08 GMT |
| | Cache-Control: private, max-age=0 |
| | Content-Length: 202 |
| | |
| | { |
| | ""error"": { |
| | ""errors"": [ |
| | { |
| | ""domain"": ""global"", |
| | ""reason"": ""notFound"", |
| | ""message"": ""Not Found"", |
| | ""customIndividual"": ""Custom individual error info"" |
| | } |
| | ], |
| | ""code"": 404, |
| | ""message"": ""Not Found"", |
| | ""custom"": ""Custom error info"" |
| | } |
| | }"); |
| | bool successful2ndResponse; |
| | public BatchMessageHandler(bool successful2ndReponse = true) |
| | { |
| | this.successful2ndResponse = successful2ndReponse; |
| | } |
| |
|
| | protected override async Task<HttpResponseMessage> SendAsyncCore(HttpRequestMessage request, |
| | CancellationToken cancellationToken) |
| | { |
| | |
| | var contentType = request.Content.Headers.ContentType; |
| | Assert.Equal("multipart/mixed", contentType.MediaType.ToString()); |
| | var parameter = Assert.Single(contentType.Parameters); |
| | Assert.True(parameter.ToString().StartsWith("boundary=", StringComparison.Ordinal), |
| | "Parameter should start with boundary="); |
| | var boundary = parameter.ToString().Substring("boundary=".Length). |
| | Replace("\"", ""); |
| | var expectedContent = ExpectedContentMessage.Replace("BOUNDARY", boundary); |
| | var actualContent = await request.Content.ReadAsStringAsync(); |
| | Assert.Equal(expectedContent, NormalizeLineEndings(actualContent)); |
| |
|
| | HttpResponseMessage response = new HttpResponseMessage(); |
| | |
| | var content = ResponseContent.Replace("BOUNDARY", boundary).Replace("SECOND_RESPONSE", |
| | successful2ndResponse ? SuccessfulResponse : UnsuccessfulResponse); |
| | response.Content = new StringContent(content, |
| | Encoding.UTF8, "multipart/mixed"); |
| | response.Content.Headers.ContentType.Parameters.Add(new NameValueHeaderValue("boundary", boundary)); |
| |
|
| | return response; |
| | } |
| | } |
| |
|
| | [Fact] |
| | public void ExecuteAsync_Test() |
| | { |
| | SubtestExecuteAsync_Test(true); |
| | } |
| |
|
| | [Fact] |
| | public void ExecuteAsync_Error_Test() |
| | { |
| | SubtestExecuteAsync_Test(false); |
| | } |
| |
|
| | |
| | |
| | void SubtestExecuteAsync_Test(bool successful2ndReponse) |
| | { |
| | var handler = new BatchMessageHandler(successful2ndReponse); |
| | var initializer = new BaseClientService.Initializer() |
| | { |
| | HttpClientFactory = new MockHttpClientFactory(handler) |
| | }; |
| |
|
| | using (var service = new MockClientService(initializer, "http://sample.com")) |
| | { |
| | var responses = new List<Tuple<MockResponse, RequestError, HttpResponseMessage>>(); |
| | var batch = new BatchRequest(service); |
| | var request1 = new TestClientServiceRequest(service, new MockRequest |
| | { |
| | ETag = "\"100\"", |
| | Name = "Name1" |
| | }); |
| | var request2 = new TestClientServiceRequest(service, new MockRequest |
| | { |
| | ETag = "\"200\"", |
| | Name = "Name1-1" |
| | }); |
| | |
| | batch.Queue<MockResponse>(request1, (content, error, index, message) => |
| | { |
| | responses.Add(new Tuple<MockResponse, RequestError, HttpResponseMessage>( |
| | content, error, message)); |
| | }); |
| | batch.Queue<MockResponse>(request2, (content, error, index, message) => |
| | { |
| | responses.Add(new Tuple<MockResponse, RequestError, HttpResponseMessage>( |
| | content, error, message)); |
| | }); |
| | batch.ExecuteAsync().Wait(); |
| |
|
| | Assert.Equal(2, responses.Count); |
| | var tuple = responses[0]; |
| | Assert.Null(tuple.Item2); |
| | var response = tuple.Item1; |
| | Assert.Equal(@"""10011""", response.ETag); |
| | Assert.Equal(1, response.Id); |
| | var httpMessage = tuple.Item3; |
| | Assert.Equal("application/json", httpMessage.Content.Headers.ContentType.MediaType); |
| | Assert.Equal(505, httpMessage.Content.Headers.ContentLength); |
| | Assert.Contains("Accept-Encoding", httpMessage.Headers.Vary); |
| | Assert.Contains("Referer", httpMessage.Headers.Vary); |
| |
|
| | tuple = responses[1]; |
| | if (successful2ndReponse) |
| | { |
| | Assert.Null(tuple.Item2); |
| | response = tuple.Item1; |
| | Assert.Equal(@"""234""", response.ETag); |
| | Assert.Equal(2, response.Id); |
| | } |
| | else |
| | { |
| | Assert.Null(tuple.Item1); |
| | RequestError reqError = tuple.Item2; |
| | Assert.Single(reqError.Errors); |
| | Assert.Equal(404, reqError.Code); |
| | Assert.Equal("Not Found", reqError.Message); |
| | Assert.NotNull(reqError.ErrorResponseContent); |
| | Assert.Contains("Custom individual error info", reqError.ErrorResponseContent); |
| | Assert.Contains("Custom error info", reqError.ErrorResponseContent); |
| | } |
| | httpMessage = tuple.Item3; |
| | Assert.Equal("application/json", httpMessage.Content.Headers.ContentType.MediaType); |
| | Assert.Equal(202, httpMessage.Content.Headers.ContentLength); |
| | } |
| | } |
| |
|
| | [Fact] |
| | public async Task ExecuteAsync_NoCallback_Test() |
| | { |
| | var handler = new BatchMessageHandler(true); |
| | var initializer = new BaseClientService.Initializer() |
| | { |
| | HttpClientFactory = new MockHttpClientFactory(handler) |
| | }; |
| |
|
| | using (var service = new MockClientService(initializer, "http://sample.com")) |
| | { |
| | var responses = new List<Tuple<MockResponse, RequestError, HttpResponseMessage>>(); |
| | var batch = new BatchRequest(service); |
| | var request1 = new TestClientServiceRequest(service, new MockRequest |
| | { |
| | ETag = "\"100\"", |
| | Name = "Name1" |
| | }); |
| | var request2 = new TestClientServiceRequest(service, new MockRequest |
| | { |
| | ETag = "\"200\"", |
| | Name = "Name1-1" |
| | }); |
| | batch.Queue<MockResponse>(request1, null); |
| | batch.Queue<MockResponse>(request2, null); |
| | await batch.ExecuteAsync(); |
| | } |
| | } |
| |
|
| | [Fact] |
| | public async Task CreateOuterRequestContent_Test() |
| | { |
| | using (var service = new MockClientService("http://sample.com")) |
| | { |
| | var request1 = new TestClientServiceRequest(service, new MockRequest |
| | { |
| | ETag = "\"100\"", |
| | Name = "Name1" |
| | }); |
| | var request2 = new TestClientServiceRequest(service, new MockRequest |
| | { |
| | ETag = "\"200\"", |
| | Name = "Name1-1" |
| | }); |
| |
|
| | var content = await BatchRequest.CreateOuterRequestContent(new[] { request1, request2 }); |
| | var requestStr = await content.ReadAsStringAsync(); |
| |
|
| | |
| | string boundary = null; |
| | using (var reader = new StreamReader(new MemoryStream(Encoding.UTF8.GetBytes(requestStr)))) |
| | { |
| | var line = reader.ReadLine(); |
| | boundary = line.Substring(2); |
| | } |
| |
|
| | Assert.Equal(ExpectedContentMessage.Replace("BOUNDARY", boundary), NormalizeLineEndings(requestStr)); |
| | } |
| | } |
| |
|
| | [Fact] |
| | public async Task CreateIndividualRequest_Test() |
| | { |
| | var expectedMessage = NormalizeLineEndings(@"POST http://sample.com/5?q=20 |
| | If-Match: ""123"" |
| | Content-Type: application/json; charset=utf-8 |
| | Content-Length: 40 |
| | |
| | {""etag_key"":""\""123\"""",""name_key"":""Name""} |
| | "); |
| | using (var service = new MockClientService("http://sample.com")) |
| | { |
| | var request = new TestClientServiceRequest(service, new MockRequest |
| | { |
| | ETag = "\"123\"", |
| | Name = "Name" |
| | }); |
| | var content = await BatchRequest.CreateIndividualRequest(request); |
| | var requestStr = await content.ReadAsStringAsync(); |
| | Assert.Equal(expectedMessage, NormalizeLineEndings(requestStr)); |
| | } |
| | } |
| |
|
| | [Fact] |
| | public async Task CreateRequestContentString_Test() |
| | { |
| | var expectedMessage = NormalizeLineEndings(@"GET http://test.com:2020/ |
| | Accept-Encoding: gzip |
| | Content-Type: application/json |
| | Content-Length: 11 |
| | |
| | hello world |
| | "); |
| | var request = new HttpRequestMessage(HttpMethod.Get, "http://test.com:2020"); |
| | request.Content = new StringContent("hello world"); |
| | request.Headers.AcceptEncoding.Add(new StringWithQualityHeaderValue("gzip")); |
| | request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); |
| |
|
| | var requestStr = await BatchRequest.CreateRequestContentString(request); |
| | Assert.Equal(expectedMessage, NormalizeLineEndings(requestStr)); |
| | } |
| |
|
| | [Fact] |
| | public void BatchUrl() |
| | { |
| | using (var service = new MockClientService("http://sample.com", "http://batch.sample.com")) |
| | { |
| | var batch = new BatchRequest(service); |
| | Assert.Equal("http://batch.sample.com", batch.BatchUrl); |
| | } |
| | using (var service = new MockClientService("http://sample.com", null)) |
| | { |
| | var batch = new BatchRequest(service); |
| | Assert.Equal("https://www.googleapis.com/batch", batch.BatchUrl); |
| | } |
| | } |
| |
|
| | class BatchEndpointErrorMessageHandler : CountableMessageHandler |
| | { |
| | private readonly string _responseContent; |
| |
|
| | public BatchEndpointErrorMessageHandler(string responseContent) => _responseContent = responseContent; |
| |
|
| | protected override Task<HttpResponseMessage> SendAsyncCore(HttpRequestMessage request, CancellationToken cancellationToken) => |
| | Task.FromResult(new HttpResponseMessage |
| | { |
| | StatusCode = HttpStatusCode.BadRequest, |
| | ReasonPhrase = "Bad Request", |
| | Content = _responseContent == null ? null : new StringContent(_responseContent, Encoding.UTF8, "application/json") |
| | }); |
| | } |
| |
|
| | [Fact] |
| | public async Task BatchEndpointErrorJsonContent() |
| | { |
| | string jsonErrorContent = NormalizeLineEndings(@" |
| | { |
| | ""error"": { |
| | ""code"": 400, |
| | ""message"": ""Failed to parse batch request, error: Failed in parsing HTTP headers: invalid content\n. Received batch body: "", |
| | ""errors"": [ |
| | { |
| | ""message"": ""Failed to parse batch request, error: Failed in parsing HTTP headers: invalid content\n. Received batch body: "", |
| | ""domain"": ""global"", |
| | ""reason"": ""badRequest"" |
| | } |
| | ], |
| | ""status"": ""INVALID_ARGUMENT"" |
| | } |
| | }"); |
| |
|
| | using (var service = new MockClientService( new BaseClientService.Initializer() |
| | { |
| | HttpClientFactory = new MockHttpClientFactory(new BatchEndpointErrorMessageHandler(jsonErrorContent)) |
| | }, "http://sample.com")) |
| | { |
| | var batchRequest = new BatchRequest(service); |
| | var request = new TestClientServiceRequest(service, new MockRequest |
| | { |
| | ETag = "\"100\"", |
| | Name = "Name1" |
| | }); |
| | |
| | batchRequest.Queue<MockResponse>( request, (c, e, i, m) => |
| | Assert.Fail("The batch endpoint call should have failed. Callbacks for individual requests shouldn't be called.")); |
| |
|
| | HttpRequestException outerException = await Assert.ThrowsAsync<HttpRequestException>(() => batchRequest.ExecuteAsync()); |
| |
|
| | Assert.Contains("400", outerException.Message); |
| | Assert.Contains("Bad Request", outerException.Message); |
| |
|
| | GoogleApiException innerException = Assert.IsType<GoogleApiException>(outerException.InnerException); |
| |
|
| | Assert.Equal(HttpStatusCode.BadRequest, innerException.HttpStatusCode); |
| | Assert.NotNull(innerException.Error); |
| |
|
| | RequestError requestError = innerException.Error; |
| | Assert.Equal(jsonErrorContent, requestError.ErrorResponseContent); |
| | Assert.Equal(400, requestError.Code); |
| |
|
| | SingleError singleError = Assert.Single(requestError.Errors); |
| | Assert.Equal("global", singleError.Domain); |
| | Assert.Equal("badRequest", singleError.Reason); |
| | } |
| | } |
| |
|
| | [Fact] |
| | public async Task BatchEndpointErrorNonJsonContent() |
| | { |
| | string nonJsonContent = "Invalid JSON"; |
| | string expectedExceptionMessage = "The service TestService has thrown an exception. HttpStatusCode is BadRequest. No error message was specified."; |
| | string expectedExceptionToStringContent = |
| | $"The service TestService has thrown an exception.{Environment.NewLine}" + |
| | $"HttpStatusCode is BadRequest.{Environment.NewLine}" + |
| | $"No JSON error details were specified.{Environment.NewLine}" + |
| | $"Raw error details are: {nonJsonContent}"; |
| |
|
| | using (var service = new MockClientService( new BaseClientService.Initializer() |
| | { |
| | HttpClientFactory = new MockHttpClientFactory(new BatchEndpointErrorMessageHandler(nonJsonContent)) |
| | }, "http://sample.com")) |
| | { |
| | var batchRequest = new BatchRequest(service); |
| | var request = new TestClientServiceRequest(service, new MockRequest |
| | { |
| | ETag = "\"100\"", |
| | Name = "Name1" |
| | }); |
| |
|
| | batchRequest.Queue<MockResponse>(request, (c, e, i, m) => |
| | Assert.Fail("The batch endpoint call should have failed. Callbacks for individual requests shouldn't be called.")); |
| |
|
| | HttpRequestException outerException = await Assert.ThrowsAsync<HttpRequestException>(() => batchRequest.ExecuteAsync()); |
| |
|
| | Assert.Contains("400", outerException.Message); |
| | Assert.Contains("Bad Request", outerException.Message); |
| |
|
| | GoogleApiException innerException = Assert.IsType<GoogleApiException>(outerException.InnerException); |
| |
|
| | Assert.Equal(HttpStatusCode.BadRequest, innerException.HttpStatusCode); |
| | Assert.True(innerException.Error.IsOnlyRawContent); |
| | Assert.Equal(expectedExceptionMessage, innerException.Message); |
| | Assert.Contains(expectedExceptionToStringContent, innerException.ToString()); |
| |
|
| | Assert.IsAssignableFrom<JsonException>(innerException.InnerException); |
| | } |
| | } |
| |
|
| | [Fact] |
| | public async Task BatchEndpointErrorNullContent() |
| | { |
| | string expectedExceptionMessage = "The service TestService has thrown an exception. HttpStatusCode is BadRequest. No error message was specified."; |
| |
|
| | using (var service = new MockClientService( new BaseClientService.Initializer() |
| | { |
| | HttpClientFactory = new MockHttpClientFactory(new BatchEndpointErrorMessageHandler(null)) |
| | }, "http://sample.com")) |
| | { |
| | var batchRequest = new BatchRequest(service); |
| | var request = new TestClientServiceRequest(service, new MockRequest |
| | { |
| | ETag = "\"100\"", |
| | Name = "Name1" |
| | }); |
| |
|
| | batchRequest.Queue<MockResponse>(request, (c, e, i, m) => |
| | Assert.Fail("The batch endpoint call should have failed. Callbacks for individual requests shouldn't be called.")); |
| |
|
| | HttpRequestException outerException = await Assert.ThrowsAsync<HttpRequestException>(() => batchRequest.ExecuteAsync()); |
| |
|
| | Assert.Contains("400", outerException.Message); |
| | Assert.Contains("Bad Request", outerException.Message); |
| |
|
| | GoogleApiException innerException = Assert.IsType<GoogleApiException>(outerException.InnerException); |
| | Assert.Equal(HttpStatusCode.BadRequest, innerException.HttpStatusCode); |
| | #if NET6_0_OR_GREATER |
| | |
| | |
| | |
| | Assert.Equal("", innerException.Error.ErrorResponseContent); |
| | string expectedExceptionToStringContent = |
| | $"The service TestService has thrown an exception.{Environment.NewLine}" + |
| | $"HttpStatusCode is BadRequest.{Environment.NewLine}" + |
| | $"No JSON error details were specified.{Environment.NewLine}" + |
| | $"Raw error details are empty or white spaces only."; |
| | #else |
| | Assert.Null(innerException.Error); |
| | string expectedExceptionToStringContent = |
| | $"The service TestService has thrown an exception.{Environment.NewLine}" + |
| | $"HttpStatusCode is BadRequest.{Environment.NewLine}" + |
| | $"No error details were specified."; |
| | #endif |
| | Assert.Equal(expectedExceptionMessage, innerException.Message); |
| | Assert.Contains(expectedExceptionToStringContent, innerException.ToString()); |
| | } |
| | } |
| |
|
| | [Fact] |
| | public async Task ParseAsHttpResponse_NormalContent() |
| | { |
| | string content = @"Content-Type: application/http |
| | |
| | HTTP/1.1 200 OK |
| | Vary: Origin |
| | Vary: X-Origin |
| | Vary: Referer |
| | Content-Type: application/json |
| | |
| | {} |
| | "; |
| | var response = BatchRequest.ParseAsHttpResponse(content); |
| | Assert.Equal("application/json", response.Content.Headers.ContentType.MediaType); |
| | Assert.Equal("{}", (await response.Content.ReadAsStringAsync()).Trim()); |
| | } |
| |
|
| | [Fact] |
| | public async Task ParseAsHttpResponse_NoContent() |
| | { |
| | string content = @"Content-Type: application/http |
| | |
| | HTTP/1.1 204 No content |
| | Vary: Origin |
| | Vary: X-Origin |
| | Vary: Referer |
| | |
| | "; |
| | var response = BatchRequest.ParseAsHttpResponse(content); |
| | var httpContent = response.Content; |
| | Assert.NotNull(content); |
| |
|
| | #if NET6_0_OR_GREATER |
| | Assert.Null(httpContent.Headers.ContentType); |
| | #else |
| | Assert.Equal("text/plain", httpContent.Headers.ContentType.MediaType); |
| | #endif |
| | Assert.Equal("", (await response.Content.ReadAsStringAsync()).Trim()); |
| | } |
| |
|
| | [Fact] |
| | public async Task ParseAsHttpResponse_ContentButNoContentType() |
| | { |
| | string content = @"Content-Type: application/http |
| | |
| | HTTP/1.1 200 OK |
| | Vary: Origin |
| | Vary: X-Origin |
| | Vary: Referer |
| | |
| | {} |
| | "; |
| | var response = BatchRequest.ParseAsHttpResponse(content); |
| | Assert.Equal("text/plain", response.Content.Headers.ContentType.MediaType); |
| | Assert.Equal("{}", (await response.Content.ReadAsStringAsync()).Trim()); |
| | } |
| |
|
| | |
| | private static string NormalizeLineEndings(string s) => |
| | Regex.Replace(s, @"\r\n|\n", "\r\n"); |
| | } |
| | } |
| |
|