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