use std::path::PathBuf; use continuum_schema_instance::build_instance_from_file; use serde_json::json; fn fixture(relative: &str) -> PathBuf { PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures").join(relative) } /// Validates `instance` against the real schema file - closes the loop: /// this confirms the builder's output is actually accepted by a real JSON /// Schema validator (the `jsonschema` crate), not just "looks right". fn assert_valid(schema_path: &PathBuf, instance: &serde_json::Value) { let schema_text = std::fs::read_to_string(schema_path).unwrap(); let mut schema: serde_json::Value = serde_json::from_str(&schema_text).unwrap(); // These fixtures have no `$id` (real schemas in continuum-schemas do, // as an absolute https:// URL) - the `jsonschema` crate resolves a // relative `$ref` against the schema's own `$id`, so give it one // pointing at this file's real location for the relative `$ref`s // inside to resolve from during this check. let absolute_path = schema_path.canonicalize().unwrap(); schema["$id"] = json!(format!("file://{}", absolute_path.display())); let validator = jsonschema::validator_for(&schema).expect("schema itself should compile"); let errors: Vec<_> = validator.iter_errors(instance).collect(); assert!(errors.is_empty(), "instance {instance} did not validate: {errors:?}"); } #[test] fn required_fields_are_filled_and_optional_fields_are_left_out() { let path = fixture("events/item_created.schema.json"); let mut instance = build_instance_from_file(&path).unwrap(); // "type" has no const at the envelope level, but item_created's own // allOf branch overrides it to one - that const wins. assert_eq!(instance["type"], json!("item_created")); // "event_id"/"item_id" are required with no const - each gets a // placeholder, ready to be overridden. assert!(instance.get("event_id").is_some()); assert!(instance.get("item_id").is_some()); // "note" is optional and correctly left out entirely. assert_eq!(instance.as_object().unwrap().len(), 3); assert_valid(&path, &instance); // The one field the caller actually has a value for - plain // serde_json::Value indexing, no special "overrides" API needed. instance["item_id"] = json!("item-42"); assert_valid(&path, &instance); } #[test] fn allof_merge_lets_a_later_branch_override_an_earlier_ones_property() { let path = fixture("events/item_deleted.schema.json"); let instance = build_instance_from_file(&path).unwrap(); // "type" is defined twice across the allOf: a plain string in the // shared envelope, then overridden to `const: "item_deleted"` in this // event's own branch - the later branch has to win. assert_eq!(instance["type"], json!("item_deleted")); assert_eq!(instance.as_object().unwrap().len(), 2); // event_id, type assert_valid(&path, &instance); }