package gitea import ( "context" "encoding/base64" "encoding/json" "io" "net/http" "net/http/httptest" "strings" "sync/atomic" "testing" ) func staticToken(tok string) TokenFunc { return func(context.Context, bool) (string, error) { return tok, nil } } func TestFileExists(t *testing.T) { cases := []struct { name string status int want bool errors bool }{ {name: "present", status: http.StatusOK, want: true}, {name: "absent", status: http.StatusNotFound, want: false}, {name: "forge broken", status: http.StatusInternalServerError, errors: true}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { var gotPath, gotAuth string srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { gotPath, gotAuth = r.URL.Path, r.Header.Get("Authorization") w.WriteHeader(tc.status) _, _ = w.Write([]byte(`{}`)) })) defer srv.Close() c := New(srv.URL, staticToken("tok")) got, err := c.FileExists(context.Background(), "unkin/terraform-git", "config/git.unkin.net/unkin/repository/widget.yaml", "main") if tc.errors { if err == nil { t.Fatal("expected an error for a broken forge, got nil") } return } if err != nil { t.Fatalf("FileExists: %v", err) } if got != tc.want { t.Errorf("FileExists = %v, want %v", got, tc.want) } wantPath := "/api/v1/repos/unkin/terraform-git/contents/config/git.unkin.net/unkin/repository/widget.yaml" if gotPath != wantPath { t.Errorf("path = %q, want %q", gotPath, wantPath) } if gotAuth != "token tok" { t.Errorf("Authorization = %q", gotAuth) } }) } } func TestCreatePullRequest(t *testing.T) { var body map[string]string srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost || r.URL.Path != "/api/v1/repos/unkin/terraform-git/pulls" { t.Errorf("unexpected %s %s", r.Method, r.URL.Path) } raw, _ := io.ReadAll(r.Body) _ = json.Unmarshal(raw, &body) w.Header().Set("Content-Type", "application/json") _, _ = w.Write([]byte(`{"number":42,"html_url":"https://git.unkin.net/unkin/terraform-git/pulls/42","state":"open"}`)) })) defer srv.Close() pr, err := New(srv.URL, staticToken("tok")).CreatePullRequest( context.Background(), "unkin/terraform-git", "repospawner/widget", "main", "Add widget", "why") if err != nil { t.Fatalf("CreatePullRequest: %v", err) } if pr.Number != 42 || !strings.HasSuffix(pr.HTMLURL, "/42") { t.Errorf("pr = %+v", pr) } if body["head"] != "repospawner/widget" || body["base"] != "main" || body["title"] != "Add widget" { t.Errorf("payload = %v", body) } } func TestCreateFileBase64EncodesContent(t *testing.T) { var body map[string]string srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { raw, _ := io.ReadAll(r.Body) _ = json.Unmarshal(raw, &body) _, _ = w.Write([]byte(`{}`)) })) defer srv.Close() err := New(srv.URL, staticToken("tok")).CreateFile(context.Background(), "unkin/terraform-git", "config/a/b.yaml", "repospawner/widget", "Add widget", []byte("description: x\n")) if err != nil { t.Fatalf("CreateFile: %v", err) } decoded, err := base64.StdEncoding.DecodeString(body["content"]) if err != nil { t.Fatalf("content is not base64: %v", err) } if string(decoded) != "description: x\n" { t.Errorf("content = %q", decoded) } if body["branch"] != "repospawner/widget" { t.Errorf("branch = %q", body["branch"]) } } func TestCreateBranchToleratesExisting(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusConflict) _, _ = w.Write([]byte(`{"message":"branch already exists"}`)) })) defer srv.Close() if err := New(srv.URL, staticToken("tok")).CreateBranch(context.Background(), "unkin/terraform-git", "repospawner/widget", "main"); err != nil { t.Fatalf("CreateBranch on an existing branch should succeed, got %v", err) } } func TestReMintsTokenOn401(t *testing.T) { var mints atomic.Int32 token := func(_ context.Context, force bool) (string, error) { if force { mints.Add(1) return "fresh", nil } return "stale", nil } var seen []string srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { auth := r.Header.Get("Authorization") seen = append(seen, auth) if auth == "token stale" { w.WriteHeader(http.StatusUnauthorized) _, _ = w.Write([]byte(`{"message":"token expired"}`)) return } _, _ = w.Write([]byte(`{"number":7,"html_url":"https://example/7","state":"open","merged":true}`)) })) defer srv.Close() pr, err := New(srv.URL, token).PullRequest(context.Background(), "unkin/terraform-git", 7) if err != nil { t.Fatalf("PullRequest: %v", err) } if !pr.Merged || pr.Number != 7 { t.Errorf("pr = %+v", pr) } if mints.Load() != 1 { t.Errorf("forced mints = %d, want 1", mints.Load()) } if len(seen) != 2 || seen[0] != "token stale" || seen[1] != "token fresh" { t.Errorf("authorization headers = %v", seen) } } func TestStatusErrorCarriesBody(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusUnprocessableEntity) _, _ = w.Write([]byte(`{"message":"branch already has a pull request"}`)) })) defer srv.Close() _, err := New(srv.URL, staticToken("tok")).CreatePullRequest( context.Background(), "unkin/terraform-git", "h", "main", "t", "b") if err == nil { t.Fatal("expected an error") } if !strings.Contains(err.Error(), "422") || !strings.Contains(err.Error(), "already has a pull request") { t.Errorf("error = %v", err) } }