242 lines
8.1 KiB
Go
242 lines
8.1 KiB
Go
package forge_test
|
||
|
||
import (
|
||
"fmt"
|
||
"reflect"
|
||
"strings"
|
||
"testing"
|
||
|
||
"github.com/cruldra/superpowers-vscode/cli/internal/forge"
|
||
"github.com/cruldra/superpowers-vscode/cli/internal/forge/forgetest"
|
||
)
|
||
|
||
// flavors runs fn once against a fake Gitea and once against a fake GitHub, each wired to a matching client.
|
||
func flavors(t *testing.T, fn func(t *testing.T, srv *forgetest.Server, f forge.Forge)) {
|
||
cases := []struct {
|
||
name string
|
||
flavor forgetest.Flavor
|
||
}{
|
||
{"gitea", forgetest.Gitea},
|
||
{"github", forgetest.GitHub},
|
||
}
|
||
for _, c := range cases {
|
||
t.Run(c.name, func(t *testing.T) {
|
||
srv := forgetest.NewServer(t, c.flavor)
|
||
fn(t, srv, clientFor(srv))
|
||
})
|
||
}
|
||
}
|
||
|
||
func clientFor(srv *forgetest.Server) forge.Forge {
|
||
if srv.Flavor == forgetest.GitHub {
|
||
return forge.NewGitHub(srv.URL, srv.Token, forgetest.Owner, forgetest.Repo)
|
||
}
|
||
return forge.NewGitea(srv.URL, srv.Token, forgetest.Owner, forgetest.Repo)
|
||
}
|
||
|
||
func TestKindMatchesConstructor(t *testing.T) {
|
||
if k := forge.NewGitea("https://g.example.com", "t", "o", "r").Kind(); k != forge.KindGitea {
|
||
t.Fatalf("NewGitea().Kind() = %s", k)
|
||
}
|
||
if k := forge.NewGitHub(forge.GitHubAPIBaseURL, "t", "o", "r").Kind(); k != forge.KindGitHub {
|
||
t.Fatalf("NewGitHub().Kind() = %s", k)
|
||
}
|
||
}
|
||
|
||
func TestCurrentUserLogin(t *testing.T) {
|
||
flavors(t, func(t *testing.T, srv *forgetest.Server, f forge.Forge) {
|
||
login, err := f.CurrentUserLogin()
|
||
if err != nil || login != "alice" {
|
||
t.Fatalf("CurrentUserLogin() = (%q, %v)", login, err)
|
||
}
|
||
})
|
||
}
|
||
|
||
func TestWrongTokenSurfacesHTTPError(t *testing.T) {
|
||
flavors(t, func(t *testing.T, srv *forgetest.Server, _ forge.Forge) {
|
||
var f forge.Forge
|
||
if srv.Flavor == forgetest.GitHub {
|
||
f = forge.NewGitHub(srv.URL, "wrong", forgetest.Owner, forgetest.Repo)
|
||
} else {
|
||
f = forge.NewGitea(srv.URL, "wrong", forgetest.Owner, forgetest.Repo)
|
||
}
|
||
_, err := f.CurrentUserLogin()
|
||
if err == nil || !strings.Contains(err.Error(), "401") {
|
||
t.Fatalf("错误 token 应返回带 401 的错误,得到 %v", err)
|
||
}
|
||
})
|
||
}
|
||
|
||
func TestCreateGetAndUpdateIssue(t *testing.T) {
|
||
flavors(t, func(t *testing.T, srv *forgetest.Server, f forge.Forge) {
|
||
created, err := f.CreateIssue("标题", "正文", []string{"alice"})
|
||
if err != nil {
|
||
t.Fatalf("CreateIssue 出错: %v", err)
|
||
}
|
||
if created.Number <= 0 || created.HTMLURL == "" {
|
||
t.Fatalf("CreateIssue 返回不完整: %+v", created)
|
||
}
|
||
if err := f.UpdateIssueBody(created.Number, "新正文"); err != nil {
|
||
t.Fatalf("UpdateIssueBody 出错: %v", err)
|
||
}
|
||
got, err := f.GetIssue(created.Number)
|
||
if err != nil {
|
||
t.Fatalf("GetIssue 出错: %v", err)
|
||
}
|
||
want := &forge.Issue{
|
||
Number: created.Number,
|
||
Title: "标题",
|
||
Body: "新正文",
|
||
State: "open",
|
||
Author: "alice",
|
||
Assignees: []string{"alice"},
|
||
HTMLURL: fmt.Sprintf("%s/acme/widgets/issues/%d", srv.URL, created.Number),
|
||
}
|
||
if !reflect.DeepEqual(got, want) {
|
||
t.Fatalf("GetIssue = %+v\nwant %+v", got, want)
|
||
}
|
||
})
|
||
}
|
||
|
||
func TestGetIssueWithoutAssigneesGivesEmptyList(t *testing.T) {
|
||
flavors(t, func(t *testing.T, srv *forgetest.Server, f forge.Forge) {
|
||
n := srv.AddIssue(forgetest.Issue{Title: "t", Author: "bob"})
|
||
got, err := f.GetIssue(n)
|
||
if err != nil {
|
||
t.Fatalf("GetIssue 出错: %v", err)
|
||
}
|
||
if got.Assignees == nil || len(got.Assignees) != 0 {
|
||
t.Fatalf("没有指派人时应是非 nil 空切片,得到 %#v", got.Assignees)
|
||
}
|
||
})
|
||
}
|
||
|
||
func TestCommentsRoundTrip(t *testing.T) {
|
||
flavors(t, func(t *testing.T, srv *forgetest.Server, f forge.Forge) {
|
||
n := srv.AddIssue(forgetest.Issue{Title: "t", Author: "bob"})
|
||
srv.AddComment(n, "bob", "第一条")
|
||
posted, err := f.CreateIssueComment(n, "第二条")
|
||
if err != nil {
|
||
t.Fatalf("CreateIssueComment 出错: %v", err)
|
||
}
|
||
if posted.ID == 0 || posted.HTMLURL == "" || posted.Author != "alice" {
|
||
t.Fatalf("CreateIssueComment 返回不完整: %+v", posted)
|
||
}
|
||
list, err := f.ListIssueComments(n)
|
||
if err != nil {
|
||
t.Fatalf("ListIssueComments 出错: %v", err)
|
||
}
|
||
if len(list) != 2 || list[0].Body != "第一条" || list[0].Author != "bob" || list[1].Body != "第二条" {
|
||
t.Fatalf("评论列表不对: %+v", list)
|
||
}
|
||
if list[0].CreatedAt == "" {
|
||
t.Fatalf("评论应带 created_at: %+v", list[0])
|
||
}
|
||
})
|
||
}
|
||
|
||
func TestListIssueCommentsReadsEveryPageOnGitHub(t *testing.T) {
|
||
srv := forgetest.NewServer(t, forgetest.GitHub)
|
||
f := clientFor(srv)
|
||
n := srv.AddIssue(forgetest.Issue{Title: "t", Author: "bob"})
|
||
for i := 1; i <= 250; i++ {
|
||
srv.AddComment(n, "bob", fmt.Sprintf("c%d", i))
|
||
}
|
||
list, err := f.ListIssueComments(n)
|
||
if err != nil {
|
||
t.Fatalf("ListIssueComments 出错: %v", err)
|
||
}
|
||
if len(list) != 250 || list[0].Body != "c1" || list[249].Body != "c250" {
|
||
t.Fatalf("应读全 250 条且保持顺序,得到 %d 条", len(list))
|
||
}
|
||
var commentCalls []string
|
||
for _, r := range srv.Requests() {
|
||
if strings.Contains(r, "/comments") {
|
||
commentCalls = append(commentCalls, r)
|
||
}
|
||
}
|
||
if len(commentCalls) != 3 {
|
||
t.Fatalf("250 条应按 per_page=100 分 3 页取,实际请求: %v", commentCalls)
|
||
}
|
||
if !strings.Contains(commentCalls[0], "per_page=100") {
|
||
t.Fatalf("第一页应带 per_page=100: %s", commentCalls[0])
|
||
}
|
||
}
|
||
|
||
func TestGetPullRequest(t *testing.T) {
|
||
flavors(t, func(t *testing.T, srv *forgetest.Server, f forge.Forge) {
|
||
n := srv.AddPull(forgetest.Pull{
|
||
Title: "加功能", Body: "说明", State: "closed", Merged: true, Author: "bob",
|
||
HeadBranch: "feat/x", HeadSHA: "aaa111", BaseBranch: "main", BaseSHA: "bbb222",
|
||
})
|
||
got, err := f.GetPullRequest(n)
|
||
if err != nil {
|
||
t.Fatalf("GetPullRequest 出错: %v", err)
|
||
}
|
||
if got.Number != n || got.Title != "加功能" || got.Body != "说明" || got.State != "closed" || !got.Merged || got.Author != "bob" {
|
||
t.Fatalf("PR 字段不对: %+v", got)
|
||
}
|
||
if got.Head != (forge.BranchRef{Branch: "feat/x", SHA: "aaa111"}) || got.Base != (forge.BranchRef{Branch: "main", SHA: "bbb222"}) {
|
||
t.Fatalf("head/base 不对: %+v / %+v", got.Head, got.Base)
|
||
}
|
||
if got.HTMLURL == "" {
|
||
t.Fatal("PR 应带 html_url")
|
||
}
|
||
})
|
||
}
|
||
|
||
func TestGetPullRequestReportsHeadBranchAfterItIsDeleted(t *testing.T) {
|
||
flavors(t, func(t *testing.T, srv *forgetest.Server, f forge.Forge) {
|
||
n := srv.AddPull(forgetest.Pull{
|
||
Title: "t", State: "closed", Merged: true, Author: "bob",
|
||
HeadBranch: "feat/gone", HeadSHA: "aaa111", BaseBranch: "main", BaseSHA: "bbb222", HeadDeleted: true,
|
||
})
|
||
got, err := f.GetPullRequest(n)
|
||
if err != nil {
|
||
t.Fatalf("GetPullRequest 出错: %v", err)
|
||
}
|
||
if got.Head.Branch != "feat/gone" || got.Base.Branch != "main" {
|
||
t.Fatalf("head 分支删掉后仍应报分支名,得到 head=%+v base=%+v", got.Head, got.Base)
|
||
}
|
||
})
|
||
}
|
||
|
||
func TestCreatePullRequestAndDefaultBranch(t *testing.T) {
|
||
flavors(t, func(t *testing.T, srv *forgetest.Server, f forge.Forge) {
|
||
srv.DefaultBranch = "trunk"
|
||
base, err := f.DefaultBranch()
|
||
if err != nil || base != "trunk" {
|
||
t.Fatalf("DefaultBranch() = (%q, %v)", base, err)
|
||
}
|
||
pr, err := f.CreatePullRequest(forge.NewPullRequest{Title: "T", Body: "B", Head: "feat/y", Base: base})
|
||
if err != nil {
|
||
t.Fatalf("CreatePullRequest 出错: %v", err)
|
||
}
|
||
stored, ok := srv.Pull(pr.Number)
|
||
if !ok || stored.Title != "T" || stored.Body != "B" || stored.HeadBranch != "feat/y" || stored.BaseBranch != "trunk" {
|
||
t.Fatalf("服务端收到的 PR 不对: %+v", stored)
|
||
}
|
||
if pr.HTMLURL == "" || pr.Head.Branch != "feat/y" || pr.Base.Branch != "trunk" {
|
||
t.Fatalf("CreatePullRequest 返回不完整: %+v", pr)
|
||
}
|
||
})
|
||
}
|
||
|
||
func TestPullRequestCommentsUseIssueCommentsEndpoint(t *testing.T) {
|
||
flavors(t, func(t *testing.T, srv *forgetest.Server, f forge.Forge) {
|
||
n := srv.AddPull(forgetest.Pull{Title: "t", Author: "bob", HeadBranch: "h", BaseBranch: "main"})
|
||
if _, err := f.CreateIssueComment(n, "看过了"); err != nil {
|
||
t.Fatalf("CreateIssueComment 出错: %v", err)
|
||
}
|
||
reqs := srv.Requests()
|
||
prefix := ""
|
||
if srv.Flavor == forgetest.Gitea {
|
||
prefix = "/api/v1"
|
||
}
|
||
want := fmt.Sprintf("POST %s/repos/acme/widgets/issues/%d/comments", prefix, n)
|
||
if reqs[len(reqs)-1] != want {
|
||
t.Fatalf("PR 评论应走 issues comments 接口,实际 %s,want %s", reqs[len(reqs)-1], want)
|
||
}
|
||
})
|
||
}
|