Skip to content

Commit 8423de7

Browse files
committed
Add tests
1 parent 096f30a commit 8423de7

File tree

1 file changed

+331
-12
lines changed

1 file changed

+331
-12
lines changed

pkg/cmd/repo/sync/sync_test.go

Lines changed: 331 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,30 @@ package sync
22

33
import (
44
"bytes"
5+
"io/ioutil"
6+
"net/http"
57
"testing"
68

9+
"github.com/cli/cli/context"
10+
"github.com/cli/cli/git"
11+
"github.com/cli/cli/internal/ghrepo"
12+
"github.com/cli/cli/pkg/cmd/repo/sync/syncfakes"
713
"github.com/cli/cli/pkg/cmdutil"
14+
"github.com/cli/cli/pkg/httpmock"
815
"github.com/cli/cli/pkg/iostreams"
16+
"github.com/cli/cli/pkg/prompt"
917
"github.com/google/shlex"
1018
"github.com/stretchr/testify/assert"
1119
)
1220

1321
func TestNewCmdSync(t *testing.T) {
1422
tests := []struct {
15-
name string
16-
tty bool
17-
input string
18-
output SyncOptions
19-
wantsErr bool
20-
errMsg string
23+
name string
24+
tty bool
25+
input string
26+
output SyncOptions
27+
wantErr bool
28+
errMsg string
2129
}{
2230
{
2331
name: "no argument",
@@ -66,11 +74,11 @@ func TestNewCmdSync(t *testing.T) {
6674
},
6775
},
6876
{
69-
name: "notty without confirm",
70-
tty: false,
71-
input: "",
72-
wantsErr: true,
73-
errMsg: "`--confirm` required when not running interactively",
77+
name: "notty without confirm",
78+
tty: false,
79+
input: "",
80+
wantErr: true,
81+
errMsg: "`--confirm` required when not running interactively",
7482
},
7583
}
7684
for _, tt := range tests {
@@ -94,7 +102,7 @@ func TestNewCmdSync(t *testing.T) {
94102
cmd.SetErr(&bytes.Buffer{})
95103

96104
_, err = cmd.ExecuteC()
97-
if tt.wantsErr {
105+
if tt.wantErr {
98106
assert.Error(t, err)
99107
assert.Equal(t, tt.errMsg, err.Error())
100108
return
@@ -111,5 +119,316 @@ func TestNewCmdSync(t *testing.T) {
111119
}
112120

113121
func Test_SyncRun(t *testing.T) {
122+
stubConfirm := func(as *prompt.AskStubber) {
123+
as.StubOne(true)
124+
}
125+
126+
tests := []struct {
127+
name string
128+
tty bool
129+
opts *SyncOptions
130+
remotes []*context.Remote
131+
httpStubs func(*httpmock.Registry)
132+
gitStubs func(*syncfakes.FakeGitClient)
133+
askStubs func(*prompt.AskStubber)
134+
wantStdout string
135+
wantStderr string
136+
wantErr bool
137+
errMsg string
138+
}{
139+
{
140+
name: "sync local repo with parent - tty",
141+
tty: true,
142+
opts: &SyncOptions{},
143+
httpStubs: func(reg *httpmock.Registry) {
144+
reg.Register(
145+
httpmock.GraphQL(`query RepositoryInfo\b`),
146+
httpmock.StringResponse(`{"data":{"repository":{"defaultBranchRef":{"name": "trunk"}}}}`))
147+
},
148+
askStubs: stubConfirm,
149+
wantStdout: "✓ Synced .:trunk from OWNER/REPO:trunk\n",
150+
},
151+
{
152+
name: "sync local repo with parent - notty",
153+
tty: false,
154+
opts: &SyncOptions{
155+
SkipConfirm: true,
156+
},
157+
httpStubs: func(reg *httpmock.Registry) {
158+
reg.Register(
159+
httpmock.GraphQL(`query RepositoryInfo\b`),
160+
httpmock.StringResponse(`{"data":{"repository":{"defaultBranchRef":{"name": "trunk"}}}}`))
161+
},
162+
askStubs: stubConfirm,
163+
wantStdout: "",
164+
},
165+
{
166+
name: "sync local repo with specified source repo",
167+
tty: true,
168+
opts: &SyncOptions{
169+
SrcArg: "OWNER2/REPO2",
170+
},
171+
httpStubs: func(reg *httpmock.Registry) {
172+
reg.Register(
173+
httpmock.GraphQL(`query RepositoryInfo\b`),
174+
httpmock.StringResponse(`{"data":{"repository":{"defaultBranchRef":{"name": "trunk"}}}}`))
175+
},
176+
askStubs: stubConfirm,
177+
wantStdout: "✓ Synced .:trunk from OWNER2/REPO2:trunk\n",
178+
},
179+
{
180+
name: "sync local repo with parent and specified branch",
181+
tty: true,
182+
opts: &SyncOptions{
183+
Branch: "test",
184+
},
185+
askStubs: stubConfirm,
186+
wantStdout: "✓ Synced .:test from OWNER/REPO:test\n",
187+
},
188+
{
189+
name: "sync local repo with parent and force specified",
190+
tty: true,
191+
opts: &SyncOptions{
192+
Force: true,
193+
},
194+
httpStubs: func(reg *httpmock.Registry) {
195+
reg.Register(
196+
httpmock.GraphQL(`query RepositoryInfo\b`),
197+
httpmock.StringResponse(`{"data":{"repository":{"defaultBranchRef":{"name": "trunk"}}}}`))
198+
},
199+
gitStubs: func(fgc *syncfakes.FakeGitClient) {
200+
fgc.HasLocalBranchReturns(true)
201+
fgc.IsAncestorReturns(false, nil)
202+
},
203+
askStubs: stubConfirm,
204+
wantStderr: "! Using --force will cause diverging commits on .:trunk to be discarded\n",
205+
wantStdout: "✓ Synced .:trunk from OWNER/REPO:trunk\n",
206+
},
207+
{
208+
name: "sync local repo with parent and not fast forward merge",
209+
tty: true,
210+
opts: &SyncOptions{},
211+
httpStubs: func(reg *httpmock.Registry) {
212+
reg.Register(
213+
httpmock.GraphQL(`query RepositoryInfo\b`),
214+
httpmock.StringResponse(`{"data":{"repository":{"defaultBranchRef":{"name": "trunk"}}}}`))
215+
},
216+
gitStubs: func(fgc *syncfakes.FakeGitClient) {
217+
fgc.HasLocalBranchReturns(true)
218+
fgc.IsAncestorReturns(false, nil)
219+
},
220+
askStubs: stubConfirm,
221+
wantErr: true,
222+
errMsg: "can't sync because there are diverging commits, you can use `--force` to overwrite the commits on .:trunk",
223+
},
224+
{
225+
name: "sync remote fork with parent - tty",
226+
tty: true,
227+
opts: &SyncOptions{
228+
DestArg: "OWNER/REPO-FORK",
229+
},
230+
httpStubs: func(reg *httpmock.Registry) {
231+
reg.Register(
232+
httpmock.GraphQL(`query RepositoryFindParent\b`),
233+
httpmock.StringResponse(`{"data":{"repository":{"parent":{"name":"REPO","owner":{"login": "OWNER"}}}}}`))
234+
reg.Register(
235+
httpmock.GraphQL(`query RepositoryInfo\b`),
236+
httpmock.StringResponse(`{"data":{"repository":{"defaultBranchRef":{"name": "trunk"}}}}`))
237+
reg.Register(
238+
httpmock.REST("GET", "repos/OWNER/REPO/git/refs/heads/trunk"),
239+
httpmock.StringResponse(`{"object":{"sha":"0xDEADBEEF"}}`))
240+
reg.Register(
241+
httpmock.REST("PATCH", "repos/OWNER/REPO-FORK/git/refs/heads/trunk"),
242+
httpmock.StringResponse(`{}`))
243+
},
244+
askStubs: stubConfirm,
245+
wantStdout: "✓ Synced OWNER/REPO-FORK:trunk from OWNER/REPO:trunk\n",
246+
},
247+
{
248+
name: "sync remote fork with parent - notty",
249+
tty: false,
250+
opts: &SyncOptions{
251+
DestArg: "OWNER/REPO-FORK",
252+
SkipConfirm: true,
253+
},
254+
httpStubs: func(reg *httpmock.Registry) {
255+
reg.Register(
256+
httpmock.GraphQL(`query RepositoryFindParent\b`),
257+
httpmock.StringResponse(`{"data":{"repository":{"parent":{"name":"REPO","owner":{"login": "OWNER"}}}}}`))
258+
reg.Register(
259+
httpmock.GraphQL(`query RepositoryInfo\b`),
260+
httpmock.StringResponse(`{"data":{"repository":{"defaultBranchRef":{"name": "trunk"}}}}`))
261+
reg.Register(
262+
httpmock.REST("GET", "repos/OWNER/REPO/git/refs/heads/trunk"),
263+
httpmock.StringResponse(`{"object":{"sha":"0xDEADBEEF"}}`))
264+
reg.Register(
265+
httpmock.REST("PATCH", "repos/OWNER/REPO-FORK/git/refs/heads/trunk"),
266+
httpmock.StringResponse(`{}`))
267+
},
268+
wantStdout: "",
269+
},
270+
{
271+
name: "sync remote repo with no parent",
272+
tty: true,
273+
opts: &SyncOptions{
274+
DestArg: "OWNER/REPO",
275+
},
276+
httpStubs: func(reg *httpmock.Registry) {
277+
reg.Register(
278+
httpmock.GraphQL(`query RepositoryFindParent\b`),
279+
httpmock.StringResponse(`{"data":{"repository":{}}}`))
280+
},
281+
wantErr: true,
282+
errMsg: "can't determine source repo for OWNER/REPO because repo is not fork",
283+
},
284+
{
285+
name: "sync remote repo with specified source repo",
286+
tty: true,
287+
opts: &SyncOptions{
288+
DestArg: "OWNER/REPO",
289+
SrcArg: "OWNER2/REPO2",
290+
},
291+
httpStubs: func(reg *httpmock.Registry) {
292+
reg.Register(
293+
httpmock.GraphQL(`query RepositoryInfo\b`),
294+
httpmock.StringResponse(`{"data":{"repository":{"defaultBranchRef":{"name": "trunk"}}}}`))
295+
reg.Register(
296+
httpmock.REST("GET", "repos/OWNER2/REPO2/git/refs/heads/trunk"),
297+
httpmock.StringResponse(`{"object":{"sha":"0xDEADBEEF"}}`))
298+
reg.Register(
299+
httpmock.REST("PATCH", "repos/OWNER/REPO/git/refs/heads/trunk"),
300+
httpmock.StringResponse(`{}`))
301+
},
302+
askStubs: stubConfirm,
303+
wantStdout: "✓ Synced OWNER/REPO:trunk from OWNER2/REPO2:trunk\n",
304+
},
305+
{
306+
name: "sync remote fork with parent and specified branch",
307+
tty: true,
308+
opts: &SyncOptions{
309+
DestArg: "OWNER/REPO-FORK",
310+
Branch: "test",
311+
},
312+
httpStubs: func(reg *httpmock.Registry) {
313+
reg.Register(
314+
httpmock.GraphQL(`query RepositoryFindParent\b`),
315+
httpmock.StringResponse(`{"data":{"repository":{"parent":{"name":"REPO","owner":{"login": "OWNER"}}}}}`))
316+
reg.Register(
317+
httpmock.REST("GET", "repos/OWNER/REPO/git/refs/heads/test"),
318+
httpmock.StringResponse(`{"object":{"sha":"0xDEADBEEF"}}`))
319+
reg.Register(
320+
httpmock.REST("PATCH", "repos/OWNER/REPO-FORK/git/refs/heads/test"),
321+
httpmock.StringResponse(`{}`))
322+
},
323+
askStubs: stubConfirm,
324+
wantStdout: "✓ Synced OWNER/REPO-FORK:test from OWNER/REPO:test\n",
325+
},
326+
{
327+
name: "sync remote fork with parent and force specified",
328+
tty: true,
329+
opts: &SyncOptions{
330+
DestArg: "OWNER/REPO-FORK",
331+
Force: true,
332+
},
333+
httpStubs: func(reg *httpmock.Registry) {
334+
reg.Register(
335+
httpmock.GraphQL(`query RepositoryFindParent\b`),
336+
httpmock.StringResponse(`{"data":{"repository":{"parent":{"name":"REPO","owner":{"login": "OWNER"}}}}}`))
337+
reg.Register(
338+
httpmock.GraphQL(`query RepositoryInfo\b`),
339+
httpmock.StringResponse(`{"data":{"repository":{"defaultBranchRef":{"name": "trunk"}}}}`))
340+
reg.Register(
341+
httpmock.REST("GET", "repos/OWNER/REPO/git/refs/heads/trunk"),
342+
httpmock.StringResponse(`{"object":{"sha":"0xDEADBEEF"}}`))
343+
reg.Register(
344+
httpmock.REST("PATCH", "repos/OWNER/REPO-FORK/git/refs/heads/trunk"),
345+
httpmock.StringResponse(`{}`))
346+
},
347+
askStubs: stubConfirm,
348+
wantStderr: "! Using --force will cause diverging commits on OWNER/REPO-FORK:trunk to be discarded\n",
349+
wantStdout: "✓ Synced OWNER/REPO-FORK:trunk from OWNER/REPO:trunk\n",
350+
},
351+
{
352+
name: "sync remote fork with parent and not fast forward merge",
353+
tty: true,
354+
opts: &SyncOptions{
355+
DestArg: "OWNER/REPO-FORK",
356+
},
357+
httpStubs: func(reg *httpmock.Registry) {
358+
reg.Register(
359+
httpmock.GraphQL(`query RepositoryFindParent\b`),
360+
httpmock.StringResponse(`{"data":{"repository":{"parent":{"name":"REPO","owner":{"login": "OWNER"}}}}}`))
361+
reg.Register(
362+
httpmock.GraphQL(`query RepositoryInfo\b`),
363+
httpmock.StringResponse(`{"data":{"repository":{"defaultBranchRef":{"name": "trunk"}}}}`))
364+
reg.Register(
365+
httpmock.REST("GET", "repos/OWNER/REPO/git/refs/heads/trunk"),
366+
httpmock.StringResponse(`{"object":{"sha":"0xDEADBEEF"}}`))
367+
reg.Register(
368+
httpmock.REST("PATCH", "repos/OWNER/REPO-FORK/git/refs/heads/trunk"),
369+
func(req *http.Request) (*http.Response, error) {
370+
return &http.Response{
371+
StatusCode: 422,
372+
Request: req,
373+
Header: map[string][]string{"Content-Type": {"application/json"}},
374+
Body: ioutil.NopCloser(bytes.NewBufferString(`{"message":"Update is not a fast forward"}`)),
375+
}, nil
376+
})
377+
},
378+
askStubs: stubConfirm,
379+
wantErr: true,
380+
errMsg: "can't sync because there are diverging commits, you can use `--force` to overwrite the commits on OWNER/REPO-FORK:trunk",
381+
},
382+
}
383+
for _, tt := range tests {
384+
reg := &httpmock.Registry{}
385+
if tt.httpStubs != nil {
386+
tt.httpStubs(reg)
387+
}
388+
tt.opts.HttpClient = func() (*http.Client, error) {
389+
return &http.Client{Transport: reg}, nil
390+
}
391+
392+
io, _, stdout, stderr := iostreams.Test()
393+
io.SetStdinTTY(tt.tty)
394+
io.SetStdoutTTY(tt.tty)
395+
tt.opts.IO = io
396+
397+
tt.opts.BaseRepo = func() (ghrepo.Interface, error) {
398+
repo, _ := ghrepo.FromFullName("OWNER/REPO")
399+
return repo, nil
400+
}
401+
402+
tt.opts.Remotes = func() (context.Remotes, error) {
403+
if tt.remotes == nil {
404+
return []*context.Remote{{Remote: &git.Remote{Name: "origin"}}}, nil
405+
}
406+
return tt.remotes, nil
407+
}
408+
409+
var gitClient = &syncfakes.FakeGitClient{}
410+
if tt.gitStubs != nil {
411+
tt.gitStubs(gitClient)
412+
}
413+
tt.opts.Git = gitClient
114414

415+
as, teardown := prompt.InitAskStubber()
416+
defer teardown()
417+
if tt.askStubs != nil {
418+
tt.askStubs(as)
419+
}
420+
421+
t.Run(tt.name, func(t *testing.T) {
422+
defer reg.Verify(t)
423+
err := syncRun(tt.opts)
424+
if tt.wantErr {
425+
assert.Error(t, err)
426+
assert.Equal(t, tt.errMsg, err.Error())
427+
return
428+
}
429+
assert.NoError(t, err)
430+
assert.Equal(t, tt.wantStderr, stderr.String())
431+
assert.Equal(t, tt.wantStdout, stdout.String())
432+
})
433+
}
115434
}

0 commit comments

Comments
 (0)