diff --git a/Makefile b/Makefile index 45e13717..2730a530 100644 --- a/Makefile +++ b/Makefile @@ -63,7 +63,7 @@ test-unit-docker: ## run unit tests with docker .PHONY: test-e2e test-e2e: - ./scripts/test_e2e.sh + cd e2etests && go test --tags e2etests .PHONY: test-e2e-docker test-e2e-docker: diff --git a/appveyor.yml b/appveyor.yml index bb5775df..5159492d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -11,4 +11,5 @@ environment: stack: go 1.13 test_script: - - go test ./... \ No newline at end of file + - go test ./... + diff --git a/e2etests/all_test.go b/e2etests/all_test.go new file mode 100644 index 00000000..92ac1579 --- /dev/null +++ b/e2etests/all_test.go @@ -0,0 +1,144 @@ +// +build e2etests + +package e2etests + +import ( + "context" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "os" + "os/exec" + "path/filepath" + "testing" + + "github.com/gobuffalo/envy" + "github.com/stretchr/testify/suite" +) + +type E2eSuite struct { + suite.Suite + goBinaryPath string + env []string + goPath string + sampleRepoPath string + stopAthens context.CancelFunc +} + +type catalogRes struct { + Modules []struct { + Module string `json:"module"` + Version string `json:"version"` + } `json:"modules"` +} + +func (m *E2eSuite) SetupSuite() { + var err error + m.goPath, err = ioutil.TempDir("/tmp", "gopath") + if err != nil { + m.Fail("Failed to make temp dir", err) + } + + m.sampleRepoPath, err = ioutil.TempDir("/tmp", "repopath") + if err != nil { + m.Fail("Failed to make temp dir for sample repo", err) + } + + m.goBinaryPath = envy.Get("GO_BINARY_PATH", "go") + + athensBin, err := buildAthens(m.goBinaryPath, m.goPath, m.env) + if err != nil { + m.Fail("Failed to build athens ", err) + } + stopAthens() // in case a dangling instance was around. + // ignoring error as if no athens is running it fails. + + ctx := context.Background() + ctx, m.stopAthens = context.WithCancel(ctx) + runAthensAndWait(ctx, athensBin, m.getEnv()) + setupTestRepo(m.sampleRepoPath, "https://github.com/athens-artifacts/happy-path.git") +} + +func (m *E2eSuite) TearDownSuite() { + m.stopAthens() + chmodR(m.goPath, 0777) + os.RemoveAll(m.goPath) + chmodR(m.sampleRepoPath, 0777) + os.RemoveAll(m.sampleRepoPath) +} + +func TestE2E(t *testing.T) { + suite.Run(t, &E2eSuite{}) +} + +func (m *E2eSuite) SetupTest() { + chmodR(m.goPath, 0777) + err := cleanGoCache(m.getEnv()) + if err != nil { + m.Fail("Failed to clear go cache", err) + } +} + +func (m *E2eSuite) TestNoGoProxy() { + cmd := exec.Command("go", "run", ".") + cmd.Env = m.env + cmd.Dir = m.sampleRepoPath + + err := cmd.Run() + if err != nil { + m.Fail("go run failed on test repo", err) + } +} + +func (m *E2eSuite) TestGoProxy() { + cmd := exec.Command("go", "run", ".") + cmd.Env = m.getEnvGoProxy(m.goPath) + cmd.Dir = m.sampleRepoPath + err := cmd.Run() + if err != nil { + m.Fail("go run failed on test repo", err) + } + resp, err := http.Get("http://localhost:3000/catalog") + if err != nil { + m.Fail("failed to read catalog", err) + } + + var catalog catalogRes + err = json.NewDecoder(resp.Body).Decode(&catalog) + if err != nil { + m.Fail("failed to decode catalog res", err) + } + m.Assert().Equal(len(catalog.Modules), 1) + m.Assert().Equal(catalog.Modules[0].Module, "github.com/athens-artifacts/no-tags") +} + +func (m *E2eSuite) TestWrongGoProxy() { + cmd := exec.Command("go", "run", ".") + cmd.Env = m.getEnvWrongGoProxy(m.goPath) + cmd.Dir = m.sampleRepoPath + err := cmd.Run() + m.Assert().NotNil(err, "Wrong proxy should fail") +} + +func (m *E2eSuite) getEnv() []string { + res := []string{ + fmt.Sprintf("GOPATH=%s", m.goPath), + "GO111MODULE=on", + fmt.Sprintf("PATH=%s", os.Getenv("PATH")), + fmt.Sprintf("GOCACHE=%s", filepath.Join(m.goPath, "cache")), + } + return res +} + +func (m *E2eSuite) getEnvGoProxy(gopath string) []string { + res := m.getEnv() + res = append(res, "GOPROXY=http://localhost:3000") + return res +} + +func (m *E2eSuite) getEnvWrongGoProxy(gopath string) []string { + res := m.getEnv() + res = append(res, "GOPROXY=http://localhost:3001") + return res +} diff --git a/e2etests/filesystem.go b/e2etests/filesystem.go new file mode 100644 index 00000000..7ff059c9 --- /dev/null +++ b/e2etests/filesystem.go @@ -0,0 +1,39 @@ +// +build e2etests + +package e2etests + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" +) + +func setupTestRepo(repoPath, repoURL string) { + os.RemoveAll(repoPath) + cmd := exec.Command("git", + "clone", + repoURL, + repoPath) + + cmd.Run() +} + +func chmodR(path string, mode os.FileMode) error { + return filepath.Walk(path, func(name string, info os.FileInfo, err error) error { + if err == nil { + os.Chmod(name, mode) + } + return err + }) +} + +func cleanGoCache(env []string) error { + cmd := exec.Command("go", "clean", "--modcache") + cmd.Env = env + output, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("Failed to clear go cache: %v - %s", err, string(output)) + } + return nil +} diff --git a/e2etests/run_athens.go b/e2etests/run_athens.go new file mode 100644 index 00000000..5546a0fe --- /dev/null +++ b/e2etests/run_athens.go @@ -0,0 +1,61 @@ +// +build e2etests + +package e2etests + +import ( + "context" + "fmt" + "net/http" + "os/exec" + "path" + "path/filepath" + "time" +) + +func buildAthens(goBin, destPath string, env []string) (string, error) { + target := path.Join(destPath, "athens-proxy") + binFolder, err := filepath.Abs("../cmd/proxy") + if err != nil { + return "", fmt.Errorf("Failed to get athens source path %v", err) + } + + cmd := exec.Command(goBin, "build", "-o", target, binFolder) + cmd.Env = env + output, err := cmd.CombinedOutput() + if err != nil { + return "", fmt.Errorf("Failed to build athens: %v - %s", err, string(output)) + } + return target, nil +} + +func stopAthens() error { + cmd := exec.Command("pkill", "athens-proxy") + output, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("Failed to stop athens: %v - %s", err, string(output)) + } + return err +} + +func runAthensAndWait(ctx context.Context, athensBin string, env []string) error { + cmd := exec.CommandContext(ctx, athensBin) + cmd.Env = env + + cmd.Start() + + ticker := time.NewTicker(time.Second) + timeout := time.After(20 * time.Second) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + resp, _ := http.Get("http://localhost:3000/readyz") + if resp.StatusCode == 200 { + return nil + } + case <-timeout: + return fmt.Errorf("Failed to run athens") + } + } +} diff --git a/scripts/test_e2e.sh b/scripts/test_e2e.sh deleted file mode 100755 index fbed5341..00000000 --- a/scripts/test_e2e.sh +++ /dev/null @@ -1,69 +0,0 @@ -#!/bin/bash - -# test_e2e.sh -# Execute end-to-end (e2e) tests to verify that everything is working right -# from the end user perspective -set -xeuo pipefail - -REPO_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )/.." - -OGOPATH=${GOPATH:-} -OGO111MODULE=${GO111MODULE:-} -OGOPROXY=${GOPROXY:-} -export GO_BINARY_PATH=${GO_BINARY_PATH:-go} -TMPDIR=$(mktemp -d) -export GOPATH=$TMPDIR -GOMOD_CACHE=$TMPDIR/pkg/mod -export PATH=${REPO_DIR}/bin:${PATH} - -clearGoModCache () { - chmod -R 0770 ${GOMOD_CACHE} - rm -fr ${GOMOD_CACHE} -} - -teardown () { - # Cleanup after our tests - [[ -z "${OGOPATH}" ]] && unset GOPATH || export GOPATH=$OGOPATH - [[ -z "${OGO111MODULE}" ]] && unset GO111MODULE || export GO111MODULE=$OGO111MODULE - [[ -z "${OGOPROXY}" ]] && unset GOPROXY || export GOPROXY=$OGOPROXY - - clearGoModCache - pkill athens-proxy || true - rm $REPO_DIR/cmd/proxy/athens-proxy || true - rm -fr ${TMPDIR} - popd 2> /dev/null || true -} -trap teardown EXIT - -export GO111MODULE=on - -# Start the proxy in the background and wait for it to be ready -cd $REPO_DIR/cmd/proxy -pkill athens-proxy || true # cleanup proxy if it is running -go build -o athens-proxy && ./athens-proxy & -while [[ "$(curl -s -o /dev/null -w ''%{http_code}'' localhost:3000/readyz)" != "200" ]]; do sleep 1; done - -# Clone our test repo -TEST_SOURCE=${TMPDIR}/happy-path -rm -fr ${TEST_SOURCE} 2> /dev/null || true -git clone https://github.com/athens-artifacts/happy-path.git ${TEST_SOURCE} -pushd ${TEST_SOURCE} - -# Make sure that our test repo works without the GOPROXY first -unset GOPROXY -$GO_BINARY_PATH run . - -# clear cache so that go uses the proxy -clearGoModCache - -# Verify that the test works against the proxy -export GOPROXY=http://localhost:3000 -$GO_BINARY_PATH run . - -CATALOG_RES=$(curl localhost:3000/catalog) -CATALOG_EXPECTED='{"modules":[{"module":"github.com/athens-artifacts/no-tags","version":"v0.0.0-20180803171426-1a540c5d67ab"}]}' - -if [[ "$CATALOG_RES" != "$CATALOG_EXPECTED" ]]; then - echo ERROR: catalog endpoint failed - exit 1 # terminate and indicate error -fi