1package create 2 3import ( 4 "net/http" 5 "testing" 6 7 "github.com/cli/cli/v2/internal/ghrepo" 8 "github.com/cli/cli/v2/pkg/httpmock" 9 "github.com/stretchr/testify/assert" 10) 11 12func Test_repoCreate(t *testing.T) { 13 tests := []struct { 14 name string 15 hostname string 16 input repoCreateInput 17 stubs func(t *testing.T, r *httpmock.Registry) 18 wantErr bool 19 wantRepo string 20 }{ 21 { 22 name: "create personal repository", 23 hostname: "github.com", 24 input: repoCreateInput{ 25 Name: "winter-foods", 26 Description: "roasted chestnuts", 27 HomepageURL: "http://example.com", 28 Visibility: "public", 29 HasIssuesEnabled: true, 30 HasWikiEnabled: true, 31 }, 32 stubs: func(t *testing.T, r *httpmock.Registry) { 33 r.Register( 34 httpmock.GraphQL(`mutation RepositoryCreate\b`), 35 httpmock.GraphQLMutation( 36 `{ 37 "data": { 38 "createRepository": { 39 "repository": { 40 "id": "REPOID", 41 "name": "REPO", 42 "owner": {"login":"OWNER"}, 43 "url": "the://URL" 44 } 45 } 46 } 47 }`, 48 func(inputs map[string]interface{}) { 49 assert.Equal(t, map[string]interface{}{ 50 "name": "winter-foods", 51 "description": "roasted chestnuts", 52 "homepageUrl": "http://example.com", 53 "visibility": "PUBLIC", 54 "hasIssuesEnabled": true, 55 "hasWikiEnabled": true, 56 }, inputs) 57 }), 58 ) 59 }, 60 wantRepo: "https://github.com/OWNER/REPO", 61 }, 62 { 63 name: "create Enterprise repository", 64 hostname: "example.com", 65 input: repoCreateInput{ 66 Name: "winter-foods", 67 Description: "roasted chestnuts", 68 HomepageURL: "http://example.com", 69 Visibility: "public", 70 HasIssuesEnabled: true, 71 HasWikiEnabled: true, 72 }, 73 stubs: func(t *testing.T, r *httpmock.Registry) { 74 r.Register( 75 httpmock.GraphQL(`mutation RepositoryCreate\b`), 76 httpmock.GraphQLMutation( 77 `{ 78 "data": { 79 "createRepository": { 80 "repository": { 81 "id": "REPOID", 82 "name": "REPO", 83 "owner": {"login":"OWNER"}, 84 "url": "the://URL" 85 } 86 } 87 } 88 }`, 89 func(inputs map[string]interface{}) { 90 assert.Equal(t, map[string]interface{}{ 91 "name": "winter-foods", 92 "description": "roasted chestnuts", 93 "homepageUrl": "http://example.com", 94 "visibility": "PUBLIC", 95 "hasIssuesEnabled": true, 96 "hasWikiEnabled": true, 97 }, inputs) 98 }), 99 ) 100 }, 101 wantRepo: "https://example.com/OWNER/REPO", 102 }, 103 { 104 name: "create in organization", 105 hostname: "github.com", 106 input: repoCreateInput{ 107 Name: "crisps", 108 Visibility: "internal", 109 OwnerLogin: "snacks-inc", 110 HasIssuesEnabled: true, 111 HasWikiEnabled: true, 112 }, 113 stubs: func(t *testing.T, r *httpmock.Registry) { 114 r.Register( 115 httpmock.REST("GET", "users/snacks-inc"), 116 httpmock.StringResponse(`{ "node_id": "ORGID", "type": "Organization" }`)) 117 r.Register( 118 httpmock.GraphQL(`mutation RepositoryCreate\b`), 119 httpmock.GraphQLMutation( 120 `{ 121 "data": { 122 "createRepository": { 123 "repository": { 124 "id": "REPOID", 125 "name": "REPO", 126 "owner": {"login":"OWNER"}, 127 "url": "the://URL" 128 } 129 } 130 } 131 }`, 132 func(inputs map[string]interface{}) { 133 assert.Equal(t, map[string]interface{}{ 134 "name": "crisps", 135 "visibility": "INTERNAL", 136 "ownerId": "ORGID", 137 "hasIssuesEnabled": true, 138 "hasWikiEnabled": true, 139 }, inputs) 140 }), 141 ) 142 }, 143 wantRepo: "https://github.com/OWNER/REPO", 144 }, 145 { 146 name: "create for team", 147 hostname: "github.com", 148 input: repoCreateInput{ 149 Name: "crisps", 150 Visibility: "internal", 151 OwnerLogin: "snacks-inc", 152 TeamSlug: "munchies", 153 HasIssuesEnabled: true, 154 HasWikiEnabled: true, 155 }, 156 stubs: func(t *testing.T, r *httpmock.Registry) { 157 r.Register( 158 httpmock.REST("GET", "orgs/snacks-inc/teams/munchies"), 159 httpmock.StringResponse(`{ "node_id": "TEAMID", "id": 1234, "organization": {"node_id": "ORGID"} }`)) 160 r.Register( 161 httpmock.GraphQL(`mutation RepositoryCreate\b`), 162 httpmock.GraphQLMutation( 163 `{ 164 "data": { 165 "createRepository": { 166 "repository": { 167 "id": "REPOID", 168 "name": "REPO", 169 "owner": {"login":"OWNER"}, 170 "url": "the://URL" 171 } 172 } 173 } 174 }`, 175 func(inputs map[string]interface{}) { 176 assert.Equal(t, map[string]interface{}{ 177 "name": "crisps", 178 "visibility": "INTERNAL", 179 "ownerId": "ORGID", 180 "teamId": "TEAMID", 181 "hasIssuesEnabled": true, 182 "hasWikiEnabled": true, 183 }, inputs) 184 }), 185 ) 186 }, 187 wantRepo: "https://github.com/OWNER/REPO", 188 }, 189 { 190 name: "create personal repo from template repo", 191 hostname: "github.com", 192 input: repoCreateInput{ 193 Name: "gen-project", 194 Description: "my generated project", 195 Visibility: "private", 196 TemplateRepositoryID: "TPLID", 197 HasIssuesEnabled: true, 198 HasWikiEnabled: true, 199 }, 200 stubs: func(t *testing.T, r *httpmock.Registry) { 201 r.Register( 202 httpmock.GraphQL(`query UserCurrent\b`), 203 httpmock.StringResponse(`{"data": {"viewer": {"id":"USERID"} } }`)) 204 r.Register( 205 httpmock.GraphQL(`mutation CloneTemplateRepository\b`), 206 httpmock.GraphQLMutation( 207 `{ 208 "data": { 209 "cloneTemplateRepository": { 210 "repository": { 211 "id": "REPOID", 212 "name": "REPO", 213 "owner": {"login":"OWNER"}, 214 "url": "the://URL" 215 } 216 } 217 } 218 }`, 219 func(inputs map[string]interface{}) { 220 assert.Equal(t, map[string]interface{}{ 221 "name": "gen-project", 222 "description": "my generated project", 223 "visibility": "PRIVATE", 224 "ownerId": "USERID", 225 "repositoryId": "TPLID", 226 }, inputs) 227 }), 228 ) 229 }, 230 wantRepo: "https://github.com/OWNER/REPO", 231 }, 232 { 233 name: "create org repo from template repo", 234 hostname: "github.com", 235 input: repoCreateInput{ 236 Name: "gen-project", 237 Description: "my generated project", 238 Visibility: "internal", 239 OwnerLogin: "myorg", 240 TemplateRepositoryID: "TPLID", 241 HasIssuesEnabled: true, 242 HasWikiEnabled: true, 243 }, 244 stubs: func(t *testing.T, r *httpmock.Registry) { 245 r.Register( 246 httpmock.REST("GET", "users/myorg"), 247 httpmock.StringResponse(`{ "node_id": "ORGID", "type": "Organization" }`)) 248 r.Register( 249 httpmock.GraphQL(`mutation CloneTemplateRepository\b`), 250 httpmock.GraphQLMutation( 251 `{ 252 "data": { 253 "cloneTemplateRepository": { 254 "repository": { 255 "id": "REPOID", 256 "name": "REPO", 257 "owner": {"login":"OWNER"}, 258 "url": "the://URL" 259 } 260 } 261 } 262 }`, 263 func(inputs map[string]interface{}) { 264 assert.Equal(t, map[string]interface{}{ 265 "name": "gen-project", 266 "description": "my generated project", 267 "visibility": "INTERNAL", 268 "ownerId": "ORGID", 269 "repositoryId": "TPLID", 270 }, inputs) 271 }), 272 ) 273 }, 274 wantRepo: "https://github.com/OWNER/REPO", 275 }, 276 { 277 name: "create with license and gitignore", 278 hostname: "github.com", 279 input: repoCreateInput{ 280 Name: "crisps", 281 Visibility: "private", 282 LicenseTemplate: "lgpl-3.0", 283 GitIgnoreTemplate: "Go", 284 HasIssuesEnabled: true, 285 HasWikiEnabled: true, 286 }, 287 stubs: func(t *testing.T, r *httpmock.Registry) { 288 r.Register( 289 httpmock.REST("POST", "user/repos"), 290 httpmock.RESTPayload(201, `{"name":"crisps", "owner":{"login": "snacks-inc"}, "html_url":"the://URL"}`, func(payload map[string]interface{}) { 291 assert.Equal(t, map[string]interface{}{ 292 "name": "crisps", 293 "private": true, 294 "gitignore_template": "Go", 295 "license_template": "lgpl-3.0", 296 "has_issues": true, 297 "has_wiki": true, 298 }, payload) 299 })) 300 }, 301 wantRepo: "https://github.com/snacks-inc/crisps", 302 }, 303 { 304 name: "create with license and gitignore on Enterprise", 305 hostname: "example.com", 306 input: repoCreateInput{ 307 Name: "crisps", 308 Visibility: "private", 309 LicenseTemplate: "lgpl-3.0", 310 GitIgnoreTemplate: "Go", 311 HasIssuesEnabled: true, 312 HasWikiEnabled: true, 313 }, 314 stubs: func(t *testing.T, r *httpmock.Registry) { 315 r.Register( 316 httpmock.REST("POST", "api/v3/user/repos"), 317 httpmock.RESTPayload(201, `{"name":"crisps", "owner":{"login": "snacks-inc"}, "html_url":"the://URL"}`, func(payload map[string]interface{}) { 318 assert.Equal(t, map[string]interface{}{ 319 "name": "crisps", 320 "private": true, 321 "gitignore_template": "Go", 322 "license_template": "lgpl-3.0", 323 "has_issues": true, 324 "has_wiki": true, 325 }, payload) 326 })) 327 }, 328 wantRepo: "https://example.com/snacks-inc/crisps", 329 }, 330 { 331 name: "create with license and gitignore in org", 332 hostname: "github.com", 333 input: repoCreateInput{ 334 Name: "crisps", 335 Visibility: "INTERNAL", 336 OwnerLogin: "snacks-inc", 337 LicenseTemplate: "lgpl-3.0", 338 GitIgnoreTemplate: "Go", 339 HasIssuesEnabled: true, 340 HasWikiEnabled: true, 341 }, 342 stubs: func(t *testing.T, r *httpmock.Registry) { 343 r.Register( 344 httpmock.REST("GET", "users/snacks-inc"), 345 httpmock.StringResponse(`{ "node_id": "ORGID", "type": "Organization" }`)) 346 r.Register( 347 httpmock.REST("POST", "orgs/snacks-inc/repos"), 348 httpmock.RESTPayload(201, `{"name":"crisps", "owner":{"login": "snacks-inc"}, "html_url":"the://URL"}`, func(payload map[string]interface{}) { 349 assert.Equal(t, map[string]interface{}{ 350 "name": "crisps", 351 "private": false, 352 "visibility": "internal", 353 "gitignore_template": "Go", 354 "license_template": "lgpl-3.0", 355 "has_issues": true, 356 "has_wiki": true, 357 }, payload) 358 })) 359 }, 360 wantRepo: "https://github.com/snacks-inc/crisps", 361 }, 362 { 363 name: "create with license and gitignore for team", 364 hostname: "github.com", 365 input: repoCreateInput{ 366 Name: "crisps", 367 Visibility: "internal", 368 OwnerLogin: "snacks-inc", 369 TeamSlug: "munchies", 370 LicenseTemplate: "lgpl-3.0", 371 GitIgnoreTemplate: "Go", 372 HasIssuesEnabled: true, 373 HasWikiEnabled: true, 374 }, 375 stubs: func(t *testing.T, r *httpmock.Registry) { 376 r.Register( 377 httpmock.REST("GET", "orgs/snacks-inc/teams/munchies"), 378 httpmock.StringResponse(`{ "node_id": "TEAMID", "id": 1234, "organization": {"node_id": "ORGID"} }`)) 379 r.Register( 380 httpmock.REST("POST", "orgs/snacks-inc/repos"), 381 httpmock.RESTPayload(201, `{"name":"crisps", "owner":{"login": "snacks-inc"}, "html_url":"the://URL"}`, func(payload map[string]interface{}) { 382 assert.Equal(t, map[string]interface{}{ 383 "name": "crisps", 384 "private": false, 385 "visibility": "internal", 386 "gitignore_template": "Go", 387 "license_template": "lgpl-3.0", 388 "team_id": float64(1234), 389 "has_issues": true, 390 "has_wiki": true, 391 }, payload) 392 })) 393 }, 394 wantRepo: "https://github.com/snacks-inc/crisps", 395 }, 396 } 397 for _, tt := range tests { 398 t.Run(tt.name, func(t *testing.T) { 399 reg := &httpmock.Registry{} 400 defer reg.Verify(t) 401 tt.stubs(t, reg) 402 httpClient := &http.Client{Transport: reg} 403 r, err := repoCreate(httpClient, tt.hostname, tt.input) 404 if tt.wantErr { 405 assert.Error(t, err) 406 return 407 } else { 408 assert.NoError(t, err) 409 } 410 assert.Equal(t, tt.wantRepo, ghrepo.GenerateRepoURL(r, "")) 411 }) 412 } 413} 414