Skip to content

Commit

Permalink
feat(install): commands check version currency before running
Browse files Browse the repository at this point in the history
Now that we have departed from packaging the CLI tools as part
of pact-go, we needed a mechanism to ensure the tools matched
the library.

The only sensible time for this is at runtime, prior to executing
the tests.
  • Loading branch information
mefellows committed Mar 25, 2018
1 parent 77e4e40 commit e137e7c
Show file tree
Hide file tree
Showing 11 changed files with 125 additions and 34 deletions.
1 change: 1 addition & 0 deletions client/service_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type ServiceManager struct {
// Setup the Management services.
func (s *ServiceManager) Setup() {
log.Println("[DEBUG] setting up a service manager")

s.commandCreatedChan = make(chan *exec.Cmd)
s.commandCompleteChan = make(chan *exec.Cmd)
s.processes = make(map[int]*exec.Cmd)
Expand Down
18 changes: 12 additions & 6 deletions command/install.go
Original file line number Diff line number Diff line change
@@ -1,23 +1,29 @@
package command

import (
"fmt"
"log"
"os"

"github.com/pact-foundation/pact-go/install"

"github.com/spf13/cobra"
)

var path string
var installCmd = &cobra.Command{
Use: "install",
Short: "Install required tools",
Long: "Installs underlying Pact standalone tools for use by the library",
Short: "Check required tools",
Long: "Checks versions of required Pact CLI tools for used by the library",
Run: func(cmd *cobra.Command, args []string) {
setLogLevel(verbose, logLevel)

// Run the installer
fmt.Println("[INFO] Installing required tools...", path)
fmt.Println("[INFO] Checking installation succeeded...")
fmt.Println("[INFO] Tooling installed and up to date!")
i := install.NewInstaller()
var err error
if err = i.CheckInstallation(); err != nil {
log.Println("[ERROR] Your Pact CLI installation is out of date, please update to the latest version. Error:", err)
os.Exit(1)
}
},
}

Expand Down
1 change: 0 additions & 1 deletion dsl/mock_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ func (m *MockService) call(method string, url string, content interface{}) error

responseBody, err := ioutil.ReadAll(res.Body)
res.Body.Close()
log.Printf("[DEBUG] mock service response Body: %s\n", responseBody)
if res.StatusCode < 200 || res.StatusCode >= 300 {
return errors.New(string(responseBody))
}
Expand Down
28 changes: 27 additions & 1 deletion dsl/pact.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (

"github.com/hashicorp/logutils"
"github.com/pact-foundation/pact-go/client"
"github.com/pact-foundation/pact-go/install"
"github.com/pact-foundation/pact-go/types"
"github.com/pact-foundation/pact-go/utils"
)
Expand Down Expand Up @@ -73,6 +74,14 @@ type Pact struct {
// Ports MockServer can be deployed to, can be CSV or Range with a dash
// Example "1234", "12324,5667", "1234-5667"
AllowedMockServerPorts string

// DisableToolValidityCheck prevents CLI version checking - use this carefully!
// The ideal situation is to check the tool installation with before running
// the tests, which should speed up large test suites significantly
DisableToolValidityCheck bool

// Check if CLI tools are up to date
toolValidityCheck bool
}

// AddInteraction creates a new Pact interaction, initialising all
Expand All @@ -97,6 +106,11 @@ func (p *Pact) Setup(startMockServer bool) *Pact {
p.Network = "tcp"
}

if !p.toolValidityCheck && !p.DisableToolValidityCheck {
checkCliCompatibility()
p.toolValidityCheck = true
}

if p.Host == "" {
p.Host = "localhost"
}
Expand Down Expand Up @@ -163,7 +177,7 @@ func (p *Pact) setupLogging() {
p.LogLevel = "INFO"
}
p.logFilter = &logutils.LevelFilter{
Levels: []logutils.LogLevel{"DEBUG", "WARN", "ERROR"},
Levels: []logutils.LogLevel{"DEBUG", "INFO", "WARN", "ERROR"},
MinLevel: logutils.LogLevel(p.LogLevel),
Writer: os.Stderr,
}
Expand Down Expand Up @@ -279,3 +293,15 @@ func (p *Pact) VerifyProvider(t *testing.T, request types.VerifyRequest) (types.

return res, err
}

var installer = install.NewInstaller()

var checkCliCompatibility = func() {
log.Println("[DEBUG] checking CLI compatability")
err := installer.CheckInstallation()

if err != nil {
log.Println("[DEBUG] asoetusaoteuasoet")
log.Fatal("[ERROR] CLI tools are out of date, please upgrade before continuing.")
}
}
5 changes: 5 additions & 0 deletions dsl/pact_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ import (
"github.com/pact-foundation/pact-go/types"
)

func init() {
// mock out this function
checkCliCompatibility = func() {}
}

func TestPact_setupLogging(t *testing.T) {
res := captureOutput(func() {
(&Pact{LogLevel: "DEBUG"}).setupLogging()
Expand Down
10 changes: 6 additions & 4 deletions examples/consumer/goconsumer/user_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,12 @@ func setup() {
// Create Pact connecting to local Daemon
func createPact() dsl.Pact {
return dsl.Pact{
Consumer: "billy",
Provider: "bobby",
LogDir: logDir,
PactDir: pactDir,
Consumer: "billy",
Provider: "bobby",
LogDir: logDir,
PactDir: pactDir,
LogLevel: "DEBUG",
DisableToolValidityCheck: true,
}
}

Expand Down
3 changes: 0 additions & 3 deletions install/install_darwin.go

This file was deleted.

3 changes: 0 additions & 3 deletions install/install_linux.go

This file was deleted.

3 changes: 0 additions & 3 deletions install/install_windows.go

This file was deleted.

25 changes: 21 additions & 4 deletions install/installer.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@ import (

// Installer manages the underlying Ruby installation
type Installer struct {
commander commander
}

const (
mockServiceRange = ">= 2.6.4, < 3.0.0"
verifierRange = ">= 1.11.0, < 2.0.0"
verifierRange = ">= 1.12.0, < 3.0.0"
brokerRange = ">= 1.14.0, < 2.0.0"
)

Expand All @@ -27,6 +28,11 @@ var versionMap = map[string]string{
"pact-broker": brokerRange,
}

// NewInstaller creates a new initialised Installer
func NewInstaller() *Installer {
return &Installer{commander: realCommander{}}
}

// CheckInstallation checks installation of all of the tools
func (i *Installer) CheckInstallation() error {

Expand Down Expand Up @@ -68,7 +74,7 @@ func (i *Installer) CheckVersion(binary, version string) error {
return nil
}

return fmt.Errorf("version %s does not match constraint %s", version, versionRange)
return fmt.Errorf("version %s of %s does not match constraint %s", version, binary, versionRange)
}

// InstallTools installs the CLI tools onto the host system
Expand All @@ -82,9 +88,20 @@ func (i *Installer) InstallTools() error {
func (i *Installer) GetVersionForBinary(binary string) (version string, err error) {
log.Println("[DEBUG] running binary", binary)

cmd := exec.Command(binary, "version")
content, err := cmd.Output()
content, err := i.commander.Output(binary, "version")
version = string(content)

return strings.TrimSpace(version), err
}

// commander wraps the exec package, allowing us
// properly test the file system
type commander interface {
Output(command string, args ...string) ([]byte, error)
}

type realCommander struct{}

func (c realCommander) Output(command string, args ...string) ([]byte, error) {
return exec.Command(command, args...).CombinedOutput()
}
62 changes: 53 additions & 9 deletions install/installer_test.go
Original file line number Diff line number Diff line change
@@ -1,45 +1,89 @@
package install

import (
"fmt"
"errors"
"testing"
)

// TODO: mock out the file system
type testCommander struct {
// Version to return
version string

// Error to return
err error
}

// set version range to be the same for all binaries
func initVersionRange() {
versionMap = map[string]string{
"pact-mock-service": ">= 1.0.0, < 2.0.0",
"pact-provider-verifier": ">= 1.0.0, < 2.0.0",
"pact-broker": ">= 1.0.0, < 2.0.0",
}
}

func (c testCommander) Output(command string, args ...string) ([]byte, error) {
return []byte(c.version), c.err
}

func getInstaller(version string, err error) *Installer {
initVersionRange()
return &Installer{testCommander{version, err}}
}

func TestInstaller_CheckVersion(t *testing.T) {
i := Installer{}
err := i.CheckVersion("pact-mock-service", "2.7.3")
i := getInstaller("1.5.0", nil)
err := i.CheckVersion("pact-mock-service", "1.5.0")

if err != nil {
t.Fatal("error:", err)
}
}

func TestInstaller_CheckVersionFail(t *testing.T) {
i := Installer{}
err := i.CheckVersion("pact-mock-service", "3.7.3")
i := getInstaller("2.0.0", nil)
err := i.CheckVersion("pact-mock-service", "2.0.0")

if err == nil {
t.Fatal("want error, got none")
}
}

func TestInstaller_getVersionForBinary(t *testing.T) {
i := Installer{}
version := "1.5.0"
i := getInstaller(version, nil)
v, err := i.GetVersionForBinary("pact-mock-service")

if err != nil {
t.Fatal("error:", err)
}
if v != version {
t.Fatal("Want", version, "got", v)
}
}

func TestInstaller_getVersionForBinaryError(t *testing.T) {
i := getInstaller("", errors.New("test error"))
_, err := i.GetVersionForBinary("pact-mock-service")

fmt.Println("version: ", v)
if err == nil {
t.Fatal("Want error, got nil")
}
}

func TestInstaller_CheckInstallation(t *testing.T) {
i := Installer{}
i := getInstaller("1.0.0", nil)
err := i.CheckInstallation()

if err != nil {
t.Fatal("error:", err)
}
}
func TestInstaller_CheckInstallationError(t *testing.T) {
i := getInstaller("2.0.0", nil)
err := i.CheckInstallation()

if err == nil {
t.Fatal("Want error, got nil")
}
}

0 comments on commit e137e7c

Please sign in to comment.