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::{Context, CustomDirective, Directive, ResolveFut, ServerResult, Value};

use super::MatcherError;

#[Directive(location = "Field", name = "shouldThrow")]
pub fn should_throw(
    msg_like: Option<String>,
    msg_like_regex: Option<String>,
) -> impl CustomDirective {
    if let Some(sub) = msg_like {
        return ShouldThrow::String(sub);
    }

    if let Some(regex) = msg_like_regex {
        return ShouldThrow::Regex(
            regex::Regex::new(&regex)
                .map_err(|err| format!("incorrect regular expression: {err}"))
                .unwrap(),
        );
    }

    ShouldThrow::Wildcard
}

enum ShouldThrow {
    Wildcard,
    Regex(regex::Regex),
    String(String),
}

impl Display for ShouldThrow {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match self {
            ShouldThrow::Wildcard => write!(f, "*"),
            ShouldThrow::Regex(regex) => write!(f, "/{regex}/"),
            ShouldThrow::String(sub) => sub.fmt(f),
        }
    }
}

impl ShouldThrow {
    pub fn matches(&self, text: &str) -> bool {
        match self {
            ShouldThrow::Wildcard => true,
            ShouldThrow::String(sub) => text.contains(sub),
            ShouldThrow::Regex(regex) => regex.is_match(text),
        }
    }
}

#[async_trait]
impl CustomDirective for ShouldThrow {
    async fn resolve_field(
        &self,
        ctx: &Context<'_>,
        resolve: ResolveFut<'_>,
    ) -> ServerResult<Option<Value>> {
        match resolve.await {
            Ok(_) => Err(MatcherError::new(
                ctx.item.pos,
                "Expected the function to throw an error.\nBut it didn't throw anything.".into(),
            )),
            Err(err) => {
                if self.matches(&err.message) {
                    // todo: this branch should probably just return Err(err)
                    Ok(None)
                } else {
                    Err(MatcherError::new(
                        ctx.item.pos,
                        format!("Expected: \"{self}\"\nReceived: \"{}\"", err.message),
                    ))
                }
            }
        }
    }
}