async-graphql-test 1.0.0

A test framework for Rust GraphQL servers.
Documentation
use std::fmt::{Display, Formatter};

use async_graphql::async_trait::async_trait;
use async_graphql::{Any, Context, CustomDirective, Directive, ResolveFut, ServerResult, Value};

use super::{MatcherError, ValueTypeName};

#[Directive(location = "Field", name = "shouldContain")]
pub fn should_contain(item: Any) -> impl CustomDirective {
    ShouldContain { item: item.0 }
}

struct ShouldContain {
    item: Value,
}

impl Display for ShouldContain {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        self.item.fmt(f)
    }
}

#[async_trait]
impl CustomDirective for ShouldContain {
    async fn resolve_field(
        &self,
        ctx: &Context<'_>,
        resolve: ResolveFut<'_>,
    ) -> ServerResult<Option<Value>> {
        match resolve.await {
            Ok(None) => Ok(None),
            Ok(Some(Value::List(list))) => {
                if list.iter().any(|it| self.item.eq(it)) {
                    Ok(Some(Value::List(list)))
                } else {
                    Err(MatcherError::new(
                        ctx.item.pos,
                        format!(
                            "Expected value: {}\nReceived array: {}",
                            self.item,
                            Value::List(list)
                        ),
                    ))
                }
            }
            Ok(Some(Value::String(string))) => {
                if let Value::String(value) = &self.item {
                    if string.contains(value) {
                        Ok(Some(Value::String(string)))
                    } else {
                        Err(MatcherError::new(
                            ctx.item.pos,
                            format!(
                                "Expected value to match:\n  {self}\nReceived:\n  \"{string}\""
                            ),
                        ))
                    }
                } else {
                    let type_name = ValueTypeName(&self.item);
                    Err(MatcherError::new(
                        ctx.item.pos,
                        format!(
                            "@shouldContain error: passed value must be a string.\nPassed has type:  {type_name}\nReceived has value: {self}"
                        ),
                    ))
                }
            }
            Ok(Some(value)) => {
                let type_name = ValueTypeName(&value);
                Err(MatcherError::new(
                    ctx.item.pos,
                    format!(
                        "@shouldContain error: received value must be an array or a string.\nReceived has type:  {type_name}\nReceived has value: {value}"
                    ),
                ))
            }
            Err(err) => Err(MatcherError::unexpected_error(err)),
        }
    }
}