use super::*;
#[test]
fn message_type_roundtrip() {
assert_eq!(
SkillMessageType::from_u32(1),
Some(SkillMessageType::Execute)
);
assert_eq!(SkillMessageType::from_u32(128), Some(SkillMessageType::Ack));
assert_eq!(SkillMessageType::from_u32(999), None);
}
#[test]
fn execute_payload_serde() {
let payload = ExecutePayload {
execution_id: "exec-1".into(),
command_name: "test:exec".into(),
command: Some("echo hello".into()),
args: None,
working_directory: None,
environment: None,
timeout_ms: 30000,
};
let json = serde_json::to_string(&payload).unwrap();
let parsed: ExecutePayload = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.execution_id, "exec-1");
assert_eq!(parsed.command_name, "test:exec");
assert_eq!(parsed.command.as_deref(), Some("echo hello"));
}
#[test]
fn completed_payload_serde() {
let payload = CompletedPayload {
id: "exec-1".into(),
exit_code: 0,
status: "success".into(),
error: None,
finished_at_unix: 1700000000,
};
let json = serde_json::to_string(&payload).unwrap();
let parsed: CompletedPayload = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.exit_code, 0);
assert_eq!(parsed.status, "success");
}
#[test]
fn error_payload_serde() {
let payload = ErrorPayload {
id: Some("exec-err".into()),
code: "SKILL_EXEC_FAILED".into(),
message: "process killed".into(),
};
let json = serde_json::to_string(&payload).unwrap();
let parsed: ErrorPayload = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.code, "SKILL_EXEC_FAILED");
assert_eq!(parsed.id.as_deref(), Some("exec-err"));
}
#[test]
fn session_started_payload_serde() {
let payload = SessionStartedPayload {
id: "sess-1".into(),
status: "active".into(),
};
let json = serde_json::to_string(&payload).unwrap();
let parsed: SessionStartedPayload = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.id, "sess-1");
assert_eq!(parsed.status, "active");
}
#[test]
fn stdout_payload_serde_with_bytes() {
let payload = DataChunkPayload {
id: "exec-1".into(),
seq: 1,
data: b"hello world".to_vec(),
};
let json = serde_json::to_string(&payload).unwrap();
let parsed: DataChunkPayload = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.data, b"hello world");
assert_eq!(parsed.seq, 1);
}
#[test]
fn all_message_types_have_values() {
assert_eq!(
SkillMessageType::from_u32(1),
Some(SkillMessageType::Execute)
);
assert_eq!(
SkillMessageType::from_u32(2),
Some(SkillMessageType::Cancel)
);
assert_eq!(
SkillMessageType::from_u32(3),
Some(SkillMessageType::StdinData)
);
assert_eq!(
SkillMessageType::from_u32(4),
Some(SkillMessageType::Resize)
);
assert_eq!(
SkillMessageType::from_u32(5),
Some(SkillMessageType::Signal)
);
assert_eq!(
SkillMessageType::from_u32(6),
Some(SkillMessageType::StartSession)
);
assert_eq!(
SkillMessageType::from_u32(7),
Some(SkillMessageType::Shutdown)
);
assert_eq!(SkillMessageType::from_u32(128), Some(SkillMessageType::Ack));
assert_eq!(
SkillMessageType::from_u32(129),
Some(SkillMessageType::StdoutChunk)
);
assert_eq!(
SkillMessageType::from_u32(130),
Some(SkillMessageType::StderrChunk)
);
assert_eq!(
SkillMessageType::from_u32(131),
Some(SkillMessageType::Progress)
);
assert_eq!(
SkillMessageType::from_u32(132),
Some(SkillMessageType::Completed)
);
assert_eq!(
SkillMessageType::from_u32(133),
Some(SkillMessageType::Error)
);
assert_eq!(
SkillMessageType::from_u32(134),
Some(SkillMessageType::SessionStarted)
);
assert_eq!(
SkillMessageType::from_u32(200),
Some(SkillMessageType::ProxySubmit)
);
assert_eq!(
SkillMessageType::from_u32(201),
Some(SkillMessageType::ProxyCancel)
);
assert_eq!(
SkillMessageType::from_u32(202),
Some(SkillMessageType::ProxyStdoutChunk)
);
assert_eq!(
SkillMessageType::from_u32(203),
Some(SkillMessageType::ProxyStderrChunk)
);
assert_eq!(
SkillMessageType::from_u32(204),
Some(SkillMessageType::ProxyCompleted)
);
assert_eq!(
SkillMessageType::from_u32(205),
Some(SkillMessageType::ProxyRejected)
);
assert_eq!(SkillMessageType::from_u32(999), None);
}
#[test]
fn proxy_message_types_map_correctly() {
assert_eq!(
SkillMessageType::from_u32(200),
Some(SkillMessageType::ProxySubmit)
);
assert_eq!(
SkillMessageType::from_u32(201),
Some(SkillMessageType::ProxyCancel)
);
assert_eq!(
SkillMessageType::from_u32(202),
Some(SkillMessageType::ProxyStdoutChunk)
);
assert_eq!(
SkillMessageType::from_u32(203),
Some(SkillMessageType::ProxyStderrChunk)
);
assert_eq!(
SkillMessageType::from_u32(204),
Some(SkillMessageType::ProxyCompleted)
);
assert_eq!(
SkillMessageType::from_u32(205),
Some(SkillMessageType::ProxyRejected)
);
}
#[test]
fn proxy_message_type_boundaries_are_none() {
assert_eq!(SkillMessageType::from_u32(199), None);
assert_eq!(SkillMessageType::from_u32(206), None);
}
#[test]
fn proxy_submit_payload_serde_pascal_case() {
let payload = ProxySubmitPayload {
proxy_id: "proxy-1".into(),
external_id: "ext-1".into(),
command_name: "test:exec".into(),
args: vec!["--flag".into(), "value".into()],
target: ProxyTarget {
kind: "local".into(),
agent_id: None,
},
};
let json = serde_json::to_string(&payload).unwrap();
assert!(json.contains("\"ProxyId\""), "json: {json}");
assert!(json.contains("\"ExternalId\""), "json: {json}");
assert!(json.contains("\"CommandName\""), "json: {json}");
assert!(json.contains("\"Args\""), "json: {json}");
assert!(json.contains("\"Target\""), "json: {json}");
let parsed: ProxySubmitPayload = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.proxy_id, "proxy-1");
assert_eq!(parsed.external_id, "ext-1");
assert_eq!(parsed.command_name, "test:exec");
assert_eq!(parsed.args, vec!["--flag", "value"]);
assert_eq!(parsed.target.kind, "local");
assert_eq!(parsed.target.agent_id, None);
}
#[test]
fn proxy_target_local_omits_agent_id() {
let target = ProxyTarget {
kind: "local".into(),
agent_id: None,
};
let json = serde_json::to_string(&target).unwrap();
assert!(json.contains("\"Kind\""), "json: {json}");
assert!(!json.contains("AgentId"), "json: {json}");
let parsed: ProxyTarget = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.kind, "local");
assert_eq!(parsed.agent_id, None);
}
#[test]
fn proxy_target_agent_includes_agent_id() {
let target = ProxyTarget {
kind: "agent".into(),
agent_id: Some("agent-42".into()),
};
let json = serde_json::to_string(&target).unwrap();
assert!(json.contains("\"Kind\""), "json: {json}");
assert!(json.contains("\"AgentId\""), "json: {json}");
let parsed: ProxyTarget = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.kind, "agent");
assert_eq!(parsed.agent_id.as_deref(), Some("agent-42"));
}
#[test]
fn proxy_rejected_payload_serde() {
let payload = ProxyRejectedPayload {
proxy_id: "proxy-1".into(),
reason_code: "DENIED".into(),
message: "not allowed".into(),
};
let json = serde_json::to_string(&payload).unwrap();
assert!(json.contains("\"ProxyId\""), "json: {json}");
assert!(json.contains("\"ReasonCode\""), "json: {json}");
assert!(json.contains("\"Message\""), "json: {json}");
let parsed: ProxyRejectedPayload = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.proxy_id, "proxy-1");
assert_eq!(parsed.reason_code, "DENIED");
assert_eq!(parsed.message, "not allowed");
}
#[test]
fn proxy_chunk_payload_serde_pascal_case_and_base64() {
let payload = ProxyChunkPayload {
proxy_id: "proxy-1".into(),
seq: 7,
data: b"hello world".to_vec(),
};
let json = serde_json::to_string(&payload).unwrap();
assert!(json.contains("\"ProxyId\""), "json: {json}");
assert!(json.contains("\"Seq\""), "json: {json}");
assert!(json.contains("\"Data\""), "json: {json}");
assert!(json.contains("\"aGVsbG8gd29ybGQ=\""), "json: {json}");
let parsed: ProxyChunkPayload = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.proxy_id, "proxy-1");
assert_eq!(parsed.seq, 7);
assert_eq!(parsed.data, b"hello world");
}
#[test]
fn proxy_completed_payload_serde_pascal_case() {
let payload = ProxyCompletedPayload {
proxy_id: "proxy-1".into(),
exit_code: 0,
status: "success".into(),
error: None,
finished_at_unix: 1700000000,
};
let json = serde_json::to_string(&payload).unwrap();
assert!(json.contains("\"ProxyId\""), "json: {json}");
assert!(json.contains("\"ExitCode\""), "json: {json}");
assert!(json.contains("\"Status\""), "json: {json}");
assert!(json.contains("\"FinishedAtUnix\""), "json: {json}");
assert!(!json.contains("Error"), "json: {json}");
let parsed: ProxyCompletedPayload = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.proxy_id, "proxy-1");
assert_eq!(parsed.exit_code, 0);
assert_eq!(parsed.status, "success");
assert_eq!(parsed.error, None);
assert_eq!(parsed.finished_at_unix, 1700000000);
}
#[test]
fn proxy_completed_payload_includes_error_when_some() {
let payload = ProxyCompletedPayload {
proxy_id: "proxy-1".into(),
exit_code: 1,
status: "failed".into(),
error: Some("boom".into()),
finished_at_unix: 1700000001,
};
let json = serde_json::to_string(&payload).unwrap();
assert!(json.contains("\"Error\""), "json: {json}");
let parsed: ProxyCompletedPayload = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.error.as_deref(), Some("boom"));
}