Skip to content

Commit 3347006

Browse files
committed
abstract clone repo to test better
1 parent d99779e commit 3347006

File tree

2 files changed

+132
-0
lines changed

2 files changed

+132
-0
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// cmd/gh-classroom/clone/utils/clone-repository.go
2+
3+
package utils
4+
5+
import (
6+
"fmt"
7+
"os"
8+
"bytes"
9+
)
10+
11+
// GitHubExec defines an interface for executing GitHub CLI commands.
12+
// This abstraction allows for easier testing and decoupling from the actual CLI.
13+
// Exec invokes a gh command in a subprocess and captures the output and error streams.
14+
type GitHubExec interface {
15+
Exec(args ...string) (stdout, stderr bytes.Buffer, err error)
16+
}
17+
18+
// CloneRepository attempts to clone a repository into the specified path.
19+
// It returns an error if the cloning process fails.
20+
func CloneRepository(clonePath string, repoFullName string, gh GitHubExec) error {
21+
if _, err := os.Stat(clonePath); os.IsNotExist(err) {
22+
fmt.Printf("Cloning into: %v\n", clonePath)
23+
_, _, err := gh.Exec("repo", "clone", repoFullName, "--", clonePath)
24+
if err != nil {
25+
fmt.Printf("error cloning %s: %v\n", repoFullName, err)
26+
return fmt.Errorf("error cloning %s: %v", repoFullName, err)
27+
}
28+
return nil // Success
29+
}
30+
fmt.Printf("Skip existing repo: %v use gh classroom pull to get new commits\n", clonePath)
31+
return fmt.Errorf("repository already exists: %s", clonePath)
32+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package utils
2+
3+
import (
4+
"io/ioutil"
5+
"os"
6+
"testing"
7+
"bytes"
8+
"fmt"
9+
"errors"
10+
"path/filepath"
11+
)
12+
13+
// MockGitHubExec is a mock of the GitHubExec interface for testing.
14+
type MockGitHubExec struct {
15+
ExecFunc func(args ...string) (stdout, stderr bytes.Buffer, err error)
16+
}
17+
18+
func (m *MockGitHubExec) Exec(args ...string) (stdout, stderr bytes.Buffer, err error) {
19+
return m.ExecFunc(args...)
20+
}
21+
22+
23+
func TestCloneRepository(t *testing.T) {
24+
tmpDir, err := ioutil.TempDir("", "cloneTest")
25+
if err != nil {
26+
t.Fatalf("Failed to create temp directory: %v", err)
27+
}
28+
defer os.RemoveAll(tmpDir) // clean up
29+
// Test cases
30+
tests := []struct {
31+
name string
32+
execMock *MockGitHubExec
33+
clonePath string
34+
repoFullName string
35+
wantErr bool
36+
errMsg string
37+
}{
38+
{
39+
name: "successful clone",
40+
execMock: &MockGitHubExec{
41+
ExecFunc: func(args ...string) (stdout bytes.Buffer, stderr bytes.Buffer, err error) {
42+
var stdoutBuf, stderrBuf bytes.Buffer
43+
stdoutBuf.Write([]byte("your string here"))
44+
return stdoutBuf, stderrBuf, nil
45+
},
46+
},
47+
clonePath: "", // Will be set to a temp directory in the test
48+
repoFullName: "example/repo",
49+
wantErr: false,
50+
},
51+
{
52+
name: "clone failure",
53+
execMock: &MockGitHubExec{
54+
ExecFunc: func(args ...string) (stdout bytes.Buffer, stderr bytes.Buffer, err error) {
55+
var stdoutBuf, stderrBuf bytes.Buffer
56+
return stdoutBuf, stderrBuf, errors.New("clone error")
57+
},
58+
},
59+
clonePath: filepath.Join(tmpDir, "repo"),
60+
repoFullName: "example/repo",
61+
wantErr: true,
62+
errMsg: "error cloning example/repo: clone error",
63+
},
64+
{
65+
name: "repository already exists",
66+
execMock: &MockGitHubExec{
67+
ExecFunc: func(args ...string) (stdout bytes.Buffer, stderr bytes.Buffer, err error) {
68+
var stdoutBuf, stderrBuf bytes.Buffer
69+
return stdoutBuf, stderrBuf, nil
70+
},
71+
},
72+
clonePath: "./repo", // Current directory always exists
73+
repoFullName: "example/repo",
74+
wantErr: true,
75+
errMsg: "repository already exists: .",
76+
},
77+
}
78+
79+
80+
for _, tt := range tests {
81+
t.Run(tt.name, func(t *testing.T) {
82+
if tt.name == "successful clone" {
83+
fmt.Println("Running successful clone test")
84+
tmpDir, err := ioutil.TempDir("", "cloneTest")
85+
if err != nil {
86+
t.Fatalf("Failed to create temp directory: %v", err)
87+
}
88+
defer os.RemoveAll(tmpDir) // clean up
89+
tt.clonePath = filepath.Join(tmpDir, "repo")
90+
}
91+
92+
93+
fmt.Println("Running test", tt.name, "with clonePath", tt.clonePath, "and repoFullName", tt.repoFullName)
94+
err := CloneRepository(tt.clonePath, tt.repoFullName, tt.execMock)
95+
if err != nil && err.Error() != tt.errMsg {
96+
t.Errorf("CloneRepository() error = %v, wantErr %v", err, tt.wantErr)
97+
}
98+
})
99+
}
100+
}

0 commit comments

Comments
 (0)