//! Behavior tests for the Shapes projection + edge-manifest derivation, //! driven off a small registered fixture (same graph the AFI fixture uses: //! a nested struct, a user context with a shared `user_id` param, and an //! `affects` mutation). use mizan_core as mizan; use mizan_core::prelude::*; use mizan_core::{generate_edge_manifest, shapes, RequestHandle}; use serde::{Deserialize, Serialize}; #[derive(Mizan, Serialize, Deserialize, Debug, Clone)] pub struct Address { pub city: String, pub zip: String, } #[derive(Mizan, Serialize, Deserialize, Debug, Clone)] pub struct Person { pub user_id: i64, pub name: String, pub address: Address, } #[derive(Mizan, Serialize, Deserialize, Debug, Clone)] pub struct Ok { pub ok: bool, } #[mizan::context("people")] pub struct PeopleCtx; #[mizan::client(context = PeopleCtx)] pub async fn person(_req: &RequestHandle<'_>, user_id: i64) -> Person { Person { user_id, name: "x".into(), address: Address { city: "c".into(), zip: "z".into(), }, } } #[mizan::client(affects = PeopleCtx)] pub async fn rename_person(_req: &RequestHandle<'_>, user_id: i64, _name: String) -> Ok { let _ = user_id; Ok { ok: true } } #[test] fn shapes_projection_descends_nested_structs() { let proj = shapes::project_function_output("person").expect("projects"); assert_eq!(proj.type_name, "personOutput"); // Scalar leaves at the top level. let leaves = proj.leaf_names(); assert!(leaves.contains(&"user_id")); assert!(leaves.contains(&"name")); // `address` is a nested struct → a sub-projection, not a leaf. assert!(!leaves.contains(&"address")); let nested = proj.nested(); assert_eq!(nested.len(), 1); let (name, sub) = nested[0]; assert_eq!(name, "address"); let sub_leaves = sub.leaf_names(); assert!(sub_leaves.contains(&"city") && sub_leaves.contains(&"zip")); } #[test] fn edge_manifest_has_context_render_strategy_and_mutation() { let m = generate_edge_manifest("/api/mizan"); // Context: user-scoped (has `user_id`) → render_strategy dynamic_cached. let people = &m["contexts"]["people"]; assert_eq!(people["user_scoped"], serde_json::json!(true)); assert_eq!(people["render_strategy"], serde_json::json!("dynamic_cached")); assert_eq!( people["endpoints"], serde_json::json!(["/api/mizan/ctx/people/"]) ); assert_eq!(people["params"], serde_json::json!(["user_id"])); // Mutation: rename_person affects people, auto-scopes user_id. let mutation = &m["mutations"]["rename_person"]; assert_eq!(mutation["affects"], serde_json::json!(["people"])); assert_eq!( mutation["auto_scoped_params"], serde_json::json!(["user_id"]) ); }