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 { IndexType = "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 ); }
private async Task <object> ExecuteAsync(ElasticTranslateResult translation, CancellationToken cancellationToken = default(CancellationToken)) { Log.Debug(null, null, "Executing query against document '{0}'", translation.SearchRequest.IndexType); try { ElasticResponse response; if (translation.SearchRequest.Query == ConstantCriteria.False) { response = new ElasticResponse(); } else { response = await requestProcessor.SearchAsync(translation.SearchRequest, cancellationToken).ConfigureAwait(false); if (response == null) { throw new InvalidOperationException("No HTTP response received."); } } var result = translation.Materializer.Materialize(response); response.hits?.hits?.Clear(); // Clear list aggressively return(result); } catch (AggregateException ex) { ExceptionDispatchInfo.Capture(ex.InnerException).Throw(); return(null); // Never called, as the above code re-throws } }
/// <inheritdoc/> public async Task <object> ExecuteAsync(Expression expression, CancellationToken cancellationToken = default(CancellationToken)) { Argument.EnsureNotNull("expression", expression); var translation = ElasticQueryTranslator.Translate(Mapping, expression); Log.Debug(null, null, "Executing query against document '{0}'", translation.SearchRequest.DocumentType); try { ElasticResponse response; if (translation.SearchRequest.Filter == ConstantCriteria.False) { response = new ElasticResponse(); } else { response = await requestProcessor.SearchAsync(translation.SearchRequest, cancellationToken); if (response == null) { throw new InvalidOperationException("No HTTP response received."); } } return(translation.Materializer.Materialize(response)); } catch (AggregateException ex) { ExceptionDispatchInfo.Capture(ex.InnerException).Throw(); return(null); // Never called, as the above code re-throws } }
private object ExecuteInternal(Expression expression) { var translation = ElasticQueryTranslator.Translate(Mapping, Prefix, expression); var elementType = TypeHelper.GetSequenceElementType(expression.Type); Log.Debug(null, null, "Executing query against type {0}", elementType); try { ElasticResponse response; if (translation.SearchRequest.Filter == ConstantCriteria.False) { response = new ElasticResponse(); } else { response = AsyncHelper.RunSync(() => requestProcessor.SearchAsync(translation.SearchRequest)); if (response == null) { throw new InvalidOperationException("No HTTP response received."); } } return(translation.Materializer.Materialize(response)); } catch (AggregateException ex) { ExceptionDispatchInfo.Capture(ex.InnerException).Throw(); return(null); // Never called, as the above code re-throws } }
public static async void SearchAsyncThrowsTaskCancelledExceptionWithSubsequentlyCancelledCancellationToken() { var request = new SearchRequest { IndexType = "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); }
public static async void SearchAsyncThrowsTaskCancelledExceptionWithAlreadyCancelledCancellationToken() { var request = new SearchRequest { DocumentType = "docType" }; var processor = new ElasticRequestProcessor(connection, mapping, log, retryPolicy); var ex = await Record.ExceptionAsync(() => processor.SearchAsync(request, new CancellationToken(true))); Assert.IsType <TaskCanceledException>(ex); }
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 ElasticSearchRequest { Type = "docType" }; await processor.SearchAsync(request); Assert.Null(messageHandler.Request.Headers.Authorization); }
/// <inheritdoc/> public async Task <object> ExecuteAsync(Expression expression, CancellationToken cancellationToken = default(CancellationToken)) { Argument.EnsureNotNull(nameof(expression), expression); var translation = ElasticQueryTranslator.Translate(Mapping, expression); Log.Debug(null, null, "Executing query against document '{0}'", translation.SearchRequest.DocumentType); try { ElasticResponse response; if (translation.SearchRequest.Filter == ConstantCriteria.False) { response = new ElasticResponse(); } else { response = await requestProcessor.SearchAsync(translation.SearchRequest, cancellationToken); if (response == null) { throw new InvalidOperationException("No HTTP response received."); } } var result = translation.Materializer.Materialize(response); if (response.hits != null) { var hits = response.hits.hits; if (hits != null && hits.Capacity > 4096) { // If the response has a large number of hits then the List<T> instance might end up allocating an array on the large object heap. // This means that the elements in that array can survive multiple rounds of garbage collection, even if they are not actually // reachable anymore. // Clearing out the collection after we materialized the result means the elements can be freed up. hits.Clear(); } } return(result); } catch (AggregateException ex) { ExceptionDispatchInfo.Capture(ex.InnerException).Throw(); return(null); // Never called, as the above code re-throws } }
public static void NonSuccessfulHttpRequestThrows() { var messageHandler = new SpyMessageHandler(); messageHandler.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 ElasticSearchRequest { Type = "docType" }; var ex = Record.Exception(() => processor.SearchAsync(request).GetAwaiter().GetResult()); Assert.IsType <HttpRequestException>(ex); Assert.Equal("Response status code does not indicate success: 404 (Not Found).", ex.Message); }
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 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 ElasticSearchRequest { Type = "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 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 { IndexType = "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 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 ElasticSearchRequest { Type = "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: {""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\] De-serialized \d+ bytes into 1 hits in \d+ms").Match(log.Messages[3]).Success); }
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", "SearchIndex"); var processor = new ElasticRequestProcessor(localConnection, mapping, spyLog, retryPolicy); var request = new SearchRequest { DocumentType = "abc123", Size = 2112 }; await processor.SearchAsync(request, CancellationToken.None); 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); }