use serde_json::{Map, Value, json};
use super::publish_request;
use crate::pipeline::EventObject;
use crate::wire;
fn event(fields: Value) -> EventObject {
let Value::Object(mut event) = json!({
"id": "evt_000000000000000000000000000",
"sessionId": "ses_wire00000000000000000000000",
"projectId": "proj_wire",
"eventType": "mcp:tools/call",
"timestamp": "2026-10-23T11:59:59.250Z",
"duration": 750,
"resourceName": "echo",
}) else {
unreachable!("a JSON object");
};
let Value::Object(fields) = fields else {
panic!("fields must be an object");
};
event.extend(fields);
event
}
fn body(event: &EventObject) -> String {
serde_json::to_string(&publish_request(event)).unwrap()
}
#[test]
fn wire_values_follow_the_stringify_text() {
let value = json!({
"10": 1.0,
"2": -0.0,
"big": 9_007_199_254_740_993_u64,
"list": [1.0, {"10": "a", "2": "b"}],
});
let fields = [
"parameters",
"response",
"error",
"identifyActorData",
"properties",
];
let mut values = Map::new();
for field in fields {
values.insert(field.to_owned(), value.clone());
}
let sent = body(&event(Value::Object(values)));
let text = wire::stringify(&value);
assert_eq!(
text,
r#"{"2":0,"10":1,"big":9007199254740992,"list":[1,{"2":"b","10":"a"}]}"#
);
for key in [
"parameters",
"response",
"error",
"identify_data",
"properties",
] {
assert!(
sent.contains(&format!(r#""{key}":{text}"#)),
"{key}: {sent}"
);
}
let floats = json!({"small": 0.000_001, "large": 1e21});
assert_eq!(
wire::stringify(&floats),
r#"{"small":0.000001,"large":1e+21}"#
);
let properties = publish_request(&event(json!({"properties": floats})))
.properties
.unwrap();
assert_eq!(serde_json::to_string(&properties["small"]).unwrap(), "1e-6");
assert_eq!(properties, floats, "the same numbers");
}
#[test]
fn sessionless_event_sends_null_session() {
assert!(body(&event(json!({"sessionId": ""}))).contains(r#""session_id":null"#));
let mut no_key = event(json!({}));
no_key.shift_remove("sessionId");
assert!(body(&no_key).contains(r#""session_id":null"#));
assert!(body(&event(json!({}))).contains(r#""session_id":"ses_wire00000000000000000000000""#));
}
#[test]
fn actor_id_follows_identity() {
let request = publish_request(&event(json!({"identifyActorGivenId": "user-42"})));
assert_eq!(request.actor_id.as_deref(), Some("user-42"));
assert_eq!(request.identify_actor_given_id.as_deref(), Some("user-42"));
let anonymous = publish_request(&event(json!({})));
assert_eq!(anonymous.actor_id, None);
assert!(!body(&event(json!({}))).contains("actor_id"));
}
#[test]
fn non_string_user_intent_is_text() {
let intent = |value: Value| publish_request(&event(json!({"userIntent": value}))).user_intent;
assert_eq!(
intent(json!(["plan", "act"])).as_deref(),
Some(r#"["plan","act"]"#)
);
assert_eq!(intent(json!(42)).as_deref(), Some("42"));
assert_eq!(intent(json!({"a": 1.0})).as_deref(), Some(r#"{"a":1}"#));
assert_eq!(intent(json!("why")).as_deref(), Some("why"));
assert_eq!(intent(Value::Null), None);
}
#[test]
fn timestamp_keeps_milliseconds() {
assert!(
body(&event(json!({}))).contains(r#""timestamp":"2026-10-23T11:59:59.250Z""#),
"{}",
body(&event(json!({})))
);
let whole = event(json!({"timestamp": "2026-10-23T12:00:00.000Z"}));
assert!(
body(&whole).contains(r#""timestamp":"2026-10-23T12:00:00Z""#),
"{}",
body(&whole)
);
}