Reject cross-host issue refs in ResolveIssueRef
Relationship mutations (parent, sub-issue, blocked-by, blocking) run against baseRepo's host with node IDs that must come from that same host. Catch a different-host ref up front with a clear error instead of letting the mutation fail with a confusing node-ID error. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
parent
eff9d48f6e
commit
628325920d
2 changed files with 87 additions and 0 deletions
|
|
@ -1496,6 +1496,90 @@ func mockProjectV2ItemUpdate(t *testing.T, reg *httpmock.Registry) {
|
|||
)
|
||||
}
|
||||
|
||||
// Test_editRun_crossHostRelationshipRefs verifies that every relationship
|
||||
// flag rejects a cross-host issue URL with the same clear error. Lives as
|
||||
// its own table rather than additional cases in Test_editRun because each
|
||||
// case shares identical setup and asserts the same error, varying only in
|
||||
// which input field carries the cross-host URL.
|
||||
func Test_editRun_crossHostRelationshipRefs(t *testing.T) {
|
||||
const crossHostURL = "https://example.com/OWNER/REPO/issues/9"
|
||||
|
||||
// Each case exercises one relationship-bearing flag with a cross-host
|
||||
// URL. ResolveIssueRef should short-circuit before any GraphQL request,
|
||||
// and the per-issue failure must surface to stderr.
|
||||
tests := []struct {
|
||||
name string
|
||||
input *EditOptions
|
||||
}{
|
||||
{
|
||||
name: "set parent",
|
||||
input: &EditOptions{
|
||||
Editable: prShared.Editable{
|
||||
Parent: prShared.EditableString{
|
||||
Value: crossHostURL,
|
||||
Edited: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "add sub-issue",
|
||||
input: &EditOptions{AddSubIssues: []string{crossHostURL}},
|
||||
},
|
||||
{
|
||||
name: "remove sub-issue",
|
||||
input: &EditOptions{RemoveSubIssues: []string{crossHostURL}},
|
||||
},
|
||||
{
|
||||
name: "add blocked-by",
|
||||
input: &EditOptions{AddBlockedBy: []string{crossHostURL}},
|
||||
},
|
||||
{
|
||||
name: "remove blocked-by",
|
||||
input: &EditOptions{RemoveBlockedBy: []string{crossHostURL}},
|
||||
},
|
||||
{
|
||||
name: "add blocking",
|
||||
input: &EditOptions{AddBlocking: []string{crossHostURL}},
|
||||
},
|
||||
{
|
||||
name: "remove blocking",
|
||||
input: &EditOptions{RemoveBlocking: []string{crossHostURL}},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ios, _, _, stderr := iostreams.Test()
|
||||
ios.SetStdoutTTY(true)
|
||||
|
||||
reg := &httpmock.Registry{}
|
||||
defer reg.Verify(t)
|
||||
mockIssueGet(t, reg)
|
||||
// No IssueNodeID stub on purpose: the cross-host guard must
|
||||
// short-circuit before any resolution request goes out.
|
||||
|
||||
tt.input.Detector = &fd.EnabledDetectorMock{}
|
||||
tt.input.IssueNumbers = []int{123}
|
||||
tt.input.Interactive = false
|
||||
tt.input.FetchOptions = func(_ *api.Client, _ ghrepo.Interface, _ *prShared.Editable, _ gh.ProjectsV1Support) error {
|
||||
return nil
|
||||
}
|
||||
tt.input.IO = ios
|
||||
tt.input.HttpClient = func() (*http.Client, error) {
|
||||
return &http.Client{Transport: reg}, nil
|
||||
}
|
||||
tt.input.BaseRepo = func() (ghrepo.Interface, error) {
|
||||
return ghrepo.New("OWNER", "REPO"), nil
|
||||
}
|
||||
|
||||
err := editRun(tt.input)
|
||||
require.Error(t, err)
|
||||
assert.Regexp(t, `belongs to a different host \(example\.com\) than the current repository \(github\.com\)`, stderr.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestApiActorsSupported(t *testing.T) {
|
||||
t.Run("when actors are assignable, query includes assignedActors", func(t *testing.T) {
|
||||
ios, _, _, _ := iostreams.Test()
|
||||
|
|
|
|||
|
|
@ -17,6 +17,9 @@ func ResolveIssueRef(client *api.Client, baseRepo ghrepo.Interface, ref string)
|
|||
|
||||
targetRepo := baseRepo
|
||||
if r, ok := repo.Value(); ok {
|
||||
if r.RepoHost() != baseRepo.RepoHost() {
|
||||
return "", fmt.Errorf("issue reference %q belongs to a different host (%s) than the current repository (%s)", ref, r.RepoHost(), baseRepo.RepoHost())
|
||||
}
|
||||
targetRepo = r
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue