use super::*;
use serde_json::json;
fn make_request(id: i64, method: &str, params: Option<Value>) -> JsonRpcRequest {
JsonRpcRequest {
jsonrpc: "2.0".into(),
id: Some(RequestId::Number(id)),
method: method.into(),
params,
}
}
fn make_notification(method: &str) -> JsonRpcRequest {
JsonRpcRequest {
jsonrpc: "2.0".into(),
id: None,
method: method.into(),
params: None,
}
}
fn initialize_server(s: &mut Server) {
let req = make_request(
1,
"initialize",
Some(json!({
"protocolVersion": "2025-11-05",
"capabilities": {},
"clientInfo": { "name": "test", "version": "0.1" }
})),
);
let mut sink = Vec::<u8>::new();
s.handle(&req, &mut sink);
s.handle_notification(&make_notification("notifications/initialized"));
}
fn send(s: &mut Server, id: i64, method: &str, params: Option<Value>) -> Value {
let req = make_request(id, method, params);
let mut sink = Vec::<u8>::new();
let resp = s.handle(&req, &mut sink).unwrap();
serde_json::to_value(&resp).unwrap()
}
#[test]
fn server_starts_uninitialized() {
let s = Server::new();
assert_eq!(s.phase, Phase::Uninitialized);
}
#[test]
fn initialize_request_transitions_to_initializing() {
let mut s = Server::new();
let req = make_request(
1,
"initialize",
Some(json!({
"protocolVersion": "2025-11-05",
"capabilities": {},
"clientInfo": {"name": "test", "version": "1"}
})),
);
let resp = s.handle(&req, &mut Vec::<u8>::new()).unwrap();
assert_eq!(s.phase, Phase::Initializing);
let v: Value = serde_json::from_str(&serde_json::to_string(&resp).unwrap()).unwrap();
assert_eq!(v["result"]["serverInfo"]["name"], "axterminator");
}
#[test]
fn initialized_notification_transitions_to_running() {
let mut s = Server::new();
let req = make_request(
1,
"initialize",
Some(json!({
"protocolVersion": "2025-11-05",
"capabilities": {},
"clientInfo": {"name": "test", "version": "1"}
})),
);
s.handle(&req, &mut Vec::<u8>::new());
assert_eq!(s.phase, Phase::Initializing);
s.handle_notification(&make_notification("notifications/initialized"));
assert_eq!(s.phase, Phase::Running);
}
#[test]
fn ping_returns_empty_object() {
let mut s = Server::new();
initialize_server(&mut s);
let req = make_request(2, "ping", None);
let resp = s.handle(&req, &mut Vec::<u8>::new()).unwrap();
let v: Value = serde_json::to_value(&resp).unwrap();
assert_eq!(v["result"], json!({}));
}
#[test]
fn tools_list_returns_correct_count_for_feature_set() {
let mut s = Server::new();
initialize_server(&mut s);
let req = make_request(3, "tools/list", None);
let resp = s.handle(&req, &mut Vec::<u8>::new()).unwrap();
let v: Value = serde_json::to_value(&resp).unwrap();
let count = v["result"]["tools"].as_array().unwrap().len();
let base = 19usize;
let extra_spaces: usize = if cfg!(feature = "spaces") { 5 } else { 0 };
let extra_audio: usize = if cfg!(feature = "audio") { 3 } else { 0 };
let extra_camera: usize = if cfg!(feature = "camera") { 3 } else { 0 };
let extra_watch: usize = if cfg!(feature = "watch") { 3 } else { 0 };
assert_eq!(
count,
base + extra_spaces + extra_audio + extra_camera + extra_watch
);
}
#[test]
fn tools_list_before_initialized_returns_error() {
let mut s = Server::new();
let req = make_request(1, "tools/list", None);
let resp = s.handle(&req, &mut Vec::<u8>::new()).unwrap();
let v: Value = serde_json::to_value(&resp).unwrap();
assert!(v.get("error").is_some());
}
#[test]
fn unknown_method_returns_method_not_found() {
let mut s = Server::new();
initialize_server(&mut s);
let req = make_request(4, "sampling/createMessage", None);
let resp = s.handle(&req, &mut Vec::<u8>::new()).unwrap();
let v: Value = serde_json::to_value(&resp).unwrap();
assert_eq!(v["error"]["code"], RpcError::METHOD_NOT_FOUND);
}
#[test]
fn notification_returns_none() {
let mut s = Server::new();
initialize_server(&mut s);
let notif = make_notification("notifications/cancelled");
let resp = s.handle(¬if, &mut Vec::<u8>::new());
assert!(resp.is_none());
}
#[test]
fn tools_call_is_accessible_succeeds() {
let mut s = Server::new();
initialize_server(&mut s);
let req = make_request(
5,
"tools/call",
Some(json!({ "name": "ax_is_accessible", "arguments": {} })),
);
let resp = s.handle(&req, &mut Vec::<u8>::new()).unwrap();
let v: Value = serde_json::to_value(&resp).unwrap();
assert!(v["result"]["content"].is_array());
}
#[test]
fn invalid_initialize_params_returns_error() {
let mut s = Server::new();
let req = make_request(1, "initialize", Some(json!({"bad": "data"})));
let resp = s.handle(&req, &mut Vec::<u8>::new()).unwrap();
let v: Value = serde_json::to_value(&resp).unwrap();
assert!(v.get("error").is_some());
}
#[test]
fn initialize_response_advertises_resources_capability() {
let mut s = Server::new();
let req = make_request(
1,
"initialize",
Some(json!({
"protocolVersion": "2025-11-05",
"capabilities": {},
"clientInfo": {"name": "test", "version": "1"}
})),
);
let resp = s.handle(&req, &mut Vec::<u8>::new()).unwrap();
let v: Value = serde_json::to_value(&resp).unwrap();
assert!(v["result"]["capabilities"]["resources"].is_object());
assert_eq!(v["result"]["capabilities"]["resources"]["subscribe"], false);
}
#[test]
fn initialize_response_advertises_prompts_capability() {
let mut s = Server::new();
let req = make_request(
1,
"initialize",
Some(json!({
"protocolVersion": "2025-11-05",
"capabilities": {},
"clientInfo": {"name": "test", "version": "1"}
})),
);
let resp = s.handle(&req, &mut Vec::<u8>::new()).unwrap();
let v: Value = serde_json::to_value(&resp).unwrap();
assert!(v["result"]["capabilities"]["prompts"].is_object());
}
#[test]
fn initialize_response_advertises_elicitation_capability() {
let mut s = Server::new();
let req = make_request(
1,
"initialize",
Some(json!({
"protocolVersion": "2025-11-05",
"capabilities": {},
"clientInfo": {"name": "test", "version": "1"}
})),
);
let resp = s.handle(&req, &mut Vec::<u8>::new()).unwrap();
let v: Value = serde_json::to_value(&resp).unwrap();
assert!(v["result"]["capabilities"]["elicitation"].is_object());
}
#[test]
fn server_handle_ping_returns_empty_object() {
let mut h = ServerHandle::new();
let init = make_request(
1,
"initialize",
Some(json!({
"protocolVersion": "2025-11-05",
"capabilities": {},
"clientInfo": {"name": "test", "version": "1"}
})),
);
h.handle(&init, &mut Vec::<u8>::new());
h.handle(
&make_notification("notifications/initialized"),
&mut Vec::<u8>::new(),
);
let req = make_request(2, "ping", None);
let resp = h.handle(&req, &mut Vec::<u8>::new()).unwrap();
let v: Value = serde_json::to_value(&resp).unwrap();
assert_eq!(v["result"], json!({}));
}
#[test]
fn server_handle_default_creates_uninitialized_instance() {
let mut h = ServerHandle::default();
let req = make_request(1, "tools/list", None);
let resp = h.handle(&req, &mut Vec::<u8>::new()).unwrap();
let v: Value = serde_json::to_value(&resp).unwrap();
assert!(v.get("error").is_some());
}
#[test]
fn resources_list_returns_static_resources() {
let mut s = Server::new();
initialize_server(&mut s);
let req = make_request(10, "resources/list", None);
let resp = s.handle(&req, &mut Vec::<u8>::new()).unwrap();
let v: Value = serde_json::to_value(&resp).unwrap();
let resources = v["result"]["resources"].as_array().unwrap();
assert!(!resources.is_empty());
let has_status = resources
.iter()
.any(|r| r["uri"] == "axterminator://system/status");
assert!(has_status);
}
#[test]
fn resources_list_before_initialized_returns_error() {
let mut s = Server::new();
let req = make_request(10, "resources/list", None);
let resp = s.handle(&req, &mut Vec::<u8>::new()).unwrap();
let v: Value = serde_json::to_value(&resp).unwrap();
assert!(v.get("error").is_some());
}
#[test]
fn resources_templates_list_returns_templates() {
let mut s = Server::new();
initialize_server(&mut s);
let req = make_request(11, "resources/templates/list", None);
let resp = s.handle(&req, &mut Vec::<u8>::new()).unwrap();
let v: Value = serde_json::to_value(&resp).unwrap();
let templates = v["result"]["resourceTemplates"].as_array().unwrap();
assert!(!templates.is_empty());
let has_tree = templates
.iter()
.any(|t| t["uriTemplate"] == "axterminator://app/{name}/tree");
assert!(has_tree);
}
#[test]
fn resources_read_system_status_returns_contents() {
let mut s = Server::new();
initialize_server(&mut s);
let req = make_request(
12,
"resources/read",
Some(json!({ "uri": "axterminator://system/status" })),
);
let resp = s.handle(&req, &mut Vec::<u8>::new()).unwrap();
let v: Value = serde_json::to_value(&resp).unwrap();
let contents = v["result"]["contents"].as_array().unwrap();
assert_eq!(contents.len(), 1);
assert!(contents[0]["text"].as_str().is_some());
}
#[test]
fn resources_read_missing_params_returns_error() {
let mut s = Server::new();
initialize_server(&mut s);
let req = make_request(13, "resources/read", None);
let resp = s.handle(&req, &mut Vec::<u8>::new()).unwrap();
let v: Value = serde_json::to_value(&resp).unwrap();
assert!(v.get("error").is_some());
}
#[test]
fn resources_read_unconnected_app_returns_error() {
let mut s = Server::new();
initialize_server(&mut s);
let req = make_request(
14,
"resources/read",
Some(json!({ "uri": "axterminator://app/NotConnected/tree" })),
);
let resp = s.handle(&req, &mut Vec::<u8>::new()).unwrap();
let v: Value = serde_json::to_value(&resp).unwrap();
assert!(v.get("error").is_some());
}
#[test]
fn prompts_list_returns_four_prompts() {
let mut s = Server::new();
initialize_server(&mut s);
let req = make_request(20, "prompts/list", None);
let resp = s.handle(&req, &mut Vec::<u8>::new()).unwrap();
let v: Value = serde_json::to_value(&resp).unwrap();
let prompts = v["result"]["prompts"].as_array().unwrap();
assert_eq!(prompts.len(), 4);
}
#[test]
fn prompts_list_before_initialized_returns_error() {
let mut s = Server::new();
let req = make_request(20, "prompts/list", None);
let resp = s.handle(&req, &mut Vec::<u8>::new()).unwrap();
let v: Value = serde_json::to_value(&resp).unwrap();
assert!(v.get("error").is_some());
}
#[test]
fn prompts_get_test_app_returns_messages() {
let mut s = Server::new();
initialize_server(&mut s);
let req = make_request(
21,
"prompts/get",
Some(json!({
"name": "test-app",
"arguments": { "app_name": "Safari" }
})),
);
let resp = s.handle(&req, &mut Vec::<u8>::new()).unwrap();
let v: Value = serde_json::to_value(&resp).unwrap();
let msgs = v["result"]["messages"].as_array().unwrap();
assert_eq!(msgs.len(), 2);
assert_eq!(msgs[0]["role"], "user");
assert_eq!(msgs[1]["role"], "assistant");
}
#[test]
fn prompts_get_unknown_prompt_returns_error() {
let mut s = Server::new();
initialize_server(&mut s);
let req = make_request(
22,
"prompts/get",
Some(json!({ "name": "nonexistent-prompt" })),
);
let resp = s.handle(&req, &mut Vec::<u8>::new()).unwrap();
let v: Value = serde_json::to_value(&resp).unwrap();
assert!(v.get("error").is_some());
}
#[test]
fn prompts_get_missing_required_arg_returns_error() {
let mut s = Server::new();
initialize_server(&mut s);
let req = make_request(
23,
"prompts/get",
Some(json!({
"name": "navigate-to",
"arguments": { "app_name": "Finder" }
})),
);
let resp = s.handle(&req, &mut Vec::<u8>::new()).unwrap();
let v: Value = serde_json::to_value(&resp).unwrap();
assert!(v.get("error").is_some());
}
#[test]
fn prompts_get_missing_params_returns_error() {
let mut s = Server::new();
initialize_server(&mut s);
let req = make_request(24, "prompts/get", None);
let resp = s.handle(&req, &mut Vec::<u8>::new()).unwrap();
let v: Value = serde_json::to_value(&resp).unwrap();
assert!(v.get("error").is_some());
}