test(tools): add unit tests for error handling functions

* Implement tests for error functions like errRequiredField, errInvalidField, and errEntityNotFound.
* Ensure proper metadata is returned for various error scenarios.
* Validate error handling in CRM, Files, and other tools.
* Introduce tests for parsing stored file IDs and UUIDs.
* Enhance coverage for helper functions related to project resolution and session management.
This commit is contained in:
Hein
2026-03-31 15:10:07 +02:00
parent acd780ac9c
commit f41c512f36
54 changed files with 1937 additions and 365 deletions

View File

@@ -0,0 +1,137 @@
package mcpserver
import (
"context"
"net/http/httptest"
"testing"
"time"
"github.com/modelcontextprotocol/go-sdk/jsonrpc"
"github.com/modelcontextprotocol/go-sdk/mcp"
"git.warky.dev/wdevs/amcs/internal/buildinfo"
"git.warky.dev/wdevs/amcs/internal/config"
"git.warky.dev/wdevs/amcs/internal/mcperrors"
"git.warky.dev/wdevs/amcs/internal/tools"
)
func TestStreamableHTTPReturnsStructuredToolErrors(t *testing.T) {
handler, err := New(config.MCPConfig{
ServerName: "test",
Version: "0.0.1",
SessionTimeout: time.Minute,
}, nil, streamableTestToolSet(), nil)
if err != nil {
t.Fatalf("mcpserver.New() error = %v", err)
}
httpServer := httptest.NewServer(handler)
defer httpServer.Close()
client := mcp.NewClient(&mcp.Implementation{Name: "client", Version: "0.0.1"}, nil)
cs, err := client.Connect(context.Background(), &mcp.StreamableClientTransport{Endpoint: httpServer.URL}, nil)
if err != nil {
t.Fatalf("connect client: %v", err)
}
defer func() {
_ = cs.Close()
}()
t.Run("schema_validation", func(t *testing.T) {
_, err := cs.CallTool(context.Background(), &mcp.CallToolParams{
Name: "create_project",
Arguments: map[string]any{},
})
if err == nil {
t.Fatal("CallTool(create_project) error = nil, want error")
}
rpcErr, data := requireWireError(t, err)
if rpcErr.Code != jsonrpc.CodeInvalidParams {
t.Fatalf("create_project code = %d, want %d", rpcErr.Code, jsonrpc.CodeInvalidParams)
}
if data.Type != mcperrors.TypeInvalidArguments {
t.Fatalf("create_project data.type = %q, want %q", data.Type, mcperrors.TypeInvalidArguments)
}
if data.Field != "name" {
t.Fatalf("create_project data.field = %q, want %q", data.Field, "name")
}
})
t.Run("project_required", func(t *testing.T) {
_, err := cs.CallTool(context.Background(), &mcp.CallToolParams{
Name: "get_project_context",
Arguments: map[string]any{},
})
if err == nil {
t.Fatal("CallTool(get_project_context) error = nil, want error")
}
rpcErr, data := requireWireError(t, err)
if rpcErr.Code != mcperrors.CodeProjectRequired {
t.Fatalf("get_project_context code = %d, want %d", rpcErr.Code, mcperrors.CodeProjectRequired)
}
if data.Type != mcperrors.TypeProjectRequired {
t.Fatalf("get_project_context data.type = %q, want %q", data.Type, mcperrors.TypeProjectRequired)
}
if data.Field != "project" {
t.Fatalf("get_project_context data.field = %q, want %q", data.Field, "project")
}
})
t.Run("version_info", func(t *testing.T) {
result, err := cs.CallTool(context.Background(), &mcp.CallToolParams{
Name: "get_version_info",
Arguments: map[string]any{},
})
if err != nil {
t.Fatalf("CallTool(get_version_info) error = %v", err)
}
got, ok := result.StructuredContent.(map[string]any)
if !ok {
t.Fatalf("structured content type = %T, want map[string]any", result.StructuredContent)
}
if got["server_name"] != "test" {
t.Fatalf("server_name = %#v, want %q", got["server_name"], "test")
}
if got["version"] != "0.0.1" {
t.Fatalf("version = %#v, want %q", got["version"], "0.0.1")
}
if got["tag_name"] != "v0.0.1" {
t.Fatalf("tag_name = %#v, want %q", got["tag_name"], "v0.0.1")
}
if got["build_date"] != "2026-03-31T00:00:00Z" {
t.Fatalf("build_date = %#v, want %q", got["build_date"], "2026-03-31T00:00:00Z")
}
})
}
func streamableTestToolSet() ToolSet {
return ToolSet{
Version: tools.NewVersionTool("test", buildinfo.Info{Version: "0.0.1", TagName: "v0.0.1", Commit: "test", BuildDate: "2026-03-31T00:00:00Z"}),
Capture: new(tools.CaptureTool),
Search: new(tools.SearchTool),
List: new(tools.ListTool),
Stats: new(tools.StatsTool),
Get: new(tools.GetTool),
Update: new(tools.UpdateTool),
Delete: new(tools.DeleteTool),
Archive: new(tools.ArchiveTool),
Projects: new(tools.ProjectsTool),
Context: new(tools.ContextTool),
Recall: new(tools.RecallTool),
Summarize: new(tools.SummarizeTool),
Links: new(tools.LinksTool),
Files: new(tools.FilesTool),
Backfill: new(tools.BackfillTool),
Reparse: new(tools.ReparseMetadataTool),
RetryMetadata: new(tools.RetryMetadataTool),
Household: new(tools.HouseholdTool),
Maintenance: new(tools.MaintenanceTool),
Calendar: new(tools.CalendarTool),
Meals: new(tools.MealsTool),
CRM: new(tools.CRMTool),
Skills: new(tools.SkillsTool),
}
}