package server import ( "encoding/json" "io" "net/http" "net/http/httptest" "sort" "strings" "testing" mcpgo "github.com/mark3labs/mcp-go/server" "gitea.fe.kaien.ru/kaien/woodpecker-mcp/internal/woodpecker" ) func TestServerRegistersAllTools(t *testing.T) { c, err := woodpecker.NewClient("http://localhost:8000/api", "token") if err != nil { t.Fatal(err) } srv := New(c) tools := srv.ListTools() got := make([]string, 0, len(tools)) for name := range tools { got = append(got, name) } sort.Strings(got) want := []string{ "get_pipeline", "get_pipeline_config", "get_repository_by_id", "get_step_logs", "list_pipelines", "list_repositories", "search_repository", } if len(got) != len(want) { t.Fatalf("registered %d tools, want %d: %v", len(got), len(want), got) } for i := range want { if got[i] != want[i] { t.Errorf("tool[%d] = %q, want %q", i, got[i], want[i]) } } } func TestServerHasNoPrompts(t *testing.T) { c, err := woodpecker.NewClient("http://localhost:8000/api", "token") if err != nil { t.Fatal(err) } srv := New(c) if prompts := srv.ListPrompts(); len(prompts) != 0 { t.Errorf("prompts must be disabled, got %d", len(prompts)) } } // TestStreamableHTTPHandshake exercises the remote transport: a POST // initialize over Streamable HTTP must answer with the server info. func TestStreamableHTTPHandshake(t *testing.T) { c, err := woodpecker.NewClient("http://localhost:8000/api", "token") if err != nil { t.Fatal(err) } httpServer := mcpgo.NewStreamableHTTPServer(New(c)) ts := httptest.NewServer(httpServer) defer ts.Close() req, err := http.NewRequest(http.MethodPost, ts.URL+"/mcp", strings.NewReader(`{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}`)) if err != nil { t.Fatal(err) } req.Header.Set("Content-Type", "application/json") req.Header.Set("Accept", "application/json") resp, err := ts.Client().Do(req) if err != nil { t.Fatal(err) } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { t.Fatal(err) } if resp.StatusCode != http.StatusOK { t.Fatalf("status = %d, body = %s", resp.StatusCode, body) } var message struct { Result struct { ProtocolVersion string `json:"protocolVersion"` ServerInfo struct { Name string `json:"name"` Version string `json:"version"` } `json:"serverInfo"` Capabilities struct { Tools struct { ListChanged bool `json:"listChanged"` } `json:"tools"` } `json:"capabilities"` } `json:"result"` } if err := json.Unmarshal(body, &message); err != nil { t.Fatalf("invalid JSON-RPC response: %v\n%s", err, body) } if message.Result.ServerInfo.Name != "woodpecker-mcp" { t.Errorf("server name = %q", message.Result.ServerInfo.Name) } if !message.Result.Capabilities.Tools.ListChanged { t.Error("tools capability missing listChanged") } }