woodpecker-mcp/internal/server/server_test.go
2026-08-08 01:37:47 +03:00

121 lines
2.9 KiB
Go

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")
}
}