github.com/ungtb10d/cli/v2@v2.0.0-20221110210412-98537dd9d6a1/pkg/cmd/repo/clone/clone_test.go (about) 1 package clone 2 3 import ( 4 "net/http" 5 "strings" 6 "testing" 7 8 "github.com/ungtb10d/cli/v2/git" 9 "github.com/ungtb10d/cli/v2/internal/config" 10 "github.com/ungtb10d/cli/v2/internal/run" 11 "github.com/ungtb10d/cli/v2/pkg/cmdutil" 12 "github.com/ungtb10d/cli/v2/pkg/httpmock" 13 "github.com/ungtb10d/cli/v2/pkg/iostreams" 14 "github.com/ungtb10d/cli/v2/test" 15 "github.com/google/shlex" 16 "github.com/stretchr/testify/assert" 17 "github.com/stretchr/testify/require" 18 ) 19 20 func TestNewCmdClone(t *testing.T) { 21 testCases := []struct { 22 name string 23 args string 24 wantOpts CloneOptions 25 wantErr string 26 }{ 27 { 28 name: "no arguments", 29 args: "", 30 wantErr: "cannot clone: repository argument required", 31 }, 32 { 33 name: "repo argument", 34 args: "OWNER/REPO", 35 wantOpts: CloneOptions{ 36 Repository: "OWNER/REPO", 37 GitArgs: []string{}, 38 }, 39 }, 40 { 41 name: "directory argument", 42 args: "OWNER/REPO mydir", 43 wantOpts: CloneOptions{ 44 Repository: "OWNER/REPO", 45 GitArgs: []string{"mydir"}, 46 }, 47 }, 48 { 49 name: "git clone arguments", 50 args: "OWNER/REPO -- --depth 1 --recurse-submodules", 51 wantOpts: CloneOptions{ 52 Repository: "OWNER/REPO", 53 GitArgs: []string{"--depth", "1", "--recurse-submodules"}, 54 }, 55 }, 56 { 57 name: "unknown argument", 58 args: "OWNER/REPO --depth 1", 59 wantErr: "unknown flag: --depth\nSeparate git clone flags with '--'.", 60 }, 61 } 62 for _, tt := range testCases { 63 t.Run(tt.name, func(t *testing.T) { 64 ios, stdin, stdout, stderr := iostreams.Test() 65 fac := &cmdutil.Factory{IOStreams: ios} 66 67 var opts *CloneOptions 68 cmd := NewCmdClone(fac, func(co *CloneOptions) error { 69 opts = co 70 return nil 71 }) 72 73 argv, err := shlex.Split(tt.args) 74 require.NoError(t, err) 75 cmd.SetArgs(argv) 76 77 cmd.SetIn(stdin) 78 cmd.SetOut(stderr) 79 cmd.SetErr(stderr) 80 81 _, err = cmd.ExecuteC() 82 if tt.wantErr != "" { 83 assert.EqualError(t, err, tt.wantErr) 84 return 85 } else { 86 assert.NoError(t, err) 87 } 88 89 assert.Equal(t, "", stdout.String()) 90 assert.Equal(t, "", stderr.String()) 91 92 assert.Equal(t, tt.wantOpts.Repository, opts.Repository) 93 assert.Equal(t, tt.wantOpts.GitArgs, opts.GitArgs) 94 }) 95 } 96 } 97 98 func runCloneCommand(httpClient *http.Client, cli string) (*test.CmdOut, error) { 99 ios, stdin, stdout, stderr := iostreams.Test() 100 fac := &cmdutil.Factory{ 101 IOStreams: ios, 102 HttpClient: func() (*http.Client, error) { 103 return httpClient, nil 104 }, 105 Config: func() (config.Config, error) { 106 return config.NewBlankConfig(), nil 107 }, 108 GitClient: &git.Client{GitPath: "some/path/git"}, 109 } 110 111 cmd := NewCmdClone(fac, nil) 112 113 argv, err := shlex.Split(cli) 114 cmd.SetArgs(argv) 115 116 cmd.SetIn(stdin) 117 cmd.SetOut(stderr) 118 cmd.SetErr(stderr) 119 120 if err != nil { 121 panic(err) 122 } 123 124 _, err = cmd.ExecuteC() 125 126 if err != nil { 127 return nil, err 128 } 129 130 return &test.CmdOut{OutBuf: stdout, ErrBuf: stderr}, nil 131 } 132 133 func Test_RepoClone(t *testing.T) { 134 tests := []struct { 135 name string 136 args string 137 want string 138 }{ 139 { 140 name: "shorthand", 141 args: "OWNER/REPO", 142 want: "git clone https://github.com/OWNER/REPO.git", 143 }, 144 { 145 name: "shorthand with directory", 146 args: "OWNER/REPO target_directory", 147 want: "git clone https://github.com/OWNER/REPO.git target_directory", 148 }, 149 { 150 name: "clone arguments", 151 args: "OWNER/REPO -- -o upstream --depth 1", 152 want: "git clone -o upstream --depth 1 https://github.com/OWNER/REPO.git", 153 }, 154 { 155 name: "clone arguments with directory", 156 args: "OWNER/REPO target_directory -- -o upstream --depth 1", 157 want: "git clone -o upstream --depth 1 https://github.com/OWNER/REPO.git target_directory", 158 }, 159 { 160 name: "HTTPS URL", 161 args: "https://github.com/OWNER/REPO", 162 want: "git clone https://github.com/OWNER/REPO.git", 163 }, 164 { 165 name: "SSH URL", 166 args: "git@github.com:OWNER/REPO.git", 167 want: "git clone git@github.com:OWNER/REPO.git", 168 }, 169 { 170 name: "Non-canonical capitalization", 171 args: "Owner/Repo", 172 want: "git clone https://github.com/OWNER/REPO.git", 173 }, 174 { 175 name: "clone wiki", 176 args: "Owner/Repo.wiki", 177 want: "git clone https://github.com/OWNER/REPO.wiki.git", 178 }, 179 { 180 name: "wiki URL", 181 args: "https://github.com/owner/repo.wiki", 182 want: "git clone https://github.com/OWNER/REPO.wiki.git", 183 }, 184 } 185 for _, tt := range tests { 186 t.Run(tt.name, func(t *testing.T) { 187 reg := &httpmock.Registry{} 188 defer reg.Verify(t) 189 reg.Register( 190 httpmock.GraphQL(`query RepositoryInfo\b`), 191 httpmock.StringResponse(` 192 { "data": { "repository": { 193 "name": "REPO", 194 "owner": { 195 "login": "OWNER" 196 }, 197 "hasWikiEnabled": true 198 } } } 199 `)) 200 201 httpClient := &http.Client{Transport: reg} 202 203 cs, restore := run.Stub() 204 defer restore(t) 205 cs.Register(`git clone`, 0, "", func(s []string) { 206 assert.Equal(t, tt.want, strings.Join(s, " ")) 207 }) 208 209 output, err := runCloneCommand(httpClient, tt.args) 210 if err != nil { 211 t.Fatalf("error running command `repo clone`: %v", err) 212 } 213 214 assert.Equal(t, "", output.String()) 215 assert.Equal(t, "", output.Stderr()) 216 }) 217 } 218 } 219 220 func Test_RepoClone_hasParent(t *testing.T) { 221 reg := &httpmock.Registry{} 222 reg.Register( 223 httpmock.GraphQL(`query RepositoryInfo\b`), 224 httpmock.StringResponse(` 225 { "data": { "repository": { 226 "name": "REPO", 227 "owner": { 228 "login": "OWNER" 229 }, 230 "parent": { 231 "name": "ORIG", 232 "owner": { 233 "login": "hubot" 234 }, 235 "defaultBranchRef": { 236 "name": "trunk" 237 } 238 } 239 } } } 240 `)) 241 242 httpClient := &http.Client{Transport: reg} 243 244 cs, cmdTeardown := run.Stub() 245 defer cmdTeardown(t) 246 247 cs.Register(`git clone https://github.com/OWNER/REPO.git`, 0, "") 248 cs.Register(`git -C REPO remote add -t trunk -f upstream https://github.com/hubot/ORIG.git`, 0, "") 249 250 _, err := runCloneCommand(httpClient, "OWNER/REPO") 251 if err != nil { 252 t.Fatalf("error running command `repo clone`: %v", err) 253 } 254 } 255 256 func Test_RepoClone_hasParent_upstreamRemoteName(t *testing.T) { 257 reg := &httpmock.Registry{} 258 reg.Register( 259 httpmock.GraphQL(`query RepositoryInfo\b`), 260 httpmock.StringResponse(` 261 { "data": { "repository": { 262 "name": "REPO", 263 "owner": { 264 "login": "OWNER" 265 }, 266 "parent": { 267 "name": "ORIG", 268 "owner": { 269 "login": "hubot" 270 }, 271 "defaultBranchRef": { 272 "name": "trunk" 273 } 274 } 275 } } } 276 `)) 277 278 httpClient := &http.Client{Transport: reg} 279 280 cs, cmdTeardown := run.Stub() 281 defer cmdTeardown(t) 282 283 cs.Register(`git clone https://github.com/OWNER/REPO.git`, 0, "") 284 cs.Register(`git -C REPO remote add -t trunk -f test https://github.com/hubot/ORIG.git`, 0, "") 285 286 _, err := runCloneCommand(httpClient, "OWNER/REPO --upstream-remote-name test") 287 if err != nil { 288 t.Fatalf("error running command `repo clone`: %v", err) 289 } 290 } 291 292 func Test_RepoClone_withoutUsername(t *testing.T) { 293 reg := &httpmock.Registry{} 294 defer reg.Verify(t) 295 reg.Register( 296 httpmock.GraphQL(`query UserCurrent\b`), 297 httpmock.StringResponse(` 298 { "data": { "viewer": { 299 "login": "OWNER" 300 }}}`)) 301 reg.Register( 302 httpmock.GraphQL(`query RepositoryInfo\b`), 303 httpmock.StringResponse(` 304 { "data": { "repository": { 305 "name": "REPO", 306 "owner": { 307 "login": "OWNER" 308 } 309 } } } 310 `)) 311 312 httpClient := &http.Client{Transport: reg} 313 314 cs, restore := run.Stub() 315 defer restore(t) 316 cs.Register(`git clone https://github\.com/OWNER/REPO\.git`, 0, "") 317 318 output, err := runCloneCommand(httpClient, "REPO") 319 if err != nil { 320 t.Fatalf("error running command `repo clone`: %v", err) 321 } 322 323 assert.Equal(t, "", output.String()) 324 assert.Equal(t, "", output.Stderr()) 325 }