Skip to main content
Glama
test.rs27.5 kB
pub(crate) use color_eyre::{ Result, eyre::eyre, }; pub(crate) use serde_json::json; use crate::{ ValidateResponse, Validator, }; pub(crate) fn valid_opt(json: &str, value: Option<serde_json::Value>) -> Result<()> { let validator: Validator = serde_json::from_str(json)?; let response = validator.validate(&value); if let ValidateResponse { error: Some(error), .. } = response { return Err(eyre!("validation error: {:?}", error)); } Ok(()) } pub(crate) fn valid(json: &str, value: impl Into<serde_json::Value>) -> Result<()> { valid_opt(json, Some(value.into())) } pub(crate) fn invalid_opt(json: &str, value: Option<serde_json::Value>) -> Result<()> { match valid_opt(json, value.clone()) { Ok(_) => Err(eyre!("expected validation error on {:?}", value)), Err(_) => Ok(()), } } pub(crate) fn invalid(json: &str, value: impl Into<serde_json::Value>) -> Result<()> { invalid_opt(json, Some(value.into())) } pub(crate) fn unsupported(json: &str) -> Result<()> { match serde_json::from_str::<Validator>(json) { Ok(_) => Err(eyre!("expected schema to be unsupported: {:?}", json)), Err(_) => Ok(()), } } mod any { use super::*; #[test] fn unsupported_basics() -> Result<()> { unsupported(r#"{ }"#)?; unsupported(r#""string"#)?; unsupported(r#"10""#)?; unsupported(r#"null"#)?; unsupported(r#"[]"#)?; Ok(()) } #[test] fn unsupported_types() -> Result<()> { unsupported(r#"{ "type": "any" }"#)?; unsupported(r#"{ "type": "alternatives" }"#)?; unsupported(r#"{ "type": "date" }"#)?; Ok(()) } } mod boolean { use super::*; #[test] fn type_boolean() -> Result<()> { let joi = r#"{ "type": "boolean" }"#; valid_opt(joi, None)?; valid(joi, true)?; valid(joi, "true")?; valid(joi, false)?; valid(joi, "false")?; Ok(()) } #[test] fn allow_values() -> Result<()> { let joi = r#"{ "type": "boolean" }"#; valid(joi, true)?; valid(joi, "true")?; valid(joi, false)?; valid(joi, "false")?; let joi = r#"{ "type": "boolean", "allow": [false] }"#; valid(joi, true)?; valid(joi, "true")?; valid(joi, false)?; valid(joi, "false")?; Ok(()) } #[test] fn valid_values() -> Result<()> { let joi = r#"{ "type": "boolean", "flags": { "only": true } }"#; valid(joi, true)?; valid(joi, "true")?; valid(joi, false)?; valid(joi, "false")?; let joi = r#"{ "type": "boolean", "flags": { "only": true }, "allow": [] }"#; invalid(joi, true)?; invalid(joi, "true")?; invalid(joi, false)?; invalid(joi, "false")?; let joi = r#"{ "type": "boolean", "flags": { "only": true }, "allow": [true] }"#; valid(joi, true)?; valid(joi, "true")?; invalid(joi, false)?; invalid(joi, "false")?; Ok(()) } #[test] fn invalid_values() -> Result<()> { let joi = r#"{ "type": "boolean", "invalid": [] }"#; valid(joi, true)?; valid(joi, "true")?; valid(joi, false)?; valid(joi, "false")?; let joi = r#"{ "type": "boolean", "invalid": [false] }"#; valid(joi, true)?; valid(joi, "true")?; invalid(joi, false)?; invalid(joi, "false")?; let joi = r#"{ "type": "boolean", "allow": [true], "invalid": [false] }"#; valid(joi, true)?; valid(joi, "true")?; invalid(joi, false)?; invalid(joi, "false")?; let joi = r#"{ "type": "boolean", "flags": { "only": true }, "invalid": [false] }"#; valid(joi, true)?; valid(joi, "true")?; invalid(joi, false)?; invalid(joi, "false")?; let joi = r#"{ "type": "boolean", "flags": { "only": true }, "allow": [true], "invalid": [false] }"#; valid(joi, true)?; valid(joi, "true")?; invalid(joi, false)?; invalid(joi, "false")?; Ok(()) } #[test] fn required() -> Result<()> { let joi = r#"{ "type": "boolean", "flags": { "presence": "required" } }"#; valid(joi, true)?; valid(joi, "true")?; valid(joi, false)?; valid(joi, "false")?; invalid(joi, json!(null))?; invalid_opt(joi, None)?; Ok(()) } #[test] fn forbidden() -> Result<()> { let joi = r#"{ "type": "boolean", "flags": { "presence": "forbidden" } }"#; invalid(joi, true)?; invalid(joi, "true")?; invalid(joi, false)?; invalid(joi, "false")?; invalid(joi, json!(null))?; valid_opt(joi, None)?; Ok(()) } #[test] fn optional() -> Result<()> { let joi = r#"{ "type": "boolean", "flags": { "presence": "optional" } }"#; valid(joi, true)?; valid(joi, "true")?; valid(joi, false)?; valid(joi, "false")?; invalid(joi, json!(null))?; valid_opt(joi, None)?; Ok(()) } #[test] fn passthrough() -> Result<()> { valid( r#"{ "type": "boolean", "flags": { "description": "a" } }"#, true, )?; valid(r#"{ "type": "boolean", "examples": [ true ] }"#, true)?; valid( r#"{ "type": "boolean", "metas": [ { "foo": "bar" } ] }"#, true, )?; valid(r#"{ "type": "boolean", "notes": [ "a", "b" ] }"#, true)?; valid(r#"{ "type": "boolean", "tags": [ "a", "b" ] }"#, true)?; Ok(()) } #[test] fn unsupported_rules() -> Result<()> { unsupported(r#"{ "type": "boolean", "rules": [ { "name": "extra" } ] }"#)?; Ok(()) } #[test] fn unsupported_flags() -> Result<()> { unsupported(r#"{ "type": "boolean", "flags": { "sensitive": false } }"#)?; unsupported(r#"{ "type": "boolean", "flags": { "sensitive": true } }"#)?; unsupported(r#"{ "type": "boolean", "flags": { "extra": true } }"#)?; Ok(()) } #[test] fn unsupported_basics() -> Result<()> { unsupported(r#"{ "type": "boolean", "truthy": ["Y"] }"#)?; unsupported(r#"{ "type": "boolean", "falsy": ["Y"] }"#)?; unsupported(r#"{ "type": "boolean", "extra": true }"#)?; unsupported(r#"{ "type": "boolean", "preferences": {} }"#)?; Ok(()) } } mod number { use std::f64::consts::PI; use super::{ Result, invalid_opt, json, unsupported, valid_opt, }; // tests both the float and string version of the value fn valid(joi: &str, value: impl Into<f64>) -> Result<()> { let value = value.into(); super::valid(joi, value)?; super::valid(joi, value.to_string())?; Ok(()) } // tests both the float and string version of the value fn invalid(joi: &str, value: impl Into<f64>) -> Result<()> { let value = value.into(); super::invalid(joi, value)?; super::invalid(joi, value.to_string())?; Ok(()) } #[test] fn type_number() -> Result<()> { let joi = r#"{ "type": "number" }"#; valid_opt(joi, None)?; valid(joi, 0)?; valid(joi, 10)?; valid(joi, PI)?; valid(joi, -PI)?; super::invalid(joi, "")?; super::invalid(joi, "a")?; super::invalid(joi, json!([]))?; super::invalid(joi, json!(["a"]))?; super::invalid(joi, json!({}))?; super::invalid(joi, json!({ "a": "b" }))?; super::invalid(joi, json!(null))?; Ok(()) } #[test] fn unsafe_numbers() -> Result<()> { // Safe range let joi = r#"{ "type": "number" }"#; super::valid(joi, 9007199254740991i64)?; super::valid(joi, -9007199254740991i64)?; valid(joi, 9007199254740991.0f64)?; valid(joi, -9007199254740991.0f64)?; super::invalid(joi, 9007199254740992i64)?; super::invalid(joi, -9007199254740992i64)?; invalid(joi, 9007199254740992.0f64)?; invalid(joi, -9007199254740992.0f64)?; let joi = r#"{ "type": "number", "flags": { "unsafe": false } }"#; super::valid(joi, 9007199254740991i64)?; super::valid(joi, -9007199254740991i64)?; valid(joi, 9007199254740991.0f64)?; valid(joi, -9007199254740991.0f64)?; super::invalid(joi, 9007199254740992i64)?; super::invalid(joi, -9007199254740992i64)?; invalid(joi, 9007199254740992.0f64)?; invalid(joi, -9007199254740992.0f64)?; let joi = r#"{ "type": "number", "flags": { "unsafe": true } }"#; super::valid(joi, 9007199254740991i64)?; super::valid(joi, -9007199254740991i64)?; valid(joi, 9007199254740991.0f64)?; valid(joi, -9007199254740991.0f64)?; super::valid(joi, 9007199254740992i64)?; super::valid(joi, -9007199254740992i64)?; valid(joi, 9007199254740992.0f64)?; valid(joi, -9007199254740992.0f64)?; Ok(()) } #[test] fn integer() -> Result<()> { let joi = r#"{ "type": "number", "rules": [ { "name": "integer" } ] }"#; valid(joi, -100)?; valid(joi, 0)?; valid(joi, 100)?; valid(joi, 0.0f64)?; invalid(joi, -0.1)?; invalid(joi, -100.5)?; invalid(joi, 0.1)?; invalid(joi, 100.5)?; unsupported(r#"{ "type": "number", "rules": [ { "name": "min", "args": { } } ] }"#)?; Ok(()) } #[test] fn min() -> Result<()> { let joi = r#"{ "type": "number", "rules": [ { "name": "min", "args": { "limit": 2 } } ] }"#; invalid(joi, -5)?; invalid(joi, 0)?; invalid(joi, 1)?; valid(joi, 2)?; valid(joi, 2.5)?; valid(joi, 3)?; valid(joi, 4)?; valid(joi, 5)?; unsupported(r#"{ "type": "number", "rules": [ { "name": "min" } ] }"#)?; unsupported(r#"{ "type": "number", "rules": [ { "name": "min", "args": { } } ] }"#)?; unsupported( r#"{ "type": "number", "rules": [ { "name": "min", "args": { "limit": 2, "extra": true } } ] }"#, )?; unsupported( r#"{ "type": "number", "rules": [ { "name": "min", "args": { "limit": "2" } } ] }"#, )?; Ok(()) } #[test] fn greater() -> Result<()> { let joi = r#"{ "type": "number", "rules": [ { "name": "greater", "args": { "limit": 2 } } ] }"#; invalid(joi, -5)?; invalid(joi, 0)?; invalid(joi, 1)?; invalid(joi, 2)?; valid(joi, 2.5)?; valid(joi, 3)?; valid(joi, 4)?; valid(joi, 5)?; unsupported(r#"{ "type": "number", "rules": [ { "name": "greater" } ] }"#)?; unsupported(r#"{ "type": "number", "rules": [ { "name": "greater", "args": { } } ] }"#)?; unsupported( r#"{ "type": "number", "rules": [ { "name": "greater", "args": { "limit": 2, "extra": true } } ] }"#, )?; unsupported( r#"{ "type": "number", "rules": [ { "name": "greater", "args": { "limit": "2" } } ] }"#, )?; Ok(()) } #[test] fn max() -> Result<()> { let joi = r#"{ "type": "number", "rules": [ { "name": "max", "args": { "limit": 3 } } ] }"#; valid(joi, -5)?; valid(joi, 0)?; valid(joi, 1)?; valid(joi, 2)?; valid(joi, 2.5)?; valid(joi, 3)?; invalid(joi, 4)?; invalid(joi, 5)?; unsupported(r#"{ "type": "number", "rules": [ { "name": "max" } ] }"#)?; unsupported(r#"{ "type": "number", "rules": [ { "name": "max", "args": { } } ] }"#)?; unsupported( r#"{ "type": "number", "rules": [ { "name": "max", "args": { "limit": 2, "extra": true } } ] }"#, )?; unsupported( r#"{ "type": "number", "rules": [ { "name": "max", "args": { "limit": "2" } } ] }"#, )?; Ok(()) } #[test] fn less() -> Result<()> { let joi = r#"{ "type": "number", "rules": [ { "name": "less", "args": { "limit": 3 } } ] }"#; valid(joi, -5)?; valid(joi, 0)?; valid(joi, 1)?; valid(joi, 2)?; valid(joi, 2.5)?; invalid(joi, 3)?; invalid(joi, 4)?; invalid(joi, 5)?; unsupported(r#"{ "type": "number", "rules": [ { "name": "less" } ] }"#)?; unsupported( r#"{ "type": "number", "rules": [ { "name": "less", "args": { "limit": 2 }, "extra": true } ] }"#, )?; unsupported(r#"{ "type": "number", "rules": [ { "name": "less", "args": { } } ] }"#)?; unsupported( r#"{ "type": "number", "rules": [ { "name": "less", "args": { "limit": 2, "extra": true } } ] }"#, )?; Ok(()) } #[test] fn allow_values() -> Result<()> { let joi = r#"{ "type": "number" }"#; valid(joi, 1)?; valid(joi, 2)?; valid(joi, 3)?; valid(joi, 4)?; valid(joi, 5)?; valid(joi, 6)?; let joi = r#"{ "type": "number", "allow": [1, 2] }"#; valid(joi, 1)?; valid(joi, 2)?; valid(joi, 3)?; valid(joi, 4)?; valid(joi, 5)?; valid(joi, 6)?; Ok(()) } #[test] fn valid_values() -> Result<()> { let joi = r#"{ "type": "number", "flags": { "only": true } }"#; valid(joi, 1)?; valid(joi, 2)?; valid(joi, 3)?; valid(joi, 4)?; valid(joi, 5)?; valid(joi, 6)?; let joi = r#"{ "type": "number", "flags": { "only": true }, "allow": [] }"#; invalid(joi, 1)?; invalid(joi, 2)?; invalid(joi, 3)?; invalid(joi, 4)?; invalid(joi, 5)?; invalid(joi, 6)?; let joi = r#"{ "type": "number", "flags": { "only": true }, "allow": [1, 2] }"#; valid(joi, 1)?; valid(joi, 2)?; invalid(joi, 3)?; invalid(joi, 4)?; invalid(joi, 5)?; invalid(joi, 6)?; Ok(()) } #[test] fn invalid_values() -> Result<()> { let joi = r#"{ "type": "number", "invalid": [] }"#; valid(joi, 1)?; valid(joi, 2)?; valid(joi, 3)?; valid(joi, 4)?; valid(joi, 5)?; valid(joi, 6)?; let joi = r#"{ "type": "number", "invalid": [3, 4] }"#; valid(joi, 1)?; valid(joi, 2)?; invalid(joi, 3)?; invalid(joi, 4)?; valid(joi, 5)?; valid(joi, 6)?; let joi = r#"{ "type": "number", "allow": [1, 2], "invalid": [3, 4] }"#; valid(joi, 1)?; valid(joi, 2)?; invalid(joi, 3)?; invalid(joi, 4)?; valid(joi, 5)?; valid(joi, 6)?; let joi = r#"{ "type": "number", "flags": { "only": true }, "invalid": [3, 4] }"#; valid(joi, 1)?; valid(joi, 2)?; invalid(joi, 3)?; invalid(joi, 4)?; valid(joi, 5)?; valid(joi, 6)?; let joi = r#"{ "type": "number", "flags": { "only": true }, "allow": [1, 2], "invalid": [3, 4] }"#; valid(joi, 1)?; valid(joi, 2)?; invalid(joi, 3)?; invalid(joi, 4)?; invalid(joi, 5)?; invalid(joi, 6)?; Ok(()) } #[test] fn required() -> Result<()> { let joi = r#"{ "type": "number", "flags": { "presence": "required" } }"#; valid(joi, 0)?; valid(joi, 1)?; super::invalid(joi, json!(null))?; invalid_opt(joi, None)?; Ok(()) } #[test] fn forbidden() -> Result<()> { let joi = r#"{ "type": "number", "flags": { "presence": "forbidden" } }"#; invalid(joi, 0)?; invalid(joi, 1)?; super::invalid(joi, json!(null))?; valid_opt(joi, None)?; Ok(()) } #[test] fn optional() -> Result<()> { let joi = r#"{ "type": "number", "flags": { "presence": "optional" } }"#; valid(joi, 0)?; valid(joi, 1)?; super::invalid(joi, json!(null))?; valid_opt(joi, None)?; Ok(()) } #[test] fn passthrough() -> Result<()> { valid( r#"{ "type": "number", "flags": { "description": "a" } }"#, 1, )?; valid(r#"{ "type": "number", "examples": [ 1, 2 ] }"#, 1)?; valid(r#"{ "type": "number", "metas": [ { "foo": "bar" } ] }"#, 1)?; valid(r#"{ "type": "number", "notes": [ "a", "b" ] }"#, 1)?; valid(r#"{ "type": "number", "tags": [ "a", "b" ] }"#, 1)?; Ok(()) } #[test] fn unsupported_rules() -> Result<()> { unsupported(r#"{ "type": "number", "rules": [ { "name": "multiple" } ] }"#)?; unsupported(r#"{ "type": "number", "rules": [ { "name": "port" } ] }"#)?; unsupported(r#"{ "type": "number", "rules": [ { "name": "precision" } ] }"#)?; unsupported(r#"{ "type": "number", "rules": [ { "name": "sign" } ] }"#)?; unsupported(r#"{ "type": "number", "rules": [ { "name": "extra" } ] }"#)?; Ok(()) } #[test] fn unsupported_flags() -> Result<()> { unsupported(r#"{ "type": "number", "flags": { "extra": true } }"#)?; Ok(()) } #[test] fn unsupported_basics() -> Result<()> { unsupported(r#"{ "type": "number", "extra": true }"#)?; unsupported(r#"{ "type": "number", "preferences": {} }"#)?; Ok(()) } } mod string { use std::f64::consts::PI; use super::*; #[test] fn type_string() -> Result<()> { let joi = r#"{ "type": "string" }"#; valid_opt(joi, None)?; invalid(joi, 0)?; invalid(joi, 10)?; invalid(joi, PI)?; invalid(joi, -PI)?; valid(joi, "")?; valid(joi, "a")?; invalid(joi, json!([]))?; invalid(joi, json!(["a"]))?; invalid(joi, json!({}))?; invalid(joi, json!({ "a": "b" }))?; invalid(joi, json!(null))?; Ok(()) } #[test] fn min() -> Result<()> { let joi = r#"{ "type": "string", "rules": [ { "name": "min", "args": { "limit": 2 } } ] }"#; invalid(joi, "")?; invalid(joi, "a")?; valid(joi, "ab")?; valid(joi, "abc")?; valid(joi, "abcd")?; valid(joi, "abcde")?; unsupported(r#"{ "type": "string", "rules": [ { "name": "min" } ] }"#)?; unsupported(r#"{ "type": "string", "rules": [ { "name": "min", "args": { } } ] }"#)?; unsupported( r#"{ "type": "string", "rules": [ { "name": "min", "args": { "limit": 2, "extra": true } } ] }"#, )?; unsupported( r#"{ "type": "string", "rules": [ { "name": "min", "args": { "limit": "2" } } ] }"#, )?; Ok(()) } #[test] fn max() -> Result<()> { let joi = r#"{ "type": "string", "rules": [ { "name": "max", "args": { "limit": 3 } } ] }"#; valid(joi, "")?; valid(joi, "a")?; valid(joi, "ab")?; valid(joi, "abc")?; invalid(joi, "abcd")?; invalid(joi, "abcde")?; unsupported(r#"{ "type": "string", "rules": [ { "name": "max" } ] }"#)?; unsupported(r#"{ "type": "string", "rules": [ { "name": "max", "args": { } } ] }"#)?; unsupported( r#"{ "type": "string", "rules": [ { "name": "max", "args": { "limit": 2, "extra": true } } ] }"#, )?; unsupported( r#"{ "type": "string", "rules": [ { "name": "max", "args": { "limit": "2" } } ] }"#, )?; Ok(()) } #[test] fn length() -> Result<()> { let joi = r#"{ "type": "string", "rules": [ { "name": "length", "args": { "limit": 2 } } ] }"#; invalid(joi, "")?; invalid(joi, "a")?; valid(joi, "ab")?; invalid(joi, "abc")?; invalid(joi, "abcd")?; invalid(joi, "abcde")?; unsupported(r#"{ "type": "string", "rules": [ { "name": "length" } ] }"#)?; unsupported(r#"{ "type": "string", "rules": [ { "name": "length", "args": { } } ] }"#)?; unsupported( r#"{ "type": "string", "rules": [ { "name": "length", "args": { "limit": 2, "extra": true } } ] }"#, )?; unsupported( r#"{ "type": "string", "rules": [ { "name": "length", "args": { "limit": "2" } } ] }"#, )?; Ok(()) } #[test] fn multi() -> Result<()> { let joi = r#"{ "type": "string", "flags": { "presence": "required", "only": true }, "rules": [ { "name": "min", "args": { "limit": 2 } }, { "name": "max", "args": { "limit": 3 } } ] }"#; invalid(joi, "")?; invalid(joi, "a")?; valid(joi, "ab")?; valid(joi, "abc")?; invalid(joi, "abcd")?; invalid(joi, "abcde")?; invalid(joi, 10)?; invalid(joi, json!(null))?; invalid_opt(joi, None)?; Ok(()) } #[test] fn allow_values() -> Result<()> { let joi = r#"{ "type": "string" }"#; valid(joi, "a")?; valid(joi, "b")?; valid(joi, "c")?; valid(joi, "d")?; valid(joi, "e")?; valid(joi, "f")?; let joi = r#"{ "type": "string", "allow": ["a", "b"] }"#; valid(joi, "a")?; valid(joi, "b")?; valid(joi, "c")?; valid(joi, "d")?; valid(joi, "e")?; valid(joi, "f")?; Ok(()) } #[test] fn valid_values() -> Result<()> { let joi = r#"{ "type": "string", "flags": { "only": true } }"#; valid(joi, "a")?; valid(joi, "b")?; valid(joi, "c")?; valid(joi, "d")?; valid(joi, "e")?; valid(joi, "f")?; let joi = r#"{ "type": "string", "flags": { "only": true }, "allow": [] }"#; invalid(joi, "a")?; invalid(joi, "b")?; invalid(joi, "c")?; invalid(joi, "d")?; invalid(joi, "e")?; invalid(joi, "f")?; let joi = r#"{ "type": "string", "flags": { "only": true }, "allow": ["a", "b"] }"#; valid(joi, "a")?; valid(joi, "b")?; invalid(joi, "c")?; invalid(joi, "d")?; invalid(joi, "e")?; invalid(joi, "f")?; Ok(()) } #[test] fn invalid_values() -> Result<()> { let joi = r#"{ "type": "string", "invalid": [] }"#; valid(joi, "a")?; valid(joi, "b")?; valid(joi, "c")?; valid(joi, "d")?; valid(joi, "e")?; valid(joi, "f")?; let joi = r#"{ "type": "string", "invalid": ["c", "d"] }"#; valid(joi, "a")?; valid(joi, "b")?; invalid(joi, "c")?; invalid(joi, "d")?; valid(joi, "e")?; valid(joi, "f")?; let joi = r#"{ "type": "string", "allow": ["a", "b"], "invalid": ["c", "d"] }"#; valid(joi, "a")?; valid(joi, "b")?; invalid(joi, "c")?; invalid(joi, "d")?; valid(joi, "e")?; valid(joi, "f")?; let joi = r#"{ "type": "string", "flags": { "only": true }, "invalid": ["c", "d"] }"#; valid(joi, "a")?; valid(joi, "b")?; invalid(joi, "c")?; invalid(joi, "d")?; valid(joi, "e")?; valid(joi, "f")?; let joi = r#"{ "type": "string", "flags": { "only": true }, "allow": ["a", "b"], "invalid": ["c", "d"] }"#; valid(joi, "a")?; valid(joi, "b")?; invalid(joi, "c")?; invalid(joi, "d")?; invalid(joi, "e")?; invalid(joi, "f")?; Ok(()) } #[test] fn required() -> Result<()> { let joi = r#"{ "type": "string", "flags": { "presence": "required" } }"#; valid(joi, "")?; valid(joi, "a")?; invalid(joi, json!(null))?; invalid_opt(joi, None)?; Ok(()) } #[test] fn forbidden() -> Result<()> { let joi = r#"{ "type": "string", "flags": { "presence": "forbidden" } }"#; invalid(joi, "")?; invalid(joi, "a")?; invalid(joi, json!(null))?; valid_opt(joi, None)?; Ok(()) } #[test] fn optional() -> Result<()> { let joi = r#"{ "type": "string", "flags": { "presence": "optional" } }"#; valid(joi, "")?; valid(joi, "a")?; invalid(joi, json!(null))?; valid_opt(joi, None)?; Ok(()) } #[test] fn passthrough() -> Result<()> { valid( r#"{ "type": "string", "flags": { "description": "a" } }"#, "", )?; valid(r#"{ "type": "string", "examples": [ "a", "b" ] }"#, "")?; valid(r#"{ "type": "string", "metas": [ { "foo": "bar" } ] }"#, "")?; valid(r#"{ "type": "string", "notes": [ "a", "b" ] }"#, "")?; valid(r#"{ "type": "string", "tags": [ "a", "b" ] }"#, "")?; Ok(()) } #[test] fn unsupported_rules() -> Result<()> { unsupported(r#"{ "type": "string", "rules": [ { "name": "alphanum" } ] }"#)?; unsupported(r#"{ "type": "string", "rules": [ { "name": "base64" } ] }"#)?; unsupported(r#"{ "type": "string", "rules": [ { "name": "case" } ] }"#)?; unsupported(r#"{ "type": "string", "rules": [ { "name": "creditCard" } ] }"#)?; unsupported(r#"{ "type": "string", "rules": [ { "name": "dataUri" } ] }"#)?; unsupported(r#"{ "type": "string", "rules": [ { "name": "domain" } ] }"#)?; unsupported(r#"{ "type": "string", "rules": [ { "name": "email" } ] }"#)?; unsupported(r#"{ "type": "string", "rules": [ { "name": "guid" } ] }"#)?; unsupported(r#"{ "type": "string", "rules": [ { "name": "hex" } ] }"#)?; unsupported(r#"{ "type": "string", "rules": [ { "name": "hostname" } ] }"#)?; unsupported(r#"{ "type": "string", "rules": [ { "name": "ip" } ] }"#)?; unsupported(r#"{ "type": "string", "rules": [ { "name": "isoDate" } ] }"#)?; unsupported(r#"{ "type": "string", "rules": [ { "name": "isoDuration" } ] }"#)?; unsupported(r#"{ "type": "string", "rules": [ { "name": "normalize" } ] }"#)?; unsupported(r#"{ "type": "string", "rules": [ { "name": "pattern" } ] }"#)?; unsupported(r#"{ "type": "string", "rules": [ { "name": "replace" } ] }"#)?; unsupported(r#"{ "type": "string", "rules": [ { "name": "token" } ] }"#)?; unsupported(r#"{ "type": "string", "rules": [ { "name": "trim" } ] }"#)?; unsupported(r#"{ "type": "string", "rules": [ { "name": "truncate" } ] }"#)?; unsupported(r#"{ "type": "string", "rules": [ { "name": "uri" } ] }"#)?; unsupported(r#"{ "type": "string", "rules": [ { "name": "extra" } ] }"#)?; Ok(()) } #[test] fn unsupported_flags() -> Result<()> { unsupported(r#"{ "type": "string", "flags": { "extra": true } }"#)?; unsupported(r#"{ "type": "string", "flags": { "insensitive": true } }"#)?; unsupported(r#"{ "type": "string", "flags": { "insensitive": false } }"#)?; Ok(()) } #[test] fn unsupported_basics() -> Result<()> { unsupported(r#"{ "type": "string", "extra": true }"#)?; unsupported(r#"{ "type": "string", "preferences": {} }"#)?; Ok(()) } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/systeminit/si'

If you have feedback or need assistance with the MCP directory API, please join our Discord server