a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
180 lines
4.8 KiB
Go
180 lines
4.8 KiB
Go
package forge
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"testing"
|
|
)
|
|
|
|
// Recorded GitHub REST responses (trimmed to the fields forge reads).
|
|
|
|
const prListJSON = `[
|
|
{
|
|
"number": 7,
|
|
"title": "Add forge",
|
|
"state": "open",
|
|
"draft": false,
|
|
"html_url": "https://github.com/octo/gortex/pull/7",
|
|
"mergeable_state": "clean",
|
|
"updated_at": "2026-06-01T10:00:00Z",
|
|
"user": {"login": "alice"},
|
|
"base": {"ref": "main"},
|
|
"head": {"ref": "feature", "sha": "headsha"}
|
|
}
|
|
]`
|
|
|
|
const prGetJSON = `{
|
|
"number": 7,
|
|
"title": "Add forge",
|
|
"state": "open",
|
|
"draft": false,
|
|
"html_url": "https://github.com/octo/gortex/pull/7",
|
|
"mergeable_state": "clean",
|
|
"updated_at": "2026-06-01T10:00:00Z",
|
|
"user": {"login": "alice"},
|
|
"base": {"ref": "main"},
|
|
"head": {"ref": "feature", "sha": "headsha"}
|
|
}`
|
|
|
|
const prFilesJSON = `[
|
|
{
|
|
"filename": "internal/forge/forge.go",
|
|
"status": "added",
|
|
"patch": "@@ -0,0 +1,3 @@\n+package forge\n+\n+// new\n"
|
|
},
|
|
{
|
|
"filename": "internal/forge/ghrest.go",
|
|
"previous_filename": "internal/forge/old.go",
|
|
"status": "renamed",
|
|
"patch": "@@ -1,2 +1,2 @@\n-old\n+new\n line2\n"
|
|
}
|
|
]`
|
|
|
|
const prReviewsJSON = `[
|
|
{"state": "APPROVED", "user": {"login": "bob"}, "submitted_at": "2026-06-01T09:00:00Z"},
|
|
{"state": "COMMENTED", "user": {"login": "carol"}, "submitted_at": "2026-06-01T09:30:00Z"},
|
|
{"state": "CHANGES_REQUESTED", "user": {"login": "carol"}, "submitted_at": "2026-06-01T09:40:00Z"}
|
|
]`
|
|
|
|
const checkRunsJSON = `{
|
|
"total_count": 2,
|
|
"check_runs": [
|
|
{"name": "build", "status": "completed", "conclusion": "success"},
|
|
{"name": "test", "status": "completed", "conclusion": "failure"}
|
|
]
|
|
}`
|
|
|
|
const combinedStatusJSON = `{
|
|
"state": "failure",
|
|
"sha": "headsha",
|
|
"total_count": 1,
|
|
"statuses": [
|
|
{"state": "success", "context": "lint"}
|
|
]
|
|
}`
|
|
|
|
func TestReviewDecision_LatestPerReviewer(t *testing.T) {
|
|
srv := fixtureServer(t)
|
|
c := newTestClient(t, srv.URL)
|
|
got, err := c.reviewDecision(context.Background(), 7)
|
|
if err != nil {
|
|
t.Fatalf("reviewDecision: %v", err)
|
|
}
|
|
// carol's latest non-comment state is CHANGES_REQUESTED → wins.
|
|
if got != "CHANGES_REQUESTED" {
|
|
t.Errorf("reviewDecision = %q, want CHANGES_REQUESTED", got)
|
|
}
|
|
}
|
|
|
|
func TestCIRollup(t *testing.T) {
|
|
srv := fixtureServer(t)
|
|
c := newTestClient(t, srv.URL)
|
|
got, err := c.ciRollup(context.Background(), "headsha")
|
|
if err != nil {
|
|
t.Fatalf("ciRollup: %v", err)
|
|
}
|
|
if got != "FAILURE" {
|
|
t.Errorf("ciRollup = %q, want FAILURE", got)
|
|
}
|
|
}
|
|
|
|
func TestCIRollup_NoRef(t *testing.T) {
|
|
c := &ghClient{owner: "octo", repo: "gortex"}
|
|
got, err := c.ciRollup(context.Background(), "")
|
|
if err != nil {
|
|
t.Fatalf("ciRollup: %v", err)
|
|
}
|
|
if got != "NONE" {
|
|
t.Errorf("ciRollup(empty ref) = %q, want NONE", got)
|
|
}
|
|
}
|
|
|
|
func TestParsePorcelainWorktrees(t *testing.T) {
|
|
out := "worktree /repo/main\nHEAD abcd1234\nbranch refs/heads/main\n\n" +
|
|
"worktree /repo/wt-feature\nHEAD ef567890\nbranch refs/heads/feature\n\n" +
|
|
"worktree /repo/detached\nHEAD 99999999\ndetached\n"
|
|
got := parsePorcelainWorktrees(out)
|
|
if len(got) != 3 {
|
|
t.Fatalf("got %d entries, want 3: %+v", len(got), got)
|
|
}
|
|
if got[0].Path != "/repo/main" || got[0].Branch != "main" || got[0].Head != "abcd1234" {
|
|
t.Errorf("entry[0] = %+v", got[0])
|
|
}
|
|
if got[1].Branch != "feature" {
|
|
t.Errorf("entry[1].Branch = %q, want feature", got[1].Branch)
|
|
}
|
|
if got[2].Branch != "" {
|
|
t.Errorf("detached entry must have empty Branch, got %q", got[2].Branch)
|
|
}
|
|
}
|
|
|
|
func TestLocalWorktrees(t *testing.T) {
|
|
if _, err := exec.LookPath("git"); err != nil {
|
|
t.Skip("git not on PATH")
|
|
}
|
|
dir := t.TempDir()
|
|
main := filepath.Join(dir, "main")
|
|
if err := os.MkdirAll(main, 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
runGit := func(wd string, args ...string) {
|
|
t.Helper()
|
|
cmd := exec.Command("git", args...)
|
|
cmd.Dir = wd
|
|
cmd.Env = append(os.Environ(),
|
|
"GIT_AUTHOR_NAME=t", "GIT_AUTHOR_EMAIL=t@t",
|
|
"GIT_COMMITTER_NAME=t", "GIT_COMMITTER_EMAIL=t@t")
|
|
if out, err := cmd.CombinedOutput(); err != nil {
|
|
t.Fatalf("git %v: %v\n%s", args, err, out)
|
|
}
|
|
}
|
|
runGit(main, "init", "-q", "-b", "main")
|
|
if err := os.WriteFile(filepath.Join(main, "f.txt"), []byte("x"), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
runGit(main, "add", ".")
|
|
runGit(main, "commit", "-q", "-m", "init")
|
|
wt := filepath.Join(dir, "wt")
|
|
runGit(main, "worktree", "add", "-q", "-b", "feature", wt)
|
|
|
|
entries, err := LocalWorktrees(context.Background(), main)
|
|
if err != nil {
|
|
t.Fatalf("LocalWorktrees: %v", err)
|
|
}
|
|
if len(entries) != 2 {
|
|
t.Fatalf("got %d worktrees, want 2: %+v", len(entries), entries)
|
|
}
|
|
branches := map[string]bool{}
|
|
for _, e := range entries {
|
|
branches[e.Branch] = true
|
|
if e.Path == "" || e.Head == "" {
|
|
t.Errorf("entry missing path/head: %+v", e)
|
|
}
|
|
}
|
|
if !branches["main"] || !branches["feature"] {
|
|
t.Errorf("branches = %v, want main+feature", branches)
|
|
}
|
|
}
|