use super::*; use pretty_assertions::assert_eq; use serde_json::json; #[test] fn parses_methods_into_typed_argument_shapes() -> Result<()> { let schema = json!({ "oneOf": [ request("map", json!({ "properties": { "value": { "type": "string" } }, "type": "object" }), /*required*/ true), request("list", json!({ "items": { "type": "string" }, "type": "array" }), /*required*/ true), request("value", json!({ "type": "null" }), /*required*/ false), { "properties": { "method": { "const": "none" } }, "required": ["method"], "type": "object" } ] }); let parsed = ApiSchema::parse(&schema)?; assert!(matches!(parsed.methods["map"].arguments, Arguments::Map(_))); assert!(matches!( parsed.methods["list"].arguments, Arguments::List(_) )); assert!(matches!( parsed.methods["value"].arguments, Arguments::Value(Argument { required: false, .. }) )); assert_eq!(parsed.methods["none"].arguments, Arguments::None); let (_, request) = parsed.resolve(parsed.methods["map"].request)?; let SchemaNode::Rules(request) = request else { panic!("method request should contain rules"); }; let request = request.object.as_ref().unwrap(); assert!(request.properties.contains_key("id")); assert!(request.properties.contains_key("method")); assert!(request.properties.contains_key("params")); Ok(()) } #[test] fn keeps_refs_typed_without_expanding_recursive_definitions() -> Result<()> { let mut schema = request_schema(json!({ "$ref": "#/definitions/Params" })); schema["definitions"] = json!({ "Params": { "properties": { "child": { "anyOf": [{ "$ref": "#/definitions/Params" }, { "type": "null" }] }, "name": { "allOf": [{ "$ref": "#/definitions/Name" }], "description": "alias" } }, "type": "object" }, "Name": { "type": "string" } }); let parsed = ApiSchema::parse(&schema)?; assert!(matches!( parsed.methods["test/method"].arguments, Arguments::Map(_) )); Ok(()) } #[test] fn rejects_malformed_protocol_shapes_and_direct_ref_cycles() { let duplicate = json!({ "oneOf": [ request("same", json!(null), /*required*/ true), request("same", json!(null), /*required*/ true) ] }); assert!(ApiSchema::parse(&duplicate).is_err()); let mut missing_ref = request_schema(json!({ "$ref": "#/definitions/Missing" })); missing_ref["definitions"] = json!({}); assert!(ApiSchema::parse(&missing_ref).is_err()); let mut cycle = request_schema(json!({ "$ref": "#/definitions/A" })); cycle["definitions"] = json!({ "A": { "$ref": "#/definitions/B" }, "B": { "$ref": "#/definitions/A" } }); assert!(ApiSchema::parse(&cycle).is_err()); let unsupported_all_of = request_schema(json!({ "allOf": [{ "type": "string" }, { "minLength": 1 }] })); assert!(ApiSchema::parse(&unsupported_all_of).is_err()); let mut opaque_ref = request_schema(json!({ "not": { "$ref": "#/definitions/Value" } })); opaque_ref["definitions"] = json!({ "Value": { "type": "string" } }); assert!(ApiSchema::parse(&opaque_ref).is_err()); } fn request(method: &str, params: serde_json::Value, required: bool) -> serde_json::Value { let mut required_fields = vec![json!("id"), json!("method")]; if required { required_fields.push(json!("params")); } json!({ "properties": { "id": { "type": "string" }, "method": { "enum": [method] }, "params": params }, "required": required_fields, "type": "object" }) } fn request_schema(params: serde_json::Value) -> serde_json::Value { json!({ "oneOf": [request("test/method", params, /*required*/ true)] }) }