1#![forbid(unsafe_code)]
2
3use include_dir::{include_dir, Dir};
4
5pub static FIXTURES: Dir<'static> = include_dir!("$CARGO_MANIFEST_DIR/fixtures");
12
13pub fn fixture_bytes(path: &str) -> Option<&'static [u8]> {
16 FIXTURES.get_file(path).map(|f| f.contents())
17}
18
19pub fn fixture_str(path: &str) -> Option<&'static str> {
23 FIXTURES.get_file(path).and_then(|f| f.contents_utf8())
24}
25
26pub const COMMONMETA_SCHEMA_V1_0: &str = include_str!("../schemas/commonmeta_v1.0.json");
29
30pub fn schema(version: &str) -> Option<&'static str> {
34 match version {
35 "1.0" => Some(COMMONMETA_SCHEMA_V1_0),
36 _ => None,
37 }
38}
39
40#[cfg(test)]
41mod tests {
42 use super::{schema, FIXTURES};
43 use jsonschema::validator_for;
44 use serde_json::{json, Value};
45
46 #[test]
47 fn known_version_is_available() {
48 assert!(schema("1.0").is_some());
49 }
50
51 #[test]
52 fn unknown_version_is_none() {
53 assert!(schema("nope").is_none());
54 }
55
56 #[test]
57 fn embedded_fixtures_are_available() {
58 use super::{fixture_bytes, fixture_str};
59 assert!(fixture_str("commonmeta/journal_article.json").is_some());
61 assert!(fixture_bytes("orcid_xml/0000-0002-0068-716X.xml").is_some());
63 let commonmeta = FIXTURES.get_dir("commonmeta").expect("commonmeta dir");
65 assert!(commonmeta.files().count() > 0);
66 assert!(fixture_str("nope/missing.json").is_none());
68 }
69
70 #[test]
71 fn all_embedded_commonmeta_fixtures_validate_against_exported_schema() {
72 let schema_text = schema("1.0").expect("schema 1.0 is embedded");
73 let schema_json: Value = serde_json::from_str(schema_text).expect("valid schema json");
74 let defs = schema_json
75 .get("$defs")
76 .cloned()
77 .expect("schema exposes $defs");
78 let entity_schema = json!({
79 "$schema": "https://json-schema.org/draft/2020-12/schema",
80 "$ref": "#/$defs/entity",
81 "$defs": defs
82 });
83 let validator = validator_for(&entity_schema).expect("entity schema compiles for validation");
84
85 let commonmeta_dir = FIXTURES.get_dir("commonmeta").expect("commonmeta dir");
86 let mut validated_count = 0usize;
87 let mut failures: Vec<String> = Vec::new();
88
89 for file in commonmeta_dir.files() {
90 let path = file.path().to_string_lossy();
91 if !path.ends_with(".json") {
92 continue;
93 }
94
95 validated_count += 1;
96
97 let text = match file.contents_utf8() {
98 Some(text) => text,
99 None => {
100 failures.push(format!("{path}: fixture is not valid UTF-8"));
101 continue;
102 }
103 };
104
105 let instance: Value = match serde_json::from_str(text) {
106 Ok(value) => value,
107 Err(err) => {
108 failures.push(format!("{path}: invalid JSON: {err}"));
109 continue;
110 }
111 };
112
113 let details: Vec<String> = validator
114 .iter_errors(&instance)
115 .take(5)
116 .map(|error| format!("{}: {}", error.instance_path, error))
117 .collect();
118 if !details.is_empty() {
119 failures.push(format!("{path}: {}", details.join(" | ")));
120 }
121 }
122
123 assert!(validated_count > 0, "no commonmeta fixtures found to validate");
124 assert!(
125 failures.is_empty(),
126 "{} fixture(s) failed schema validation:\n{}",
127 failures.len(),
128 failures.join("\n")
129 );
130 }
131}