-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
671 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
// Package gittest provides a test helper for creating a git server that serves a git repository. | ||
package gittest | ||
|
||
import ( | ||
"bytes" | ||
"encoding/pem" | ||
"fmt" | ||
"net" | ||
"net/http/cgi" | ||
"net/url" | ||
"os" | ||
"os/exec" | ||
"path/filepath" | ||
"strings" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
|
||
"github.com/rudderlabs/rudder-go-kit/testhelper/httptest" | ||
) | ||
|
||
type Server struct { | ||
*httptest.Server | ||
URL string | ||
} | ||
|
||
// NewHttpServer creates a new httptest.Server that serves a git repository from the given sourcePath. | ||
func NewHttpServer(t testing.TB, sourcePath string) *Server { | ||
return newServer(t, sourcePath, false) | ||
} | ||
|
||
// NewHttpsServer creates a new httptest.Server that serves a git repository from the given sourcePath. | ||
func NewHttpsServer(t testing.TB, sourcePath string) *Server { | ||
return newServer(t, sourcePath, true) | ||
} | ||
|
||
// newServer creates a new httptest.Server that serves a git repository from the given sourcePath. | ||
func newServer(t testing.TB, sourcePath string, secure bool) *Server { | ||
t.Helper() | ||
tempDir := t.TempDir() | ||
org := "org" | ||
repo := filepath.Base(sourcePath) | ||
if !strings.HasSuffix(repo, ".git") { | ||
repo = repo + ".git" | ||
} | ||
source := sourcePath | ||
if !strings.HasSuffix(sourcePath, "/") { | ||
source = source + "/" | ||
} | ||
|
||
workingDir := filepath.Join(tempDir, "workdir") | ||
require.NoErrorf(t, os.MkdirAll(workingDir, os.ModePerm), "should be able to create %s", workingDir) | ||
gitRoot := filepath.Join(tempDir, org, repo) | ||
require.NoErrorf(t, os.MkdirAll(gitRoot, os.ModePerm), "should be able to create %s", gitRoot) | ||
|
||
out, err := execCmd("rsync", "--recursive", source, workingDir) | ||
require.NoErrorf(t, err, "should be able to copy %s to %s: %s", source, workingDir, out) | ||
|
||
gitPath, err := exec.LookPath("git") | ||
require.NoError(t, err, "should be able to find git in PATH") | ||
out, err = execCmd("git", "init", workingDir) | ||
require.NoErrorf(t, err, "should be able to initialize git repository: %s", out) | ||
out, err = execCmd("git", "-C", workingDir, "branch", "-m", "main") | ||
require.NoErrorf(t, err, "should be able to rename the default branch to main: %s", out) | ||
out, err = execCmd("git", "-C", workingDir, "add", ".") | ||
require.NoErrorf(t, err, "should be able to add files to git repository: %s", out) | ||
commitCmd := exec.Command("git", "-C", workingDir, "commit", "-m", "initial commit") | ||
commitCmd.Env = append(commitCmd.Env, "GIT_AUTHOR_NAME=git test", "GIT_COMMITTER_NAME=git test", "GIT_AUTHOR_EMAIL=gittest@example.com", "GIT_COMMITTER_EMAIL=gittest@example.com") | ||
require.NoError(t, commitCmd.Run(), "should be able to commit files to git repository") | ||
|
||
out, err = execCmd("git", "clone", "--bare", workingDir, gitRoot) | ||
require.NoErrorf(t, err, "should be able to clone the bare repository: %s", out) | ||
|
||
handler := &cgi.Handler{ | ||
Path: gitPath, | ||
Args: []string{"http-backend"}, | ||
Env: []string{ | ||
fmt.Sprintf("GIT_PROJECT_ROOT=%s", tempDir), | ||
"GIT_HTTP_EXPORT_ALL=true", | ||
"REMOTE_USER=git", | ||
}, | ||
} | ||
|
||
localIP := getLocalIP(t) | ||
var s *httptest.Server | ||
if !secure { | ||
s = httptest.NewServer(handler) | ||
} else { | ||
s = httptest.NewTLSServer(localIP, handler) | ||
certPath := filepath.Join(tempDir, "server.crt") | ||
require.NoError(t, writeServerCA(s, certPath)) | ||
t.Setenv("SSL_CERT_FILE", certPath) | ||
|
||
} | ||
serverURL, err := url.Parse(s.URL) | ||
require.NoError(t, err) | ||
_, port, err := net.SplitHostPort(serverURL.Host) | ||
require.NoError(t, err) | ||
serverURL.Host = net.JoinHostPort(getLocalIP(t), port) | ||
url := serverURL.String() + "/" + org + "/" + repo | ||
return &Server{ | ||
Server: s, | ||
URL: url, | ||
} | ||
} | ||
|
||
// getServerCA returns a byte slice containing the PEM encoding of the server's CA certificate | ||
func (s *Server) GetServerCA() []byte { | ||
return getServerCA(s.Server) | ||
} | ||
|
||
func getServerCA(server *httptest.Server) []byte { | ||
return pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: server.TLS.Certificates[0].Certificate[0]}) | ||
} | ||
|
||
// writeServerCA writes the PEM-encoded server certificate to a given path | ||
func writeServerCA(server *httptest.Server, path string) error { | ||
certOut, err := os.Create(path) | ||
if err != nil { | ||
return err | ||
} | ||
defer certOut.Close() | ||
|
||
if _, err := certOut.Write(getServerCA(server)); err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func getLocalIP(t testing.TB) string { | ||
conn, err := net.Dial("udp", "8.8.8.8:80") | ||
require.NoError(t, err) | ||
defer func() { | ||
require.NoError(t, conn.Close()) | ||
}() | ||
localAddr := conn.LocalAddr().(*net.UDPAddr) | ||
return localAddr.IP.String() | ||
} | ||
|
||
func execCmd(name string, args ...string) (string, error) { | ||
var buf bytes.Buffer | ||
cmd := exec.Command(name, args...) | ||
cmd.Stdout = &buf | ||
cmd.Stderr = &buf | ||
err := cmd.Run() | ||
return buf.String(), err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
package gittest_test | ||
|
||
import ( | ||
"bytes" | ||
"os" | ||
"os/exec" | ||
"path/filepath" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
|
||
"github.com/rudderlabs/rudder-go-kit/testhelper/gittest" | ||
) | ||
|
||
func TestGitServer(t *testing.T) { | ||
t.Setenv("GIT_AUTHOR_NAME", "unit test") | ||
t.Setenv("GIT_COMMITTER_NAME", "unit test") | ||
t.Setenv("GIT_AUTHOR_EMAIL", "unit.test@example.com") | ||
t.Setenv("GIT_COMMITTER_EMAIL", "unit.test@example.com") | ||
t.Run("http", func(t *testing.T) { | ||
s := gittest.NewHttpServer(t, "testdata/gitrepo") | ||
defer s.Close() | ||
tempDir := t.TempDir() | ||
url := s.URL | ||
out, err := execCmd("git", "clone", url, tempDir) | ||
require.NoErrorf(t, err, "should be able to clone the repository: %s", out) | ||
require.FileExists(t, tempDir+"/README.md", "README.md should exist in the cloned repository") | ||
|
||
require.NoError(t, os.WriteFile(filepath.Join(tempDir, "file1.txt"), []byte("Hello, World!"), 0o644), "should be able to write to file1.txt") | ||
out, err = execCmd("git", "-C", tempDir, "add", "file1.txt") | ||
require.NoErrorf(t, err, "should be able to add file1.txt: %s", out) | ||
|
||
out, err = execCmd("git", "-C", tempDir, "commit", "-m", "add file1.txt") | ||
require.NoErrorf(t, err, "should be able to commit file1.txt: %s", out) | ||
|
||
out, err = execCmd("git", "-C", tempDir, "push", "origin", "main") | ||
require.NoErrorf(t, err, "should be able to push the main branch: %s", out) | ||
|
||
out, err = execCmd("git", "-C", tempDir, "checkout", "-b", "develop") | ||
require.NoErrorf(t, err, "should be able to create a develop repository: %s", out) | ||
|
||
out, err = execCmd("git", "-C", tempDir, "push", "origin", "develop:develop") | ||
require.NoErrorf(t, err, "should be able to push the develop branch: %s", out) | ||
|
||
require.NoError(t, os.WriteFile(filepath.Join(tempDir, "file2.txt"), []byte("Hello, World!"), 0o644), "should be able to write to file2.txt") | ||
out, err = execCmd("git", "-C", tempDir, "add", "file2.txt") | ||
require.NoErrorf(t, err, "should be able to add file2.txt: %s", out) | ||
|
||
out, err = execCmd("git", "-C", tempDir, "commit", "-m", "add file2.txt") | ||
require.NoErrorf(t, err, "should be able to commit file2.txt: %s", out) | ||
|
||
out, err = execCmd("git", "-C", tempDir, "push", "origin", "develop") | ||
require.NoErrorf(t, err, "should be able to push the develop branch: %s", out) | ||
|
||
out, err = execCmd("git", "-C", tempDir, "tag", "-a", "v1.0.0", "-m", "v1.0.0") | ||
require.NoErrorf(t, err, "should be able to create a tag: %s", out) | ||
|
||
out, err = execCmd("git", "-C", tempDir, "push", "origin", "v1.0.0") | ||
require.NoErrorf(t, err, "should be able to push the tag: %s", out) | ||
}) | ||
|
||
t.Run("https", func(t *testing.T) { | ||
s := gittest.NewHttpsServer(t, "testdata/gitrepo") | ||
defer s.Close() | ||
tempDir := t.TempDir() | ||
url := s.URL | ||
require.NoError(t, exec.Command("git", "-c", "http.sslVerify=false", "clone", url, tempDir).Run(), "should be able to clone the repository") | ||
require.FileExists(t, tempDir+"/README.md", "README.md should exist in the cloned repository") | ||
}) | ||
} | ||
|
||
func execCmd(name string, args ...string) (string, error) { // nolint: unparam | ||
var buf bytes.Buffer | ||
cmd := exec.Command(name, args...) | ||
cmd.Stdout = &buf | ||
cmd.Stderr = &buf | ||
err := cmd.Run() | ||
return buf.String(), err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Hello World |
Oops, something went wrong.