Closes #52 — unblocks parallax#3 dispatch labeling, which needs to both discover a repo's label set and attach labels to an issue by name (the CAD pipeline doesn't track Gitea's internal numeric label IDs). - gitea.Client: ListLabels, AddIssueLabels (additive POST, matches Gitea's own semantics — no delete-then-post needed); extend the existing Label struct with Color for label_list's output. - tools.LabelList (read-only, allowlisted): lists a repo's labels. - tools.IssueLabel (allowlisted): resolves label names to IDs via ListLabels, so callers pass names (the primary interface) instead of hunting for numeric IDs; also accepts label_ids for callers that already have them. An unknown name fails closed, naming exactly which label wasn't found. - Bump TestRegisteredToolCount 39 -> 41 in the same commit (this project was bitten today by a locked count going stale silently). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
59 lines
2.1 KiB
Go
59 lines
2.1 KiB
Go
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(), 41)
|
|
}
|