Skip to main content

radixdb_orm/
artifacts.rs

1//! Versioned, language-neutral conformance artifacts.
2
3pub const ORM_IR_JSON_SCHEMA: &str = include_str!("../schemas/radixdb.orm.v1.schema.json");
4pub const SCHEMA_DESCRIPTOR_JSON_SCHEMA: &str =
5    include_str!("../schemas/radixdb.schema.v1.schema.json");
6pub const GUI_TABLE_FORM_JSON_SCHEMA: &str =
7    include_str!("../schemas/radixdb.gui.table-form.v1.schema.json");
8pub const ORM_CONFORMANCE_MANIFEST: &str =
9    include_str!("../schemas/radixdb.orm.v1.conformance.json");
10pub const ORM_CONFORMANCE_FIXTURES: &str = include_str!("../schemas/radixdb.orm.v1.fixtures.json");
11
12#[cfg(test)]
13mod tests {
14    use std::collections::BTreeSet;
15
16    use super::*;
17
18    #[test]
19    fn published_artifacts_are_json_and_language_neutral() {
20        for artifact in [
21            ORM_IR_JSON_SCHEMA,
22            SCHEMA_DESCRIPTOR_JSON_SCHEMA,
23            GUI_TABLE_FORM_JSON_SCHEMA,
24            ORM_CONFORMANCE_MANIFEST,
25            ORM_CONFORMANCE_FIXTURES,
26        ] {
27            serde_json::from_str::<serde_json::Value>(artifact).unwrap();
28            assert!(!artifact.contains("std::"));
29            assert!(!artifact.contains("radixdb_orm::"));
30        }
31    }
32
33    #[test]
34    fn published_json_schemas_are_valid_and_accept_every_ir_fixture() {
35        for artifact in [
36            ORM_IR_JSON_SCHEMA,
37            SCHEMA_DESCRIPTOR_JSON_SCHEMA,
38            GUI_TABLE_FORM_JSON_SCHEMA,
39        ] {
40            let schema: serde_json::Value = serde_json::from_str(artifact).unwrap();
41            if let Err(error) = jsonschema::meta::validate(&schema) {
42                panic!("invalid published JSON Schema: {error}");
43            }
44        }
45
46        let schema: serde_json::Value = serde_json::from_str(ORM_IR_JSON_SCHEMA).unwrap();
47        let validator = jsonschema::validator_for(&schema).unwrap();
48        let fixtures: serde_json::Value = serde_json::from_str(ORM_CONFORMANCE_FIXTURES).unwrap();
49        for case in fixtures["cases"].as_array().expect("fixture cases") {
50            let id = case["id"].as_str().expect("fixture id");
51            for (index, step) in case["steps"]
52                .as_array()
53                .expect("fixture steps")
54                .iter()
55                .enumerate()
56            {
57                let document = &step["document"];
58                let errors: Vec<_> = validator
59                    .iter_errors(document)
60                    .map(|error| error.to_string())
61                    .collect();
62                assert!(
63                    errors.is_empty(),
64                    "{id} step {index} violates the public ORM schema: {errors:?}"
65                );
66            }
67        }
68    }
69
70    #[test]
71    fn conformance_fixtures_compile_to_the_published_sql_and_parameters() {
72        let artifact: serde_json::Value = serde_json::from_str(ORM_CONFORMANCE_FIXTURES).unwrap();
73        assert_eq!(
74            artifact["fixtures"],
75            serde_json::Value::String("radixdb.orm.fixtures.v1".to_string())
76        );
77        let cases = artifact["cases"].as_array().expect("fixture cases");
78        let mut fixture_ids = BTreeSet::new();
79        for case in cases {
80            let id = case["id"].as_str().expect("stable fixture id");
81            assert!(fixture_ids.insert(id), "duplicate fixture ID {id}");
82            let steps = case["steps"].as_array().expect("fixture steps");
83            assert!(!steps.is_empty(), "{id}: no executable steps");
84            for (index, step) in steps.iter().enumerate() {
85                let document = crate::IrDocument::from_json(
86                    &serde_json::to_string(&step["document"]).unwrap(),
87                )
88                .unwrap_or_else(|error| panic!("{id} step {index}: invalid IR: {error}"));
89                let compiled = document
90                    .to_sql()
91                    .unwrap_or_else(|error| panic!("{id} step {index}: render failed: {error}"));
92                assert_eq!(
93                    compiled.sql,
94                    step["sql"].as_str().unwrap(),
95                    "{id} step {index}"
96                );
97                assert_eq!(
98                    serde_json::to_value(&compiled.parameters).unwrap(),
99                    step["parameters"],
100                    "{id} step {index}"
101                );
102            }
103            if let Some(rejections) = case.get("rejected_documents") {
104                for rejected in rejections.as_array().expect("rejection cases") {
105                    assert!(
106                        crate::IrDocument::from_json(&serde_json::to_string(rejected).unwrap())
107                            .is_err(),
108                        "{id}: rejection unexpectedly decoded"
109                    );
110                }
111            }
112            assert!(case.get("result").is_some(), "{id}: missing result oracle");
113        }
114
115        for id in [
116            "ORM-CONFORMANCE-001",
117            "ORM-CONFORMANCE-002",
118            "ORM-CONFORMANCE-003",
119        ] {
120            assert!(fixture_ids.contains(id), "missing base fixture {id}");
121        }
122    }
123}