Skip to content

Commit e137e7c

Browse files
committed
feat(install): commands check version currency before running
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.
1 parent 77e4e40 commit e137e7c

File tree

11 files changed

+125
-34
lines changed

11 files changed

+125
-34
lines changed

client/service_manager.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ type ServiceManager struct {
2121
// Setup the Management services.
2222
func (s *ServiceManager) Setup() {
2323
log.Println("[DEBUG] setting up a service manager")
24+
2425
s.commandCreatedChan = make(chan *exec.Cmd)
2526
s.commandCompleteChan = make(chan *exec.Cmd)
2627
s.processes = make(map[int]*exec.Cmd)

command/install.go

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,29 @@
11
package command
22

33
import (
4-
"fmt"
4+
"log"
5+
"os"
6+
7+
"github.com/pact-foundation/pact-go/install"
58

69
"github.com/spf13/cobra"
710
)
811

912
var path string
1013
var installCmd = &cobra.Command{
1114
Use: "install",
12-
Short: "Install required tools",
13-
Long: "Installs underlying Pact standalone tools for use by the library",
15+
Short: "Check required tools",
16+
Long: "Checks versions of required Pact CLI tools for used by the library",
1417
Run: func(cmd *cobra.Command, args []string) {
1518
setLogLevel(verbose, logLevel)
1619

1720
// Run the installer
18-
fmt.Println("[INFO] Installing required tools...", path)
19-
fmt.Println("[INFO] Checking installation succeeded...")
20-
fmt.Println("[INFO] Tooling installed and up to date!")
21+
i := install.NewInstaller()
22+
var err error
23+
if err = i.CheckInstallation(); err != nil {
24+
log.Println("[ERROR] Your Pact CLI installation is out of date, please update to the latest version. Error:", err)
25+
os.Exit(1)
26+
}
2127
},
2228
}
2329

dsl/mock_service.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ func (m *MockService) call(method string, url string, content interface{}) error
6161

6262
responseBody, err := ioutil.ReadAll(res.Body)
6363
res.Body.Close()
64-
log.Printf("[DEBUG] mock service response Body: %s\n", responseBody)
6564
if res.StatusCode < 200 || res.StatusCode >= 300 {
6665
return errors.New(string(responseBody))
6766
}

dsl/pact.go

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313

1414
"github.com/hashicorp/logutils"
1515
"github.com/pact-foundation/pact-go/client"
16+
"github.com/pact-foundation/pact-go/install"
1617
"github.com/pact-foundation/pact-go/types"
1718
"github.com/pact-foundation/pact-go/utils"
1819
)
@@ -73,6 +74,14 @@ type Pact struct {
7374
// Ports MockServer can be deployed to, can be CSV or Range with a dash
7475
// Example "1234", "12324,5667", "1234-5667"
7576
AllowedMockServerPorts string
77+
78+
// DisableToolValidityCheck prevents CLI version checking - use this carefully!
79+
// The ideal situation is to check the tool installation with before running
80+
// the tests, which should speed up large test suites significantly
81+
DisableToolValidityCheck bool
82+
83+
// Check if CLI tools are up to date
84+
toolValidityCheck bool
7685
}
7786

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

109+
if !p.toolValidityCheck && !p.DisableToolValidityCheck {
110+
checkCliCompatibility()
111+
p.toolValidityCheck = true
112+
}
113+
100114
if p.Host == "" {
101115
p.Host = "localhost"
102116
}
@@ -163,7 +177,7 @@ func (p *Pact) setupLogging() {
163177
p.LogLevel = "INFO"
164178
}
165179
p.logFilter = &logutils.LevelFilter{
166-
Levels: []logutils.LogLevel{"DEBUG", "WARN", "ERROR"},
180+
Levels: []logutils.LogLevel{"DEBUG", "INFO", "WARN", "ERROR"},
167181
MinLevel: logutils.LogLevel(p.LogLevel),
168182
Writer: os.Stderr,
169183
}
@@ -279,3 +293,15 @@ func (p *Pact) VerifyProvider(t *testing.T, request types.VerifyRequest) (types.
279293

280294
return res, err
281295
}
296+
297+
var installer = install.NewInstaller()
298+
299+
var checkCliCompatibility = func() {
300+
log.Println("[DEBUG] checking CLI compatability")
301+
err := installer.CheckInstallation()
302+
303+
if err != nil {
304+
log.Println("[DEBUG] asoetusaoteuasoet")
305+
log.Fatal("[ERROR] CLI tools are out of date, please upgrade before continuing.")
306+
}
307+
}

dsl/pact_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ import (
1010
"github.com/pact-foundation/pact-go/types"
1111
)
1212

13+
func init() {
14+
// mock out this function
15+
checkCliCompatibility = func() {}
16+
}
17+
1318
func TestPact_setupLogging(t *testing.T) {
1419
res := captureOutput(func() {
1520
(&Pact{LogLevel: "DEBUG"}).setupLogging()

examples/consumer/goconsumer/user_service_test.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,10 +97,12 @@ func setup() {
9797
// Create Pact connecting to local Daemon
9898
func createPact() dsl.Pact {
9999
return dsl.Pact{
100-
Consumer: "billy",
101-
Provider: "bobby",
102-
LogDir: logDir,
103-
PactDir: pactDir,
100+
Consumer: "billy",
101+
Provider: "bobby",
102+
LogDir: logDir,
103+
PactDir: pactDir,
104+
LogLevel: "DEBUG",
105+
DisableToolValidityCheck: true,
104106
}
105107
}
106108

install/install_darwin.go

Lines changed: 0 additions & 3 deletions
This file was deleted.

install/install_linux.go

Lines changed: 0 additions & 3 deletions
This file was deleted.

install/install_windows.go

Lines changed: 0 additions & 3 deletions
This file was deleted.

install/installer.go

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,12 @@ import (
1313

1414
// Installer manages the underlying Ruby installation
1515
type Installer struct {
16+
commander commander
1617
}
1718

1819
const (
1920
mockServiceRange = ">= 2.6.4, < 3.0.0"
20-
verifierRange = ">= 1.11.0, < 2.0.0"
21+
verifierRange = ">= 1.12.0, < 3.0.0"
2122
brokerRange = ">= 1.14.0, < 2.0.0"
2223
)
2324

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

31+
// NewInstaller creates a new initialised Installer
32+
func NewInstaller() *Installer {
33+
return &Installer{commander: realCommander{}}
34+
}
35+
3036
// CheckInstallation checks installation of all of the tools
3137
func (i *Installer) CheckInstallation() error {
3238

@@ -68,7 +74,7 @@ func (i *Installer) CheckVersion(binary, version string) error {
6874
return nil
6975
}
7076

71-
return fmt.Errorf("version %s does not match constraint %s", version, versionRange)
77+
return fmt.Errorf("version %s of %s does not match constraint %s", version, binary, versionRange)
7278
}
7379

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

85-
cmd := exec.Command(binary, "version")
86-
content, err := cmd.Output()
91+
content, err := i.commander.Output(binary, "version")
8792
version = string(content)
8893

8994
return strings.TrimSpace(version), err
9095
}
96+
97+
// commander wraps the exec package, allowing us
98+
// properly test the file system
99+
type commander interface {
100+
Output(command string, args ...string) ([]byte, error)
101+
}
102+
103+
type realCommander struct{}
104+
105+
func (c realCommander) Output(command string, args ...string) ([]byte, error) {
106+
return exec.Command(command, args...).CombinedOutput()
107+
}

install/installer_test.go

Lines changed: 53 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,89 @@
11
package install
22

33
import (
4-
"fmt"
4+
"errors"
55
"testing"
66
)
77

8-
// TODO: mock out the file system
8+
type testCommander struct {
9+
// Version to return
10+
version string
11+
12+
// Error to return
13+
err error
14+
}
15+
16+
// set version range to be the same for all binaries
17+
func initVersionRange() {
18+
versionMap = map[string]string{
19+
"pact-mock-service": ">= 1.0.0, < 2.0.0",
20+
"pact-provider-verifier": ">= 1.0.0, < 2.0.0",
21+
"pact-broker": ">= 1.0.0, < 2.0.0",
22+
}
23+
}
24+
25+
func (c testCommander) Output(command string, args ...string) ([]byte, error) {
26+
return []byte(c.version), c.err
27+
}
28+
29+
func getInstaller(version string, err error) *Installer {
30+
initVersionRange()
31+
return &Installer{testCommander{version, err}}
32+
}
33+
934
func TestInstaller_CheckVersion(t *testing.T) {
10-
i := Installer{}
11-
err := i.CheckVersion("pact-mock-service", "2.7.3")
35+
i := getInstaller("1.5.0", nil)
36+
err := i.CheckVersion("pact-mock-service", "1.5.0")
1237

1338
if err != nil {
1439
t.Fatal("error:", err)
1540
}
1641
}
1742

1843
func TestInstaller_CheckVersionFail(t *testing.T) {
19-
i := Installer{}
20-
err := i.CheckVersion("pact-mock-service", "3.7.3")
44+
i := getInstaller("2.0.0", nil)
45+
err := i.CheckVersion("pact-mock-service", "2.0.0")
2146

2247
if err == nil {
2348
t.Fatal("want error, got none")
2449
}
2550
}
2651

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

3157
if err != nil {
3258
t.Fatal("error:", err)
3359
}
60+
if v != version {
61+
t.Fatal("Want", version, "got", v)
62+
}
63+
}
64+
65+
func TestInstaller_getVersionForBinaryError(t *testing.T) {
66+
i := getInstaller("", errors.New("test error"))
67+
_, err := i.GetVersionForBinary("pact-mock-service")
3468

35-
fmt.Println("version: ", v)
69+
if err == nil {
70+
t.Fatal("Want error, got nil")
71+
}
3672
}
3773

3874
func TestInstaller_CheckInstallation(t *testing.T) {
39-
i := Installer{}
75+
i := getInstaller("1.0.0", nil)
4076
err := i.CheckInstallation()
4177

4278
if err != nil {
4379
t.Fatal("error:", err)
4480
}
4581
}
82+
func TestInstaller_CheckInstallationError(t *testing.T) {
83+
i := getInstaller("2.0.0", nil)
84+
err := i.CheckInstallation()
85+
86+
if err == nil {
87+
t.Fatal("Want error, got nil")
88+
}
89+
}

0 commit comments

Comments
 (0)