Skip to content
This repository has been archived by the owner on Apr 23, 2022. It is now read-only.

Commit

Permalink
Merge pull request #36 from KohlsTechnology/e2e
Browse files Browse the repository at this point in the history
Add automated end to end test
  • Loading branch information
seanmalloy authored Mar 9, 2020
2 parents 9f404c0 + 0be04a7 commit 842c5f8
Show file tree
Hide file tree
Showing 11 changed files with 239 additions and 0 deletions.
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ test: fmt vet test-unit
test-unit:
go test -race -coverprofile=coverage.txt -covermode=atomic ./...

.PHONY: test-e2e
test-e2e: build
go test -v ./pkg/e2e/e2e_test.go

# Make sure go.mod and go.sum are not modified
.PHONY: test-dirty
test-dirty: build
Expand Down
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,21 @@ See [CONTRIBUTING.md](.github/CONTRIBUTING.md) for details.
$ make build
```

### End To End Testing

The end to end test can be run by simply running `make test-e2e`.
To run the test, you need to have the [consul binary](https://releases.hashicorp.com/consul/) available in your path.
It simulates a create and update of consul KV pairs and confirms every operation is successful.
The test data is stored within this repo so developers do not have to setup an external repo to test.

The tests can manually be run by starting Consul in dev mode, and then manually running `git2consul` with one of the config files provided.
For example:
```
$ consul agent -dev
$ # In a separate terminal
$ ./git2consul -config pkg/e2e/data/create-config.json -once -debug
```

### Releases
This project is using [goreleaser](https://goreleaser.com). GitHub release creation is automated using Travis
CI. New releases are automatically created when new tags are pushed to the repo.
Expand Down
12 changes: 12 additions & 0 deletions pkg/e2e/data/create-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"repos": [
{
"name": "e2e",
"url": "http://github.com/KohlsTechnology/git2consul-go.git",
"branches": [
"master"
],
"source_root": "/pkg/e2e/data/create/"
}
]
}
1 change: 1 addition & 0 deletions pkg/e2e/data/create/artist
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mozart
1 change: 1 addition & 0 deletions pkg/e2e/data/create/genre
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
classical
12 changes: 12 additions & 0 deletions pkg/e2e/data/delete-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"repos": [
{
"name": "e2e",
"url": "http://github.com/KohlsTechnology/git2consul-go.git",
"branches": [
"master"
],
"source_root": "/pkg/e2e/data/delete/"
}
]
}
1 change: 1 addition & 0 deletions pkg/e2e/data/delete/artist
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mozart
12 changes: 12 additions & 0 deletions pkg/e2e/data/update-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"repos": [
{
"name": "e2e",
"url": "http://github.com/KohlsTechnology/git2consul-go.git",
"branches": [
"master"
],
"source_root": "/pkg/e2e/data/update/"
}
]
}
1 change: 1 addition & 0 deletions pkg/e2e/data/update/artist
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
beethoven
1 change: 1 addition & 0 deletions pkg/e2e/data/update/genre
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
classical
179 changes: 179 additions & 0 deletions pkg/e2e/e2e_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
// +build e2e

package e2e

import (
"bufio"
"errors"
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"strings"
"syscall"
"testing"
"time"

"github.com/hashicorp/consul/api"
)

func TestE2E(t *testing.T) {
// Setup local Consul test server
consulCmd := exec.Command("consul", "agent", "-dev")
consulPipeReader, consulPipeWriter := io.Pipe()
consulScanner := bufio.NewScanner(consulPipeReader)
consulTee := io.MultiWriter(consulPipeWriter, os.Stderr) // dumping the output on Stderr can be useful when debugging this test

consulCmd.Stdout = consulTee
consulCmd.Stderr = consulTee
if err := consulCmd.Start(); err != nil {
t.Fatalf("failed to start local consul server: %s", err)
}
defer consulCmd.Process.Signal(syscall.SIGTERM)

consulDone := make(chan error, 0)
go func() {
consulDone <- consulCmd.Wait()
}()

err := waitForString(consulScanner, 20*time.Second, "==> Consul agent running!")
if err != nil {
t.Fatal("initialization of consul server failed", err)
}
t.Log("initialization of consul server finished")

client, err := api.NewClient(api.DefaultConfig())
if err != nil {
t.Fatal("failed to initialize consul api client", err)
}
t.Log("initilization of consul api finished")
kv := client.KV()

projectDir, err := GetRootProjectDir()
if err != nil {
t.Fatal("failed to get working project directory", err)
}

g2cCmd := exec.Command(projectDir+"/git2consul",
"-config",
projectDir+"/pkg/e2e/data/create-config.json",
"-debug",
"-once")
err = executeCommand(g2cCmd, "Terminating git2consul")
if err != nil {
t.Fatal("git2consul run failed", err)
} else {
t.Log("git2consul ran successfully")
}

pair, _, err := kv.Get("e2e/master/genre", nil)
if err != nil {
t.Fatal("failed to get pair e2e/master/genre", err)
}
if pair == nil {
t.Fatal("did not find expected pair at e2e/master/genre")
}
if strings.TrimSpace(string(pair.Value)) != "classical" {
t.Errorf("got %s want %s", string(pair.Value), "classical")
} else {
t.Log("confirmed e2e/master/genre was properly set")
}

// TODO: remove delete when issue #31 is resolved this is a workaround because if
// the ref matches the current commit, the kv pairs will not be updated
_, err = kv.Delete("e2e/master.ref", nil)
if err != nil {
t.Fatal("failed to delete git reference")
}

g2cCmd = exec.Command(projectDir+"/git2consul",
"-config",
projectDir+"/pkg/e2e/data/update-config.json",
"-debug",
"-once")
err = executeCommand(g2cCmd, "Terminating git2consul")
if err != nil {
t.Fatal("git2consul run failed", err)
} else {
t.Log("git2consul ran successfully")
}

pair, _, err = kv.Get("e2e/master/artist", nil)
if err != nil {
t.Fatal("failed to get pair e2e/master/artist", err)
}
if pair == nil {
t.Fatal("did not find expected pair at e2e/master/artist")
}
if strings.TrimSpace(string(pair.Value)) != "beethoven" {
t.Errorf("got %s want %s", string(pair.Value), "beethoven")
} else {
t.Log("confirmed e2e/master/genre was properly updated")
}

// TODO: add stage to simulate delete of kv pair
}

func executeCommand(command *exec.Cmd, expectedLog string) (err error) {
commandPipeReader, commandPipeWriter := io.Pipe()

commandScanner := bufio.NewScanner(commandPipeReader)
commandTee := io.MultiWriter(commandPipeWriter, os.Stderr) // dumping the output on Stderr can be useful when debugging this test

command.Stdout = commandTee
command.Stderr = commandTee
if err := command.Start(); err != nil {
return err
}
defer command.Process.Signal(syscall.SIGTERM)

commandDone := make(chan error, 0)
go func() {
commandDone <- command.Wait()
}()

err = waitForString(commandScanner, 20*time.Second, expectedLog)
if err != nil {
return err
}

return nil
}

func waitForString(s *bufio.Scanner, timeout time.Duration, want string) error {
done := make(chan error)
go func() {
for s.Scan() {
if strings.Contains(s.Text(), want) {
done <- nil
return
}
}
if s.Err() != nil {
done <- s.Err()
}
done <- fmt.Errorf("process finished without printing expected string %q", want)
}()
select {
case err := <-done:
return err
case <-time.After(timeout):
return fmt.Errorf("wait for string %q timed out", want)
}
}

// GetRootProjectDir returns path to root directory of project
func GetRootProjectDir() (string, error) {
wd, err := os.Getwd()
if err != nil {
return "", err
}
for !strings.HasSuffix(wd, "git2consul-go") {
if wd == "/" {
return "", errors.New(`cannot find project directory, "/" reached`)
}
wd = filepath.Dir(wd)
}
return wd, nil
}

0 comments on commit 842c5f8

Please sign in to comment.