121 lines
2.9 KiB
Go
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")
|
|
}
|
|
}
|