-
Notifications
You must be signed in to change notification settings - Fork 2.8k
fix(rpc): register openhuman.system_info and add legacy alias (Sentry CORE-RUST-G0, closes #2871) #2872
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(rpc): register openhuman.system_info and add legacy alias (Sentry CORE-RUST-G0, closes #2871) #2872
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,90 @@ | ||
| //! JSON-RPC / CLI controller surface for the process health registry. | ||
|
|
||
| use serde::Serialize; | ||
|
|
||
| use crate::openhuman::health; | ||
| use crate::rpc::RpcOutcome; | ||
|
|
||
| pub fn health_snapshot() -> RpcOutcome<serde_json::Value> { | ||
| RpcOutcome::single_log(health::snapshot_json(), "health_snapshot requested") | ||
| } | ||
|
|
||
| /// Static system information returned by `openhuman.health_system_info`. | ||
| #[derive(Debug, Serialize)] | ||
| pub struct SystemInfo { | ||
| /// Cargo package version of the running core binary. | ||
| pub version: &'static str, | ||
| /// Target operating system name (`linux`, `macos`, `windows`, …). | ||
| pub os: &'static str, | ||
| /// Target CPU architecture (`x86_64`, `aarch64`, …). | ||
| pub arch: &'static str, | ||
| /// Current process ID. | ||
| pub pid: u32, | ||
| } | ||
|
|
||
| /// Returns static system information: version, OS, architecture, and PID. | ||
| /// | ||
| /// This is the handler backing the `openhuman.health_system_info` RPC method | ||
| /// (legacy callers may send `openhuman.system_info`, which the alias table | ||
| /// rewrites before dispatch). | ||
| pub fn system_info() -> RpcOutcome<SystemInfo> { | ||
| let info = SystemInfo { | ||
| version: env!("CARGO_PKG_VERSION"), | ||
| os: std::env::consts::OS, | ||
| arch: std::env::consts::ARCH, | ||
| pid: std::process::id(), | ||
| }; | ||
| tracing::debug!( | ||
| version = info.version, | ||
| os = info.os, | ||
| arch = info.arch, | ||
| pid = info.pid, | ||
| "[health] system_info requested" | ||
| ); | ||
| RpcOutcome::single_log(info, "system_info requested") | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
| mod tests { | ||
| use super::*; | ||
|
|
||
| #[test] | ||
| fn system_info_returns_non_empty_version() { | ||
| let outcome = system_info(); | ||
| let json = outcome | ||
| .into_cli_compatible_json() | ||
| .expect("serialization ok"); | ||
| let version = json["version"].as_str().expect("version is a string"); | ||
| assert!(!version.is_empty(), "version must be non-empty"); | ||
| } | ||
|
|
||
| #[test] | ||
| fn system_info_returns_known_os() { | ||
| let outcome = system_info(); | ||
| let json = outcome | ||
| .into_cli_compatible_json() | ||
| .expect("serialization ok"); | ||
| let os = json["os"].as_str().expect("os is a string"); | ||
| // std::env::consts::OS is always one of the compile-time Rust target OS names. | ||
| assert!(!os.is_empty(), "os must be non-empty"); | ||
| } | ||
|
|
||
| #[test] | ||
| fn system_info_returns_non_zero_pid() { | ||
| let outcome = system_info(); | ||
| let json = outcome | ||
| .into_cli_compatible_json() | ||
| .expect("serialization ok"); | ||
| let pid = json["pid"].as_u64().expect("pid is a u64"); | ||
| assert!(pid > 0, "pid must be greater than zero"); | ||
| } | ||
|
|
||
| #[test] | ||
| fn health_snapshot_returns_serializable_value() { | ||
| let outcome = health_snapshot(); | ||
| let json = outcome | ||
| .into_cli_compatible_json() | ||
| .expect("serialization ok"); | ||
| assert!(json.is_object(), "snapshot must be a JSON object"); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -5,14 +5,20 @@ use crate::core::{ControllerSchema, FieldSchema, TypeSchema}; | |||||||||||||||||||||||||||||||||||||||
| use crate::rpc::RpcOutcome; | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| pub fn all_controller_schemas() -> Vec<ControllerSchema> { | ||||||||||||||||||||||||||||||||||||||||
| vec![schemas("snapshot")] | ||||||||||||||||||||||||||||||||||||||||
| vec![schemas("snapshot"), schemas("system_info")] | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| pub fn all_registered_controllers() -> Vec<RegisteredController> { | ||||||||||||||||||||||||||||||||||||||||
| vec![RegisteredController { | ||||||||||||||||||||||||||||||||||||||||
| schema: schemas("snapshot"), | ||||||||||||||||||||||||||||||||||||||||
| handler: handle_snapshot, | ||||||||||||||||||||||||||||||||||||||||
| }] | ||||||||||||||||||||||||||||||||||||||||
| vec![ | ||||||||||||||||||||||||||||||||||||||||
| RegisteredController { | ||||||||||||||||||||||||||||||||||||||||
| schema: schemas("snapshot"), | ||||||||||||||||||||||||||||||||||||||||
| handler: handle_snapshot, | ||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||
| RegisteredController { | ||||||||||||||||||||||||||||||||||||||||
| schema: schemas("system_info"), | ||||||||||||||||||||||||||||||||||||||||
| handler: handle_system_info, | ||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| pub fn schemas(function: &str) -> ControllerSchema { | ||||||||||||||||||||||||||||||||||||||||
|
|
@@ -29,6 +35,39 @@ pub fn schemas(function: &str) -> ControllerSchema { | |||||||||||||||||||||||||||||||||||||||
| required: true, | ||||||||||||||||||||||||||||||||||||||||
| }], | ||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||
| "system_info" => ControllerSchema { | ||||||||||||||||||||||||||||||||||||||||
| namespace: "health", | ||||||||||||||||||||||||||||||||||||||||
| function: "system_info", | ||||||||||||||||||||||||||||||||||||||||
| description: | ||||||||||||||||||||||||||||||||||||||||
| "Return static system information: app version, OS, architecture, and PID.", | ||||||||||||||||||||||||||||||||||||||||
| inputs: vec![], | ||||||||||||||||||||||||||||||||||||||||
| outputs: vec![ | ||||||||||||||||||||||||||||||||||||||||
| FieldSchema { | ||||||||||||||||||||||||||||||||||||||||
| name: "version", | ||||||||||||||||||||||||||||||||||||||||
| ty: TypeSchema::String, | ||||||||||||||||||||||||||||||||||||||||
| comment: "Running core binary version (CARGO_PKG_VERSION).", | ||||||||||||||||||||||||||||||||||||||||
| required: true, | ||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||
| FieldSchema { | ||||||||||||||||||||||||||||||||||||||||
| name: "os", | ||||||||||||||||||||||||||||||||||||||||
| ty: TypeSchema::String, | ||||||||||||||||||||||||||||||||||||||||
| comment: "Host operating system name (linux, macos, windows, …).", | ||||||||||||||||||||||||||||||||||||||||
| required: true, | ||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||
| FieldSchema { | ||||||||||||||||||||||||||||||||||||||||
| name: "arch", | ||||||||||||||||||||||||||||||||||||||||
| ty: TypeSchema::String, | ||||||||||||||||||||||||||||||||||||||||
| comment: "CPU architecture (x86_64, aarch64, …).", | ||||||||||||||||||||||||||||||||||||||||
| required: true, | ||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||
| FieldSchema { | ||||||||||||||||||||||||||||||||||||||||
| name: "pid", | ||||||||||||||||||||||||||||||||||||||||
| ty: TypeSchema::String, | ||||||||||||||||||||||||||||||||||||||||
| comment: "Current process ID.", | ||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+63
to
+66
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Align Line 65 declares 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||
| required: true, | ||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||
| _ => ControllerSchema { | ||||||||||||||||||||||||||||||||||||||||
| namespace: "health", | ||||||||||||||||||||||||||||||||||||||||
| function: "unknown", | ||||||||||||||||||||||||||||||||||||||||
|
|
@@ -48,6 +87,10 @@ fn handle_snapshot(_params: Map<String, Value>) -> ControllerFuture { | |||||||||||||||||||||||||||||||||||||||
| Box::pin(async { to_json(crate::openhuman::health::rpc::health_snapshot()) }) | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| fn handle_system_info(_params: Map<String, Value>) -> ControllerFuture { | ||||||||||||||||||||||||||||||||||||||||
| Box::pin(async { to_json(crate::openhuman::health::rpc::system_info()) }) | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| fn to_json<T: serde::Serialize>(outcome: RpcOutcome<T>) -> Result<Value, String> { | ||||||||||||||||||||||||||||||||||||||||
| outcome.into_cli_compatible_json() | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
@@ -57,13 +100,13 @@ mod tests { | |||||||||||||||||||||||||||||||||||||||
| use super::*; | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| #[test] | ||||||||||||||||||||||||||||||||||||||||
| fn all_schemas_returns_one() { | ||||||||||||||||||||||||||||||||||||||||
| assert_eq!(all_controller_schemas().len(), 1); | ||||||||||||||||||||||||||||||||||||||||
| fn all_schemas_returns_two() { | ||||||||||||||||||||||||||||||||||||||||
| assert_eq!(all_controller_schemas().len(), 2); | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| #[test] | ||||||||||||||||||||||||||||||||||||||||
| fn all_controllers_returns_one() { | ||||||||||||||||||||||||||||||||||||||||
| assert_eq!(all_registered_controllers().len(), 1); | ||||||||||||||||||||||||||||||||||||||||
| fn all_controllers_returns_two() { | ||||||||||||||||||||||||||||||||||||||||
| assert_eq!(all_registered_controllers().len(), 2); | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| #[test] | ||||||||||||||||||||||||||||||||||||||||
|
|
@@ -75,6 +118,16 @@ mod tests { | |||||||||||||||||||||||||||||||||||||||
| assert!(!s.outputs.is_empty()); | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| #[test] | ||||||||||||||||||||||||||||||||||||||||
| fn system_info_schema() { | ||||||||||||||||||||||||||||||||||||||||
| let s = schemas("system_info"); | ||||||||||||||||||||||||||||||||||||||||
| assert_eq!(s.namespace, "health"); | ||||||||||||||||||||||||||||||||||||||||
| assert_eq!(s.function, "system_info"); | ||||||||||||||||||||||||||||||||||||||||
| assert!(s.inputs.is_empty()); | ||||||||||||||||||||||||||||||||||||||||
| // version, os, arch, pid | ||||||||||||||||||||||||||||||||||||||||
| assert_eq!(s.outputs.len(), 4); | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| #[test] | ||||||||||||||||||||||||||||||||||||||||
| fn unknown_function_returns_unknown() { | ||||||||||||||||||||||||||||||||||||||||
| let s = schemas("bad"); | ||||||||||||||||||||||||||||||||||||||||
|
|
@@ -86,7 +139,10 @@ mod tests { | |||||||||||||||||||||||||||||||||||||||
| fn schemas_and_controllers_match() { | ||||||||||||||||||||||||||||||||||||||||
| let s = all_controller_schemas(); | ||||||||||||||||||||||||||||||||||||||||
| let c = all_registered_controllers(); | ||||||||||||||||||||||||||||||||||||||||
| assert_eq!(s[0].function, c[0].schema.function); | ||||||||||||||||||||||||||||||||||||||||
| assert_eq!(s.len(), c.len()); | ||||||||||||||||||||||||||||||||||||||||
| for (schema, controller) in s.iter().zip(c.iter()) { | ||||||||||||||||||||||||||||||||||||||||
| assert_eq!(schema.function, controller.schema.function); | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| #[tokio::test] | ||||||||||||||||||||||||||||||||||||||||
|
|
@@ -96,6 +152,18 @@ mod tests { | |||||||||||||||||||||||||||||||||||||||
| assert!(result.unwrap().is_object()); | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| #[tokio::test] | ||||||||||||||||||||||||||||||||||||||||
| async fn handle_system_info_returns_json_object() { | ||||||||||||||||||||||||||||||||||||||||
| let result = handle_system_info(Map::new()).await; | ||||||||||||||||||||||||||||||||||||||||
| assert!(result.is_ok()); | ||||||||||||||||||||||||||||||||||||||||
| let json = result.unwrap(); | ||||||||||||||||||||||||||||||||||||||||
| assert!(json.is_object()); | ||||||||||||||||||||||||||||||||||||||||
| assert!(json["version"].as_str().is_some()); | ||||||||||||||||||||||||||||||||||||||||
| assert!(json["os"].as_str().is_some()); | ||||||||||||||||||||||||||||||||||||||||
| assert!(json["arch"].as_str().is_some()); | ||||||||||||||||||||||||||||||||||||||||
| assert!(json["pid"].as_u64().is_some()); | ||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+156
to
+164
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Read Because Suggested patch async fn handle_system_info_returns_json_object() {
let result = handle_system_info(Map::new()).await;
assert!(result.is_ok());
let json = result.unwrap();
assert!(json.is_object());
- assert!(json["version"].as_str().is_some());
- assert!(json["os"].as_str().is_some());
- assert!(json["arch"].as_str().is_some());
- assert!(json["pid"].as_u64().is_some());
+ assert!(json["result"]["version"].as_str().is_some());
+ assert!(json["result"]["os"].as_str().is_some());
+ assert!(json["result"]["arch"].as_str().is_some());
+ assert!(json["result"]["pid"].as_u64().is_some());
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| #[test] | ||||||||||||||||||||||||||||||||||||||||
| fn to_json_helper() { | ||||||||||||||||||||||||||||||||||||||||
| let outcome = RpcOutcome::single_log(serde_json::json!({"ok": true}), "log"); | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix assertion paths to use the logged
RpcOutcomeenvelope.Line 57, Line 67, and Line 78 read top-level fields, but
system_info()returnsRpcOutcome::single_log(...)(Line 44), which serializes as{ "result": ..., "logs": [...] }. These assertions will panic on missing keys.Suggested patch
fn system_info_returns_non_empty_version() { let outcome = system_info(); let json = outcome .into_cli_compatible_json() .expect("serialization ok"); - let version = json["version"].as_str().expect("version is a string"); + let version = json["result"]["version"] + .as_str() + .expect("version is a string"); assert!(!version.is_empty(), "version must be non-empty"); } @@ fn system_info_returns_known_os() { let outcome = system_info(); let json = outcome .into_cli_compatible_json() .expect("serialization ok"); - let os = json["os"].as_str().expect("os is a string"); + let os = json["result"]["os"].as_str().expect("os is a string"); // std::env::consts::OS is always one of the compile-time Rust target OS names. assert!(!os.is_empty(), "os must be non-empty"); } @@ fn system_info_returns_non_zero_pid() { let outcome = system_info(); let json = outcome .into_cli_compatible_json() .expect("serialization ok"); - let pid = json["pid"].as_u64().expect("pid is a u64"); + let pid = json["result"]["pid"].as_u64().expect("pid is a u64"); assert!(pid > 0, "pid must be greater than zero"); }📝 Committable suggestion
🤖 Prompt for AI Agents