use apcore::context::{Context, Identity};
use std::collections::HashMap;
fn make_identity() -> Identity {
Identity::new(
"user-1".to_string(),
"user".to_string(),
vec!["admin".to_string()],
{
let mut attrs = HashMap::new();
attrs.insert("org".to_string(), serde_json::json!("acme"));
attrs
},
)
}
fn make_ctx() -> Context<()> {
let identity = make_identity();
Context {
trace_id: "trace-abc-123".to_string(),
identity: Some(identity),
services: (),
caller_id: Some("api.users.get".to_string()),
data: std::sync::Arc::new(parking_lot::RwLock::new(HashMap::new())),
call_chain: vec!["api.users.get".to_string()],
redacted_inputs: None,
cancel_token: None,
global_deadline: None,
executor: None,
}
}
#[test]
fn test_serialize_includes_context_version() {
let ctx = make_ctx();
let serialized = ctx.serialize();
assert_eq!(serialized["_context_version"], 1);
}
#[test]
fn test_serialize_includes_required_fields() {
let ctx = make_ctx();
let serialized = ctx.serialize();
let obj = serialized.as_object().unwrap();
assert!(obj.contains_key("trace_id"));
assert!(obj.contains_key("caller_id"));
assert!(obj.contains_key("call_chain"));
assert!(obj.contains_key("identity"));
assert!(obj.contains_key("data"));
}
#[test]
fn test_serialize_identity_structure() {
let ctx = make_ctx();
let serialized = ctx.serialize();
let identity = &serialized["identity"];
assert_eq!(identity["id"], "user-1");
assert_eq!(identity["type"], "user");
assert_eq!(identity["roles"], serde_json::json!(["admin"]));
assert_eq!(identity["attrs"]["org"], "acme");
}
#[test]
fn test_serialize_excludes_executor() {
let ctx = make_ctx();
let serialized = ctx.serialize();
let obj = serialized.as_object().unwrap();
assert!(!obj.contains_key("executor"));
}
#[test]
fn test_serialize_excludes_services() {
let ctx = make_ctx();
let serialized = ctx.serialize();
let obj = serialized.as_object().unwrap();
assert!(!obj.contains_key("services"));
}
#[test]
fn test_serialize_excludes_cancel_token() {
let ctx = make_ctx();
let serialized = ctx.serialize();
let obj = serialized.as_object().unwrap();
assert!(!obj.contains_key("cancel_token"));
}
#[test]
fn test_serialize_excludes_global_deadline() {
let ctx = make_ctx();
let serialized = ctx.serialize();
let obj = serialized.as_object().unwrap();
assert!(!obj.contains_key("global_deadline"));
}
#[test]
fn test_serialize_filters_underscore_data_keys() {
let ctx = make_ctx();
{
let mut data = ctx.data.write();
data.insert("_apcore.internal".to_string(), serde_json::json!("hidden"));
data.insert("_secret_key".to_string(), serde_json::json!("hidden"));
data.insert("public.counter".to_string(), serde_json::json!(42));
data.insert("app.name".to_string(), serde_json::json!("test"));
}
let serialized = ctx.serialize();
let data = serialized["data"].as_object().unwrap();
assert!(!data.contains_key("_apcore.internal"));
assert!(!data.contains_key("_secret_key"));
assert_eq!(data["public.counter"], 42);
assert_eq!(data["app.name"], "test");
}
#[test]
fn test_serialize_empty_data() {
let ctx = make_ctx();
{
let mut data = ctx.data.write();
data.insert("_private".to_string(), serde_json::json!("hidden"));
}
let serialized = ctx.serialize();
let data = serialized["data"].as_object().unwrap();
assert!(data.is_empty());
}
#[test]
fn test_deserialize_roundtrip() {
let ctx = make_ctx();
{
let mut data = ctx.data.write();
data.insert("app.counter".to_string(), serde_json::json!(42));
}
let serialized = ctx.serialize();
let restored: Context<()> = Context::deserialize(serialized).unwrap();
assert_eq!(restored.trace_id, ctx.trace_id);
assert_eq!(restored.caller_id, ctx.caller_id);
let data = restored.data.read();
assert_eq!(data.get("app.counter"), Some(&serde_json::json!(42)));
}
#[test]
fn test_deserialize_executor_is_none() {
let ctx = make_ctx();
let serialized = ctx.serialize();
let restored: Context<()> = Context::deserialize(serialized).unwrap();
assert!(restored.executor.is_none());
}
#[test]
fn test_deserialize_services_is_default() {
let ctx = make_ctx();
let serialized = ctx.serialize();
let restored: Context<()> = Context::deserialize(serialized).unwrap();
assert_eq!(restored.services, ());
}
#[test]
fn test_deserialize_cancel_token_is_none() {
let ctx = make_ctx();
let serialized = ctx.serialize();
let restored: Context<()> = Context::deserialize(serialized).unwrap();
assert!(restored.cancel_token.is_none());
}
#[test]
fn test_deserialize_global_deadline_is_none() {
let ctx = make_ctx();
let serialized = ctx.serialize();
let restored: Context<()> = Context::deserialize(serialized).unwrap();
assert!(restored.global_deadline.is_none());
}
#[test]
fn test_deserialize_future_version_does_not_crash() {
let data = serde_json::json!({
"_context_version": 99,
"trace_id": "abc-123",
"caller_id": "test",
"call_chain": [],
"data": {}
});
let restored: Context<()> = Context::deserialize(data).unwrap();
assert_eq!(restored.trace_id, "abc-123");
}
#[test]
fn test_deserialize_unknown_top_level_fields() {
let data = serde_json::json!({
"_context_version": 1,
"trace_id": "abc-123",
"caller_id": "test",
"call_chain": [],
"data": {"custom": "value"},
"future_field": "should_not_crash"
});
let restored: Context<()> = Context::deserialize(data).unwrap();
assert_eq!(restored.trace_id, "abc-123");
let data_map = restored.data.read();
assert_eq!(data_map.get("custom"), Some(&serde_json::json!("value")));
}