diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-06-29 22:27:19 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-06-29 22:27:19 +0300 |
| commit | 25aeaa46f6c807be686b710c5c3aa656969f07e5 (patch) | |
| tree | 8b9b4d4abce5951c2d87ee22c38eacb544eff81a /src/api/schema/tests.rs | |
| parent | 9150ed6268874d1f2a314b1ddbf531b66df1e105 (diff) | |
feat: generate socket protocol schema
Diffstat (limited to 'src/api/schema/tests.rs')
| -rw-r--r-- | src/api/schema/tests.rs | 98 |
1 files changed, 98 insertions, 0 deletions
diff --git a/src/api/schema/tests.rs b/src/api/schema/tests.rs index 5d61e972..01ec9baa 100644 --- a/src/api/schema/tests.rs +++ b/src/api/schema/tests.rs @@ -2,6 +2,49 @@ use std::collections::HashMap; use super::*; +fn protocol_schema_entry<T: schemars::JsonSchema>(name: &str) -> serde_json::Value { + let mut schema = serde_json::to_value(schemars::schema_for!(T)).unwrap(); + rewrite_schema_refs(&mut schema, name); + schema +} + +fn rewrite_schema_refs(value: &mut serde_json::Value, schema_name: &str) { + match value { + serde_json::Value::Object(object) => { + if let Some(serde_json::Value::String(reference)) = object.get_mut("$ref") { + if let Some(path) = reference.strip_prefix("#/") { + *reference = format!("#/schemas/{schema_name}/{path}"); + } + } + for child in object.values_mut() { + rewrite_schema_refs(child, schema_name); + } + } + serde_json::Value::Array(items) => { + for item in items { + rewrite_schema_refs(item, schema_name); + } + } + _ => {} + } +} + +fn protocol_schema_document() -> serde_json::Value { + serde_json::json!({ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Herdr API", + "schema_version": 1, + "protocol": crate::protocol::PROTOCOL_VERSION, + "schemas": { + "request": protocol_schema_entry::<Request>("request"), + "success_response": protocol_schema_entry::<SuccessResponse>("success_response"), + "error_response": protocol_schema_entry::<ErrorResponse>("error_response"), + "event": protocol_schema_entry::<EventEnvelope>("event"), + "subscription_event": protocol_schema_entry::<SubscriptionEventEnvelope>("subscription_event"), + }, + }) +} + #[test] fn request_uses_dot_method_names() { let request = Request { @@ -19,6 +62,61 @@ fn request_uses_dot_method_names() { } #[test] +fn bundled_protocol_schema_refs_resolve_inside_bundle() { + fn assert_no_standalone_refs(value: &serde_json::Value) { + match value { + serde_json::Value::Object(object) => { + if let Some(serde_json::Value::String(reference)) = object.get("$ref") { + assert!( + !reference.starts_with("#/$defs/"), + "schema bundle contains standalone ref {reference}" + ); + } + for child in object.values() { + assert_no_standalone_refs(child); + } + } + serde_json::Value::Array(items) => { + for item in items { + assert_no_standalone_refs(item); + } + } + _ => {} + } + } + + assert_no_standalone_refs(&protocol_schema_document()); +} + +#[test] +fn generated_protocol_schema_artifact_is_current() { + let actual = format!( + "{}\n", + serde_json::to_string_pretty(&protocol_schema_document()).unwrap() + ); + let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) + .join("docs/next/api/herdr-api.schema.json"); + + if std::env::var_os("HERDR_UPDATE_API_SCHEMA").is_some() { + std::fs::create_dir_all(path.parent().unwrap()).unwrap(); + std::fs::write(&path, &actual).unwrap(); + return; + } + + let expected = std::fs::read_to_string(&path).unwrap_or_else(|err| { + panic!( + "failed to read {}; run `HERDR_UPDATE_API_SCHEMA=1 just test-one generated_protocol_schema_artifact_is_current`: {err}", + path.display() + ) + }); + assert_eq!( + expected, + actual, + "generated API schema artifact is stale; run `HERDR_UPDATE_API_SCHEMA=1 just test-one generated_protocol_schema_artifact_is_current`" + ); +} + +#[test] fn request_round_trips_for_server_stop() { let request = Request { id: "req_stop".into(), |
