139 lines
4.8 KiB
Go
139 lines
4.8 KiB
Go
package e2emutate
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
type fakeHTTP struct {
|
|
do func(*http.Request) (*http.Response, error)
|
|
calls []*http.Request
|
|
}
|
|
|
|
func (f *fakeHTTP) Do(request *http.Request) (*http.Response, error) {
|
|
f.calls = append(f.calls, request)
|
|
return f.do(request)
|
|
}
|
|
|
|
func mutationResponse(status int, body string) *http.Response {
|
|
return &http.Response{StatusCode: status, Body: io.NopCloser(strings.NewReader(body)), Header: make(http.Header)}
|
|
}
|
|
|
|
func testOptions() Options {
|
|
return Options{
|
|
ForgejoURL: "https://git.example.test",
|
|
Owner: "test-org-2",
|
|
Repo: "maidn-e2e-repo",
|
|
Branch: "maidn-e2e-branch",
|
|
SHA: "0123456789abcdef0123456789abcdef01234567",
|
|
Token: "test-token",
|
|
}
|
|
}
|
|
|
|
func TestOptionsValidateAcceptsOnlyFixtureTargets(t *testing.T) {
|
|
if err := testOptions().Validate(); err != nil {
|
|
t.Fatalf("valid fixture options: %v", err)
|
|
}
|
|
for _, update := range []func(*Options){
|
|
func(o *Options) { o.Owner = "other-org" },
|
|
func(o *Options) { o.Repo = "production" },
|
|
func(o *Options) { o.Branch = "feature/maidn-e2e-branch" },
|
|
func(o *Options) { o.Branch = "maidn-e2e-branch..unsafe" },
|
|
func(o *Options) { o.Repo = "maidn-e2e-repo.lock" },
|
|
} {
|
|
options := testOptions()
|
|
update(&options)
|
|
if err := options.Validate(); err == nil {
|
|
t.Fatalf("Validate accepted %#v", options)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestMutatorUpdatesFixtureRefAndEnsuresOnePR(t *testing.T) {
|
|
fake := &fakeHTTP{do: func(request *http.Request) (*http.Response, error) {
|
|
if request.Header.Get("Authorization") != "token test-token" {
|
|
t.Fatal("mutation request did not authenticate at the API boundary")
|
|
}
|
|
switch {
|
|
case request.Method == http.MethodPatch && request.URL.Path == "/api/v1/repos/test-org-2/maidn-e2e-repo/git/refs/heads/maidn-e2e-branch":
|
|
var body struct {
|
|
SHA string `json:"sha"`
|
|
Force bool `json:"force"`
|
|
}
|
|
if err := json.NewDecoder(request.Body).Decode(&body); err != nil || body.SHA != testOptions().SHA || body.Force {
|
|
t.Fatalf("unexpected branch update: %#v, %v", body, err)
|
|
}
|
|
return mutationResponse(http.StatusOK, ""), nil
|
|
case request.Method == http.MethodGet && request.URL.Path == "/api/v1/repos/test-org-2/maidn-e2e-repo/pulls":
|
|
if request.URL.Query().Get("head") != "test-org-2:maidn-e2e-branch" || request.URL.Query().Get("state") != "open" {
|
|
t.Fatal("pull request lookup did not target the fixture branch")
|
|
}
|
|
return mutationResponse(http.StatusOK, "[]"), nil
|
|
case request.Method == http.MethodPost && request.URL.Path == "/api/v1/repos/test-org-2/maidn-e2e-repo/pulls":
|
|
var body struct {
|
|
Head string `json:"head"`
|
|
Base string `json:"base"`
|
|
}
|
|
if err := json.NewDecoder(request.Body).Decode(&body); err != nil || body.Head != "maidn-e2e-branch" || body.Base != "main" {
|
|
t.Fatalf("unexpected pull request creation: %#v, %v", body, err)
|
|
}
|
|
return mutationResponse(http.StatusCreated, ""), nil
|
|
default:
|
|
t.Fatalf("unexpected Forgejo request: %s %s", request.Method, request.URL)
|
|
return nil, nil
|
|
}
|
|
}}
|
|
options := testOptions()
|
|
options.OpenPR = true
|
|
if err := (Mutator{HTTP: fake}).Run(context.Background(), options); err != nil || len(fake.calls) != 3 {
|
|
t.Fatalf("Run() = %v, calls = %d", err, len(fake.calls))
|
|
}
|
|
}
|
|
|
|
func TestMutatorCreatesFixtureRefWhenAbsent(t *testing.T) {
|
|
fake := &fakeHTTP{do: func(request *http.Request) (*http.Response, error) {
|
|
switch request.Method {
|
|
case http.MethodPatch:
|
|
return mutationResponse(http.StatusNotFound, ""), nil
|
|
case http.MethodPost:
|
|
if request.URL.Path != "/api/v1/repos/test-org-2/maidn-e2e-repo/git/refs" {
|
|
t.Fatalf("branch creation targeted %q", request.URL.Path)
|
|
}
|
|
return mutationResponse(http.StatusCreated, ""), nil
|
|
default:
|
|
t.Fatalf("unexpected Forgejo request: %s %s", request.Method, request.URL)
|
|
return nil, nil
|
|
}
|
|
}}
|
|
if err := (Mutator{HTTP: fake}).Run(context.Background(), testOptions()); err != nil || len(fake.calls) != 2 {
|
|
t.Fatalf("Run() = %v, calls = %d", err, len(fake.calls))
|
|
}
|
|
}
|
|
|
|
func TestMutatorRejectsUnsafeTargetsBeforeAPIAndDoesNotExposeToken(t *testing.T) {
|
|
fake := &fakeHTTP{do: func(*http.Request) (*http.Response, error) {
|
|
t.Fatal("unsafe target reached the Forgejo API")
|
|
return nil, nil
|
|
}}
|
|
options := testOptions()
|
|
options.Owner = "production"
|
|
options.Token = "secret-token"
|
|
err := (Mutator{HTTP: fake}).Run(context.Background(), options)
|
|
if err == nil || strings.Contains(err.Error(), options.Token) {
|
|
t.Fatalf("Run() returned unsafe error: %v", err)
|
|
}
|
|
|
|
fake.do = func(*http.Request) (*http.Response, error) { return nil, errors.New(options.Token) }
|
|
options = testOptions()
|
|
options.Token = "secret-token"
|
|
err = (Mutator{HTTP: fake}).Run(context.Background(), options)
|
|
if err == nil || strings.Contains(err.Error(), options.Token) {
|
|
t.Fatalf("Run() exposed token: %v", err)
|
|
}
|
|
}
|