#![allow(missing_docs, clippy::should_implement_trait)]
use serde::{Deserialize, Serialize};
use soma_som_core::quad::{Quad, Tree};
pub const COMMAND_PREFIX: &str = "command.";
pub const RESULT_PREFIX: &str = "result.";
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct RingCommand {
pub command_type: String,
pub payload: String,
pub actor: String,
pub role_key: String,
pub request_id: String,
}
impl RingCommand {
pub fn new(
command_type: impl Into<String>,
payload: impl Into<String>,
actor: impl Into<String>,
role_key: impl Into<String>,
request_id: impl Into<String>,
) -> Self {
Self {
command_type: command_type.into(),
payload: payload.into(),
actor: actor.into(),
role_key: role_key.into(),
request_id: request_id.into(),
}
}
pub fn inject_into(&self, tree: &mut Tree) {
tree.insert("command.type".into(), self.command_type.as_bytes().to_vec());
tree.insert("command.payload".into(), self.payload.as_bytes().to_vec());
tree.insert("command.admin".into(), self.actor.as_bytes().to_vec());
tree.insert("command.role".into(), self.role_key.as_bytes().to_vec());
tree.insert(
"command.request_id".into(),
self.request_id.as_bytes().to_vec(),
);
}
pub fn extract_from(tree: &Tree) -> Option<Self> {
let command_type = tree
.get("command.type")
.map(|v| String::from_utf8_lossy(v).into_owned())?;
let payload = tree
.get("command.payload")
.map(|v| String::from_utf8_lossy(v).into_owned())
.unwrap_or_default();
let actor = tree
.get("command.admin")
.map(|v| String::from_utf8_lossy(v).into_owned())
.unwrap_or_default();
let role_key = tree
.get("command.role")
.map(|v| String::from_utf8_lossy(v).into_owned())
.unwrap_or_default();
let request_id = tree
.get("command.request_id")
.map(|v| String::from_utf8_lossy(v).into_owned())
.unwrap_or_default();
Some(Self {
command_type,
payload,
actor,
role_key,
request_id,
})
}
pub fn is_command(tree: &Tree) -> bool {
tree.contains_key("command.type")
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub enum CommandStatus {
Success,
Denied,
Error,
}
impl CommandStatus {
pub fn as_str(&self) -> &'static str {
match self {
CommandStatus::Success => "success",
CommandStatus::Denied => "denied",
CommandStatus::Error => "error",
}
}
pub fn from_str(s: &str) -> Option<Self> {
match s {
"success" => Some(CommandStatus::Success),
"denied" => Some(CommandStatus::Denied),
"error" => Some(CommandStatus::Error),
_ => None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct CommandResult {
pub status: CommandStatus,
pub command_type: String,
pub payload: String,
pub error: String,
pub request_id: String,
}
impl CommandResult {
pub fn success(
command_type: impl Into<String>,
payload: impl Into<String>,
request_id: impl Into<String>,
) -> Self {
Self {
status: CommandStatus::Success,
command_type: command_type.into(),
payload: payload.into(),
error: String::new(),
request_id: request_id.into(),
}
}
pub fn denied(
command_type: impl Into<String>,
reason: impl Into<String>,
request_id: impl Into<String>,
) -> Self {
Self {
status: CommandStatus::Denied,
command_type: command_type.into(),
payload: String::new(),
error: reason.into(),
request_id: request_id.into(),
}
}
pub fn error(
command_type: impl Into<String>,
error: impl Into<String>,
request_id: impl Into<String>,
) -> Self {
Self {
status: CommandStatus::Error,
command_type: command_type.into(),
payload: String::new(),
error: error.into(),
request_id: request_id.into(),
}
}
pub fn inject_into(&self, tree: &mut Tree) {
tree.insert(
"result.status".into(),
self.status.as_str().as_bytes().to_vec(),
);
tree.insert(
"result.command_type".into(),
self.command_type.as_bytes().to_vec(),
);
tree.insert("result.payload".into(), self.payload.as_bytes().to_vec());
tree.insert("result.error".into(), self.error.as_bytes().to_vec());
tree.insert(
"result.request_id".into(),
self.request_id.as_bytes().to_vec(),
);
}
pub fn extract_from(tree: &Tree) -> Option<Self> {
let status_str = tree
.get("result.status")
.map(|v| String::from_utf8_lossy(v).into_owned())?;
let status = CommandStatus::from_str(&status_str)?;
let command_type = tree
.get("result.command_type")
.map(|v| String::from_utf8_lossy(v).into_owned())
.unwrap_or_default();
let payload = tree
.get("result.payload")
.map(|v| String::from_utf8_lossy(v).into_owned())
.unwrap_or_default();
let error = tree
.get("result.error")
.map(|v| String::from_utf8_lossy(v).into_owned())
.unwrap_or_default();
let request_id = tree
.get("result.request_id")
.map(|v| String::from_utf8_lossy(v).into_owned())
.unwrap_or_default();
Some(Self {
status,
command_type,
payload,
error,
request_id,
})
}
pub fn is_result(tree: &Tree) -> bool {
tree.contains_key("result.status")
}
}
pub fn inject_command(quad: &Quad, command: &RingCommand) -> Quad {
let mut tree = quad.tree.clone();
command.inject_into(&mut tree);
let root = {
let mut hasher = blake3::Hasher::new();
hasher.update(b"command_injection");
hasher.update(&quad.root);
hasher.update(command.command_type.as_bytes());
hasher.update(command.request_id.as_bytes());
*hasher.finalize().as_bytes()
};
Quad::new(root, quad.pointer, tree)
}
pub fn validate_payload(
schema: &soma_som_core::extension::CommandSchema,
payload: &str,
) -> Result<(), String> {
if payload.is_empty() || payload == "{}" {
let has_required = schema.fields.iter().any(|f| f.required);
if has_required {
let missing: Vec<&str> = schema
.fields
.iter()
.filter(|f| f.required)
.map(|f| f.name.as_str())
.collect();
return Err(format!(
"payload validation failed for '{}': missing required fields: {}",
schema.command_type,
missing.join(", ")
));
}
return Ok(());
}
let value: serde_json::Value = serde_json::from_str(payload).map_err(|e| {
format!(
"payload validation failed for '{}': invalid JSON: {e}",
schema.command_type
)
})?;
let obj = match value.as_object() {
Some(o) => o,
None => {
return Err(format!(
"payload validation failed for '{}': payload must be a JSON object",
schema.command_type
));
}
};
let mut errors = Vec::new();
for field in &schema.fields {
match obj.get(&field.name) {
None if field.required => {
errors.push(format!("missing required field '{}'", field.name));
}
None => {} Some(val) => {
use soma_som_core::extension::SchemaFieldType;
let type_ok = match field.field_type {
SchemaFieldType::String => val.is_string(),
SchemaFieldType::Number => val.is_number(),
SchemaFieldType::Boolean => val.is_boolean(),
SchemaFieldType::Object => val.is_object(),
SchemaFieldType::Array => val.is_array(),
_ => false,
};
if !type_ok {
let actual = match val {
serde_json::Value::Null => "null",
serde_json::Value::Bool(_) => "boolean",
serde_json::Value::Number(_) => "number",
serde_json::Value::String(_) => "string",
serde_json::Value::Array(_) => "array",
serde_json::Value::Object(_) => "object",
};
errors.push(format!(
"field '{}' expected type '{}', got '{}'",
field.name, field.field_type, actual,
));
}
}
}
}
if errors.is_empty() {
Ok(())
} else {
Err(format!(
"payload validation failed for '{}': {}",
schema.command_type,
errors.join("; ")
))
}
}
pub const VIEW_PREFIX: &str = "view.";
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct ViewIntent {
pub view_id: String,
pub scope: String,
pub role_key: String,
pub request_id: String,
}
impl ViewIntent {
pub fn new(
view_id: impl Into<String>,
scope: impl Into<String>,
role_key: impl Into<String>,
request_id: impl Into<String>,
) -> Self {
Self {
view_id: view_id.into(),
scope: scope.into(),
role_key: role_key.into(),
request_id: request_id.into(),
}
}
pub fn inject_into(&self, tree: &mut Tree) {
tree.insert("view.id".into(), self.view_id.as_bytes().to_vec());
tree.insert("view.scope".into(), self.scope.as_bytes().to_vec());
tree.insert(
"view.requestor_role".into(),
self.role_key.as_bytes().to_vec(),
);
tree.insert(
"view.request_id".into(),
self.request_id.as_bytes().to_vec(),
);
}
pub fn extract_from(tree: &Tree) -> Option<Self> {
let view_id = tree
.get("view.id")
.map(|v| String::from_utf8_lossy(v).into_owned())?;
let scope = tree
.get("view.scope")
.map(|v| String::from_utf8_lossy(v).into_owned())
.unwrap_or_default();
let role_key = tree
.get("view.requestor_role")
.map(|v| String::from_utf8_lossy(v).into_owned())
.unwrap_or_default();
let request_id = tree
.get("view.request_id")
.map(|v| String::from_utf8_lossy(v).into_owned())
.unwrap_or_default();
Some(Self {
view_id,
scope,
role_key,
request_id,
})
}
pub fn is_view_intent(tree: &Tree) -> bool {
tree.contains_key("view.id")
}
}
pub fn inject_view_intent(quad: &Quad, intent: &ViewIntent) -> Quad {
let mut tree = quad.tree.clone();
intent.inject_into(&mut tree);
let root = {
let mut hasher = blake3::Hasher::new();
hasher.update(b"view_intent_injection");
hasher.update(&quad.root);
hasher.update(intent.view_id.as_bytes());
hasher.update(intent.request_id.as_bytes());
*hasher.finalize().as_bytes()
};
Quad::new(root, quad.pointer, tree)
}
#[cfg(test)]
mod tests {
use super::*;
use soma_som_core::quad::Quad;
#[test]
fn ring_command_new() {
let cmd = RingCommand::new(
"user.create",
"{\"username\":\"alice\"}",
"admin",
"admin",
"req-001",
);
assert_eq!(cmd.command_type, "user.create");
assert_eq!(cmd.actor, "admin");
assert_eq!(cmd.role_key, "admin");
assert_eq!(cmd.request_id, "req-001");
}
#[test]
fn command_inject_extract_roundtrip() {
let cmd = RingCommand::new(
"user.delete",
"{\"username\":\"bob\"}",
"admin",
"admin",
"req-002",
);
let mut tree = Tree::new();
cmd.inject_into(&mut tree);
let extracted = RingCommand::extract_from(&tree).expect("should extract");
assert_eq!(extracted, cmd);
}
#[test]
fn command_extract_returns_none_without_type() {
let tree = Tree::new();
assert!(RingCommand::extract_from(&tree).is_none());
}
#[test]
fn command_is_command_detection() {
let mut tree = Tree::new();
assert!(!RingCommand::is_command(&tree));
tree.insert("command.type".into(), b"user.list".to_vec());
assert!(RingCommand::is_command(&tree));
}
#[test]
fn inject_command_preserves_existing_tree() {
let mut tree = Tree::new();
tree.insert("existing.key".into(), b"value".to_vec());
let quad = Quad::from_strings("root", "ptr", tree);
let cmd = RingCommand::new("user.list", "{}", "admin", "admin", "req-003");
let injected = inject_command(&quad, &cmd);
assert_eq!(
injected.tree.get("existing.key"),
Some(&b"value".to_vec()),
"existing keys must be preserved"
);
assert!(injected.tree.contains_key("command.type"));
}
#[test]
fn inject_command_recomputes_root() {
let quad = Quad::from_strings("root", "ptr", Tree::new());
let cmd = RingCommand::new("user.create", "{}", "admin", "admin", "req-004");
let injected = inject_command(&quad, &cmd);
assert_ne!(
injected.root, quad.root,
"root must change after command injection"
);
}
#[test]
fn inject_command_different_commands_different_roots() {
let quad = Quad::from_strings("root", "ptr", Tree::new());
let cmd_a = RingCommand::new("user.create", "{}", "admin", "admin", "req-a");
let cmd_b = RingCommand::new("user.delete", "{}", "admin", "admin", "req-b");
let injected_a = inject_command(&quad, &cmd_a);
let injected_b = inject_command(&quad, &cmd_b);
assert_ne!(
injected_a.root, injected_b.root,
"different commands must produce different roots"
);
}
#[test]
fn inject_command_preserves_pointer() {
let quad = Quad::from_strings("root", "ptr", Tree::new());
let cmd = RingCommand::new("user.list", "{}", "admin", "admin", "req-005");
let injected = inject_command(&quad, &cmd);
assert_eq!(injected.pointer, quad.pointer, "pointer must be preserved");
}
#[test]
fn result_success() {
let r = CommandResult::success("user.create", "{\"username\":\"alice\"}", "req-001");
assert_eq!(r.status, CommandStatus::Success);
assert_eq!(r.command_type, "user.create");
assert!(!r.payload.is_empty());
assert!(r.error.is_empty());
}
#[test]
fn result_denied() {
let r = CommandResult::denied("user.delete", "insufficient privileges", "req-002");
assert_eq!(r.status, CommandStatus::Denied);
assert!(r.payload.is_empty());
assert_eq!(r.error, "insufficient privileges");
}
#[test]
fn result_error() {
let r = CommandResult::error("user.create", "username already exists", "req-003");
assert_eq!(r.status, CommandStatus::Error);
assert_eq!(r.error, "username already exists");
}
#[test]
fn result_inject_extract_roundtrip() {
let r = CommandResult::success("user.list", "[{\"username\":\"admin\"}]", "req-004");
let mut tree = Tree::new();
r.inject_into(&mut tree);
let extracted = CommandResult::extract_from(&tree).expect("should extract");
assert_eq!(extracted, r);
}
#[test]
fn result_extract_returns_none_without_status() {
let tree = Tree::new();
assert!(CommandResult::extract_from(&tree).is_none());
}
#[test]
fn result_is_result_detection() {
let mut tree = Tree::new();
assert!(!CommandResult::is_result(&tree));
tree.insert("result.status".into(), b"success".to_vec());
assert!(CommandResult::is_result(&tree));
}
#[test]
fn result_denied_roundtrip() {
let r = CommandResult::denied("user.delete", "not authorized", "req-005");
let mut tree = Tree::new();
r.inject_into(&mut tree);
let extracted = CommandResult::extract_from(&tree).unwrap();
assert_eq!(extracted.status, CommandStatus::Denied);
assert_eq!(extracted.error, "not authorized");
}
#[test]
fn result_error_roundtrip() {
let r = CommandResult::error("user.create", "db write failed", "req-006");
let mut tree = Tree::new();
r.inject_into(&mut tree);
let extracted = CommandResult::extract_from(&tree).unwrap();
assert_eq!(extracted.status, CommandStatus::Error);
assert_eq!(extracted.error, "db write failed");
}
#[test]
fn command_status_roundtrip() {
for status in [
CommandStatus::Success,
CommandStatus::Denied,
CommandStatus::Error,
] {
let parsed = CommandStatus::from_str(status.as_str()).unwrap();
assert_eq!(parsed, status);
}
}
#[test]
fn command_status_unknown_returns_none() {
assert!(CommandStatus::from_str("unknown").is_none());
assert!(CommandStatus::from_str("").is_none());
}
#[test]
fn ring_command_serde_roundtrip() {
let cmd = RingCommand::new(
"user.create",
"{\"username\":\"carol\"}",
"admin",
"admin",
"req-007",
);
let json = serde_json::to_string(&cmd).unwrap();
let decoded: RingCommand = serde_json::from_str(&json).unwrap();
assert_eq!(decoded, cmd);
}
#[test]
fn command_result_serde_roundtrip() {
let r = CommandResult::success("user.list", "[]", "req-008");
let json = serde_json::to_string(&r).unwrap();
let decoded: CommandResult = serde_json::from_str(&json).unwrap();
assert_eq!(decoded, r);
}
#[test]
fn command_keys_use_command_prefix() {
let cmd = RingCommand::new("user.create", "{}", "admin", "admin", "req-009");
let mut tree = Tree::new();
cmd.inject_into(&mut tree);
for key in tree.keys() {
assert!(
key.starts_with(COMMAND_PREFIX),
"command key '{key}' must start with '{COMMAND_PREFIX}'"
);
}
}
#[test]
fn result_keys_use_result_prefix() {
let r = CommandResult::success("user.create", "{}", "req-010");
let mut tree = Tree::new();
r.inject_into(&mut tree);
for key in tree.keys() {
assert!(
key.starts_with(RESULT_PREFIX),
"result key '{key}' must start with '{RESULT_PREFIX}'"
);
}
}
#[test]
fn command_and_event_namespaces_do_not_collide() {
let mut tree = Tree::new();
tree.insert("event.type".into(), b"login_attempt".to_vec());
tree.insert("event.source".into(), b"web".to_vec());
let cmd = RingCommand::new("user.list", "{}", "admin", "admin", "req-011");
cmd.inject_into(&mut tree);
assert_eq!(tree.get("event.type"), Some(&b"login_attempt".to_vec()),);
assert_eq!(tree.get("command.type"), Some(&b"user.list".to_vec()),);
assert!(RingCommand::extract_from(&tree).is_some());
}
use soma_som_core::extension::{CommandSchema, SchemaField, SchemaFieldType};
fn user_create_schema() -> CommandSchema {
CommandSchema::new("user.create")
.field(SchemaField::required("username", SchemaFieldType::String))
.field(SchemaField::required("password", SchemaFieldType::String))
.field(SchemaField::optional("role", SchemaFieldType::String))
}
#[test]
fn validate_valid_payload() {
let schema = user_create_schema();
let payload = r#"{"username":"alice","password":"secret"}"#;
assert!(super::validate_payload(&schema, payload).is_ok());
}
#[test]
fn validate_valid_payload_with_extra_fields() {
let schema = user_create_schema();
let payload = r#"{"username":"alice","password":"secret","extra":"ignored"}"#;
assert!(super::validate_payload(&schema, payload).is_ok());
}
#[test]
fn validate_missing_required_field() {
let schema = user_create_schema();
let payload = r#"{"password":"secret"}"#;
let err = super::validate_payload(&schema, payload).unwrap_err();
assert!(err.contains("missing required field 'username'"));
}
#[test]
fn validate_wrong_field_type() {
let schema = CommandSchema::new("test.cmd")
.field(SchemaField::required("timeout", SchemaFieldType::Number));
let payload = r#"{"timeout":"not-a-number"}"#;
let err = super::validate_payload(&schema, payload).unwrap_err();
assert!(err.contains("expected type 'number'"));
assert!(err.contains("got 'string'"));
}
#[test]
fn validate_no_schema_fields_accepts_anything() {
let schema = CommandSchema::new("user.list");
assert!(super::validate_payload(&schema, "{}").is_ok());
assert!(super::validate_payload(&schema, r#"{"any":"thing"}"#).is_ok());
}
#[test]
fn validate_empty_payload_with_no_required_fields() {
let schema = CommandSchema::new("test.cmd")
.field(SchemaField::optional("debug", SchemaFieldType::Boolean));
assert!(super::validate_payload(&schema, "{}").is_ok());
assert!(super::validate_payload(&schema, "").is_ok());
}
#[test]
fn validate_empty_payload_with_required_fields_fails() {
let schema = user_create_schema();
let err = super::validate_payload(&schema, "{}").unwrap_err();
assert!(err.contains("missing required fields"));
}
#[test]
fn validate_invalid_json_fails() {
let schema = user_create_schema();
let err = super::validate_payload(&schema, "not json").unwrap_err();
assert!(err.contains("invalid JSON"));
}
#[test]
fn validate_non_object_json_fails() {
let schema = user_create_schema();
let err = super::validate_payload(&schema, r#""just a string""#).unwrap_err();
assert!(err.contains("must be a JSON object"));
}
#[test]
fn validate_optional_field_type_checked_when_present() {
let schema = CommandSchema::new("test.cmd")
.field(SchemaField::optional("count", SchemaFieldType::Number));
assert!(super::validate_payload(&schema, "{}").is_ok());
assert!(super::validate_payload(&schema, r#"{"count":42}"#).is_ok());
let err = super::validate_payload(&schema, r#"{"count":"nope"}"#).unwrap_err();
assert!(err.contains("expected type 'number'"));
}
#[test]
fn validate_all_field_types() {
let schema = CommandSchema::new("test.types")
.field(SchemaField::required("s", SchemaFieldType::String))
.field(SchemaField::required("n", SchemaFieldType::Number))
.field(SchemaField::required("b", SchemaFieldType::Boolean))
.field(SchemaField::required("o", SchemaFieldType::Object))
.field(SchemaField::required("a", SchemaFieldType::Array));
let payload = r#"{"s":"x","n":1,"b":true,"o":{},"a":[]}"#;
assert!(super::validate_payload(&schema, payload).is_ok());
}
#[test]
fn view_intent_new() {
let intent = ViewIntent::new("organ.mirror", "health", "admin", "req-001");
assert_eq!(intent.view_id, "organ.mirror");
assert_eq!(intent.scope, "health");
assert_eq!(intent.role_key, "admin");
assert_eq!(intent.request_id, "req-001");
}
#[test]
fn view_intent_inject_extract_roundtrip() {
let intent = ViewIntent::new(
"term.fu.data.tree",
"identity",
"viewer",
"req-002",
);
let mut tree = Tree::new();
intent.inject_into(&mut tree);
let extracted = ViewIntent::extract_from(&tree).expect("should extract");
assert_eq!(extracted, intent);
}
#[test]
fn view_intent_extract_returns_none_without_id() {
let tree = Tree::new();
assert!(ViewIntent::extract_from(&tree).is_none());
}
#[test]
fn view_intent_is_view_intent_detection() {
let mut tree = Tree::new();
assert!(!ViewIntent::is_view_intent(&tree));
tree.insert("view.id".into(), b"organ.mirror".to_vec());
assert!(ViewIntent::is_view_intent(&tree));
}
#[test]
fn inject_view_intent_preserves_existing_tree() {
let mut tree = Tree::new();
tree.insert("existing.key".into(), b"value".to_vec());
let quad = Quad::from_strings("root", "ptr", tree);
let intent = ViewIntent::new("organ.guard", "policy", "admin", "req-003");
let injected = inject_view_intent(&quad, &intent);
assert_eq!(
injected.tree.get("existing.key"),
Some(&b"value".to_vec()),
"existing keys must be preserved"
);
assert!(injected.tree.contains_key("view.id"));
}
#[test]
fn inject_view_intent_recomputes_root() {
let quad = Quad::from_strings("root", "ptr", Tree::new());
let intent = ViewIntent::new("organ.store", "data", "admin", "req-004");
let injected = inject_view_intent(&quad, &intent);
assert_ne!(
injected.root, quad.root,
"root must change after view intent injection"
);
}
#[test]
fn inject_view_intent_different_intents_different_roots() {
let quad = Quad::from_strings("root", "ptr", Tree::new());
let intent_a = ViewIntent::new("organ.mirror", "health", "admin", "req-a");
let intent_b = ViewIntent::new("organ.guard", "policy", "admin", "req-b");
let injected_a = inject_view_intent(&quad, &intent_a);
let injected_b = inject_view_intent(&quad, &intent_b);
assert_ne!(
injected_a.root, injected_b.root,
"different view intents must produce different roots"
);
}
#[test]
fn inject_view_intent_preserves_pointer() {
let quad = Quad::from_strings("root", "ptr", Tree::new());
let intent = ViewIntent::new("organ.wall", "perimeter", "admin", "req-005");
let injected = inject_view_intent(&quad, &intent);
assert_eq!(injected.pointer, quad.pointer, "pointer must be preserved");
}
#[test]
fn view_intent_serde_roundtrip() {
let intent = ViewIntent::new(
"term.mu.data.tree",
"observability",
"operator",
"req-006",
);
let json = serde_json::to_string(&intent).unwrap();
let decoded: ViewIntent = serde_json::from_str(&json).unwrap();
assert_eq!(decoded, intent);
}
#[test]
fn view_intent_keys_use_view_prefix() {
let intent = ViewIntent::new("organ.mirror", "health", "admin", "req-007");
let mut tree = Tree::new();
intent.inject_into(&mut tree);
for key in tree.keys() {
assert!(
key.starts_with(VIEW_PREFIX),
"view key '{key}' must start with '{VIEW_PREFIX}'"
);
}
}
#[test]
fn view_and_command_namespaces_do_not_collide() {
let mut tree = Tree::new();
tree.insert("event.type".into(), b"login_attempt".to_vec());
let cmd = RingCommand::new("user.list", "{}", "admin", "admin", "req-cmd");
cmd.inject_into(&mut tree);
let r = CommandResult::success("user.list", "[]", "req-cmd");
r.inject_into(&mut tree);
let intent = ViewIntent::new("organ.mirror", "health", "admin", "req-view");
intent.inject_into(&mut tree);
assert_eq!(tree.get("event.type"), Some(&b"login_attempt".to_vec()));
assert_eq!(tree.get("command.type"), Some(&b"user.list".to_vec()));
assert_eq!(tree.get("result.status"), Some(&b"success".to_vec()));
assert_eq!(tree.get("view.id"), Some(&b"organ.mirror".to_vec()));
assert!(RingCommand::is_command(&tree));
assert!(CommandResult::is_result(&tree));
assert!(ViewIntent::is_view_intent(&tree));
let xcmd = RingCommand::extract_from(&tree).expect("cmd");
let xres = CommandResult::extract_from(&tree).expect("res");
let xview = ViewIntent::extract_from(&tree).expect("view");
assert_eq!(xcmd.command_type, "user.list");
assert_eq!(xres.command_type, "user.list");
assert_eq!(xview.view_id, "organ.mirror");
assert_eq!(xcmd.request_id, "req-cmd");
assert_eq!(xres.request_id, "req-cmd");
assert_eq!(xview.request_id, "req-view");
}
#[test]
fn view_intent_does_not_appear_as_command() {
let mut tree = Tree::new();
let intent = ViewIntent::new("organ.mirror", "health", "admin", "req-only-view");
intent.inject_into(&mut tree);
assert!(!RingCommand::is_command(&tree));
assert!(!CommandResult::is_result(&tree));
assert!(ViewIntent::is_view_intent(&tree));
assert!(RingCommand::extract_from(&tree).is_none());
assert!(CommandResult::extract_from(&tree).is_none());
assert!(ViewIntent::extract_from(&tree).is_some());
}
#[test]
fn view_prefix_constant_matches_key_layout() {
assert_eq!(VIEW_PREFIX, "view.");
let intent = ViewIntent::new("organ.mirror", "s", "r", "i");
let mut tree = Tree::new();
intent.inject_into(&mut tree);
for key in tree.keys() {
assert!(
key.starts_with(VIEW_PREFIX),
"expected prefix '{VIEW_PREFIX}' on key '{key}'"
);
}
assert_ne!(VIEW_PREFIX, COMMAND_PREFIX);
assert_ne!(VIEW_PREFIX, RESULT_PREFIX);
}
}