clync 0.4.1

Encrypted sync for Claude Code across machines
use super::*;

#[test]
fn truncate_str_short() {
    assert_eq!(truncate_str("hello", 10), "hello");
}

#[test]
fn truncate_str_exact() {
    assert_eq!(truncate_str("hello", 5), "hello");
}

#[test]
fn truncate_str_long() {
    let result = truncate_str("hello world", 8);
    assert!(result.ends_with("..."));
    assert!(result.len() <= 8);
}

#[test]
fn truncate_str_multibyte() {
    let s = "hélló wörld";
    let result = truncate_str(s, 8);
    assert!(result.ends_with("..."));
    assert!(result.is_char_boundary(0));
}

#[test]
fn handle_initialize() {
    let req = JsonRpcRequest {
        jsonrpc: "2.0".into(),
        id: Some(json!(1)),
        method: "initialize".into(),
        params: json!({}),
    };
    let resp = handle_request(&req);
    let result = resp.result.unwrap();
    assert_eq!(result["capabilities"]["tools"], json!({}));
    assert_eq!(result["serverInfo"]["name"], "clync");
    assert!(resp.error.is_none());
}

#[test]
fn handle_tools_list() {
    let req = JsonRpcRequest {
        jsonrpc: "2.0".into(),
        id: Some(json!(2)),
        method: "tools/list".into(),
        params: json!({}),
    };
    let resp = handle_request(&req);
    let tools = resp.result.unwrap()["tools"].as_array().unwrap().clone();
    assert!(!tools.is_empty());
    let names: Vec<&str> = tools.iter().filter_map(|t| t["name"].as_str()).collect();
    assert!(names.contains(&"list_sessions"));
    assert!(names.contains(&"sync_push"));
    assert!(names.contains(&"sync_pull"));
    assert!(names.contains(&"help"));
}

#[test]
fn handle_unknown_method() {
    let req = JsonRpcRequest {
        jsonrpc: "2.0".into(),
        id: Some(json!(3)),
        method: "nonexistent".into(),
        params: json!({}),
    };
    let resp = handle_request(&req);
    assert!(resp.result.is_none());
    assert!(resp.error.is_some());
    assert_eq!(resp.error.unwrap()["code"], -32601);
}

#[test]
fn handle_unknown_tool() {
    let req = JsonRpcRequest {
        jsonrpc: "2.0".into(),
        id: Some(json!(4)),
        method: "tools/call".into(),
        params: json!({"name": "nonexistent_tool", "arguments": {}}),
    };
    let resp = handle_request(&req);
    let result = resp.result.unwrap();
    assert_eq!(result["isError"], true);
    let text = result["content"][0]["text"].as_str().unwrap();
    assert!(text.contains("unknown tool"));
}

#[test]
fn handle_help_tool() {
    let req = JsonRpcRequest {
        jsonrpc: "2.0".into(),
        id: Some(json!(5)),
        method: "tools/call".into(),
        params: json!({"name": "help", "arguments": {"topic": "all"}}),
    };
    let resp = handle_request(&req);
    let result = resp.result.unwrap();
    let text = result["content"][0]["text"].as_str().unwrap();
    assert!(text.contains("clync"));
}

#[test]
fn handle_null_id() {
    let req = JsonRpcRequest {
        jsonrpc: "2.0".into(),
        id: None,
        method: "initialize".into(),
        params: json!({}),
    };
    let resp = handle_request(&req);
    assert_eq!(resp.id, Value::Null);
    assert!(resp.result.is_some());
}

#[test]
fn tool_definitions_have_required_fields() {
    let tools = tool_definitions();
    let arr = tools.as_array().unwrap();
    for tool in arr {
        assert!(tool.get("name").is_some(), "tool missing name");
        assert!(
            tool.get("description").is_some(),
            "tool missing description"
        );
        assert!(
            tool.get("inputSchema").is_some(),
            "tool missing inputSchema"
        );
    }
}