using System.Text; using System.Text.Json; using System.Text.Json.Serialization; namespace Books.Api.Tests.Helpers; /// /// Helper client for executing GraphQL queries and mutations in tests. /// public class GraphQLTestClient(HttpClient httpClient) { private static readonly JsonSerializerOptions JsonOptions = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, PropertyNameCaseInsensitive = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }; /// /// Executes a GraphQL query and returns the typed result. /// public async Task> QueryAsync( string query, object? variables = null, CancellationToken cancellationToken = default) { return await ExecuteAsync(query, variables, cancellationToken); } /// /// Executes a GraphQL mutation and returns the typed result. /// public async Task> MutateAsync( string mutation, object? variables = null, CancellationToken cancellationToken = default) { return await ExecuteAsync(mutation, variables, cancellationToken); } /// /// Executes a GraphQL operation and returns the raw JSON response. /// public async Task ExecuteRawAsync( string query, object? variables = null, CancellationToken cancellationToken = default) { var request = new GraphQLRequest(query, variables); var json = JsonSerializer.Serialize(request, JsonOptions); var content = new StringContent(json, Encoding.UTF8, "application/json"); var response = await httpClient.PostAsync("/graphql", content, cancellationToken); return await response.Content.ReadAsStringAsync(cancellationToken); } private async Task> ExecuteAsync( string query, object? variables, CancellationToken cancellationToken) { var request = new GraphQLRequest(query, variables); var json = JsonSerializer.Serialize(request, JsonOptions); var content = new StringContent(json, Encoding.UTF8, "application/json"); var response = await httpClient.PostAsync("/graphql", content, cancellationToken); var responseJson = await response.Content.ReadAsStringAsync(cancellationToken); var result = JsonSerializer.Deserialize>(responseJson, JsonOptions); return result ?? throw new InvalidOperationException("Failed to deserialize GraphQL response"); } } public record GraphQLRequest( [property: JsonPropertyName("query")] string Query, [property: JsonPropertyName("variables")] object? Variables = null); public class GraphQLResponse { [JsonPropertyName("data")] public T? Data { get; set; } [JsonPropertyName("errors")] public List? Errors { get; set; } public bool HasErrors => Errors?.Count > 0; public void EnsureNoErrors() { if (HasErrors) { var errorDetails = Errors!.Select(e => { var msg = e.Message; if (e.Extensions != null) { foreach (var ext in e.Extensions) { msg += $"{Environment.NewLine} [{ext.Key}]: {ext.Value}"; } } return msg; }); var messages = string.Join(Environment.NewLine, errorDetails); throw new GraphQLException($"GraphQL errors occurred:{Environment.NewLine}{messages}", Errors!); } } } public class GraphQLError { [JsonPropertyName("message")] public string Message { get; set; } = string.Empty; [JsonPropertyName("path")] public List? Path { get; set; } [JsonPropertyName("extensions")] public Dictionary? Extensions { get; set; } } public class GraphQLException(string message, List errors) : Exception(message) { public List Errors { get; } = errors; }