use crate::context::GraphQLContext;
use async_graphql::Result as GqlResult;
use async_trait::async_trait;
#[async_trait]
pub trait Resolver: Send + Sync {
type Output;
async fn resolve(&self) -> GqlResult<Self::Output>;
}
#[async_trait]
pub trait ContextResolver: Send + Sync {
type Output;
async fn resolve_with_context(&self, ctx: &GraphQLContext) -> GqlResult<Self::Output>;
}
#[cfg(test)]
mod tests {
use super::*;
use rstest::rstest;
use serde_json::json;
struct TestResolver {
value: i32,
}
#[async_trait]
impl Resolver for TestResolver {
type Output = i32;
async fn resolve(&self) -> GqlResult<Self::Output> {
Ok(self.value * 2)
}
}
struct StringResolver {
message: String,
}
#[async_trait]
impl Resolver for StringResolver {
type Output = String;
async fn resolve(&self) -> GqlResult<Self::Output> {
Ok(format!("Resolved: {}", self.message))
}
}
struct ContextDataResolver {
key: String,
}
#[async_trait]
impl ContextResolver for ContextDataResolver {
type Output = String;
async fn resolve_with_context(&self, ctx: &GraphQLContext) -> GqlResult<Self::Output> {
let value = ctx.require_data(&self.key)?;
Ok(format!("Value: {}", value))
}
}
#[rstest]
#[tokio::test]
async fn test_resolver_trait_implementation() {
let resolver = TestResolver { value: 21 };
let result = resolver.resolve().await;
assert!(result.is_ok());
assert_eq!(result.unwrap(), 42);
}
#[rstest]
#[tokio::test]
async fn test_string_resolver() {
let resolver = StringResolver {
message: "Hello GraphQL".to_string(),
};
let result = resolver.resolve().await;
assert!(result.is_ok());
assert_eq!(result.unwrap(), "Resolved: Hello GraphQL");
}
#[rstest]
#[tokio::test]
async fn test_resolver_multiple_calls() {
let resolver = TestResolver { value: 10 };
let result1 = resolver.resolve().await.unwrap();
let result2 = resolver.resolve().await.unwrap();
assert_eq!(result1, result2);
assert_eq!(result1, 20);
}
#[rstest]
#[tokio::test]
async fn test_context_resolver_returns_data_when_present() {
let ctx = GraphQLContext::new();
ctx.set_data("user_id".to_string(), json!("user-42"));
let resolver = ContextDataResolver {
key: "user_id".to_string(),
};
let result = resolver.resolve_with_context(&ctx).await;
assert!(result.is_ok());
assert_eq!(result.unwrap(), "Value: \"user-42\"");
}
#[rstest]
#[tokio::test]
async fn test_context_resolver_returns_error_when_data_missing() {
let ctx = GraphQLContext::new();
let resolver = ContextDataResolver {
key: "missing_key".to_string(),
};
let result = resolver.resolve_with_context(&ctx).await;
assert!(result.is_err());
let err = result.unwrap_err();
assert!(
err.message.contains("missing_key"),
"Error should mention the missing key, got: {}",
err.message
);
}
#[rstest]
#[tokio::test]
async fn test_context_resolver_does_not_panic_on_missing_data() {
let ctx = GraphQLContext::new();
let resolver = ContextDataResolver {
key: "nonexistent".to_string(),
};
let result = resolver.resolve_with_context(&ctx).await;
assert!(result.is_err());
assert!(
result
.unwrap_err()
.message
.contains("Required context data not found")
);
}
}