项目文件夹

文件
Asim Aslam 4311b73361 Enhance ADK vs Go Micro comparison and apply lint fixes (#2994)
* docs: compare Go Micro with Google ADK in the comparison guide

Adds a 'vs Agent Frameworks (Google ADK)' section: ADK builds an agent,
Go Micro builds the distributed system the agent lives in (agents are
services in the mesh). Covers the category difference, a feature table,
when to choose each, and MCP/A2A interoperability.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CmdEY7pYmV5zzwCjNJ4ykL

* docs: replace ADK comparison slogan with concrete explanation

State plainly what each tool provides (ADK builds an agent process; Go Micro
builds the surrounding service mesh) instead of marketing phrasing.

* lint: apply golangci-lint autofixes; exclude ST1003 and demo errcheck

Mechanical, behaviour-preserving fixes applied by 'golangci-lint run --fix':
gofmt, misspell (US spelling), usestdlibvars (http.Method*/Status*), unconvert,
and the auto-fixable staticcheck simplifications (QF*, S1017/S1019/S1023/S1039).

Config: exclude ST1003 (remaining offenders are exported API renames, e.g.
web.Id, which would break compatibility) and skip errcheck for examples/ and
internal/harness/ (demo code where fire-and-forget is intentional).

Build and test compilation verified.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CmdEY7pYmV5zzwCjNJ4ykL

* lint: WIP cleanup checkpoint (errcheck config + partial fixes)

Checkpoint of an in-progress golangci-lint cleanup (background pass). Builds
cleanly; lint is not yet zero. Follow-up commit will complete the cleanup and
switch CI to a blocking full-tree lint.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CmdEY7pYmV5zzwCjNJ4ykL

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-22 17:21:47 +01:00

167 行
4.5 KiB
Go

package a2a
import (
"bytes"
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"time"
pb "go-micro.dev/v6/agent/proto"
"go-micro.dev/v6/client"
"go-micro.dev/v6/registry"
"go-micro.dev/v6/selector"
"go-micro.dev/v6/server"
)
// echoAgent is a stub that implements the Agent proto handler — enough to
// exercise the gateway's task→Agent.Chat translation without pulling in
// the agent package (which would import this one, a test-only cycle).
type echoAgent struct{}
func (echoAgent) Chat(_ context.Context, req *pb.ChatRequest, rsp *pb.ChatResponse) error {
rsp.Reply = "pong"
rsp.Agent = "echo"
return nil
}
func waitFor(reg registry.Registry, name string) {
deadline := time.Now().Add(5 * time.Second)
for time.Now().Before(deadline) {
if svcs, err := reg.GetService(name); err == nil && len(svcs) > 0 && len(svcs[0].Nodes) > 0 {
return
}
time.Sleep(20 * time.Millisecond)
}
}
func newGatewayWithAgent(t *testing.T) (*httptest.Server, func()) {
t.Helper()
reg := registry.NewMemoryRegistry()
cl := client.NewClient(client.Registry(reg), client.Selector(selector.NewSelector(selector.Registry(reg))))
srv := server.NewServer(
server.Name("echo"),
server.Registry(reg),
server.Metadata(map[string]string{"type": "agent", "services": ""}),
)
if err := pb.RegisterAgentHandler(srv, echoAgent{}); err != nil {
t.Fatalf("register agent handler: %v", err)
}
if err := srv.Start(); err != nil {
t.Fatalf("start server: %v", err)
}
waitFor(reg, "echo")
g := New(Options{Registry: reg, Client: cl, BaseURL: "http://gw"})
ts := httptest.NewServer(g.Handler())
return ts, func() { ts.Close(); srv.Stop() }
}
func TestAgentCardFromRegistry(t *testing.T) {
ts, cleanup := newGatewayWithAgent(t)
defer cleanup()
resp, err := http.Get(ts.URL + "/agents/echo/.well-known/agent.json")
if err != nil {
t.Fatalf("get card: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
t.Fatalf("card status = %d", resp.StatusCode)
}
var card AgentCard
if err := json.NewDecoder(resp.Body).Decode(&card); err != nil {
t.Fatalf("decode card: %v", err)
}
if card.Name != "echo" {
t.Errorf("card name = %q, want echo", card.Name)
}
if card.URL != "http://gw/agents/echo" {
t.Errorf("card url = %q", card.URL)
}
if card.ProtocolVersion == "" || len(card.Skills) == 0 {
t.Errorf("card missing protocolVersion or skills: %+v", card)
}
}
func TestMessageSendAndGet(t *testing.T) {
ts, cleanup := newGatewayWithAgent(t)
defer cleanup()
task := rpcTask(t, ts.URL+"/agents/echo", `{
"jsonrpc":"2.0","id":1,"method":"message/send",
"params":{"message":{"role":"user","kind":"message","messageId":"m1",
"parts":[{"kind":"text","text":"ping"}]}}}`)
if task.Status.State != stateCompleted {
t.Fatalf("task state = %q, want completed", task.Status.State)
}
if len(task.Artifacts) != 1 || textOf(task.Artifacts[0].Parts) != "pong" {
t.Fatalf("artifact = %+v, want text 'pong'", task.Artifacts)
}
got := rpcTask(t, ts.URL+"/agents/echo", `{
"jsonrpc":"2.0","id":2,"method":"tasks/get","params":{"id":"`+task.ID+`"}}`)
if got.ID != task.ID || got.Status.State != stateCompleted {
t.Errorf("tasks/get returned %+v", got)
}
}
func TestUnknownMethod(t *testing.T) {
ts, cleanup := newGatewayWithAgent(t)
defer cleanup()
var resp struct {
Error *rpcError `json:"error"`
}
rpc(t, ts.URL+"/agents/echo", `{"jsonrpc":"2.0","id":1,"method":"message/stream","params":{}}`, &resp)
if resp.Error == nil || resp.Error.Code != errMethodNotFound {
t.Errorf("expected method-not-found for streaming, got %+v", resp.Error)
}
}
func TestListAgents(t *testing.T) {
ts, cleanup := newGatewayWithAgent(t)
defer cleanup()
resp, err := http.Get(ts.URL + "/agents")
if err != nil {
t.Fatalf("list: %v", err)
}
defer resp.Body.Close()
var out struct {
Agents []AgentCard `json:"agents"`
}
json.NewDecoder(resp.Body).Decode(&out)
if len(out.Agents) != 1 || out.Agents[0].Name != "echo" {
t.Errorf("agents list = %+v", out.Agents)
}
}
func rpc(t *testing.T, url, body string, v any) {
t.Helper()
resp, err := http.Post(url, "application/json", bytes.NewBufferString(body))
if err != nil {
t.Fatalf("post: %v", err)
}
defer resp.Body.Close()
if err := json.NewDecoder(resp.Body).Decode(v); err != nil {
t.Fatalf("decode: %v", err)
}
}
func rpcTask(t *testing.T, url, body string) Task {
t.Helper()
var resp struct {
Result Task `json:"result"`
Error *rpcError `json:"error"`
}
rpc(t, url, body, &resp)
if resp.Error != nil {
t.Fatalf("rpc error: %+v", resp.Error)
}
return resp.Result
}