|
|
|
|
@@ -3,6 +3,7 @@ package tools_test
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"encoding/json"
|
|
|
|
|
"io"
|
|
|
|
|
"net/http"
|
|
|
|
|
"net/http/httptest"
|
|
|
|
|
"testing"
|
|
|
|
|
@@ -14,43 +15,139 @@ import (
|
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func TestRepoUpdateTool(t *testing.T) {
|
|
|
|
|
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) {
|
|
|
|
|
assert.Equal(t, http.MethodPatch, r.Method)
|
|
|
|
|
assert.Equal(t, "/api/v1/repos/mathias/infra", r.URL.Path)
|
|
|
|
|
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":"infra","full_name":"mathias/infra","default_branch":"main","description":"updated","private":true,"clone_url":"https://gitea.example.com/mathias/infra.git","html_url":"https://gitea.example.com/mathias/infra"}`))
|
|
|
|
|
_, _ = w.Write([]byte(`{"name":"old-svc","full_name":"mathias/old-svc","default_branch":"main","template":false,"private":false}`))
|
|
|
|
|
}))
|
|
|
|
|
defer srv.Close()
|
|
|
|
|
|
|
|
|
|
tool := tools.NewRepoUpdate(gitea.NewClient(srv.URL, "tok"), allowlist.New([]string{"mathias"}))
|
|
|
|
|
out, err := tool.Call(context.Background(), json.RawMessage(`{"owner":"mathias","name":"infra","description":"updated"}`))
|
|
|
|
|
tool := newRepoUpdateTool(srv.URL)
|
|
|
|
|
result, err := tool.Call(context.Background(), json.RawMessage(
|
|
|
|
|
`{"owner":"mathias","name":"old-svc","archived":true}`,
|
|
|
|
|
))
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
assert.Contains(t, string(out), `"description":"updated"`)
|
|
|
|
|
|
|
|
|
|
// 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)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestRepoUpdateTool_MakePublicRequiresConfirm(t *testing.T) {
|
|
|
|
|
tool := tools.NewRepoUpdate(gitea.NewClient("http://unused", ""), allowlist.New([]string{"mathias"}))
|
|
|
|
|
_, err := tool.Call(context.Background(), json.RawMessage(`{"owner":"mathias","name":"infra","private":false}`))
|
|
|
|
|
// 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")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestRepoUpdateTool_MakePublicWithConfirm(t *testing.T) {
|
|
|
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
|
_, _ = w.Write([]byte(`{"name":"infra","full_name":"mathias/infra","default_branch":"main","private":false,"clone_url":"","html_url":""}`))
|
|
|
|
|
// 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 := tools.NewRepoUpdate(gitea.NewClient(srv.URL, "tok"), allowlist.New([]string{"mathias"}))
|
|
|
|
|
out, err := tool.Call(context.Background(), json.RawMessage(`{"owner":"mathias","name":"infra","private":false,"confirm":"infra"}`))
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
assert.Contains(t, string(out), `"full_name":"mathias/infra"`)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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":"x"}`))
|
|
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
|