diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 21bae7a..0991829 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -8,6 +8,8 @@ jobs: fetch-depth: 0 fetch-tags: true - uses: "actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491" # v5.0.0 + with: + go-version-file: go.mod - name: test run: | make lint diff --git a/.gitignore b/.gitignore index 5c94511..f38cb90 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,8 @@ *.out .idea +.gocache/ + pacttesting/target pacttesting/logs pact/ diff --git a/.golangci.yml b/.golangci.yml index a2f1af9..fb6cd5a 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,4 +1,4 @@ -# Using golangci-lint@v1.56.2 +# Using golangci-lint@v1.64.8 run: timeout: 5m tests: true diff --git a/Makefile b/Makefile index 9d1da3c..dba080d 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ .DEFAULT_GOAL := default -GOLANGCI_VERSION := 1.56.2 +GOLANGCI_VERSION := 1.64.8 platform := $(shell uname) pact_version := "1.88.51" diff --git a/go.mod b/go.mod index f999875..89c95b1 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.20 require ( github.com/avast/retry-go/v4 v4.5.1 - github.com/pact-foundation/pact-go v1.8.0 + github.com/pact-foundation/pact-go v1.10.0 github.com/sirupsen/logrus v1.9.3 github.com/spf13/viper v1.18.2 github.com/stretchr/testify v1.8.4 diff --git a/go.sum b/go.sum index ef491e8..35ee8b0 100644 --- a/go.sum +++ b/go.sum @@ -83,8 +83,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= -github.com/pact-foundation/pact-go v1.8.0 h1:On9JjJZrzwqmT2UICedWqLpcAtqVyd/ktt0SVJFTdL4= -github.com/pact-foundation/pact-go v1.8.0/go.mod h1:YLt/uSQGo9x5ZUjynLzNy3IiORlA4BtbR9p2yxgD2as= +github.com/pact-foundation/pact-go v1.10.0 h1:LE954RwmeV0rSm21umyPGGCvzHkEhglowkNGyuMwNGM= +github.com/pact-foundation/pact-go v1.10.0/go.mod h1:YLt/uSQGo9x5ZUjynLzNy3IiORlA4BtbR9p2yxgD2as= github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= diff --git a/pacttesting/message_testing.go b/pacttesting/message_testing.go index f1e7c96..7068cf4 100644 --- a/pacttesting/message_testing.go +++ b/pacttesting/message_testing.go @@ -21,6 +21,14 @@ import ( const providerHTTPScheme = "http://" +const ( + verificationDirMode = 0o744 + verificationFileMode = 0o600 + messageReadHeaderTimeout = 3 * time.Second + messageWaitForPortTimeout = 5 * time.Second + waitForPortPollInterval = 50 * time.Millisecond +) + func VerifyProviderMessagingPacts(params PactProviderTestParams, messageProducers dsl.MessageHandlers) { buildPactClientOnce() @@ -81,9 +89,9 @@ func VerifyProviderMessagingPacts(params PactProviderTestParams, messageProducer err == nil && allTestsSucceeded, version) verificationDir := filepath.Join(topLevelDir, "build", "pact-verifications") - _ = os.MkdirAll(verificationDir+"/", 0o744) + _ = os.MkdirAll(verificationDir+"/", verificationDirMode) verificationFile := filepath.Join(verificationDir, filename) - if err := os.WriteFile(verificationFile, []byte(verificationJSON), 0o600); err != nil { + if err := os.WriteFile(verificationFile, []byte(verificationJSON), verificationFileMode); err != nil { t.Fatal(err) } outputJSON, err := json.Marshal(response) @@ -92,7 +100,7 @@ func VerifyProviderMessagingPacts(params PactProviderTestParams, messageProducer } outFile := filepath.Join(topLevelDir, "build/pact-verifications/", "output-"+filename) - if err := os.WriteFile(outFile, outputJSON, 0o600); err != nil { + if err := os.WriteFile(outFile, outputJSON, verificationFileMode); err != nil { t.Fatal(err) } }) @@ -213,13 +221,13 @@ func VerifyMessageProviderRaw( server := http.Server{ Handler: mux, - ReadHeaderTimeout: 3 * time.Second, + ReadHeaderTimeout: messageReadHeaderTimeout, } log.Printf("[DEBUG] API handler starting: port %d (%s)", port, ln.Addr()) go func() { _ = server.Serve(ln) }() - portErr := waitForPort(port, "tcp", getBindAddress(), 5*time.Second, + portErr := waitForPort(port, "tcp", getBindAddress(), messageWaitForPortTimeout, fmt.Sprintf(`Timed out waiting for Daemon on port %d - are you sure it's running?`, port)) if portErr != nil { @@ -244,7 +252,7 @@ func waitForPort(port int, network string, address string, timeoutDuration time. case <-timeout: log.Printf("[ERROR] Expected server to start < %s. %s", timeoutDuration, message) return fmt.Errorf("expected server to start < %s. %s", timeoutDuration, message) - case <-time.After(50 * time.Millisecond): + case <-time.After(waitForPortPollInterval): _, err := net.Dial(network, fmt.Sprintf("%s:%d", address, port)) if err == nil { return nil diff --git a/pacttesting/mock_service.go b/pacttesting/mock_service.go index 858ffa5..4eda9bc 100644 --- a/pacttesting/mock_service.go +++ b/pacttesting/mock_service.go @@ -25,6 +25,13 @@ type MockServer struct { Running bool `json:"-"` } +const ( + pidDirMode = 0o755 + pidFileMode = 0o600 + stopRetryAttempts = 25 + stopRetryDelay = 200 * time.Millisecond +) + // call sends a message to the Pact service func (m *MockServer) call(method string, url string, content *string) error { client := &http.Client{} @@ -90,11 +97,11 @@ func (m *MockServer) writePidFile() { return } dir, _ := os.Getwd() - _ = os.MkdirAll(filepath.FromSlash(filepath.Join(dir, "pact", "pids")), os.ModePerm) + _ = os.MkdirAll(filepath.FromSlash(filepath.Join(dir, "pact", "pids")), pidDirMode) file := filepath.FromSlash( fmt.Sprintf("%s/pact-%s-%s.json", filepath.Join(dir, "pact", "pids"), m.Provider, m.Consumer), ) - err = os.WriteFile(file, bytes, os.ModePerm) + err = os.WriteFile(file, bytes, pidFileMode) if err != nil { log.WithError(err).Errorf("unable to store mock server details") return @@ -120,7 +127,7 @@ func (m *MockServer) Stop() error { return errors.New("server process is still alive") } return nil - }, retry.Attempts(25), retry.Delay(200*time.Millisecond), retry.DelayType(retry.FixedDelay)); err != nil { + }, retry.Attempts(stopRetryAttempts), retry.Delay(stopRetryDelay), retry.DelayType(retry.FixedDelay)); err != nil { err = p.Kill() if err != nil { return fmt.Errorf("failed to kill process: %w", err) diff --git a/pacttesting/pact_file_split.go b/pacttesting/pact_file_split.go index 6123a5d..661aeb8 100644 --- a/pacttesting/pact_file_split.go +++ b/pacttesting/pact_file_split.go @@ -11,6 +11,8 @@ import ( type PactRequestMatchingFilter = func(map[string]interface{}) +const splitPactFileMode = 0o644 + // SplitPactBulkFile reads bulk PACT files, splits it into smaller ones // and writes output to destination directory func SplitPactBulkFile(bulkFilePath string, outputDirPath string, requestFilters ...PactRequestMatchingFilter) error { @@ -51,7 +53,7 @@ func SplitPactBulkFile(bulkFilePath string, outputDirPath string, requestFilters description := sanitize(tc.Interactions[0].Description) tcFilePath := filepath.Join(outputDirPath, description+".json") - if writeErr := os.WriteFile(tcFilePath, json, os.ModePerm); writeErr != nil { + if writeErr := os.WriteFile(tcFilePath, json, splitPactFileMode); writeErr != nil { return fmt.Errorf("couldn't write test case to file - interaction idx: %d , "+ "output file path: %s, err: %w", idx, tcFilePath, writeErr) } diff --git a/pacttesting/pact_testing_integration_stage_test.go b/pacttesting/pact_testing_integration_stage_test.go index 9816aaf..63a1d04 100644 --- a/pacttesting/pact_testing_integration_stage_test.go +++ b/pacttesting/pact_testing_integration_stage_test.go @@ -211,7 +211,7 @@ func (s *pactTestingStage) the_service_gets_preassigned() *pactTestingStage { func (s *pactTestingStage) the_service_has_a_preassigned_port() *pactTestingStage { assert.NotEqual(s.t, "", viper.GetString("testservice-pre")) assert.NotNil(s.t, pactServers["testservice-prego-pact-testing"]) - assert.Greater(s.t, pactServers["testservice-prego-pact-testing"].Port, 0) + assert.Positive(s.t, pactServers["testservice-prego-pact-testing"].Port) return s } diff --git a/pacttesting/testing.go b/pacttesting/testing.go index 6cefa21..1177403 100644 --- a/pacttesting/testing.go +++ b/pacttesting/testing.go @@ -34,6 +34,15 @@ type pactName struct { Name string `json:"name"` } +const ( + defaultRetryAttempts = 150000 + defaultRetryDelay = 200 * time.Millisecond + mockServerHealthAttempts = 100 + mockServerHealthDelay = 100 * time.Millisecond + providerVerificationDirMode = 0o744 + providerVerificationFileMode = 0o600 +) + //nolint:gochecknoglobals // fixing is a breaking API change var ( pathOnce sync.Once @@ -44,8 +53,8 @@ var ( func defaultRetryOptions() []retry.Option { return []retry.Option{ - retry.Attempts(150000), - retry.Delay(200 * time.Millisecond), + retry.Attempts(defaultRetryAttempts), + retry.Delay(defaultRetryDelay), retry.DelayType(retry.FixedDelay), } } @@ -163,7 +172,7 @@ func setBinPath() { func buildPactClientOnce() { once.Do(func() { setBinPath() - pactClient = dsl.NewClient() + pactClient = dsl.NewClient("") }) } @@ -378,7 +387,7 @@ func EnsurePactRunning(provider, consumer string) string { return fmt.Errorf("calling mock server: %w", retry.Unrecoverable(err)) } return err - }, retry.DelayType(retry.FixedDelay), retry.Delay(100*time.Millisecond), retry.Attempts(100)) + }, retry.DelayType(retry.FixedDelay), retry.Delay(mockServerHealthDelay), retry.Attempts(mockServerHealthAttempts)) if err != nil { log. WithError(err). @@ -414,7 +423,7 @@ func RunIntegrationTest(t *testing.T, pactFilePaths []Pact, testFunc func(), ret if err := retry.Do(verify, retryOptions...); err != nil { log.Error("Pact verification failed!!" + "For more info on the error check the logs/pact*.log files, they are quite detailed") - t.Errorf(err.Error()) + t.Error(err) } }) } @@ -546,9 +555,9 @@ func VerifyProviderPacts(params PactProviderTestParams) { allTestsSucceeded, version) verificationDir := filepath.Join(topLevelDir, "build", "pact-verifications") - _ = os.MkdirAll(verificationDir+"/", 0o744) + _ = os.MkdirAll(verificationDir+"/", providerVerificationDirMode) verificationFile := filepath.Join(verificationDir, filename) - if err := os.WriteFile(verificationFile, []byte(verificationJSON), 0o600); err != nil { + if err := os.WriteFile(verificationFile, []byte(verificationJSON), providerVerificationFileMode); err != nil { t.Fatal(err) } outputJSON, err := json.Marshal(response) @@ -557,7 +566,7 @@ func VerifyProviderPacts(params PactProviderTestParams) { } outFile := filepath.Join(topLevelDir, "build/pact-verifications/", "output-"+filename) - if err := os.WriteFile(outFile, outputJSON, 0o600); err != nil { + if err := os.WriteFile(outFile, outputJSON, providerVerificationFileMode); err != nil { t.Fatal(err) } }) diff --git a/vendor/github.com/pact-foundation/pact-go/client/service.go b/vendor/github.com/pact-foundation/pact-go/client/service.go index e08ce3b..beffbfa 100644 --- a/vendor/github.com/pact-foundation/pact-go/client/service.go +++ b/vendor/github.com/pact-foundation/pact-go/client/service.go @@ -17,7 +17,7 @@ import ( // Service is a process wrapper for 3rd party binaries. It will spawn an instance // of the binary and manage the life-cycle and IO of the process. type Service interface { - Setup() + Setup(pactCLIDir string) Stop(pid int) (bool, error) List() map[int]*exec.Cmd Command() *exec.Cmd diff --git a/vendor/github.com/pact-foundation/pact-go/client/service_manager.go b/vendor/github.com/pact-foundation/pact-go/client/service_manager.go index d1dfa66..269094c 100644 --- a/vendor/github.com/pact-foundation/pact-go/client/service_manager.go +++ b/vendor/github.com/pact-foundation/pact-go/client/service_manager.go @@ -5,12 +5,14 @@ import ( "log" "os" "os/exec" + "path" "sync" "time" ) // ServiceManager is the default implementation of the Service interface. type ServiceManager struct { + PactCLIDir string Cmd string processMap processMap Args []string @@ -20,9 +22,11 @@ type ServiceManager struct { } // Setup the Management services. -func (s *ServiceManager) Setup() { +func (s *ServiceManager) Setup(pactCLIDir string) { log.Println("[DEBUG] setting up a service manager") + s.PactCLIDir = pactCLIDir + s.commandCreatedChan = make(chan *exec.Cmd) s.commandCompleteChan = make(chan *exec.Cmd) s.processMap = processMap{processes: make(map[int]*exec.Cmd)} @@ -97,7 +101,7 @@ func (s *ServiceManager) List() map[int]*exec.Cmd { // Command creates an os command to be run func (s *ServiceManager) Command() *exec.Cmd { - cmd := exec.Command(s.Cmd, s.Args...) + cmd := exec.Command(path.Join(s.PactCLIDir, s.Cmd), s.Args...) env := os.Environ() env = append(env, s.Env...) cmd.Env = env @@ -108,10 +112,7 @@ func (s *ServiceManager) Command() *exec.Cmd { // Start a Service and log its output. func (s *ServiceManager) Start() *exec.Cmd { log.Println("[DEBUG] starting service") - cmd := exec.Command(s.Cmd, s.Args...) - env := os.Environ() - env = append(env, s.Env...) - cmd.Env = env + cmd := s.Command() cmdReader, err := cmd.StdoutPipe() if err != nil { diff --git a/vendor/github.com/pact-foundation/pact-go/dsl/client.go b/vendor/github.com/pact-foundation/pact-go/dsl/client.go index 5451da7..4342aaa 100644 --- a/vendor/github.com/pact-foundation/pact-go/dsl/client.go +++ b/vendor/github.com/pact-foundation/pact-go/dsl/client.go @@ -70,11 +70,11 @@ type PactClient struct { } // newClient creates a new Pact client manager with the provided services -func newClient(mockServiceManager client.Service, verificationServiceManager client.Service, messageServiceManager client.Service, publishServiceManager client.Service) *PactClient { - mockServiceManager.Setup() - verificationServiceManager.Setup() - messageServiceManager.Setup() - publishServiceManager.Setup() +func newClient(pactCLIDir string, mockServiceManager client.Service, verificationServiceManager client.Service, messageServiceManager client.Service, publishServiceManager client.Service) *PactClient { + mockServiceManager.Setup(pactCLIDir) + verificationServiceManager.Setup(pactCLIDir) + messageServiceManager.Setup(pactCLIDir) + publishServiceManager.Setup(pactCLIDir) return &PactClient{ pactMockSvcManager: mockServiceManager, @@ -86,8 +86,8 @@ func newClient(mockServiceManager client.Service, verificationServiceManager cli } // NewClient creates a new Pact client manager with defaults -func NewClient() *PactClient { - return newClient(&client.MockService{}, &client.VerificationService{}, &client.MessageService{}, &client.PublishService{}) +func NewClient(pactCLIDir string) *PactClient { + return newClient(pactCLIDir, &client.MockService{}, &client.VerificationService{}, &client.MessageService{}, &client.PublishService{}) } // StartServer starts a remote Pact Mock Server. diff --git a/vendor/github.com/pact-foundation/pact-go/dsl/pact.go b/vendor/github.com/pact-foundation/pact-go/dsl/pact.go index e14ad5f..756a77f 100644 --- a/vendor/github.com/pact-foundation/pact-go/dsl/pact.go +++ b/vendor/github.com/pact-foundation/pact-go/dsl/pact.go @@ -32,6 +32,9 @@ type Pact struct { // Current server for the consumer. Server *types.MockServer + // Directory of Pactflow standalone CLI (blank if CLI is installed on system) + PactCLIDir string + // Pact RPC Client. pactClient Client @@ -158,7 +161,7 @@ func (p *Pact) Setup(startMockServer bool) *Pact { } if p.pactClient == nil { - c := NewClient() + c := NewClient(p.PactCLIDir) c.TimeoutDuration = p.ClientTimeout p.pactClient = c } diff --git a/vendor/github.com/pact-foundation/pact-go/dsl/publish.go b/vendor/github.com/pact-foundation/pact-go/dsl/publish.go index 7847b02..dd74249 100644 --- a/vendor/github.com/pact-foundation/pact-go/dsl/publish.go +++ b/vendor/github.com/pact-foundation/pact-go/dsl/publish.go @@ -25,6 +25,9 @@ type PactName struct { // Publisher is the API to send Pact files to a Pact Broker. type Publisher struct { + // Directory of Pactflow standalone CLI (blank if CLI is installed on system) + PactCLIDir string + pactClient Client // Log levels. @@ -40,7 +43,7 @@ func (p *Publisher) Publish(request types.PublishRequest) error { log.Println("[DEBUG] pact publisher: publish pact") if p.pactClient == nil { - c := NewClient() + c := NewClient(p.PactCLIDir) p.pactClient = c } diff --git a/vendor/github.com/pact-foundation/pact-go/dsl/service_mock.go b/vendor/github.com/pact-foundation/pact-go/dsl/service_mock.go index e9d1166..5057860 100644 --- a/vendor/github.com/pact-foundation/pact-go/dsl/service_mock.go +++ b/vendor/github.com/pact-foundation/pact-go/dsl/service_mock.go @@ -25,7 +25,7 @@ type ServiceMock struct { } // Setup the Management services. -func (s *ServiceMock) Setup() { +func (s *ServiceMock) Setup(pactCLIDir string) { s.ServicesSetupCalled = true } diff --git a/vendor/github.com/pact-foundation/pact-go/types/publish_request.go b/vendor/github.com/pact-foundation/pact-go/types/publish_request.go index b5f41b1..8ea6242 100644 --- a/vendor/github.com/pact-foundation/pact-go/types/publish_request.go +++ b/vendor/github.com/pact-foundation/pact-go/types/publish_request.go @@ -29,6 +29,11 @@ type PublishRequest struct { // e.g. "production", "master" and "development" are some common examples. Tags []string + // Branches are used to identify which pacts a provider should verify using + // consumer version selectors, associating the consumer API version with + // a branch. This supersedes tags. https://docs.pact.io/pact_broker/branches + Branch string + // Verbose increases verbosity of output // Deprecated Verbose bool @@ -71,6 +76,10 @@ func (p *PublishRequest) Validate() error { p.Args = append(p.Args, "--broker-token", p.BrokerToken) } + if p.Branch != "" { + p.Args = append(p.Args, "--branch", p.Branch) + } + if p.ConsumerVersion == "" { return fmt.Errorf("'ConsumerVersion' is mandatory") } diff --git a/vendor/modules.txt b/vendor/modules.txt index 84449b8..1cdc486 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -31,7 +31,7 @@ github.com/magiconair/properties # github.com/mitchellh/mapstructure v1.5.0 ## explicit; go 1.14 github.com/mitchellh/mapstructure -# github.com/pact-foundation/pact-go v1.8.0 +# github.com/pact-foundation/pact-go v1.10.0 ## explicit; go 1.12 github.com/pact-foundation/pact-go/client github.com/pact-foundation/pact-go/dsl