Generates a new repo from mathias/template-go-web via Gitea's generate API, then substitutes __PROJECT_NAME__ and __MODULE_PATH__ placeholders in six known files (best-effort, partial failure surfaced in result). Validates name regex, allowlist, template flag, and destination non-existence before generating. Adds Template field to gitea.Repo. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
157 lines
5.7 KiB
Go
157 lines
5.7 KiB
Go
package gitea_test
|
|
|
|
import (
|
|
"context"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"errors"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"sync/atomic"
|
|
"testing"
|
|
|
|
"gitea.d-ma.be/mathias/gitea-mcp/internal/gitea"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestGenerateFromTemplate(t *testing.T) {
|
|
var capturedBody []byte
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
assert.Equal(t, "/api/v1/repos/mathias/template-go-web/generate", r.URL.Path)
|
|
assert.Equal(t, http.MethodPost, r.Method)
|
|
var err error
|
|
capturedBody, err = io.ReadAll(r.Body)
|
|
require.NoError(t, err)
|
|
w.WriteHeader(http.StatusCreated)
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_, _ = w.Write([]byte(`{
|
|
"name":"new-svc",
|
|
"full_name":"mathias/new-svc",
|
|
"default_branch":"main",
|
|
"description":"A new service",
|
|
"private":true,
|
|
"clone_url":"http://gitea.example.com/mathias/new-svc.git",
|
|
"html_url":"http://gitea.example.com/mathias/new-svc",
|
|
"template":false
|
|
}`))
|
|
}))
|
|
defer srv.Close()
|
|
|
|
c := gitea.NewClient(srv.URL, "tok")
|
|
repo, err := c.GenerateFromTemplate(context.Background(), "mathias", "template-go-web", gitea.GenerateFromTemplateArgs{
|
|
Owner: "mathias",
|
|
Name: "new-svc",
|
|
Description: "A new service",
|
|
Private: true,
|
|
GitContent: true,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Verify the captured POST body contains the expected fields.
|
|
var payload map[string]any
|
|
require.NoError(t, json.Unmarshal(capturedBody, &payload))
|
|
assert.Equal(t, "mathias", payload["owner"])
|
|
assert.Equal(t, "new-svc", payload["name"])
|
|
assert.Equal(t, "A new service", payload["description"])
|
|
assert.Equal(t, true, payload["private"])
|
|
assert.Equal(t, true, payload["git_content"])
|
|
|
|
// Verify the decoded repo fields.
|
|
assert.Equal(t, "new-svc", repo.Name)
|
|
assert.Equal(t, "mathias/new-svc", repo.FullName)
|
|
assert.Equal(t, "main", repo.DefaultBranch)
|
|
assert.Equal(t, "A new service", repo.Description)
|
|
assert.True(t, repo.Private)
|
|
assert.Equal(t, "http://gitea.example.com/mathias/new-svc.git", repo.CloneURL)
|
|
assert.Equal(t, "http://gitea.example.com/mathias/new-svc", repo.HTMLURL)
|
|
}
|
|
|
|
func TestSubstituteFileApplies(t *testing.T) {
|
|
originalContent := "module __MODULE_PATH__\n\ngo 1.22\n"
|
|
encoded := base64.StdEncoding.EncodeToString([]byte(originalContent))
|
|
|
|
var capturedPutBody []byte
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
switch r.Method {
|
|
case http.MethodGet:
|
|
assert.Equal(t, "/api/v1/repos/mathias/new-svc/contents/go.mod", r.URL.Path)
|
|
assert.Equal(t, "main", r.URL.Query().Get("ref"))
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_, _ = w.Write([]byte(`{"path":"go.mod","sha":"abc123","size":30,"content":"` + encoded + `","encoding":"base64"}`))
|
|
case http.MethodPut:
|
|
assert.Equal(t, "/api/v1/repos/mathias/new-svc/contents/go.mod", r.URL.Path)
|
|
var err error
|
|
capturedPutBody, err = io.ReadAll(r.Body)
|
|
require.NoError(t, err)
|
|
w.WriteHeader(http.StatusOK)
|
|
_, _ = w.Write([]byte(`{"content":{"path":"go.mod","sha":"newsha","html_url":""},"commit":{"sha":"commitsha","html_url":""}}`))
|
|
default:
|
|
t.Errorf("unexpected method %s", r.Method)
|
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
|
}
|
|
}))
|
|
defer srv.Close()
|
|
|
|
c := gitea.NewClient(srv.URL, "tok")
|
|
err := c.SubstituteFile(context.Background(), "mathias", "new-svc", "main", "go.mod", map[string]string{
|
|
"__MODULE_PATH__": "gitea.d-ma.be/mathias/new-svc",
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Verify the PUT body contains the substituted content.
|
|
require.NotNil(t, capturedPutBody, "PUT should have been called")
|
|
var payload map[string]string
|
|
require.NoError(t, json.Unmarshal(capturedPutBody, &payload))
|
|
|
|
decoded, err := base64.StdEncoding.DecodeString(payload["content"])
|
|
require.NoError(t, err)
|
|
assert.Contains(t, string(decoded), "gitea.d-ma.be/mathias/new-svc")
|
|
assert.NotContains(t, string(decoded), "__MODULE_PATH__")
|
|
assert.Equal(t, "abc123", payload["sha"])
|
|
assert.Equal(t, "Apply template substitutions", payload["message"])
|
|
}
|
|
|
|
func TestSubstituteFileNoChangeSkipsWrite(t *testing.T) {
|
|
originalContent := "module gitea.d-ma.be/mathias/existing\n\ngo 1.22\n"
|
|
encoded := base64.StdEncoding.EncodeToString([]byte(originalContent))
|
|
|
|
var putCount atomic.Int32
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
switch r.Method {
|
|
case http.MethodGet:
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_, _ = w.Write([]byte(`{"path":"go.mod","sha":"abc123","size":40,"content":"` + encoded + `","encoding":"base64"}`))
|
|
case http.MethodPut:
|
|
putCount.Add(1)
|
|
w.WriteHeader(http.StatusOK)
|
|
_, _ = w.Write([]byte(`{"content":{"path":"go.mod","sha":"newsha","html_url":""},"commit":{"sha":"c","html_url":""}}`))
|
|
}
|
|
}))
|
|
defer srv.Close()
|
|
|
|
c := gitea.NewClient(srv.URL, "tok")
|
|
// Replacements that don't match anything in the content.
|
|
err := c.SubstituteFile(context.Background(), "mathias", "new-svc", "main", "go.mod", map[string]string{
|
|
"__MODULE_PATH__": "gitea.d-ma.be/mathias/new-svc",
|
|
})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, int32(0), putCount.Load(), "PUT should not be called when content is unchanged")
|
|
}
|
|
|
|
func TestSubstituteFileReadError(t *testing.T) {
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusNotFound)
|
|
_, _ = w.Write([]byte(`{"message":"file not found"}`))
|
|
}))
|
|
defer srv.Close()
|
|
|
|
c := gitea.NewClient(srv.URL, "tok")
|
|
err := c.SubstituteFile(context.Background(), "mathias", "new-svc", "main", "go.mod", map[string]string{
|
|
"__MODULE_PATH__": "gitea.d-ma.be/mathias/new-svc",
|
|
})
|
|
require.Error(t, err)
|
|
assert.True(t, errors.Is(err, gitea.ErrNotFound), "error should wrap ErrNotFound, got: %v", err)
|
|
}
|