diff --git a/Lombiq.Tests.UI/Extensions/HttpClientUITestContextExtensions.cs b/Lombiq.Tests.UI/Extensions/HttpClientUITestContextExtensions.cs new file mode 100644 index 000000000..29cc79416 --- /dev/null +++ b/Lombiq.Tests.UI/Extensions/HttpClientUITestContextExtensions.cs @@ -0,0 +1,184 @@ +using Lombiq.Tests.UI.Services; +using Microsoft.SqlServer.Management.Dmf; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Net.Mime; +using System.Text; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Threading.Tasks; + +namespace Lombiq.Tests.UI.Extensions; + +[SuppressMessage( + "Security", + "SCS0004: Certificate Validation has been disabled.", + Justification = "Certificate validation is unnecessary for UI testing.")] +[SuppressMessage( + "Reliability", + "CA2000:Dispose objects before losing scope", + Justification = "Disposed by the HttpClient.")] +public static class HttpClientUITestContextExtensions +{ + /// + /// Creates a new and authorizes it with a Bearer token that is created based on the provided + /// parameters. + /// + public static async Task CreateAndAuthorizeClientAsync( + this UITestContext context, + string grantType = "client_credentials", + string clientId = "UITest", + string clientSecret = "Password", + string userName = null, + string password = null) + { + var handler = new HttpClientHandler + { + ServerCertificateCustomValidationCallback = (_, _, _, _) => true, + CheckCertificateRevocationList = true, + }; + + var client = new HttpClient(handler) + { + BaseAddress = context.Scope.BaseUri, + }; + + var parameters = new List> + { + new("grant_type", grantType), + new("client_id", clientId), + new("client_secret", clientSecret), + }; + + if (string.Equals(grantType, "password", StringComparison.OrdinalIgnoreCase)) + { + parameters.Add(new KeyValuePair("username", userName)); + parameters.Add(new KeyValuePair("password", password)); + } + + using var requestBody = new FormUrlEncodedContent(parameters); + + var tokenUrl = context.Scope.BaseUri.AbsoluteUri + "connect/token"; + var tokenResponse = await client.PostAsync(tokenUrl, requestBody); + + if (!tokenResponse.IsSuccessStatusCode) + { + throw new InvalidOperandException( + $"Failed to get token for user in {nameof(CreateAndAuthorizeClientAsync)}. TokenResponse: {tokenResponse}"); + } + + var responseContent = await tokenResponse.Content.ReadAsStringAsync(); + var token = JsonNode.Parse(responseContent)?["access_token"]?.ToString(); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); + + return client; + } + + /// + /// Issues a GET request to the given using the provided . + /// + /// The response's as a string. + public static async Task GetAndReadResponseContentAsync( + this UITestContext context, + HttpClient client, + string requestUri) + { + var response = await client.GetAsync(requestUri); + return await response.Content.ReadAsStringAsync(); + } + + /// + /// Issues a GET request to the given using the provided then + /// deserializes the response content to the given . + /// + /// The response's as a string. + public static async Task GetAndReadResponseContentAsync( + this UITestContext context, + HttpClient client, + string requestUri) + where TObject : class + { + var content = await GetAndReadResponseContentAsync(context, client, requestUri); + var parsed = JToken.Parse(content); + return parsed.ToObject(); + } + + /// + /// Issues a POST request to the given using the provided and + /// . + /// + /// The response's as a string. + public static async Task PostAndReadResponseContentAsync( + this UITestContext context, + HttpClient client, + string requestUri, + string json) => + await (await PostAndGetResponseAsync(client, requestUri, json)).Content.ReadAsStringAsync(); + + /// + /// Issues a POST request to the given using the provided then + /// deserializes the response content to the given . + /// + /// The deserialized object. + public static async Task PostAndReadResponseContentAsync( + this UITestContext context, + HttpClient client, + string requestUri, + string json) + where TObject : class + { + var content = await (await PostAndGetResponseAsync(client, requestUri, json)).Content.ReadAsStringAsync(); + var parsed = JToken.Parse(content); + + return parsed.ToObject(); + } + + /// + /// Issues a POST request to the given using the provided + /// which will be serialized as json. + /// + /// The response's as a string. + public static Task PostAndReadResponseContentAsync( + this UITestContext context, + HttpClient client, + object objectToSerialize, + string requestUri) + { + var json = JsonSerializer.Serialize(objectToSerialize); + return PostAndReadResponseContentAsync(context, client, requestUri, json); + } + + /// + /// Issues a POST request to the given using the provided + /// which will be serialized as json. + /// + /// The . + public static Task PostAndGetResponseAsync( + this UITestContext context, + HttpClient client, + object objectToSerialize, + string requestUri) + { + var json = JsonSerializer.Serialize(objectToSerialize); + return PostAndGetResponseAsync(client, requestUri, json); + } + + /// + /// Issues a POST request to the given using the provided . + /// + /// The . + public static async Task PostAndGetResponseAsync( + HttpClient client, + string requestUri, + string json) + { + var stringContent = new StringContent(json, Encoding.UTF8, MediaTypeNames.Application.Json); + var response = await client.PostAsync(requestUri, stringContent); + + return response; + } +}