Update edit tests

Updated test mocks and logic to consistently use lowercase 'monalisa' for login names and display names for user assignees. Improved handling of dynamic assignee fetching in interactive flows by relying on searchFunc and metadata population, and clarified logic in FetchOptions to fetch assignees only when necessary. These changes ensure more accurate simulation of interactive assignment and better test coverage for actor assignee features.
This commit is contained in:
Kynan Ware 2026-01-08 14:26:21 -07:00
parent d46f42a752
commit 07dfdf97ae
4 changed files with 50 additions and 41 deletions

View file

@ -228,15 +228,27 @@ func TestAccessiblePrompter(t *testing.T) {
console := newTestVirtualTerminal(t)
p := newTestAccessiblePrompter(t, console)
persistentOptions := []string{"persistent-option-1"}
searchFunc := func(input string) prompter.MultiSelectSearchResult {
var searchResultKeys []string
var searchResultLabels []string
searchFunc := func(input string) prompter.MultiSelectSearchResult {
var searchResultKeys []string
var searchResultLabels []string
// Initial search with no input
if input == "" {
moreResults := 2
searchResultKeys = []string{"initial-result-1", "initial-result-2"}
searchResultLabels = []string{"Initial Result Label 1", "Initial Result Label 2"}
// Initial search with no input
if input == "" {
moreResults := 2
searchResultKeys = []string{"initial-result-1", "initial-result-2"}
searchResultLabels = []string{"Initial Result Label 1", "Initial Result Label 2"}
return prompter.MultiSelectSearchResult{
Keys: searchResultKeys,
Labels: searchResultLabels,
MoreResults: moreResults,
Err: nil,
}
}
// Subsequent search with input
moreResults := 0
searchResultKeys = []string{"search-result-1", "search-result-2"}
searchResultLabels = []string{"Search Result Label 1", "Search Result Label 2"}
return prompter.MultiSelectSearchResult{
Keys: searchResultKeys,
Labels: searchResultLabels,
@ -245,19 +257,7 @@ func TestAccessiblePrompter(t *testing.T) {
}
}
// Subsequent search with input
moreResults := 0
searchResultKeys = []string{"search-result-1", "search-result-2"}
searchResultLabels = []string{"Search Result Label 1", "Search Result Label 2"}
return prompter.MultiSelectSearchResult{
Keys: searchResultKeys,
Labels: searchResultLabels,
MoreResults: moreResults,
Err: nil,
}
}
go func() {
go func() {
// Wait for prompt to appear
_, err := console.ExpectString("Select an option \r\n")
require.NoError(t, err)

View file

@ -845,7 +845,7 @@ func mockRepoMetadata(_ *testing.T, reg *httpmock.Registry) {
{ "data": { "repository": { "suggestedActors": {
"nodes": [
{ "login": "hubot", "id": "HUBOTID", "__typename": "Bot" },
{ "login": "MonaLisa", "id": "MONAID", "name": "Mona Display Name", "__typename": "User" }
{ "login": "monalisa", "id": "MONAID", "name": "Mona Display Name", "__typename": "User" }
],
"pageInfo": { "hasNextPage": false }
} } } }

View file

@ -714,7 +714,13 @@ func Test_editRun(t *testing.T) {
editFields: func(e *shared.Editable, _ string) error {
e.Title.Value = "new title"
e.Body.Value = "new body"
e.Assignees.Value = []string{"monalisa", "hubot"}
// When ActorAssignees is enabled, the interactive flow returns display names (or logins for non-users)
e.Assignees.Value = []string{"monalisa (Mona Display Name)", "hubot"}
// Populate metadata to simulate what searchFunc would do during prompting
e.Metadata.AssignableActors = []api.AssignableActor{
api.NewAssignableBot("HUBOTID", "hubot"),
api.NewAssignableUser("MONAID", "monalisa", "Mona Display Name"),
}
e.Labels.Value = []string{"feature", "TODO", "bug"}
e.Labels.Add = []string{"feature", "TODO", "bug"}
e.Labels.Remove = []string{"docs"}
@ -728,7 +734,8 @@ func Test_editRun(t *testing.T) {
},
httpStubs: func(t *testing.T, reg *httpmock.Registry) {
// interactive but reviewers not chosen; need everything except reviewers/teams
mockRepoMetadata(reg, mockRepoMetadataOptions{assignees: true, labels: true, projects: true, milestones: true})
// assignees: false because searchFunc handles dynamic fetching (metadata populated in test mock)
mockRepoMetadata(reg, mockRepoMetadataOptions{assignees: false, labels: true, projects: true, milestones: true})
mockPullRequestUpdate(reg)
mockPullRequestUpdateActorAssignees(reg)
mockPullRequestUpdateLabels(reg)
@ -822,8 +829,13 @@ func Test_editRun(t *testing.T) {
require.Equal(t, []string{"hubot"}, e.Assignees.Default)
require.Equal(t, []string{"hubot"}, e.Assignees.DefaultLogins)
// Adding MonaLisa as PR assignee, should preserve hubot.
e.Assignees.Value = []string{"hubot", "MonaLisa (Mona Display Name)"}
// Adding monalisa as PR assignee, should preserve hubot.
e.Assignees.Value = []string{"hubot", "monalisa (Mona Display Name)"}
// Populate metadata to simulate what searchFunc would do during prompting
e.Metadata.AssignableActors = []api.AssignableActor{
api.NewAssignableBot("HUBOTID", "hubot"),
api.NewAssignableUser("MONAID", "monalisa", "Mona Display Name"),
}
return nil
},
},
@ -831,17 +843,8 @@ func Test_editRun(t *testing.T) {
EditorRetriever: testEditorRetriever{},
},
httpStubs: func(t *testing.T, reg *httpmock.Registry) {
reg.Register(
httpmock.GraphQL(`query RepositoryAssignableActors\b`),
httpmock.StringResponse(`
{ "data": { "repository": { "suggestedActors": {
"nodes": [
{ "login": "hubot", "id": "HUBOTID", "__typename": "Bot" },
{ "login": "MonaLisa", "id": "MONAID", "name": "Mona Display Name", "__typename": "User" }
],
"pageInfo": { "hasNextPage": false }
} } } }
`))
// No RepositoryAssignableActors query needed - searchFunc handles dynamic fetching
// (metadata populated in test mock)
mockPullRequestUpdate(reg)
reg.Register(
httpmock.GraphQL(`mutation ReplaceActorsForAssignable\b`),
@ -886,7 +889,7 @@ func Test_editRun(t *testing.T) {
{ "data": { "repository": { "assignableUsers": {
"nodes": [
{ "login": "hubot", "id": "HUBOTID" },
{ "login": "MonaLisa", "id": "MONAID" }
{ "login": "monalisa", "id": "MONAID" }
],
"pageInfo": { "hasNextPage": false }
} } } }
@ -1001,7 +1004,7 @@ func mockRepoMetadata(reg *httpmock.Registry, opt mockRepoMetadataOptions) {
{ "data": { "repository": { "suggestedActors": {
"nodes": [
{ "login": "hubot", "id": "HUBOTID", "__typename": "Bot" },
{ "login": "MonaLisa", "id": "MONAID", "name": "Mona Display Name", "__typename": "User" }
{ "login": "monalisa", "id": "MONAID", "name": "Mona Display Name", "__typename": "User" }
],
"pageInfo": { "hasNextPage": false }
} } } }

View file

@ -430,11 +430,17 @@ func FetchOptions(client *api.Client, repo ghrepo.Interface, editable *Editable,
fetchAssignees := false
if editable.Assignees.Edited {
// Similar as above, this is likely an interactive flow if no Add/Remove slices are set.
// The addition here is that we also check for an assignee search func because
// if that is set, the prompter will handle dynamic fetching of assignees.
// The addition here is that we also check for an assignee search func.
// If we have a search func, we don't need to fetch assignees since we
// assume that will be done dynamically in the prompting flow.
if len(editable.Assignees.Add) == 0 && len(editable.Assignees.Remove) == 0 && editable.AssigneeSearchFunc == nil {
fetchAssignees = true
}
// However, if we have Add/Remove operations (non-interactive flow),
// we do need to fetch the assignees.
if len(editable.Assignees.Add) > 0 || len(editable.Assignees.Remove) > 0 {
fetchAssignees = true
}
}
input := api.RepoMetadataInput{