Skip to main content
Glama

Convex MCP server

Official
by get-convex
tests.rs6.7 kB
use std::{ collections::BTreeMap, str::FromStr, }; use maplit::btreemap; use must_let::must_let; use serde_json::json; use shape_inference::{ testing::TestConfig, CountedShape, }; use value::{ assert_obj, id_v6::DeveloperDocumentId, ConvexValue, TableName, TableNamespace, }; use crate::{ shapes::{ dashboard_shape_json, reduced::ReducedShape, }, testing::TestIdGenerator, virtual_system_mapping::VirtualSystemMapping, }; #[test] fn test_map_reduce_type() -> anyhow::Result<()> { let mut s = CountedShape::<TestConfig>::empty(); let empty_obj = assert_obj!("fields" => ConvexValue::Map(BTreeMap::new().try_into()?)); let nonempty_obj = assert_obj!("fields" => ConvexValue::Map(btreemap!{ ConvexValue::try_from("hi")? => ConvexValue::try_from("there")? }.try_into()?)); s = s.insert(&empty_obj); s = s.insert(&nonempty_obj); let reduced = ReducedShape::from_type(&s, &|_| false); must_let!(let ReducedShape::Object(fields) = reduced); let field = fields.get("fields").unwrap(); assert!(!field.optional); must_let!(let ReducedShape::Map {..} = &field.shape); Ok(()) } // CX-1550 was a bug in our `reduce` logic with reducing object shapes with // union shapes as their fields. // // With the example below, we'd reduce each individual shape to... // ``` // {field: string | number} // {field: string | null} // ``` // Then, when unifying the "field" types, we'd look at the first shape and set // our accumulated union type for field to a singleton union of (string | // number). Then, adding the second field shape (string | null) would end up // with the nested union shape (null | (string | number)), which is illegal. #[test] fn test_cx_1550() -> anyhow::Result<()> { let x = [ r#"[{"field": "1"}, {"field": 2.0}]"#, r#"[{"field": "3"}, {}]"#, ]; let mut shape = CountedShape::<TestConfig>::empty(); for value in x { let value = parse_json_string(value)?; shape = shape.insert_value(&value); } let reduced = ReducedShape::from_type(&shape, &|_| false); must_let!(let ReducedShape::Array(items) = reduced); must_let!(let ReducedShape::Object(fields) = *items); let field = fields.get("field").unwrap(); assert!(field.optional); must_let!(let ReducedShape::Union(union_shapes) = &field.shape); assert_eq!(union_shapes.len(), 2); must_let!(let ReducedShape::Float64(_) = union_shapes.first().unwrap()); must_let!(let ReducedShape::String = union_shapes.last().unwrap()); Ok(()) } #[test] fn test_id_strings() -> anyhow::Result<()> { let mut id_generator = TestIdGenerator::new(); // Create three IDs from three different tables let message_id: DeveloperDocumentId = id_generator .user_generate(&TableName::from_str("messages")?) .into(); let deleted1_table = TableName::from_str("deleted1")?; let deleted2_table = TableName::from_str("deleted2")?; let deleted1_id: DeveloperDocumentId = id_generator.user_generate(&deleted1_table).into(); let deleted2_id: DeveloperDocumentId = id_generator.user_generate(&deleted2_table).into(); let table_mapping = id_generator.namespace(TableNamespace::test_user()); // Delete two of the tables let deleted1_table_id = table_mapping.id(&deleted1_table)?; id_generator.remove(deleted1_table_id.tablet_id); let deleted2_table_id = table_mapping.id(&deleted2_table)?; id_generator.remove(deleted2_table_id.tablet_id); let table_mapping = id_generator.namespace(TableNamespace::test_user()); // Insert all of these into a type let inferred_type = CountedShape::<TestConfig>::empty() .insert_value(&message_id.into()) .insert_value(&deleted1_id.into()) .insert_value(&deleted2_id.into()); let reduced_shape = ReducedShape::from_type(&inferred_type, &table_mapping.table_number_exists()); let shape_json = dashboard_shape_json( &reduced_shape, &id_generator.namespace(TableNamespace::test_user()), &id_generator.virtual_system_mapping, )?; assert_eq!( shape_json, json!({"type": "Union", "shapes": vec![ json!({ "type": "Id", "tableName": "messages"}), json!({ "type": "String" }), ]}) ); Ok(()) } #[test] fn test_float_merge_shape_inference() -> anyhow::Result<()> { let id_generator = TestIdGenerator::new(); let inferred_type = CountedShape::<TestConfig>::empty() .insert_value(&ConvexValue::Float64(f64::INFINITY)) .insert_value(&ConvexValue::Float64(123.0)) .insert_value(&ConvexValue::Null); let table_mapping = id_generator.namespace(TableNamespace::test_user()); let reduced_shape = ReducedShape::from_type(&inferred_type, &table_mapping.table_number_exists()); let shape_json = dashboard_shape_json( &reduced_shape, &id_generator.namespace(TableNamespace::test_user()), &VirtualSystemMapping::default(), )?; assert_eq!( shape_json, json!({"type": "Union", "shapes": vec![ json!({ "type": "Null" }), json!({ "type": "Float64", "float64Range": { "hasSpecialValues": true} }), ]}) ); Ok(()) } fn parse_json_string(s: &str) -> anyhow::Result<ConvexValue> { let json_v: serde_json::Value = serde_json::from_str(s)?; parse_json(json_v) } fn parse_json(json_v: serde_json::Value) -> anyhow::Result<ConvexValue> { ConvexValue::try_from(json_v) } mod reduce { use cmd_util::env::env_config; use proptest::prelude::*; use shape_inference::{ testing::TestConfig, CountedShape, }; use value::ConvexValue; use super::parse_json_string; use crate::shapes::reduced::ReducedShape; fn test(first: ConvexValue, second: ConvexValue) { let shape = CountedShape::<TestConfig>::empty() .insert_value(&first) .insert_value(&second); ReducedShape::from_type(&shape, &|_| false); } proptest! { #![proptest_config( ProptestConfig { cases: 256 * env_config("CONVEX_PROPTEST_MULTIPLIER", 1), failure_persistence: None, ..ProptestConfig::default() } )] #[test] fn proptest(a in any::<ConvexValue>(), b in any::<ConvexValue>()) { test(a, b); } } #[test] fn proptest_trophies() -> anyhow::Result<()> { let trophies = [(r#"{"a": "b"}"#, r#"{"c": {}}"#)]; for (first, second) in trophies.iter() { test(parse_json_string(first)?, parse_json_string(second)?); } Ok(()) } }

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/get-convex/convex-backend'

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