package tools_test import ( "context" "encoding/json" "errors" "testing" "git.d-ma.be/mathias/gitea-mcp/internal/allowlist" "git.d-ma.be/mathias/gitea-mcp/internal/gitea" "git.d-ma.be/mathias/gitea-mcp/internal/registry" "git.d-ma.be/mathias/gitea-mcp/internal/tools" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // buildRegistry wires the full tool set exactly as main.go does, against an // unroutable gitea base URL so any handler that reaches the network fails fast // (connection refused) rather than hanging. func buildRegistry() *registry.Registry { reg := registry.New() c := gitea.NewClient("http://127.0.0.1:1", "") a := allowlist.New([]string{"mathias"}) tools.RegisterAll(reg, c, a, "http://127.0.0.1:1", "mathias", "template-go-web") return reg } // Every registered tool must be dispatchable and advertise a parseable input // schema. This is the regression guard for #36's whole class: a tool that is // wired up but unroutable (or ships a malformed schema) fails CI here instead // of 404ing a live caller. func TestEveryRegisteredToolIsDispatchable(t *testing.T) { reg := buildRegistry() descs := reg.Tools() require.NotEmpty(t, descs) for _, d := range descs { t.Run(d.Name, func(t *testing.T) { require.NotEmpty(t, d.Name, "tool has empty name") assert.True(t, json.Valid(d.InputSchema), "tool %q ships invalid JSON input schema", d.Name) // Dispatch with empty args. We do not care whether the call // succeeds (most fail allowlist/validation/network) — only that // the name resolves to a handler. ErrToolNotFound here means the // tool advertised a name the dispatcher cannot route. _, err := reg.Dispatch(context.Background(), d.Name, json.RawMessage(`{}`)) assert.False(t, errors.Is(err, registry.ErrToolNotFound), "registered tool %q does not dispatch", d.Name) }) } } // Lock the tool count so an accidental drop of a registration in RegisterAll // (the single source main.go and this test share) fails loudly. func TestRegisteredToolCount(t *testing.T) { assert.Len(t, buildRegistry().Tools(), 39) }