// jsonrpc_test.go -- JSON-RPC 2.0 类型的单元测试. // // 仅测试数据类型(序列化/反序列化/Error 方法). // 传输层测试见 transport_test.go;客户端协议测试见 client_test.go. package mcp import ( "encoding/json" "testing" ) // TestRPCError_Error 测试 RPCError 的 Error() 方法 func TestRPCError_Error(t *testing.T) { // 无 data err := &RPCError{Code: -32600, Message: "Invalid Request"} s := err.Error() if s != "JSON-RPC error -32600: Invalid Request" { t.Errorf("错误字符串不匹配: %q", s) } // 有 data err = &RPCError{Code: -32601, Message: "Method not found", Data: json.RawMessage(`"extra info"`)} s = err.Error() if s != `JSON-RPC error -32601: Method not found (data: "extra info")` { t.Errorf("错误字符串不匹配: %q", s) } } // TestRequest_Marshal 测试请求序列化 func TestRequest_Marshal(t *testing.T) { req := Request{ JSONRPC: "2.0", ID: json.RawMessage(`1`), Method: "tools/list", Params: map[string]string{"cursor": "abc"}, } data, err := json.Marshal(req) if err != nil { t.Fatalf("序列化失败: %v", err) } var parsed map[string]any json.Unmarshal(data, &parsed) if parsed["jsonrpc"] != "2.0" { t.Errorf("jsonrpc: %v", parsed["jsonrpc"]) } if parsed["method"] != "tools/list" { t.Errorf("method: %v", parsed["method"]) } } // TestResponse_Marshal 测试响应序列化 func TestResponse_Marshal(t *testing.T) { resp := Response{ JSONRPC: "2.0", ID: json.RawMessage(`1`), Result: json.RawMessage(`{"tools":[]}`), } data, err := json.Marshal(resp) if err != nil { t.Fatalf("序列化失败: %v", err) } var parsed map[string]any json.Unmarshal(data, &parsed) if parsed["jsonrpc"] != "2.0" { t.Errorf("jsonrpc: %v", parsed["jsonrpc"]) } } // TestResponse_WithError 测试带错误的响应 func TestResponse_WithError(t *testing.T) { resp := Response{ JSONRPC: "2.0", ID: json.RawMessage(`1`), Error: &RPCError{Code: -32600, Message: "Invalid Request"}, } data, err := json.Marshal(resp) if err != nil { t.Fatalf("序列化失败: %v", err) } var parsed Response json.Unmarshal(data, &parsed) if parsed.Error == nil { t.Fatal("error 不应为 nil") } if parsed.Error.Code != -32600 { t.Errorf("error code: %d", parsed.Error.Code) } } // TestNotification_Marshal 测试通知序列化 func TestNotification_Marshal(t *testing.T) { notif := Notification{ JSONRPC: "2.0", Method: "notifications/initialized", } data, err := json.Marshal(notif) if err != nil { t.Fatalf("序列化失败: %v", err) } var parsed map[string]any json.Unmarshal(data, &parsed) if parsed["method"] != "notifications/initialized" { t.Errorf("method: %v", parsed["method"]) } } // TestRawMessage_Unmarshal 测试原始消息解析(响应 vs 通知区分) func TestRawMessage_Unmarshal(t *testing.T) { // 响应类型(有 id) respJSON := `{"jsonrpc":"2.0","id":1,"result":{"tools":[]}}` var msg rawMessage if err := json.Unmarshal([]byte(respJSON), &msg); err != nil { t.Fatalf("解析失败: %v", err) } if msg.ID == nil { t.Error("响应的 ID 不应为 nil") } // 通知类型(无 id,有 method) notifJSON := `{"jsonrpc":"2.0","method":"notifications/progress"}` var msg2 rawMessage if err := json.Unmarshal([]byte(notifJSON), &msg2); err != nil { t.Fatalf("解析失败: %v", err) } if msg2.ID != nil { t.Error("通知的 ID 应为 nil") } if msg2.Method != "notifications/progress" { t.Errorf("method: %q", msg2.Method) } }