package a2a import ( "context" "encoding/json" "net/http" "net/http/httptest" "testing" "github.com/nomadcode/nomadcode-core/internal/agent" ) func TestSendMessagePostsJSONRPCMessageSend(t *testing.T) { var gotAuth string var gotReq jsonRPCRequest var gotParams map[string]any server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { gotAuth = r.Header.Get("Authorization") if err := json.NewDecoder(r.Body).Decode(&gotReq); err != nil { t.Fatalf("decode request: %v", err) } paramsBytes, _ := json.Marshal(gotReq.Params) if err := json.Unmarshal(paramsBytes, &gotParams); err != nil { t.Fatalf("decode params: %v", err) } _, _ = w.Write([]byte(`{ "jsonrpc": "2.0", "id": "nomadcode-1", "result": { "kind": "task", "id": "task-1", "contextId": "ctx-1", "status": {"state": "working"} } }`)) })) defer server.Close() client := NewClient(Config{URL: server.URL, Token: "secret"}, nil) result, err := client.SendMessage(context.Background(), agent.SendMessageInput{ Text: "fix issue", ContextID: "ctx-1", AcceptedOutputModes: []string{"text/plain"}, Blocking: true, Metadata: map[string]any{ "nomad_task_id": "task-local", }, }) if err != nil { t.Fatalf("SendMessage returned error: %v", err) } if gotAuth != "Bearer secret" { t.Fatalf("unexpected auth header: %q", gotAuth) } if gotReq.JSONRPC != "2.0" || gotReq.Method != "message/send" || gotReq.ID != "nomadcode-1" { t.Fatalf("unexpected json-rpc request: %#v", gotReq) } message := gotParams["message"].(map[string]any) if message["role"] != "user" || message["kind"] != "message" || message["contextId"] != "ctx-1" { t.Fatalf("unexpected message: %#v", message) } parts := message["parts"].([]any) part := parts[0].(map[string]any) if part["kind"] != "text" || part["text"] != "fix issue" { t.Fatalf("unexpected part: %#v", part) } config := gotParams["configuration"].(map[string]any) if config["blocking"] != true { t.Fatalf("expected blocking=true, got %#v", config["blocking"]) } metadata := gotParams["metadata"].(map[string]any) if metadata["nomad_task_id"] != "task-local" { t.Fatalf("unexpected metadata: %#v", metadata) } if result.Task == nil || result.Task.ID != "task-1" || result.Task.Status.State != agent.TaskStateWorking { t.Fatalf("unexpected result: %#v", result) } } func TestSendMessageReturnsDirectMessage(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { _, _ = w.Write([]byte(`{ "jsonrpc": "2.0", "id": "nomadcode-1", "result": { "kind": "message", "role": "agent", "messageId": "msg-2", "parts": [{"kind": "text", "text": "done"}] } }`)) })) defer server.Close() client := NewClient(Config{URL: server.URL}, nil) result, err := client.SendMessage(context.Background(), agent.SendMessageInput{Text: "status"}) if err != nil { t.Fatalf("SendMessage returned error: %v", err) } if result.Message == nil || result.Message.Role != "agent" || result.Message.Parts[0].Text != "done" { t.Fatalf("unexpected result: %#v", result) } } func TestGetAndCancelTask(t *testing.T) { var methods []string server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var req jsonRPCRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { t.Fatalf("decode request: %v", err) } methods = append(methods, req.Method) _, _ = w.Write([]byte(`{ "jsonrpc": "2.0", "id": "` + req.ID + `", "result": { "kind": "task", "id": "task-1", "status": {"state": "completed"} } }`)) })) defer server.Close() client := NewClient(Config{URL: server.URL}, nil) task, err := client.GetTask(context.Background(), agent.GetTaskInput{TaskID: "task-1"}) if err != nil { t.Fatalf("GetTask returned error: %v", err) } if task.Status.State != agent.TaskStateCompleted { t.Fatalf("unexpected get task: %#v", task) } task, err = client.CancelTask(context.Background(), agent.CancelTaskInput{TaskID: "task-1"}) if err != nil { t.Fatalf("CancelTask returned error: %v", err) } if task.ID != "task-1" { t.Fatalf("unexpected cancel task: %#v", task) } if len(methods) != 2 || methods[0] != "tasks/get" || methods[1] != "tasks/cancel" { t.Fatalf("unexpected methods: %#v", methods) } } func TestJSONRPCError(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { _, _ = w.Write([]byte(`{ "jsonrpc": "2.0", "id": "nomadcode-1", "error": {"code": -32602, "message": "invalid params"} }`)) })) defer server.Close() client := NewClient(Config{URL: server.URL}, nil) if _, err := client.SendMessage(context.Background(), agent.SendMessageInput{Text: "hello"}); err == nil { t.Fatal("expected JSON-RPC error") } }