test: expand routing and compact coverage
This commit is contained in:
committed by
Steve Yegge
parent
cb280b0fad
commit
12b797781d
@@ -5,6 +5,14 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
var gitCommandRunner = func(repoPath string, args ...string) ([]byte, error) {
|
||||
cmd := exec.Command("git", args...)
|
||||
if repoPath != "" {
|
||||
cmd.Dir = repoPath
|
||||
}
|
||||
return cmd.Output()
|
||||
}
|
||||
|
||||
// UserRole represents whether the user is a maintainer or contributor
|
||||
type UserRole string
|
||||
|
||||
@@ -22,11 +30,7 @@ const (
|
||||
// 3. Fall back to contributor if uncertain
|
||||
func DetectUserRole(repoPath string) (UserRole, error) {
|
||||
// First check for explicit role in git config
|
||||
cmd := exec.Command("git", "config", "--get", "beads.role")
|
||||
if repoPath != "" {
|
||||
cmd.Dir = repoPath
|
||||
}
|
||||
output, err := cmd.Output()
|
||||
output, err := gitCommandRunner(repoPath, "config", "--get", "beads.role")
|
||||
if err == nil {
|
||||
role := strings.TrimSpace(string(output))
|
||||
if role == string(Maintainer) {
|
||||
@@ -38,18 +42,10 @@ func DetectUserRole(repoPath string) (UserRole, error) {
|
||||
}
|
||||
|
||||
// Check push access by examining remote URL
|
||||
cmd = exec.Command("git", "remote", "get-url", "--push", "origin")
|
||||
if repoPath != "" {
|
||||
cmd.Dir = repoPath
|
||||
}
|
||||
output, err = cmd.Output()
|
||||
output, err = gitCommandRunner(repoPath, "remote", "get-url", "--push", "origin")
|
||||
if err != nil {
|
||||
// Fallback to standard fetch URL if push URL fails (some git versions/configs)
|
||||
cmd = exec.Command("git", "remote", "get-url", "origin")
|
||||
if repoPath != "" {
|
||||
cmd.Dir = repoPath
|
||||
}
|
||||
output, err = cmd.Output()
|
||||
output, err = gitCommandRunner(repoPath, "remote", "get-url", "origin")
|
||||
if err != nil {
|
||||
// No remote or error - default to contributor
|
||||
return Contributor, nil
|
||||
@@ -57,13 +53,13 @@ func DetectUserRole(repoPath string) (UserRole, error) {
|
||||
}
|
||||
|
||||
pushURL := strings.TrimSpace(string(output))
|
||||
|
||||
|
||||
// Check if URL indicates write access
|
||||
// SSH URLs (git@github.com:user/repo.git) typically indicate write access
|
||||
// HTTPS with token/password also indicates write access
|
||||
if strings.HasPrefix(pushURL, "git@") ||
|
||||
strings.HasPrefix(pushURL, "ssh://") ||
|
||||
strings.Contains(pushURL, "@") {
|
||||
if strings.HasPrefix(pushURL, "git@") ||
|
||||
strings.HasPrefix(pushURL, "ssh://") ||
|
||||
strings.Contains(pushURL, "@") {
|
||||
return Maintainer, nil
|
||||
}
|
||||
|
||||
@@ -73,11 +69,11 @@ func DetectUserRole(repoPath string) (UserRole, error) {
|
||||
|
||||
// RoutingConfig defines routing rules for issues
|
||||
type RoutingConfig struct {
|
||||
Mode string // "auto" or "explicit"
|
||||
DefaultRepo string // Default repo for new issues
|
||||
MaintainerRepo string // Repo for maintainers (in auto mode)
|
||||
ContributorRepo string // Repo for contributors (in auto mode)
|
||||
ExplicitOverride string // Explicit --repo flag override
|
||||
Mode string // "auto" or "explicit"
|
||||
DefaultRepo string // Default repo for new issues
|
||||
MaintainerRepo string // Repo for maintainers (in auto mode)
|
||||
ContributorRepo string // Repo for contributors (in auto mode)
|
||||
ExplicitOverride string // Explicit --repo flag override
|
||||
}
|
||||
|
||||
// DetermineTargetRepo determines which repo should receive a new issue
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package routing
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -15,11 +17,11 @@ func TestDetermineTargetRepo(t *testing.T) {
|
||||
{
|
||||
name: "explicit override takes precedence",
|
||||
config: &RoutingConfig{
|
||||
Mode: "auto",
|
||||
DefaultRepo: "~/planning",
|
||||
MaintainerRepo: ".",
|
||||
ContributorRepo: "~/contributor-planning",
|
||||
ExplicitOverride: "/tmp/custom",
|
||||
Mode: "auto",
|
||||
DefaultRepo: "~/planning",
|
||||
MaintainerRepo: ".",
|
||||
ContributorRepo: "~/contributor-planning",
|
||||
ExplicitOverride: "/tmp/custom",
|
||||
},
|
||||
userRole: Maintainer,
|
||||
repoPath: ".",
|
||||
@@ -97,9 +99,9 @@ func TestExtractPrefix(t *testing.T) {
|
||||
{"gt-abc123", "gt-"},
|
||||
{"bd-xyz", "bd-"},
|
||||
{"hq-1234", "hq-"},
|
||||
{"abc123", ""}, // No hyphen
|
||||
{"", ""}, // Empty string
|
||||
{"-abc", "-"}, // Starts with hyphen
|
||||
{"abc123", ""}, // No hyphen
|
||||
{"", ""}, // Empty string
|
||||
{"-abc", "-"}, // Starts with hyphen
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
@@ -142,3 +144,104 @@ func TestResolveToExternalRef(t *testing.T) {
|
||||
t.Errorf("ResolveToExternalRef() = %q, want empty string for nonexistent path", got)
|
||||
}
|
||||
}
|
||||
|
||||
type gitCall struct {
|
||||
repo string
|
||||
args []string
|
||||
}
|
||||
|
||||
type gitResponse struct {
|
||||
expect gitCall
|
||||
output string
|
||||
err error
|
||||
}
|
||||
|
||||
type gitStub struct {
|
||||
t *testing.T
|
||||
responses []gitResponse
|
||||
idx int
|
||||
}
|
||||
|
||||
func (s *gitStub) run(repo string, args ...string) ([]byte, error) {
|
||||
if s.idx >= len(s.responses) {
|
||||
s.t.Fatalf("unexpected git call %v in repo %s", args, repo)
|
||||
}
|
||||
resp := s.responses[s.idx]
|
||||
s.idx++
|
||||
if resp.expect.repo != repo {
|
||||
s.t.Fatalf("repo mismatch: got %q want %q", repo, resp.expect.repo)
|
||||
}
|
||||
if !reflect.DeepEqual(resp.expect.args, args) {
|
||||
s.t.Fatalf("args mismatch: got %v want %v", args, resp.expect.args)
|
||||
}
|
||||
return []byte(resp.output), resp.err
|
||||
}
|
||||
|
||||
func (s *gitStub) verify() {
|
||||
if s.idx != len(s.responses) {
|
||||
s.t.Fatalf("expected %d git calls, got %d", len(s.responses), s.idx)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDetectUserRole_ConfigOverrideMaintainer(t *testing.T) {
|
||||
orig := gitCommandRunner
|
||||
stub := &gitStub{t: t, responses: []gitResponse{
|
||||
{expect: gitCall{"", []string{"config", "--get", "beads.role"}}, output: "maintainer\n"},
|
||||
}}
|
||||
gitCommandRunner = stub.run
|
||||
t.Cleanup(func() {
|
||||
gitCommandRunner = orig
|
||||
stub.verify()
|
||||
})
|
||||
|
||||
role, err := DetectUserRole("")
|
||||
if err != nil {
|
||||
t.Fatalf("DetectUserRole error = %v", err)
|
||||
}
|
||||
if role != Maintainer {
|
||||
t.Fatalf("expected %s, got %s", Maintainer, role)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDetectUserRole_PushURLMaintainer(t *testing.T) {
|
||||
orig := gitCommandRunner
|
||||
stub := &gitStub{t: t, responses: []gitResponse{
|
||||
{expect: gitCall{"/repo", []string{"config", "--get", "beads.role"}}, output: "unknown"},
|
||||
{expect: gitCall{"/repo", []string{"remote", "get-url", "--push", "origin"}}, output: "git@github.com:owner/repo.git"},
|
||||
}}
|
||||
gitCommandRunner = stub.run
|
||||
t.Cleanup(func() {
|
||||
gitCommandRunner = orig
|
||||
stub.verify()
|
||||
})
|
||||
|
||||
role, err := DetectUserRole("/repo")
|
||||
if err != nil {
|
||||
t.Fatalf("DetectUserRole error = %v", err)
|
||||
}
|
||||
if role != Maintainer {
|
||||
t.Fatalf("expected %s, got %s", Maintainer, role)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDetectUserRole_DefaultContributor(t *testing.T) {
|
||||
orig := gitCommandRunner
|
||||
stub := &gitStub{t: t, responses: []gitResponse{
|
||||
{expect: gitCall{"", []string{"config", "--get", "beads.role"}}, err: errors.New("missing")},
|
||||
{expect: gitCall{"", []string{"remote", "get-url", "--push", "origin"}}, err: errors.New("no push")},
|
||||
{expect: gitCall{"", []string{"remote", "get-url", "origin"}}, output: "https://github.com/owner/repo.git"},
|
||||
}}
|
||||
gitCommandRunner = stub.run
|
||||
t.Cleanup(func() {
|
||||
gitCommandRunner = orig
|
||||
stub.verify()
|
||||
})
|
||||
|
||||
role, err := DetectUserRole("")
|
||||
if err != nil {
|
||||
t.Fatalf("DetectUserRole error = %v", err)
|
||||
}
|
||||
if role != Contributor {
|
||||
t.Fatalf("expected %s, got %s", Contributor, role)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user