Gitea host renamed (infra ADR-0004); the mcp-chassis dep already migrated
(3329ff3), so the sequencing gate is clear. `go mod edit -module` + bulk import
rewrite across all .go files. gitea-mcp is a server binary (not an imported
library), so no downstream consumers break. build + task check green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
154 lines
5.4 KiB
Go
154 lines
5.4 KiB
Go
package tools_test
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"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/tools"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func newRepoUpdateTool(srvURL string) *tools.RepoUpdate {
|
|
return tools.NewRepoUpdate(gitea.NewClient(srvURL, "tok"), allowlist.New([]string{"mathias"}))
|
|
}
|
|
|
|
// TestRepoUpdateArchive: happy path — set archived=true.
|
|
func TestRepoUpdateArchive(t *testing.T) {
|
|
var patchedBody []byte
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
require.Equal(t, http.MethodPatch, r.Method)
|
|
require.Equal(t, "/api/v1/repos/mathias/old-svc", r.URL.Path)
|
|
patchedBody, _ = io.ReadAll(r.Body)
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_, _ = w.Write([]byte(`{"name":"old-svc","full_name":"mathias/old-svc","default_branch":"main","template":false,"private":false}`))
|
|
}))
|
|
defer srv.Close()
|
|
|
|
tool := newRepoUpdateTool(srv.URL)
|
|
result, err := tool.Call(context.Background(), json.RawMessage(
|
|
`{"owner":"mathias","name":"old-svc","archived":true}`,
|
|
))
|
|
require.NoError(t, err)
|
|
|
|
// Wire payload only contains the field that was actually set.
|
|
var sent map[string]any
|
|
require.NoError(t, json.Unmarshal(patchedBody, &sent))
|
|
assert.Equal(t, true, sent["archived"])
|
|
assert.NotContains(t, sent, "description")
|
|
assert.NotContains(t, sent, "private")
|
|
assert.NotContains(t, sent, "website")
|
|
assert.NotContains(t, sent, "template")
|
|
|
|
var repo gitea.Repo
|
|
require.NoError(t, json.Unmarshal(result, &repo))
|
|
assert.Equal(t, "mathias/old-svc", repo.FullName)
|
|
}
|
|
|
|
// TestRepoUpdateMultipleFields: set description + template flag in one call.
|
|
func TestRepoUpdateMultipleFields(t *testing.T) {
|
|
var patchedBody []byte
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
patchedBody, _ = io.ReadAll(r.Body)
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_, _ = w.Write([]byte(`{"name":"template-go-agent","full_name":"mathias/template-go-agent","description":"Go agent template","template":true}`))
|
|
}))
|
|
defer srv.Close()
|
|
|
|
tool := newRepoUpdateTool(srv.URL)
|
|
_, err := tool.Call(context.Background(), json.RawMessage(
|
|
`{"owner":"mathias","name":"template-go-agent","description":"Go agent template","template":true}`,
|
|
))
|
|
require.NoError(t, err)
|
|
|
|
var sent map[string]any
|
|
require.NoError(t, json.Unmarshal(patchedBody, &sent))
|
|
assert.Equal(t, "Go agent template", sent["description"])
|
|
assert.Equal(t, true, sent["template"])
|
|
assert.NotContains(t, sent, "archived")
|
|
assert.NotContains(t, sent, "private")
|
|
}
|
|
|
|
// TestRepoUpdateNoFieldsRejected: zero updatable fields → validation error before network.
|
|
func TestRepoUpdateNoFieldsRejected(t *testing.T) {
|
|
tool := tools.NewRepoUpdate(
|
|
gitea.NewClient("http://unused", ""),
|
|
allowlist.New([]string{"mathias"}),
|
|
)
|
|
_, err := tool.Call(context.Background(), json.RawMessage(
|
|
`{"owner":"mathias","name":"some-repo"}`,
|
|
))
|
|
require.Error(t, err)
|
|
assert.ErrorIs(t, err, gitea.ErrValidation)
|
|
}
|
|
|
|
// TestRepoUpdateMakePublic: private=false requires confirm=<repo name> as a safety
|
|
// gate (kept from main #21 during the v02-patch merge). With confirm matching, the
|
|
// patch goes through.
|
|
func TestRepoUpdateMakePublic(t *testing.T) {
|
|
var patchedBody []byte
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
patchedBody, _ = io.ReadAll(r.Body)
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_, _ = w.Write([]byte(`{"name":"open-repo","full_name":"mathias/open-repo","private":false}`))
|
|
}))
|
|
defer srv.Close()
|
|
|
|
tool := newRepoUpdateTool(srv.URL)
|
|
_, err := tool.Call(context.Background(), json.RawMessage(
|
|
`{"owner":"mathias","name":"open-repo","private":false,"confirm":"open-repo"}`,
|
|
))
|
|
require.NoError(t, err)
|
|
|
|
var sent map[string]any
|
|
require.NoError(t, json.Unmarshal(patchedBody, &sent))
|
|
assert.Equal(t, false, sent["private"])
|
|
}
|
|
|
|
// TestRepoUpdateMakePublicWithoutConfirm: confirm gate blocks private=false without confirmation.
|
|
func TestRepoUpdateMakePublicWithoutConfirm(t *testing.T) {
|
|
tool := tools.NewRepoUpdate(
|
|
gitea.NewClient("http://unused", ""),
|
|
allowlist.New([]string{"mathias"}),
|
|
)
|
|
_, err := tool.Call(context.Background(), json.RawMessage(
|
|
`{"owner":"mathias","name":"open-repo","private":false}`,
|
|
))
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "confirm")
|
|
}
|
|
|
|
// TestRepoUpdateAllowlistRejects: owner outside allowlist denied without network call.
|
|
func TestRepoUpdateAllowlistRejects(t *testing.T) {
|
|
tool := tools.NewRepoUpdate(
|
|
gitea.NewClient("http://unused", ""),
|
|
allowlist.New([]string{"mathias"}),
|
|
)
|
|
_, err := tool.Call(context.Background(), json.RawMessage(
|
|
`{"owner":"evil","name":"some-repo","archived":true}`,
|
|
))
|
|
require.Error(t, err)
|
|
}
|
|
|
|
// TestRepoUpdateUpstreamError: server 500 propagates as ErrUpstream.
|
|
func TestRepoUpdateUpstreamError(t *testing.T) {
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
_, _ = w.Write([]byte(`{"message":"internal"}`))
|
|
}))
|
|
defer srv.Close()
|
|
|
|
tool := newRepoUpdateTool(srv.URL)
|
|
_, err := tool.Call(context.Background(), json.RawMessage(
|
|
`{"owner":"mathias","name":"some-repo","archived":true}`,
|
|
))
|
|
require.Error(t, err)
|
|
assert.ErrorIs(t, err, gitea.ErrUpstream)
|
|
}
|