protected void ResultCheck(WebApiAgentResult result) { if (_expectedStatusCode != null) { Assert.AreEqual(_expectedStatusCode, result.StatusCode); } }
/// <summary> /// Send a <see cref="HttpMethod"/> <b>PATCH</b> <paramref name="json"/> request as an asynchronous operation. /// </summary> /// <param name="urlSuffix">The url suffix for the operation.</param> /// <param name="patchOption">The <see cref="WebApiPatchOption"/>.</param> /// <param name="json">The json value.</param> /// <param name="requestOptions">The optional <see cref="WebApiRequestOptions"/>.</param> /// <param name="args">The operation arguments to be substituted within the <paramref name="urlSuffix"/>.</param> /// <param name="memberName">The method or property name of the caller to the method.</param> /// <param name="filePath">The full path of the source file that contains the caller.</param> /// <param name="lineNumber">The line number in the source file at which the method is called.</param> /// <returns>The <see cref="WebApiAgentResult{TResult}"/>.</returns> public async Task <WebApiAgentResult> PatchAsync(string urlSuffix, WebApiPatchOption patchOption, JToken json, WebApiRequestOptions?requestOptions = null, WebApiArg[]?args = null, [CallerMemberName] string?memberName = null, [CallerFilePath] string?filePath = null, [CallerLineNumber] int lineNumber = 0) { if (json == null) { throw new ArgumentNullException(nameof(json)); } if (patchOption == WebApiPatchOption.NotSpecified) { throw new ArgumentException("A valid patch option must be specified.", nameof(patchOption)); } var uri = CreateFullUri(urlSuffix, args, requestOptions); if (args != null && args.Any(x => x.ArgType == WebApiArgType.FromBody)) { throw new ArgumentException("No arguments can be marked as IsFromBody for a PATCH.", nameof(args)); } return(await WebApiServiceAgentInvoker.Default.InvokeAsync(this, async() => { var content = new StringContent(json.ToString()); content.Headers.ContentType = MediaTypeHeaderValue.Parse(patchOption == WebApiPatchOption.JsonPatch ? "application/json-patch+json" : "application/merge-patch+json"); var result = new WebApiAgentResult(await Client.SendAsync(CreateRequestMessage(new HttpMethod("PATCH"), uri, content, requestOptions)).ConfigureAwait(false)); result.Content = await result.Response.Content.ReadAsStringAsync().ConfigureAwait(false); return VerifyResult(result); }, json, memberName, filePath, lineNumber).ConfigureAwait(false)); }
/// <summary> /// Send a <see cref="HttpMethod.Delete"/> request as an asynchronous operation. /// </summary> /// <param name="urlSuffix">The url suffix for the operation.</param> /// <param name="requestOptions">The optional <see cref="WebApiRequestOptions"/>.</param> /// <param name="args">The operation arguments to be substituted within the <paramref name="urlSuffix"/>.</param> /// <param name="memberName">The method or property name of the caller to the method.</param> /// <param name="filePath">The full path of the source file that contains the caller.</param> /// <param name="lineNumber">The line number in the source file at which the method is called.</param> /// <returns>The <see cref="WebApiAgentResult{T}"/>.</returns> public async Task <WebApiAgentResult> DeleteAsync(string urlSuffix, WebApiRequestOptions?requestOptions = null, WebApiArg[]?args = null, [CallerMemberName] string?memberName = null, [CallerFilePath] string?filePath = null, [CallerLineNumber] int lineNumber = 0) { var uri = CreateFullUri(urlSuffix, args, requestOptions); return(await WebApiServiceAgentInvoker.Default.InvokeAsync(this, async() => { var result = new WebApiAgentResult(await Client.SendAsync(CreateRequestMessage(HttpMethod.Delete, uri, requestOptions: requestOptions)).ConfigureAwait(false)); result.Content = await result.Response.Content.ReadAsStringAsync().ConfigureAwait(false); return VerifyResult(result); }, null !, memberName, filePath, lineNumber).ConfigureAwait(false)); }
/// <summary> /// Send a <see cref="HttpMethod.Post"/> request as an asynchronous operation with an expected response. /// </summary> /// <typeparam name="TResult">The result <see cref="Type"/>.</typeparam> /// <param name="urlSuffix">The url suffix for the operation.</param> /// <param name="requestOptions">The optional <see cref="WebApiRequestOptions"/>.</param> /// <param name="args">The operation arguments to be substituted within the <paramref name="urlSuffix"/>.</param> /// <param name="memberName">The method or property name of the caller to the method.</param> /// <param name="filePath">The full path of the source file that contains the caller.</param> /// <param name="lineNumber">The line number in the source file at which the method is called.</param> /// <returns>The <see cref="WebApiAgentResult{TResult}"/>.</returns> public async Task <WebApiAgentResult <TResult> > PostAsync <TResult>(string urlSuffix, WebApiRequestOptions?requestOptions = null, WebApiArg[]?args = null, [CallerMemberName] string?memberName = null, [CallerFilePath] string?filePath = null, [CallerLineNumber] int lineNumber = 0) { var uri = CreateFullUri(urlSuffix, args, requestOptions); return(await WebApiServiceAgentInvoker.Default.InvokeAsync(this, async() => { var value = args?.Where(x => x.ArgType == WebApiArgType.FromBody).SingleOrDefault()?.GetValue(); var result = new WebApiAgentResult(await Client.SendAsync(CreateRequestMessage(HttpMethod.Post, uri, CreateJsonContentFromValue(value), requestOptions)).ConfigureAwait(false)); result.Content = await result.Response.Content.ReadAsStringAsync().ConfigureAwait(false); return new WebApiAgentResult <TResult>(VerifyResult(result)); }, null !, memberName, filePath, lineNumber).ConfigureAwait(false)); }
/// <summary> /// Runs the <paramref name="func"/> where the agent is self-instantied and executed asynchonously checking against the expected outcomes. /// </summary> /// <param name="func">The function to execute.</param> /// <returns>The corresponding <see cref="Task{TResult}"/>.</returns> public async Task <WebApiAgentResult> RunOverrideAsync(Func <Task <WebApiAgentResult> > func) { if (func == null) { throw new ArgumentNullException(nameof(func)); } var sw = Stopwatch.StartNew(); WebApiAgentResult result = await func().ConfigureAwait(false); sw.Stop(); ResultCheck(result, sw); PublishedEventsCheck(); return(result); }
/// <summary> /// Send a <see cref="HttpMethod.Post"/> <paramref name="value"/> request as an asynchronous operation with an expected response. /// </summary> /// <typeparam name="TResult">The result <see cref="Type"/>.</typeparam> /// <param name="urlSuffix">The url suffix for the operation.</param> /// <param name="value">The content value.</param> /// <param name="requestOptions">The optional <see cref="WebApiRequestOptions"/>.</param> /// <param name="args">The operation arguments to be substituted within the <paramref name="urlSuffix"/>.</param> /// <param name="memberName">The method or property name of the caller to the method.</param> /// <param name="filePath">The full path of the source file that contains the caller.</param> /// <param name="lineNumber">The line number in the source file at which the method is called.</param> /// <returns>The <see cref="WebApiAgentResult{TResult}"/>.</returns> public async Task <WebApiAgentResult <TResult> > PostAsync <TResult>(string urlSuffix, object value, WebApiRequestOptions?requestOptions = null, WebApiArg[]?args = null, [CallerMemberName] string?memberName = null, [CallerFilePath] string?filePath = null, [CallerLineNumber] int lineNumber = 0) { if (value == null) { throw new ArgumentNullException(nameof(value)); } var uri = CreateFullUri(urlSuffix, args, requestOptions); if (args != null && args.Any(x => x.ArgType == WebApiArgType.FromBody)) { throw new ArgumentException("No arguments can be marked as IsFromBody where a content value is used.", nameof(args)); } return(await WebApiServiceAgentInvoker.Default.InvokeAsync(this, async() => { var result = new WebApiAgentResult(await Client.SendAsync(CreateRequestMessage(HttpMethod.Post, uri, CreateJsonContentFromValue(value), requestOptions)).ConfigureAwait(false)); result.Content = await result.Response.Content.ReadAsStringAsync().ConfigureAwait(false); return new WebApiAgentResult <TResult>(VerifyResult(result)); }, value, memberName, filePath, lineNumber).ConfigureAwait(false)); }
private void LogResult(WebApiAgentResult result) { _logger.LogInformation($"REQUEST{Environment.NewLine}{result.Request}"); _logger.LogInformation($"RESPONSE{Environment.NewLine}{result.Response}"); }
/// <summary> /// Check the result to make sure it is valid. /// </summary> /// <param name="result">The <see cref="WebApiAgentResult"/>.</param> /// <param name="sw">The <see cref="Stopwatch"/> used to measure <see cref="WebApiAgentBase"/> invocation.</param> protected void ResultCheck(WebApiAgentResult result, Stopwatch sw) { if (result == null) { throw new ArgumentNullException(nameof(result)); } // Log to output. TestContext.Out.WriteLine(""); TestContext.Out.WriteLine("AGENT TESTER..."); TestContext.Out.WriteLine(""); TestContext.Out.WriteLine($"REQUEST >"); TestContext.Out.WriteLine($"Request: {result.Request.Method} {result.Request.RequestUri}"); if (!string.IsNullOrEmpty(Username)) { TestContext.Out.WriteLine($"Username: {Username}"); } TestContext.Out.WriteLine($"Headers: {(result.Request.Headers == null || !result.Request.Headers.Any() ? "none" : "")}"); if (result.Request.Headers != null && result.Request.Headers.Any()) { foreach (var hdr in result.Request.Headers) { var sb = new StringBuilder(); foreach (var v in hdr.Value) { if (sb.Length > 0) { sb.Append(", "); } sb.Append(v); } TestContext.Out.WriteLine($" {hdr.Key}: {sb}"); } } JToken?json = null; if (result.Request.Content != null) { // HACK: The Request Content is a forward only stream that is already read; we need to reset this private variable back to the start. if (result.Request.Content is StreamContent) { var fi = typeof(StreamContent).GetField("_content", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic); var ms = (MemoryStream)fi !.GetValue(result.Request.Content) !; ms.Position = 0; } // Parse out the content. try { json = JToken.Parse(result.Request.Content.ReadAsStringAsync().Result); } #pragma warning disable CA1031 // Do not catch general exception types; by-design. catch (Exception) { } #pragma warning restore CA1031 TestContext.Out.WriteLine($"Content: [{result.Request.Content?.Headers?.ContentType?.MediaType ?? "None"}]"); TestContext.Out.WriteLine(json == null ? result.Request.Content?.ToString() : json.ToString()); } TestContext.Out.WriteLine(""); TestContext.Out.WriteLine($"RESPONSE >"); TestContext.Out.WriteLine($"HttpStatusCode: {result.StatusCode} ({(int)result.StatusCode})"); TestContext.Out.WriteLine($"Elapsed (ms): {(sw == null ? "none" : sw.ElapsedMilliseconds.ToString(System.Globalization.CultureInfo.InvariantCulture))}"); var hdrs = result.Response?.Headers?.ToString().Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries); TestContext.Out.WriteLine($"Headers: {(hdrs == null || !hdrs.Any() ? "none" : "")}"); if (hdrs != null && hdrs.Any()) { foreach (var hdr in hdrs) { TestContext.Out.WriteLine($" {hdr}"); } } TestContext.Out.WriteLine($"Messages: {(result.Messages == null || result.Messages.Count == 0 ? "none" : "")}"); if (result.Messages != null && result.Messages.Count > 0) { foreach (var m in result.Messages) { TestContext.Out.WriteLine($" {m.Type}: {m.Text} {(m.Property == null ? "" : "(" + m.Property + ")")}"); } TestContext.Out.WriteLine(""); } json = null; if (!string.IsNullOrEmpty(result.Content) && result.Response?.Content?.Headers?.ContentType?.MediaType == "application/json") { try { json = JToken.Parse(result.Content); } #pragma warning disable CA1031 // Do not catch general exception types; by-design. catch (Exception) { /* This is being swallowed by design. */ } #pragma warning restore CA1031 } TestContext.Out.Write($"Content: [{result.Response?.Content?.Headers?.ContentType?.MediaType ?? "none"}]"); if (json != null) { TestContext.Out.WriteLine(""); TestContext.Out.WriteLine(json.ToString()); } else { TestContext.Out.WriteLine($"{(string.IsNullOrEmpty(result.Content) ? "none" : result.Content)}"); } TestContext.Out.WriteLine(""); TestContext.Out.WriteLine($"EVENTS PUBLISHED >"); var events = ExpectEvent.GetEvents(CorrelationId); if (events.Count == 0) { TestContext.Out.WriteLine(" None."); } else { foreach (var e in events) { TestContext.Out.WriteLine($" Subject: {e.Subject}, Action: {e.Action}"); } } TestContext.Out.WriteLine(""); TestContext.Out.WriteLine($"LOGGING >"); var messages = CorrelationIdLogger.GetMessages(CorrelationId); if (messages.Count == 0) { TestContext.Out.WriteLine(" None."); } else { foreach (var l in messages) { TestContext.Out.WriteLine($"{l}"); } } TestContext.Out.WriteLine(""); TestContext.Out.WriteLine(new string('=', 80)); TestContext.Out.WriteLine(""); // Perform checks. if (_expectedStatusCode.HasValue && _expectedStatusCode != result.StatusCode) { Assert.Fail($"Expected HttpStatusCode was '{_expectedStatusCode} ({(int)_expectedStatusCode})'; actual was {result.StatusCode} ({(int)result.StatusCode})."); } if (_expectedErrorType.HasValue && _expectedErrorType != result.ErrorType) { Assert.Fail($"Expected ErrorType was '{_expectedErrorType}'; actual was '{result.ErrorType}'."); } if (_expectedErrorMessage != null && _expectedErrorMessage != result.ErrorMessage) { Assert.Fail($"Expected ErrorMessage was '{_expectedErrorMessage}'; actual was '{result.ErrorMessage}'."); } if (_expectedMessages != null) { ExpectValidationException.CompareExpectedVsActual(_expectedMessages, result.Messages); } }
/// <summary> /// Check the result to make sure it is valid. /// </summary> /// <param name="result">The <see cref="WebApiAgentResult"/>.</param> /// <param name="sw">The <see cref="Stopwatch"/> used to measure <see cref="WebApiServiceAgentBase"/> invocation.</param> protected void ResultCheck(WebApiAgentResult result, Stopwatch sw) { Check.NotNull(result, nameof(result)); // Log to output. Logger.Default.Info(""); Logger.Default.Info("AGENT TESTER..."); Logger.Default.Info(""); Logger.Default.Info($"REQUEST >"); Logger.Default.Info($"Request: {result.Request.Method} {result.Request.RequestUri}"); if (!string.IsNullOrEmpty(Username)) { Logger.Default.Info($"Username: {Username}"); } Logger.Default.Info($"Headers: {(result.Request.Headers == null || !result.Request.Headers.Any() ? "none" : "")}"); if (result.Request.Headers != null && result.Request.Headers.Any()) { foreach (var hdr in result.Request.Headers) { var sb = new StringBuilder(); foreach (var v in hdr.Value) { if (sb.Length > 0) { sb.Append(", "); } sb.Append(v); } Logger.Default.Info($" {hdr.Key}: {sb}"); } } JToken json = null; if (result.Request.Content != null) { try { json = JToken.Parse(result.Request.Content.ReadAsStringAsync().Result); } catch (Exception) { } Logger.Default.Info($"Content [{result.Request.Content?.Headers?.ContentType?.MediaType}]:"); Logger.Default.Info(json == null ? result.Request.Content.ToString() : json.ToString()); } Logger.Default.Info(""); Logger.Default.Info($"RESPONSE >"); Logger.Default.Info($"HttpStatusCode: {result.StatusCode} ({(int)result.StatusCode})"); Logger.Default.Info($"Elapsed (ms): {(sw == null ? "none" : sw.ElapsedMilliseconds.ToString())}"); var hdrs = result.Response?.Headers?.ToString().Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries); Logger.Default.Info($"Headers: {(hdrs == null || !hdrs.Any() ? "none" : "")}"); if (hdrs != null && hdrs.Any()) { foreach (var hdr in hdrs) { Logger.Default.Info($" {hdr}"); } } Logger.Default.Info($"Messages: {(result.Messages == null || result.Messages.Count == 0 ? "none" : "")}"); if (result.Messages != null && result.Messages.Count > 0) { foreach (var m in result.Messages) { Logger.Default.Info($" {m.Type}: {m.Text} {(m.Property == null ? "" : "(" + m.Property + ")")}"); } Logger.Default.Info(null); } json = null; if (!string.IsNullOrEmpty(result.Content) && result.Response?.Content?.Headers?.ContentType?.MediaType == "application/json") { try { json = JToken.Parse(result.Content); } catch (Exception) { /* This is being swallowed by design. */ } } TestContext.Out.Write($"Content: "); if (json != null) { Logger.Default.Info(null); Logger.Default.Info(json.ToString()); } else { Logger.Default.Info($"{(string.IsNullOrEmpty(result.Content) ? "none" : result.Content)}"); } Logger.Default.Info(null); Logger.Default.Info(new string('=', 80)); Logger.Default.Info(null); // Perform checks. if (_expectedStatusCode.HasValue && _expectedStatusCode != result.StatusCode) { Assert.Fail($"Expected HttpStatusCode was '{_expectedStatusCode} ({(int)_expectedStatusCode})'; actual was {result.StatusCode} ({(int)result.StatusCode})."); } if (_expectedErrorType.HasValue && _expectedErrorType != result.ErrorType) { Assert.Fail($"Expected ErrorType was '{_expectedErrorType}'; actual was '{result.ErrorType}'."); } if (_expectedErrorMessage != null && _expectedErrorMessage != result.ErrorMessage) { Assert.Fail($"Expected ErrorMessage was '{_expectedErrorMessage}'; actual was '{result.ErrorMessage}'."); } if (_expectedMessages != null) { ExpectValidationException.CompareExpectedVsActual(_expectedMessages, result.Messages); } }