async-graphql-test 1.0.0

A test framework for Rust GraphQL servers.
Documentation
use async_graphql::async_trait::async_trait;
use async_graphql::{Context, CustomDirective, Directive, ResolveFut, ServerResult, Value};

use super::{MatcherError, ValueTypeName};

/// Use `@shouldHaveLength` to check that an object has a `.length` property, and it is set to a certain numeric value.
///
/// This is especially useful for checking arrays or strings size.
#[Directive(location = "Field", name = "shouldHaveLength")]
pub fn should_have_length(n: usize) -> impl CustomDirective {
    ShouldHaveLength { len: n }
}

struct ShouldHaveLength {
    len: usize,
}

#[async_trait]
impl CustomDirective for ShouldHaveLength {
    async fn resolve_field(
        &self,
        ctx: &Context<'_>,
        resolve: ResolveFut<'_>,
    ) -> ServerResult<Option<Value>> {
        match resolve.await {
            Ok(None) => Ok(None),
            // todo: returned HashMap can also have length
            Ok(Some(Value::List(list))) => {
                if list.len() == self.len {
                    Ok(Some(Value::List(list)))
                } else {
                    Err(MatcherError::new(
                        ctx.item.pos,
                        format!(
                            "Expected length: {}\nReceived length: {}\nReceived array:  {}",
                            self.len,
                            list.len(),
                            Value::List(list)
                        ),
                    ))
                }
            }
            Ok(Some(Value::String(string))) => {
                if string.len() == self.len {
                    Ok(Some(Value::String(string)))
                } else {
                    Err(MatcherError::new(
                        ctx.item.pos,
                        format!(
                            "Expected length: {}\nReceived length: {}\nReceived string: {}",
                            self.len,
                            string.len(),
                            Value::String(string)
                        ),
                    ))
                }
            }
            Ok(Some(value)) => {
                let type_name = ValueTypeName(&value);
                Err(MatcherError::new(
                    ctx.item.pos,
                    format!(
                        "@shouldHaveLength error: value must have a length property.\nReceived has type:  {type_name}\nReceived has value: {value}"
                    ),
                ))
            }
            Err(err) => Err(MatcherError::unexpected_error(err)),
        }
    }
}