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