public static async void NonSuccessfulHttpRequestThrows() { var client = Substitute.For<IElasticsearchClient>(); client.SearchAsync<string>( "_all", "docType", @"{""timeout"":""10s""}", Arg.Any<Func<SearchRequestParameters, SearchRequestParameters>>()) .Returns(Task.FromResult(ElasticsearchResponse<string>.Create( new ConnectionConfiguration(), 404, "_search", "_all", new byte[0]))); var localConnection = new ElasticNetConnection(client); var request = new SearchRequest { DocumentType = "docType" }; var formatter = new SearchRequestFormatter(localConnection, mapping, request); var ex = await Record.ExceptionAsync(() => localConnection.SearchAsync( formatter.Body, request, CancellationToken.None, log)); Assert.IsType<HttpRequestException>(ex); Assert.Equal("Response status code does not indicate success: 404 (Not Found).", ex.Message); }
public static async Task ShouldCallElasticSearchClient() { var spyLog = new SpyLog(); var mockConnection = Substitute.For<IElasticConnection>(); mockConnection.Index.Returns("SearchIndex"); mockConnection.Options.Returns(new ElasticConnectionOptions()); mockConnection.Timeout.Returns(TimeSpan.FromSeconds(10)); var request = new SearchRequest { DocumentType = "abc123", Size = 2112 }; var token = new CancellationToken(); var processor = new ElasticRequestProcessor(mockConnection, mapping, spyLog, retryPolicy); await processor.SearchAsync(request, token); #pragma warning disable 4014 // Remove this and await the SearchAsync below once NSubstitute 1.8.3 available mockConnection.Received(1).SearchAsync( #pragma warning restore 4014 @"{""size"":2112,""timeout"":""10s""}", request, token, spyLog ); }
/// <inheritdoc/> public override async Task<ElasticResponse> SearchAsync( string body, SearchRequest searchRequest, CancellationToken token, ILog log) { var stopwatch = Stopwatch.StartNew(); var response = await Task.Run(() => client.SearchAsync<string>( Index ?? "_all", searchRequest.DocumentType, body, searchParams => SetRequestParameters(searchParams, searchRequest)), token); stopwatch.Stop(); log.Log(TraceEventType.Verbose, null, null, "Request: POST {0}", response.RequestUrl); log.Log(TraceEventType.Verbose, null, null, "Body:\n{0}", body); log.Log(TraceEventType.Verbose, null, null, "Response: {0} {1} (in {2}ms)", response.HttpStatusCode, response.HttpStatusCode.HasValue ? ((HttpStatusCode)response.HttpStatusCode).ToString() : "", stopwatch.ElapsedMilliseconds); if (!response.Success) throw new HttpRequestException("Response status code does not indicate success: 404 (Not Found)."); return ParseResponse(response.Response, log); }
public static async void SearchAsyncThrowsTaskCancelledExceptionWithSubsequentlyCancelledCancellationToken() { var request = new SearchRequest { DocumentType = "docType" }; var processor = new ElasticRequestProcessor(connection, mapping, log, retryPolicy); var ex = await Record.ExceptionAsync(() => processor.SearchAsync(request, new CancellationTokenSource(500).Token)); Assert.IsType<TaskCanceledException>(ex); }
SearchRequestParameters SetRequestParameters( SearchRequestParameters searchRequestParameters, SearchRequest searchRequest) { if (string.Equals(searchRequest.SearchType, "count", StringComparison.OrdinalIgnoreCase)) searchRequestParameters.SearchType(SearchType.Count); return searchRequestParameters; }
public void BodyContainsMinScoreWhenSpecified() { var searchRequest = new SearchRequest { MinScore = 1.3 }; var formatter = new SearchRequestFormatter(defaultConnection, mapping, searchRequest); var body = JObject.Parse(formatter.Body); var result = body.TraverseWithAssert("min_score"); Assert.Equal(searchRequest.MinScore.ToString(), result); }
public void ConstructorSetsProperties() { var expectedSearch = new SearchRequest { DocumentType = "someType" }; var expectedMaterializer = new ListHitsElasticMaterializer(o => o, typeof(ElasticConnectionTests)); var result = new ElasticTranslateResult(expectedSearch, expectedMaterializer); Assert.Same(expectedSearch, result.SearchRequest); Assert.Same(expectedMaterializer, result.Materializer); }
public void ConstructorHasSensibleDefaultValues() { var request = new SearchRequest(); Assert.Equal(0, request.From); Assert.Null(request.Size); Assert.Empty(request.Fields); Assert.Empty(request.SortOptions); Assert.Null(request.Filter); }
public static async Task NoAuthorizationWithEmptyUserName() { var messageHandler = new SpyMessageHandler(); var localConnection = new ElasticConnection(messageHandler, new Uri("http://localhost")); var processor = new ElasticRequestProcessor(localConnection, mapping, log, retryPolicy); var request = new SearchRequest { DocumentType = "docType" }; await processor.SearchAsync(request); Assert.Null(messageHandler.Request.Headers.Authorization); }
public void BodyContainsStatisticalFacet() { var expectedFacet = new StatisticalFacet("TotalSales", "OrderTotal"); var searchRequest = new SearchRequest { Facets = new List<IFacet>(new[] { expectedFacet }) }; var formatter = new SearchRequestFormatter(defaultConnection, mapping, searchRequest); var body = JObject.Parse(formatter.Body); var result = body.TraverseWithAssert("facets", expectedFacet.Name, expectedFacet.Type, "field"); Assert.Equal(expectedFacet.Fields[0], result.ToString()); }
public void BodyContainsTermsFacetWithNoSizeWhenNotSpecified() { var expectedFacet = new TermsFacet("Totals", null, "OrderTotal"); var searchRequest = new SearchRequest { Facets = new List<IFacet>(new[] { expectedFacet }) }; var formatter = new SearchRequestFormatter(defaultConnection, mapping, searchRequest); var body = JObject.Parse(formatter.Body); var result = body.TraverseWithAssert("facets", expectedFacet.Name, expectedFacet.Type); Assert.False(result.Contains("size")); }
public static async void NonSuccessfulHttpRequestThrows() { var messageHandler = new SpyMessageHandler { Response = { StatusCode = HttpStatusCode.NotFound } }; var localConnection = new ElasticConnection(messageHandler, new Uri("http://localhost"), "myUser", "myPass"); var processor = new ElasticRequestProcessor(localConnection, mapping, log, retryPolicy); var request = new SearchRequest { DocumentType = "docType" }; var ex = await Record.ExceptionAsync(() => processor.SearchAsync(request, CancellationToken.None)); Assert.IsType<HttpRequestException>(ex); Assert.Equal("Response status code does not indicate success: 404 (Not Found).", ex.Message); }
public void BodyContainsFilterFacet() { var expectedFilter = new ExistsCriteria("IsLocal"); var expectedFacet = new FilterFacet("LocalSales", expectedFilter); var searchRequest = new SearchRequest { Facets = new List<IFacet>(new[] { expectedFacet }) }; var formatter = new SearchRequestFormatter(defaultConnection, mapping, searchRequest); var body = JObject.Parse(formatter.Body); var result = body.TraverseWithAssert("facets", expectedFacet.Name, expectedFacet.Type, expectedFilter.Name, "field"); Assert.Equal(expectedFilter.Field, result.ToString()); }
public static async Task ForcesBasicAuthorizationWhenProvidedWithUsernameAndPassword() { var messageHandler = new SpyMessageHandler(); var localConnection = new ElasticConnection(messageHandler, new Uri("http://localhost"), "myUser", "myPass"); var processor = new ElasticRequestProcessor(localConnection, mapping, log, retryPolicy); var request = new SearchRequest { DocumentType = "docType" }; await processor.SearchAsync(request); var auth = messageHandler.Request.Headers.Authorization; Assert.NotNull(auth); Assert.Equal("Basic", auth.Scheme); Assert.Equal("myUser:myPass", Encoding.ASCII.GetString(Convert.FromBase64String(auth.Parameter))); }
public void BodyContainsFilterFacetAndedWithRequestFilter() { var expectedFacet = new FilterFacet("LocalSales", new ExistsCriteria("IsLocal")); var searchRequest = new SearchRequest { Filter = new MissingCriteria("Country"), Query = new PrefixCriteria("Field", "Prefix"), Facets = new List<IFacet>(new[] { expectedFacet }) }; var formatter = new SearchRequestFormatter(defaultConnection, mapping, searchRequest); var body = JObject.Parse(formatter.Body); var andFilter = body.TraverseWithAssert("facets", expectedFacet.Name, expectedFacet.Type, "and"); Assert.Equal(2, andFilter.Count()); }
public static async void SearchAsyncCapturesRequestInfoOnFailure() { var spyLog = new SpyLog(); var brokenConnection = new ElasticConnection(new Uri("http://localhost:12"), index: "MyIndex"); var processor = new ElasticRequestProcessor(brokenConnection, mapping, spyLog, new RetryPolicy(spyLog, 100, 1)); var searchRequest = new SearchRequest { DocumentType = "docType" }; var formatter = new SearchRequestFormatter(brokenConnection, mapping, searchRequest); var ex = await Record.ExceptionAsync(() => processor.SearchAsync(searchRequest, CancellationToken.None)); Assert.IsType<RetryFailedException>(ex); var retryLogEntry = Assert.Single(spyLog.Entries, s => s.AdditionalInfo.ContainsKey("category") && s.AdditionalInfo["category"].Equals("retry")); Assert.Equal("MyIndex", retryLogEntry.AdditionalInfo["index"]); Assert.Equal(brokenConnection.GetSearchUri(searchRequest), retryLogEntry.AdditionalInfo["uri"]); Assert.Equal(formatter.Body, retryLogEntry.AdditionalInfo["query"]); }
public void BodyContainsMultipleFacets() { var expectedFacets = new List<IFacet> { new FilterFacet("LocalSales", new ExistsCriteria("IsLocal")), new StatisticalFacet("TotalSales", "OrderTotal") }; var searchRequest = new SearchRequest { Facets = expectedFacets }; var formatter = new SearchRequestFormatter(defaultConnection, mapping, searchRequest); var body = JObject.Parse(formatter.Body); var facetResults = body.TraverseWithAssert("facets"); foreach (var expectedFacet in expectedFacets) facetResults.TraverseWithAssert(expectedFacet.Name, expectedFacet.Type); }
public void BodyContainsTermsFacet() { const int expectedSize = 1234; var expectedFacet = new TermsFacet("Totals", expectedSize, "OrderTotal", "OrderCost"); var searchRequest = new SearchRequest { Facets = new List<IFacet>(new[] { expectedFacet }) }; var formatter = new SearchRequestFormatter(defaultConnection, mapping, searchRequest); var body = JObject.Parse(formatter.Body); var result = body.TraverseWithAssert("facets", expectedFacet.Name, expectedFacet.Type); Assert.Equal(expectedSize.ToString(CultureInfo.InvariantCulture), result.TraverseWithAssert("size").ToString()); var actualFields = result.TraverseWithAssert("fields").ToArray(); foreach (var expectedField in expectedFacet.Fields) Assert.Contains(expectedField, actualFields); }
public Task<ElasticResponse> SearchAsync(SearchRequest searchRequest, CancellationToken cancellationToken) { var formatter = new SearchRequestFormatter(connection, mapping, searchRequest); return retryPolicy.ExecuteAsync( async token => await connection.SearchAsync( formatter.Body, searchRequest, token, log), (response, exception) => !cancellationToken.IsCancellationRequested && exception != null, (response, additionalInfo) => { additionalInfo["index"] = connection.Index; additionalInfo["uri"] = connection.GetSearchUri(searchRequest); additionalInfo["query"] = formatter.Body; }, cancellationToken); }
public override async Task<ElasticResponse> Search( string index, string document, string body, SearchRequest searchRequest, ILog log) { var response = await client.SearchAsync<string>( index, searchRequest.DocumentType, body, searchParams => SetRequestParameters(searchParams, searchRequest)); if (!response.Success) throw new HttpRequestException("Response status code does not indicate success: 404 (Not Found)."); return ParseResponse(response.Response, log); }
public Task<ElasticResponse> SearchAsync(SearchRequest searchRequest, CancellationToken cancellationToken) { var formatter = new SearchRequestFormatter(connection, mapping, searchRequest); log.Debug(null, null, "Request: POST {0}", formatter.Uri); log.Debug(null, null, "Body:\n{0}", formatter.Body); return retryPolicy.ExecuteAsync( async token => { using (var requestMessage = new HttpRequestMessage(HttpMethod.Post, formatter.Uri) { Content = new StringContent(formatter.Body) }) using (var response = await SendRequestAsync(connection.HttpClient, requestMessage, token)) using (var responseStream = await response.Content.ReadAsStreamAsync()) return ParseResponse(responseStream, log); }, (response, exception) => !cancellationToken.IsCancellationRequested && exception != null, (response, additionalInfo) => { additionalInfo["index"] = connection.Index; additionalInfo["uri"] = formatter.Uri; additionalInfo["query"] = formatter.Body; }, cancellationToken); }
/// <inheritdoc/> public override async Task<ElasticResponse> SearchAsync( string body, SearchRequest searchRequest, CancellationToken token, ILog log) { var uri = GetSearchUri(searchRequest); log.Debug(null, null, "Request: POST {0}", uri); log.Debug(null, null, "Body:\n{0}", body); using (var requestMessage = new HttpRequestMessage(HttpMethod.Post, uri) { Content = new StringContent(body) }) using (var response = await SendRequestAsync(requestMessage, token, log)) using (var responseStream = await response.Content.ReadAsStreamAsync()) return ParseResponse(responseStream, log); }
public void BodyContainsTermsFacetWithDefaultSizeFromConnection() { const int expectedSize = 678; var sizedConnection = new ElasticConnection(defaultConnection.Endpoint, options:new ElasticConnectionOptions { SearchSizeDefault = expectedSize }); var expectedFacet = new TermsFacet("Totals", null, "OrderTotal", "OrderCost"); var searchRequest = new SearchRequest { Facets = new List<IFacet>(new[] { expectedFacet }) }; var formatter = new SearchRequestFormatter(sizedConnection, mapping, searchRequest); var body = JObject.Parse(formatter.Body); var result = body.TraverseWithAssert("facets", expectedFacet.Name, expectedFacet.Type); Assert.Equal(expectedSize.ToString(CultureInfo.InvariantCulture), result.TraverseWithAssert("size").ToString()); }
public static async void NonSuccessfulHttpRequestThrows() { var messageHandler = new SpyMessageHandler(); messageHandler.Response.StatusCode = HttpStatusCode.NotFound; var localConnection = new ElasticConnection(messageHandler, new Uri("http://localhost"), "myUser", "myPass"); var request = new SearchRequest { DocumentType = "docType" }; var formatter = new SearchRequestFormatter(localConnection, mapping, request); var ex = await Record.ExceptionAsync(() => localConnection.SearchAsync( formatter.Body, request, token, log)); Assert.IsType<HttpRequestException>(ex); Assert.Equal("Response status code does not indicate success: 404 (Not Found).", ex.Message); }
public static async Task LogsDebugMessagesDuringExecution() { var responseString = BuildResponseString(2, 1, 1, 0.3141, "testIndex", "testType", "testId"); var messageHandler = new SpyMessageHandler(); var spyLog = new SpyLog(); messageHandler.Response.Content = new StringContent(responseString); var localConnection = new ElasticConnection(messageHandler, new Uri("http://localhost"), "myUser", "myPass", index: "SearchIndex"); var request = new SearchRequest { DocumentType = "abc123", Size = 2112 }; var formatter = new SearchRequestFormatter(localConnection, mapping, request); await localConnection.SearchAsync( formatter.Body, request, token, spyLog); Assert.Equal(4, spyLog.Entries.Count); Assert.Equal(@"Request: POST http://localhost/SearchIndex/abc123/_search", spyLog.Entries[0].Message); Assert.Equal(@"Body:" + '\n' + @"{""size"":2112,""timeout"":""10s""}", spyLog.Entries[1].Message); Assert.True(new Regex(@"Response: 200 OK \(in \d+ms\)").Match(spyLog.Entries[2].Message).Success); Assert.True(new Regex(@"Deserialized \d+ bytes into 1 hits in \d+ms").Match(spyLog.Entries[3].Message).Success); }
public static async void SearchAsyncThrowsTaskCancelledExceptionWithAlreadyCancelledCancellationToken() { var spyLog = new SpyLog(); var localConnection = new ElasticConnection(new Uri("http://localhost"), index: "SearchIndex"); var request = new SearchRequest { DocumentType = "docType" }; var formatter = new SearchRequestFormatter(localConnection, mapping, request); var ex = await Record.ExceptionAsync(() => localConnection.SearchAsync( formatter.Body, request, new CancellationToken(true), spyLog)); Assert.IsType<TaskCanceledException>(ex); }
public void BodyContainsTermsStatsFacet() { const int expectedSize = 101; var expectedFacet = new TermsStatsFacet("Name", "Key", "Value", expectedSize); var searchRequest = new SearchRequest { Facets = new List<IFacet>(new[] { expectedFacet }) }; var formatter = new SearchRequestFormatter(defaultConnection, mapping, searchRequest); var body = JObject.Parse(formatter.Body); var result = body.TraverseWithAssert("facets", expectedFacet.Name, expectedFacet.Type); Assert.Equal(expectedFacet.Key, result.TraverseWithAssert("key_field").ToString()); Assert.Equal(expectedFacet.Value, result.TraverseWithAssert("value_field").ToString()); Assert.Equal(expectedSize.ToString(CultureInfo.InvariantCulture), result.TraverseWithAssert("size").ToString()); }
public static async Task LogsDebugMessagesDuringExecution() { var responseString = BuildResponseString(2, 1, 1, 0.3141, "testIndex", "testType", "testId"); var messageHandler = new SpyMessageHandler(); var log = new SpyLog(); messageHandler.Response.Content = new StringContent(responseString); var localConnection = new ElasticConnection(messageHandler, new Uri("http://localhost"), "myUser", "myPass", index: "SearchIndex"); var processor = new ElasticRequestProcessor(localConnection, mapping, log, retryPolicy); var request = new SearchRequest { DocumentType = "abc123", Size = 2112 }; await processor.SearchAsync(request); Assert.Equal(4, log.Messages.Count); Assert.Equal(@"[VERBOSE] Request: POST http://localhost/SearchIndex/abc123/_search", log.Messages[0]); Assert.Equal(@"[VERBOSE] Body:" +'\n' + @"{""size"":2112,""timeout"":""10s""}", log.Messages[1]); Assert.True(new Regex(@"\[VERBOSE\] Response: 200 OK \(in \d+ms\)").Match(log.Messages[2]).Success); Assert.True(new Regex(@"\[VERBOSE\] Deserialized \d+ bytes into 1 hits in \d+ms").Match(log.Messages[3]).Success); }
/// <inheritdoc/> public override Uri GetSearchUri(SearchRequest searchRequest) { var builder = new UriBuilder(endpoint); builder.Path += (Index ?? "_all") + "/"; if (!String.IsNullOrEmpty(searchRequest.DocumentType)) builder.Path += searchRequest.DocumentType + "/"; builder.Path += "_search"; var parameters = builder.Uri.GetComponents(UriComponents.Query, UriFormat.Unescaped) .Split(parameterSeparator, StringSplitOptions.RemoveEmptyEntries) .Select(p => p.Split('=')) .ToDictionary(k => k[0], v => v.Length > 1 ? v[1] : null); if (!String.IsNullOrEmpty(searchRequest.SearchType)) parameters["search_type"] = searchRequest.SearchType; if (Options.Pretty) parameters["pretty"] = "true"; builder.Query = String.Join("&", parameters.Select(p => p.Value == null ? p.Key : p.Key + "=" + p.Value)); return builder.Uri; }
public static async Task NoAuthorizationWithEmptyUserName() { var messageHandler = new SpyMessageHandler(); var localConnection = new ElasticConnection(messageHandler, new Uri("http://localhost")); var request = new SearchRequest { DocumentType = "docType" }; var formatter = new SearchRequestFormatter(localConnection, mapping, request); await localConnection.SearchAsync( formatter.Body, request, token, log); Assert.Null(messageHandler.Request.Headers.Authorization); }