//! Native checks for generated schemas, dispatch, and missing core transports. use sausage_plugin::serde_json::json; use sausage_plugin::{Ctx, Interface, Result, rpc, schema}; /// The addition parameters. #[schema] struct Add { /// The first integer. a: i32, /// The second integer. b: i32, } /// A type with different serialize and deserialize contracts. #[schema] struct Defaulted { /// A value which the provider defaults. #[serde(default)] value: String, } /// A documented string enum. #[schema] enum Choice { /// The first choice. First, /// The second choice. Second, } struct Plugin; #[rpc(endpoints(control=Plugin))] impl Plugin { /// Return the same named object without a contract change. fn identity(&self, p: Add) -> Result { Ok(p) } /// Choose the first value. fn choice(&self) -> Result { Ok(Choice::First) } /// Round-trip the defaulted object. fn defaults(&self, p: Defaulted) -> Result { Ok(p) } /// Add integers. /// /// Return their sum. fn add(&self, p: Add) -> Result { Ok(p.a + p.b) } } #[test] fn discovery_and_dispatch() { let doc = Plugin.discover(); assert!(doc["components"]["schemas"].get("Empty").is_none()); let choice = doc["methods"] .as_array() .unwrap() .iter() .find(|m| m["name"] == "choice") .unwrap(); assert!(choice.get("x-sausage-params").is_none()); assert!( doc["x-sausage-endpoints"]["control"]["methods"] .as_array() .unwrap() .iter() .any(|m| m["name"] == "add") ); assert!(doc["components"]["schemas"].get("AddResult").is_none()); assert_eq!( doc["methods"] .as_array() .unwrap() .iter() .find(|m| m["name"] == "identity") .unwrap()["result"]["schema"]["$ref"], "#/components/schemas/Add" ); assert!( doc["components"]["schemas"]["Defaulted"] .get("required") .is_none() ); assert_eq!( doc["components"]["schemas"]["DefaultedResult"]["required"], json!(["value"]) ); assert_eq!( doc["components"]["schemas"]["Choice"]["oneOf"][0]["description"], json!("The first choice.") ); let add = doc["methods"] .as_array() .unwrap() .iter() .find(|m| m["name"] == "add") .unwrap(); assert_eq!(add["summary"], "Add integers."); assert_eq!( doc["components"]["schemas"]["Add"]["description"], "The addition parameters." ); assert_eq!(add["params"][0]["description"], "The first integer."); eprintln!("DISCOVER={doc}"); assert_eq!( sausage_plugin::block_on(Plugin.dispatch("add", json!({"a":2,"b":3}), &Ctx::default())) .unwrap(), 5 ); assert_eq!( sausage_plugin::block_on(Plugin.dispatch("add", json!({}), &Ctx::default())) .unwrap_err() .code, -32602 ); let internal: sausage_plugin::Error = sausage_plugin::serde_json::from_str::("bad") .unwrap_err() .into(); assert_eq!(internal.code, -32603); } #[sausage_plugin::test] async fn missing_core_fails_without_wait() { assert!( Ctx::default() .notify("progress", json!({})) .unwrap_err() .message .contains("no core transport") ); assert!( sausage_plugin::call("core.lock", json!({})) .await .unwrap_err() .message .contains("no core transport") ); } #[test] fn compile_fail() { trybuild::TestCases::new().compile_fail("tests/ui/*.rs"); }