#![allow(clippy::unwrap_used)]
use fraiseql_cli::schema::{
SchemaConverter,
intermediate::{IntermediateField, IntermediateQuery, IntermediateSchema, IntermediateType},
};
use fraiseql_core::schema::InjectedParamSource;
use indexmap::IndexMap;
fn order_type() -> IntermediateType {
IntermediateType {
name: "Order".to_string(),
fields: vec![IntermediateField {
field_type: "ID".to_string(),
name: "id".to_string(),
nullable: false,
description: None,
directives: None,
requires_scope: None,
on_deny: None,
}],
..Default::default()
}
}
fn schema_with_inject(inject: IndexMap<String, String>) -> IntermediateSchema {
IntermediateSchema {
types: vec![order_type()],
queries: vec![IntermediateQuery {
name: "orders".to_string(),
return_type: "Order".to_string(),
returns_list: true,
sql_source: Some("v_order".to_string()),
inject,
..Default::default()
}],
..Default::default()
}
}
#[test]
fn parse_inject_source_jwt_sub() {
let mut inject = IndexMap::new();
inject.insert("user_id".to_string(), "jwt:sub".to_string());
let compiled =
SchemaConverter::convert(schema_with_inject(inject)).expect("convert must succeed");
let q = compiled.find_query("orders").expect("'orders' must be present");
assert_eq!(q.inject_params.len(), 1);
assert_eq!(
*q.inject_params.get("user_id").unwrap(),
InjectedParamSource::Jwt("sub".to_string()),
"jwt:sub must produce Jwt(\"sub\")"
);
}
#[test]
fn parse_inject_source_jwt_tenant_id() {
let mut inject = IndexMap::new();
inject.insert("tenant_id".to_string(), "jwt:tenant_id".to_string());
let compiled =
SchemaConverter::convert(schema_with_inject(inject)).expect("convert must succeed");
let q = compiled.find_query("orders").expect("'orders' must be present");
assert_eq!(
*q.inject_params.get("tenant_id").unwrap(),
InjectedParamSource::Jwt("tenant_id".to_string()),
"jwt:tenant_id must produce Jwt(\"tenant_id\")"
);
}
#[test]
fn parse_inject_source_jwt_org_id() {
let mut inject = IndexMap::new();
inject.insert("org_id".to_string(), "jwt:org_id".to_string());
let compiled =
SchemaConverter::convert(schema_with_inject(inject)).expect("convert must succeed");
let q = compiled.find_query("orders").expect("'orders' must be present");
assert_eq!(
*q.inject_params.get("org_id").unwrap(),
InjectedParamSource::Jwt("org_id".to_string()),
"jwt:org_id must produce Jwt(\"org_id\")"
);
}
#[test]
fn parse_inject_source_jwt_arbitrary_claim() {
let mut inject = IndexMap::new();
inject.insert("department".to_string(), "jwt:department".to_string());
let compiled =
SchemaConverter::convert(schema_with_inject(inject)).expect("convert must succeed");
let q = compiled.find_query("orders").expect("'orders' must be present");
assert_eq!(
*q.inject_params.get("department").unwrap(),
InjectedParamSource::Jwt("department".to_string()),
"arbitrary jwt claim must produce Jwt(\"department\")"
);
}
#[test]
fn parse_inject_source_jwt_claim_with_underscores() {
let mut inject = IndexMap::new();
inject.insert("custom_attr".to_string(), "jwt:custom_attr".to_string());
let compiled =
SchemaConverter::convert(schema_with_inject(inject)).expect("convert must succeed");
let q = compiled.find_query("orders").expect("'orders' must be present");
assert_eq!(
*q.inject_params.get("custom_attr").unwrap(),
InjectedParamSource::Jwt("custom_attr".to_string()),
);
}
#[test]
fn parse_inject_source_invalid_prefix_returns_error() {
let mut inject = IndexMap::new();
inject.insert("tenant_id".to_string(), "not_jwt:sub".to_string());
let result = SchemaConverter::convert(schema_with_inject(inject));
assert!(result.is_err(), "unknown prefix must return Err");
let msg = format!("{:#}", result.unwrap_err());
assert!(
msg.contains("jwt:") || msg.contains("Unknown inject source"),
"error must mention the supported format: {msg}"
);
}
#[test]
fn parse_inject_source_empty_claim_returns_error() {
let mut inject = IndexMap::new();
inject.insert("tenant_id".to_string(), "jwt:".to_string());
let result = SchemaConverter::convert(schema_with_inject(inject));
assert!(result.is_err(), "empty claim name must return Err");
let msg = format!("{:#}", result.unwrap_err());
assert!(
msg.contains("claim") || msg.contains("jwt:"),
"error must describe the problem: {msg}"
);
}
#[test]
fn parse_inject_source_bare_string_returns_error() {
let mut inject = IndexMap::new();
inject.insert("tenant_id".to_string(), "just_a_string".to_string());
let result = SchemaConverter::convert(schema_with_inject(inject));
assert!(result.is_err(), "bare string without prefix must return Err");
}
#[test]
fn parse_inject_source_arg_conflict_returns_error() {
use fraiseql_cli::schema::intermediate::IntermediateArgument;
let mut inject = IndexMap::new();
inject.insert("filter_id".to_string(), "jwt:sub".to_string());
let schema = IntermediateSchema {
types: vec![order_type()],
queries: vec![IntermediateQuery {
name: "filteredOrders".to_string(),
return_type: "Order".to_string(),
returns_list: true,
sql_source: Some("v_order".to_string()),
arguments: vec![IntermediateArgument {
name: "filter_id".to_string(),
arg_type: "ID".to_string(),
nullable: false,
default: None,
deprecated: None,
}],
inject,
..Default::default()
}],
..Default::default()
};
let result = SchemaConverter::convert(schema);
assert!(result.is_err(), "arg/inject name conflict must return Err");
let msg = format!("{:#}", result.unwrap_err());
assert!(
msg.contains("filter_id") || msg.contains("conflict"),
"error must name the conflicting param: {msg}"
);
}
#[test]
fn converter_multi_inject_params_all_survive() {
let mut inject = IndexMap::new();
inject.insert("tenant_id".to_string(), "jwt:tenant_id".to_string());
inject.insert("user_id".to_string(), "jwt:sub".to_string());
inject.insert("org_id".to_string(), "jwt:org_id".to_string());
let compiled =
SchemaConverter::convert(schema_with_inject(inject)).expect("convert must succeed");
let q = compiled.find_query("orders").expect("'orders' must be present");
assert_eq!(q.inject_params.len(), 3, "all three inject params must survive conversion");
assert_eq!(
*q.inject_params.get("tenant_id").unwrap(),
InjectedParamSource::Jwt("tenant_id".to_string())
);
assert_eq!(
*q.inject_params.get("user_id").unwrap(),
InjectedParamSource::Jwt("sub".to_string())
);
assert_eq!(
*q.inject_params.get("org_id").unwrap(),
InjectedParamSource::Jwt("org_id".to_string())
);
}
#[test]
fn converter_empty_inject_produces_empty_map() {
let schema = IntermediateSchema {
types: vec![order_type()],
queries: vec![IntermediateQuery {
name: "allOrders".to_string(),
return_type: "Order".to_string(),
returns_list: true,
sql_source: Some("v_order".to_string()),
..Default::default()
}],
..Default::default()
};
let compiled = SchemaConverter::convert(schema).expect("convert must succeed");
let q = compiled.find_query("allOrders").expect("'allOrders' must be present");
assert!(q.inject_params.is_empty(), "query with no inject must have empty inject_params");
}
#[test]
fn converter_inject_params_on_mutation_survive() {
use fraiseql_cli::schema::intermediate::IntermediateMutation;
let mut inject = IndexMap::new();
inject.insert("user_id".to_string(), "jwt:sub".to_string());
let schema = IntermediateSchema {
types: vec![order_type()],
mutations: vec![IntermediateMutation {
name: "placeOrder".to_string(),
return_type: "Order".to_string(),
sql_source: Some("fn_place_order".to_string()),
inject,
..Default::default()
}],
..Default::default()
};
let compiled = SchemaConverter::convert(schema).expect("convert must succeed");
let m = compiled.find_mutation("placeOrder").expect("'placeOrder' must be present");
assert_eq!(m.inject_params.len(), 1);
assert_eq!(
*m.inject_params.get("user_id").unwrap(),
InjectedParamSource::Jwt("sub".to_string()),
"inject on mutation must survive SchemaConverter::convert()"
);
}