#![allow(dead_code)]
#![allow(unused_variables)]
use serde::{Deserialize, Serialize};
use server_less::{graphql, graphql_enum};
#[derive(Clone)]
struct SimpleService {
prefix: String,
}
impl SimpleService {
fn new() -> Self {
Self {
prefix: "Hello".to_string(),
}
}
}
#[graphql]
impl SimpleService {
pub fn get_greeting(&self) -> String {
format!("{}, World!", self.prefix)
}
pub fn list_items(&self) -> Vec<String> {
vec!["a".to_string(), "b".to_string()]
}
pub fn create_item(&self, name: String) -> String {
format!("Created: {}", name)
}
pub fn get_count(&self) -> i32 {
42
}
pub fn is_active(&self) -> bool {
true
}
}
#[test]
fn test_graphql_schema_created() {
let service = SimpleService::new();
let schema = service.graphql_schema();
let _ = schema;
}
#[test]
fn test_graphql_sdl_generated() {
let service = SimpleService::new();
let sdl = service.graphql_sdl();
assert!(
sdl.contains("SimpleServiceQuery"),
"SDL should have Query type, got:\n{}",
sdl
);
assert!(
sdl.contains("SimpleServiceMutation"),
"SDL should have Mutation type"
);
assert!(
sdl.contains("getGreeting"),
"SDL should have getGreeting query"
);
assert!(sdl.contains("listItems"), "SDL should have listItems query");
assert!(
sdl.contains("createItem"),
"SDL should have createItem mutation"
);
}
#[test]
fn test_graphql_router_created() {
let service = SimpleService::new();
let router = service.graphql_router();
let _ = router;
}
#[derive(Clone)]
struct ReadOnlyService;
#[graphql]
impl ReadOnlyService {
pub fn get_info(&self) -> String {
"read only".to_string()
}
pub fn list_things(&self) -> Vec<String> {
vec![]
}
}
#[test]
fn test_graphql_query_only_service() {
let service = ReadOnlyService;
let sdl = service.graphql_sdl();
assert!(sdl.contains("ReadOnlyServiceQuery"));
}
#[tokio::test]
async fn test_graphql_execute_query() {
let service = SimpleService::new();
let schema = service.graphql_schema();
let result = schema.execute("{ getGreeting }").await;
assert!(
result.errors.is_empty(),
"Query should succeed: {:?}",
result.errors
);
let data = result.data.into_json().unwrap();
assert!(data["getGreeting"].as_str().is_some());
}
#[tokio::test]
async fn test_graphql_execute_query_with_int() {
let service = SimpleService::new();
let schema = service.graphql_schema();
let result = schema.execute("{ getCount }").await;
assert!(
result.errors.is_empty(),
"Query should succeed: {:?}",
result.errors
);
}
#[tokio::test]
async fn test_graphql_execute_query_with_bool() {
let service = SimpleService::new();
let schema = service.graphql_schema();
let result = schema.execute("{ isActive }").await;
assert!(
result.errors.is_empty(),
"Query should succeed: {:?}",
result.errors
);
}
#[tokio::test]
async fn test_graphql_execute_mutation() {
let service = SimpleService::new();
let schema = service.graphql_schema();
let result = schema
.execute(r#"mutation { createItem(name: "test") }"#)
.await;
assert!(
result.errors.is_empty(),
"Mutation should succeed: {:?}",
result.errors
);
}
#[tokio::test]
async fn test_graphql_execute_list_query() {
let service = SimpleService::new();
let schema = service.graphql_schema();
let result = schema.execute("{ listItems }").await;
assert!(
result.errors.is_empty(),
"List query should succeed: {:?}",
result.errors
);
}
#[derive(Clone, Debug, Serialize, Deserialize)]
struct User {
id: i32,
name: String,
email: String,
active: bool,
}
#[derive(Clone)]
struct UserService;
#[graphql]
impl UserService {
pub fn get_user(&self, id: i32) -> User {
User {
id,
name: "Alice".to_string(),
email: "alice@example.com".to_string(),
active: true,
}
}
pub fn list_users(&self) -> Vec<User> {
vec![
User {
id: 1,
name: "Alice".to_string(),
email: "alice@example.com".to_string(),
active: true,
},
User {
id: 2,
name: "Bob".to_string(),
email: "bob@example.com".to_string(),
active: false,
},
]
}
pub fn create_user(&self, name: String, email: String) -> User {
User {
id: 99,
name,
email,
active: true,
}
}
}
#[tokio::test]
async fn test_graphql_custom_struct_query() {
let service = UserService;
let schema = service.graphql_schema();
let result = schema.execute("{ getUser(id: 1) }").await;
assert!(
result.errors.is_empty(),
"Custom struct query should succeed: {:?}",
result.errors
);
let data = result.data.into_json().unwrap();
let user = &data["getUser"];
assert!(user.is_object(), "Should return object, got: {:?}", user);
assert_eq!(user["id"], 1, "Should have correct id field");
assert_eq!(user["name"], "Alice", "Should have correct name field");
assert_eq!(
user["email"], "alice@example.com",
"Should have correct email field"
);
assert_eq!(user["active"], true, "Should have correct active field");
}
#[tokio::test]
async fn test_graphql_custom_struct_list_query() {
let service = UserService;
let schema = service.graphql_schema();
let result = schema.execute("{ listUsers }").await;
assert!(
result.errors.is_empty(),
"Custom struct list query should succeed: {:?}",
result.errors
);
let data = result.data.into_json().unwrap();
let users = &data["listUsers"];
assert!(users.is_array(), "Should return array");
let users_array = users.as_array().unwrap();
assert_eq!(users_array.len(), 2, "Should have 2 users");
assert!(users_array[0].is_object(), "User should be object");
assert_eq!(users_array[0]["id"], 1);
assert_eq!(users_array[0]["name"], "Alice");
}
#[tokio::test]
async fn test_graphql_custom_struct_mutation() {
let service = UserService;
let schema = service.graphql_schema();
let result = schema
.execute(r#"mutation { createUser(name: "Charlie", email: "charlie@example.com") }"#)
.await;
assert!(
result.errors.is_empty(),
"Custom struct mutation should succeed: {:?}",
result.errors
);
let data = result.data.into_json().unwrap();
let user = &data["createUser"];
assert!(user.is_object(), "Should return object");
assert_eq!(user["id"], 99);
assert_eq!(user["name"], "Charlie");
assert_eq!(user["email"], "charlie@example.com");
assert_eq!(user["active"], true);
}
#[test]
fn test_graphql_openapi_paths_generated() {
let paths = SimpleService::graphql_openapi_paths();
assert_eq!(paths.len(), 2);
let post_path = paths.iter().find(|p| p.method == "post").unwrap();
assert_eq!(post_path.path, "/graphql");
assert!(
post_path
.operation
.summary
.as_ref()
.unwrap()
.contains("query")
);
assert!(post_path.operation.request_body.is_some());
assert!(post_path.operation.responses.contains_key("200"));
let get_path = paths.iter().find(|p| p.method == "get").unwrap();
assert_eq!(get_path.path, "/graphql");
assert!(
get_path
.operation
.summary
.as_ref()
.unwrap()
.contains("Playground")
);
}
#[derive(Clone)]
struct JsonService;
#[graphql]
impl JsonService {
pub fn get_data(&self) -> serde_json::Value {
serde_json::json!({"key": "value"})
}
pub fn create_entry(&self, data: serde_json::Value) -> serde_json::Value {
data
}
}
#[test]
fn test_graphql_json_scalar_schema() {
let service = JsonService;
let sdl = service.graphql_sdl();
assert!(
sdl.contains("scalar JSON"),
"Should register JSON scalar type. SDL:\n{}",
sdl
);
}
#[tokio::test]
async fn test_graphql_json_scalar_query() {
let service = JsonService;
let schema = service.graphql_schema();
let result = schema.execute("{ getData }").await;
assert!(
result.errors.is_empty(),
"JSON scalar query should succeed: {:?}",
result.errors
);
let data = result.data.into_json().unwrap();
assert!(data["getData"].is_object(), "Should return JSON object");
assert_eq!(data["getData"]["key"], "value");
}
#[graphql_enum]
#[derive(Clone, Debug)]
enum Priority {
Low,
Medium,
High,
Critical,
}
#[test]
fn test_graphql_enum_type_definition() {
let enum_type = Priority::__graphql_enum_type();
let _ = enum_type;
}
#[test]
fn test_graphql_enum_to_value() {
let value = Priority::High.__to_graphql_value();
assert_eq!(
value,
async_graphql::Value::Enum(async_graphql::Name::new("HIGH"))
);
}
#[test]
fn test_graphql_enum_all_variants() {
assert_eq!(
Priority::Low.__to_graphql_value(),
async_graphql::Value::Enum(async_graphql::Name::new("LOW"))
);
assert_eq!(
Priority::Medium.__to_graphql_value(),
async_graphql::Value::Enum(async_graphql::Name::new("MEDIUM"))
);
assert_eq!(
Priority::High.__to_graphql_value(),
async_graphql::Value::Enum(async_graphql::Name::new("HIGH"))
);
assert_eq!(
Priority::Critical.__to_graphql_value(),
async_graphql::Value::Enum(async_graphql::Name::new("CRITICAL"))
);
}
#[derive(Clone)]
struct PriorityService;
#[graphql(enums(Priority))]
impl PriorityService {
pub fn get_default_priority(&self) -> String {
"HIGH".to_string()
}
}
#[test]
fn test_graphql_enum_registered_in_schema() {
let service = PriorityService;
let sdl = service.graphql_sdl();
assert!(
sdl.contains("enum Priority"),
"Should register Priority enum type. SDL:\n{}",
sdl
);
assert!(
sdl.contains("LOW"),
"Should have LOW variant. SDL:\n{}",
sdl
);
assert!(
sdl.contains("MEDIUM"),
"Should have MEDIUM variant. SDL:\n{}",
sdl
);
assert!(
sdl.contains("HIGH"),
"Should have HIGH variant. SDL:\n{}",
sdl
);
assert!(
sdl.contains("CRITICAL"),
"Should have CRITICAL variant. SDL:\n{}",
sdl
);
}
#[derive(Clone, Debug, Serialize, Deserialize)]
struct NestedProfile {
bio: String,
avatar_url: String,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
struct UserWithProfile {
id: i32,
name: String,
profile: NestedProfile,
}
#[derive(Clone)]
struct NestedService;
#[graphql]
impl NestedService {
pub fn get_user_with_profile(&self, id: i32) -> UserWithProfile {
UserWithProfile {
id,
name: "Alice".to_string(),
profile: NestedProfile {
bio: "Software engineer".to_string(),
avatar_url: "https://example.com/avatar.jpg".to_string(),
},
}
}
pub fn list_users_with_profiles(&self) -> Vec<UserWithProfile> {
vec![
UserWithProfile {
id: 1,
name: "Alice".to_string(),
profile: NestedProfile {
bio: "Engineer".to_string(),
avatar_url: "https://example.com/alice.jpg".to_string(),
},
},
UserWithProfile {
id: 2,
name: "Bob".to_string(),
profile: NestedProfile {
bio: "Designer".to_string(),
avatar_url: "https://example.com/bob.jpg".to_string(),
},
},
]
}
}
#[tokio::test]
async fn test_graphql_nested_object_query() {
let service = NestedService;
let schema = service.graphql_schema();
let result = schema.execute("{ getUserWithProfile(id: 42) }").await;
assert!(
result.errors.is_empty(),
"Query should succeed: {:?}",
result.errors
);
let json: serde_json::Value = serde_json::to_value(&result.data).unwrap();
let user = &json["getUserWithProfile"];
assert_eq!(user["id"], 42);
assert_eq!(user["name"], "Alice");
let profile = &user["profile"];
assert!(
profile.is_object(),
"Profile should be an object, not a string. Got: {:?}",
profile
);
assert_eq!(profile["bio"], "Software engineer");
assert_eq!(profile["avatar_url"], "https://example.com/avatar.jpg");
}
#[tokio::test]
async fn test_graphql_nested_object_in_list() {
let service = NestedService;
let schema = service.graphql_schema();
let result = schema.execute("{ listUsersWithProfiles }").await;
assert!(
result.errors.is_empty(),
"Query should succeed: {:?}",
result.errors
);
let json: serde_json::Value = serde_json::to_value(&result.data).unwrap();
let users = json["listUsersWithProfiles"]
.as_array()
.expect("Should be an array");
assert_eq!(users.len(), 2);
let alice = &users[0];
assert_eq!(alice["name"], "Alice");
let alice_profile = &alice["profile"];
assert!(
alice_profile.is_object(),
"Profile should be an object. Got: {:?}",
alice_profile
);
assert_eq!(alice_profile["bio"], "Engineer");
let bob = &users[1];
assert_eq!(bob["name"], "Bob");
let bob_profile = &bob["profile"];
assert!(
bob_profile.is_object(),
"Profile should be an object. Got: {:?}",
bob_profile
);
assert_eq!(bob_profile["bio"], "Designer");
}
use server_less::graphql_input;
#[graphql_input]
#[derive(Clone, Debug, Deserialize)]
struct CreateUserInput {
name: String,
email: String,
age: Option<i32>,
}
#[derive(Clone)]
struct InputService;
#[graphql(inputs(CreateUserInput))]
impl InputService {
pub fn get_status(&self) -> String {
"running".to_string()
}
pub fn create_user(&self, input: CreateUserInput) -> String {
format!("Created: {} <{}>", input.name, input.email)
}
}
#[test]
fn test_graphql_input_type_generated() {
let input_type = CreateUserInput::__graphql_input_type();
assert_eq!(input_type.type_name(), "CreateUserInput");
}
#[test]
fn test_graphql_input_schema_registration() {
let service = InputService;
let sdl = service.graphql_sdl();
assert!(
sdl.contains("input CreateUserInput"),
"Should register CreateUserInput input type. SDL:\n{}",
sdl
);
assert!(
sdl.contains("name: String!"),
"Should have name field. SDL:\n{}",
sdl
);
assert!(
sdl.contains("email: String!"),
"Should have email field. SDL:\n{}",
sdl
);
assert!(
sdl.contains("age: Int"),
"Should have age field. SDL:\n{}",
sdl
);
}