github.com/ungtb10d/cli/v2@v2.0.0-20221110210412-98537dd9d6a1/api/queries_repo_test.go (about) 1 package api 2 3 import ( 4 "io" 5 "net/http" 6 "strings" 7 "testing" 8 9 "github.com/ungtb10d/cli/v2/internal/ghrepo" 10 "github.com/ungtb10d/cli/v2/pkg/httpmock" 11 ) 12 13 func TestGitHubRepo_notFound(t *testing.T) { 14 httpReg := &httpmock.Registry{} 15 defer httpReg.Verify(t) 16 17 httpReg.Register( 18 httpmock.GraphQL(`query RepositoryInfo\b`), 19 httpmock.StringResponse(`{ "data": { "repository": null } }`)) 20 21 client := newTestClient(httpReg) 22 repo, err := GitHubRepo(client, ghrepo.New("OWNER", "REPO")) 23 if err == nil { 24 t.Fatal("GitHubRepo did not return an error") 25 } 26 if wants := "GraphQL: Could not resolve to a Repository with the name 'OWNER/REPO'."; err.Error() != wants { 27 t.Errorf("GitHubRepo error: want %q, got %q", wants, err.Error()) 28 } 29 if repo != nil { 30 t.Errorf("GitHubRepo: expected nil repo, got %v", repo) 31 } 32 } 33 34 func Test_RepoMetadata(t *testing.T) { 35 http := &httpmock.Registry{} 36 client := newTestClient(http) 37 38 repo, _ := ghrepo.FromFullName("OWNER/REPO") 39 input := RepoMetadataInput{ 40 Assignees: true, 41 Reviewers: true, 42 Labels: true, 43 Projects: true, 44 Milestones: true, 45 } 46 47 http.Register( 48 httpmock.GraphQL(`query RepositoryAssignableUsers\b`), 49 httpmock.StringResponse(` 50 { "data": { "repository": { "assignableUsers": { 51 "nodes": [ 52 { "login": "hubot", "id": "HUBOTID" }, 53 { "login": "MonaLisa", "id": "MONAID" } 54 ], 55 "pageInfo": { "hasNextPage": false } 56 } } } } 57 `)) 58 http.Register( 59 httpmock.GraphQL(`query RepositoryLabelList\b`), 60 httpmock.StringResponse(` 61 { "data": { "repository": { "labels": { 62 "nodes": [ 63 { "name": "feature", "id": "FEATUREID" }, 64 { "name": "TODO", "id": "TODOID" }, 65 { "name": "bug", "id": "BUGID" } 66 ], 67 "pageInfo": { "hasNextPage": false } 68 } } } } 69 `)) 70 http.Register( 71 httpmock.GraphQL(`query RepositoryMilestoneList\b`), 72 httpmock.StringResponse(` 73 { "data": { "repository": { "milestones": { 74 "nodes": [ 75 { "title": "GA", "id": "GAID" }, 76 { "title": "Big One.oh", "id": "BIGONEID" } 77 ], 78 "pageInfo": { "hasNextPage": false } 79 } } } } 80 `)) 81 http.Register( 82 httpmock.GraphQL(`query RepositoryProjectList\b`), 83 httpmock.StringResponse(` 84 { "data": { "repository": { "projects": { 85 "nodes": [ 86 { "name": "Cleanup", "id": "CLEANUPID" }, 87 { "name": "Roadmap", "id": "ROADMAPID" } 88 ], 89 "pageInfo": { "hasNextPage": false } 90 } } } } 91 `)) 92 http.Register( 93 httpmock.GraphQL(`query OrganizationProjectList\b`), 94 httpmock.StringResponse(` 95 { "data": { "organization": { "projects": { 96 "nodes": [ 97 { "name": "Triage", "id": "TRIAGEID" } 98 ], 99 "pageInfo": { "hasNextPage": false } 100 } } } } 101 `)) 102 http.Register( 103 httpmock.GraphQL(`query OrganizationTeamList\b`), 104 httpmock.StringResponse(` 105 { "data": { "organization": { "teams": { 106 "nodes": [ 107 { "slug": "owners", "id": "OWNERSID" }, 108 { "slug": "Core", "id": "COREID" } 109 ], 110 "pageInfo": { "hasNextPage": false } 111 } } } } 112 `)) 113 114 result, err := RepoMetadata(client, repo, input) 115 if err != nil { 116 t.Fatalf("unexpected error: %v", err) 117 } 118 119 expectedMemberIDs := []string{"MONAID", "HUBOTID"} 120 memberIDs, err := result.MembersToIDs([]string{"monalisa", "hubot"}) 121 if err != nil { 122 t.Errorf("error resolving members: %v", err) 123 } 124 if !sliceEqual(memberIDs, expectedMemberIDs) { 125 t.Errorf("expected members %v, got %v", expectedMemberIDs, memberIDs) 126 } 127 128 expectedTeamIDs := []string{"COREID", "OWNERSID"} 129 teamIDs, err := result.TeamsToIDs([]string{"OWNER/core", "/owners"}) 130 if err != nil { 131 t.Errorf("error resolving teams: %v", err) 132 } 133 if !sliceEqual(teamIDs, expectedTeamIDs) { 134 t.Errorf("expected teams %v, got %v", expectedTeamIDs, teamIDs) 135 } 136 137 expectedLabelIDs := []string{"BUGID", "TODOID"} 138 labelIDs, err := result.LabelsToIDs([]string{"bug", "todo"}) 139 if err != nil { 140 t.Errorf("error resolving labels: %v", err) 141 } 142 if !sliceEqual(labelIDs, expectedLabelIDs) { 143 t.Errorf("expected labels %v, got %v", expectedLabelIDs, labelIDs) 144 } 145 146 expectedProjectIDs := []string{"TRIAGEID", "ROADMAPID"} 147 projectIDs, err := result.ProjectsToIDs([]string{"triage", "roadmap"}) 148 if err != nil { 149 t.Errorf("error resolving projects: %v", err) 150 } 151 if !sliceEqual(projectIDs, expectedProjectIDs) { 152 t.Errorf("expected projects %v, got %v", expectedProjectIDs, projectIDs) 153 } 154 155 expectedMilestoneID := "BIGONEID" 156 milestoneID, err := result.MilestoneToID("big one.oh") 157 if err != nil { 158 t.Errorf("error resolving milestone: %v", err) 159 } 160 if milestoneID != expectedMilestoneID { 161 t.Errorf("expected milestone %v, got %v", expectedMilestoneID, milestoneID) 162 } 163 } 164 165 func Test_ProjectsToPaths(t *testing.T) { 166 expectedProjectPaths := []string{"OWNER/REPO/PROJECT_NUMBER", "ORG/PROJECT_NUMBER"} 167 projects := []RepoProject{ 168 {ID: "id1", Name: "My Project", ResourcePath: "/OWNER/REPO/projects/PROJECT_NUMBER"}, 169 {ID: "id2", Name: "Org Project", ResourcePath: "/orgs/ORG/projects/PROJECT_NUMBER"}, 170 {ID: "id3", Name: "Project", ResourcePath: "/orgs/ORG/projects/PROJECT_NUMBER_2"}, 171 } 172 projectNames := []string{"My Project", "Org Project"} 173 174 projectPaths, err := ProjectsToPaths(projects, projectNames) 175 if err != nil { 176 t.Errorf("error resolving projects: %v", err) 177 } 178 if !sliceEqual(projectPaths, expectedProjectPaths) { 179 t.Errorf("expected projects %v, got %v", expectedProjectPaths, projectPaths) 180 } 181 } 182 183 func Test_ProjectNamesToPaths(t *testing.T) { 184 http := &httpmock.Registry{} 185 client := newTestClient(http) 186 187 repo, _ := ghrepo.FromFullName("OWNER/REPO") 188 189 http.Register( 190 httpmock.GraphQL(`query RepositoryProjectList\b`), 191 httpmock.StringResponse(` 192 { "data": { "repository": { "projects": { 193 "nodes": [ 194 { "name": "Cleanup", "id": "CLEANUPID", "resourcePath": "/OWNER/REPO/projects/1" }, 195 { "name": "Roadmap", "id": "ROADMAPID", "resourcePath": "/OWNER/REPO/projects/2" } 196 ], 197 "pageInfo": { "hasNextPage": false } 198 } } } } 199 `)) 200 http.Register( 201 httpmock.GraphQL(`query OrganizationProjectList\b`), 202 httpmock.StringResponse(` 203 { "data": { "organization": { "projects": { 204 "nodes": [ 205 { "name": "Triage", "id": "TRIAGEID", "resourcePath": "/orgs/ORG/projects/1" } 206 ], 207 "pageInfo": { "hasNextPage": false } 208 } } } } 209 `)) 210 211 projectPaths, err := ProjectNamesToPaths(client, repo, []string{"Triage", "Roadmap"}) 212 if err != nil { 213 t.Fatalf("unexpected error: %v", err) 214 } 215 216 expectedProjectPaths := []string{"ORG/1", "OWNER/REPO/2"} 217 if !sliceEqual(projectPaths, expectedProjectPaths) { 218 t.Errorf("expected projects paths %v, got %v", expectedProjectPaths, projectPaths) 219 } 220 } 221 222 func Test_RepoResolveMetadataIDs(t *testing.T) { 223 http := &httpmock.Registry{} 224 client := newTestClient(http) 225 226 repo, _ := ghrepo.FromFullName("OWNER/REPO") 227 input := RepoResolveInput{ 228 Assignees: []string{"monalisa", "hubot"}, 229 Reviewers: []string{"monalisa", "octocat", "OWNER/core", "/robots"}, 230 Labels: []string{"bug", "help wanted"}, 231 } 232 233 expectedQuery := `query RepositoryResolveMetadataIDs { 234 u000: user(login:"monalisa"){id,login} 235 u001: user(login:"hubot"){id,login} 236 u002: user(login:"octocat"){id,login} 237 repository(owner:"OWNER",name:"REPO"){ 238 l000: label(name:"bug"){id,name} 239 l001: label(name:"help wanted"){id,name} 240 } 241 organization(login:"OWNER"){ 242 t000: team(slug:"core"){id,slug} 243 t001: team(slug:"robots"){id,slug} 244 } 245 } 246 ` 247 responseJSON := ` 248 { "data": { 249 "u000": { "login": "MonaLisa", "id": "MONAID" }, 250 "u001": { "login": "hubot", "id": "HUBOTID" }, 251 "u002": { "login": "octocat", "id": "OCTOID" }, 252 "repository": { 253 "l000": { "name": "bug", "id": "BUGID" }, 254 "l001": { "name": "Help Wanted", "id": "HELPID" } 255 }, 256 "organization": { 257 "t000": { "slug": "core", "id": "COREID" }, 258 "t001": { "slug": "Robots", "id": "ROBOTID" } 259 } 260 } } 261 ` 262 263 http.Register( 264 httpmock.GraphQL(`query RepositoryResolveMetadataIDs\b`), 265 httpmock.GraphQLQuery(responseJSON, func(q string, _ map[string]interface{}) { 266 if q != expectedQuery { 267 t.Errorf("expected query %q, got %q", expectedQuery, q) 268 } 269 })) 270 271 result, err := RepoResolveMetadataIDs(client, repo, input) 272 if err != nil { 273 t.Fatalf("unexpected error: %v", err) 274 } 275 276 expectedMemberIDs := []string{"MONAID", "HUBOTID", "OCTOID"} 277 memberIDs, err := result.MembersToIDs([]string{"monalisa", "hubot", "octocat"}) 278 if err != nil { 279 t.Errorf("error resolving members: %v", err) 280 } 281 if !sliceEqual(memberIDs, expectedMemberIDs) { 282 t.Errorf("expected members %v, got %v", expectedMemberIDs, memberIDs) 283 } 284 285 expectedTeamIDs := []string{"COREID", "ROBOTID"} 286 teamIDs, err := result.TeamsToIDs([]string{"/core", "/robots"}) 287 if err != nil { 288 t.Errorf("error resolving teams: %v", err) 289 } 290 if !sliceEqual(teamIDs, expectedTeamIDs) { 291 t.Errorf("expected members %v, got %v", expectedTeamIDs, teamIDs) 292 } 293 294 expectedLabelIDs := []string{"BUGID", "HELPID"} 295 labelIDs, err := result.LabelsToIDs([]string{"bug", "help wanted"}) 296 if err != nil { 297 t.Errorf("error resolving labels: %v", err) 298 } 299 if !sliceEqual(labelIDs, expectedLabelIDs) { 300 t.Errorf("expected members %v, got %v", expectedLabelIDs, labelIDs) 301 } 302 } 303 304 func sliceEqual(a, b []string) bool { 305 if len(a) != len(b) { 306 return false 307 } 308 309 for i := range a { 310 if a[i] != b[i] { 311 return false 312 } 313 } 314 315 return true 316 } 317 318 func Test_RepoMilestones(t *testing.T) { 319 tests := []struct { 320 state string 321 want string 322 wantErr bool 323 }{ 324 { 325 state: "open", 326 want: `"states":["OPEN"]`, 327 }, 328 { 329 state: "closed", 330 want: `"states":["CLOSED"]`, 331 }, 332 { 333 state: "all", 334 want: `"states":["OPEN","CLOSED"]`, 335 }, 336 { 337 state: "invalid state", 338 wantErr: true, 339 }, 340 } 341 for _, tt := range tests { 342 var query string 343 reg := &httpmock.Registry{} 344 reg.Register(httpmock.MatchAny, func(req *http.Request) (*http.Response, error) { 345 buf := new(strings.Builder) 346 _, err := io.Copy(buf, req.Body) 347 if err != nil { 348 return nil, err 349 } 350 query = buf.String() 351 return httpmock.StringResponse("{}")(req) 352 }) 353 client := newTestClient(reg) 354 355 _, err := RepoMilestones(client, ghrepo.New("OWNER", "REPO"), tt.state) 356 if (err != nil) != tt.wantErr { 357 t.Errorf("RepoMilestones() error = %v, wantErr %v", err, tt.wantErr) 358 return 359 } 360 if !strings.Contains(query, tt.want) { 361 t.Errorf("query does not contain %v", tt.want) 362 } 363 } 364 } 365 366 func TestDisplayName(t *testing.T) { 367 tests := []struct { 368 name string 369 assignee RepoAssignee 370 want string 371 }{ 372 { 373 name: "assignee with name", 374 assignee: RepoAssignee{"123", "octocat123", "Octavious Cath"}, 375 want: "octocat123 (Octavious Cath)", 376 }, 377 { 378 name: "assignee without name", 379 assignee: RepoAssignee{"123", "octocat123", ""}, 380 want: "octocat123", 381 }, 382 } 383 for _, tt := range tests { 384 actual := tt.assignee.DisplayName() 385 if actual != tt.want { 386 t.Errorf("display name was %s wanted %s", actual, tt.want) 387 } 388 } 389 }