aboutsummaryrefslogtreecommitdiff
path: root/src/api/schema/tests.rs
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-06-29 22:27:19 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-06-29 22:27:19 +0300
commit25aeaa46f6c807be686b710c5c3aa656969f07e5 (patch)
tree8b9b4d4abce5951c2d87ee22c38eacb544eff81a /src/api/schema/tests.rs
parent9150ed6268874d1f2a314b1ddbf531b66df1e105 (diff)
feat: generate socket protocol schema
Diffstat (limited to 'src/api/schema/tests.rs')
-rw-r--r--src/api/schema/tests.rs98
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(),