Skip to content

Commit

Permalink
feat: refactor log package, rename consumer entry point to HTTPMockPr…
Browse files Browse the repository at this point in the history
…ovider, add dynamic host into execute test method
  • Loading branch information
mefellows committed Aug 9, 2020
1 parent 140497d commit 07c7fc9
Show file tree
Hide file tree
Showing 13 changed files with 108 additions and 208 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go:
- 1.11.x
- 1.12.x
- 1.13.x
- 1.14.x

services:
- docker
Expand Down
12 changes: 6 additions & 6 deletions examples/v3/consumer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,29 +26,29 @@ func TestConsumer(t *testing.T) {
LastName string `json:"lastName" pact:"example=sampson"`
Date string `json:"datetime" pact:"example=20200101,regex=[0-9a-z-A-Z]+"`
}
v3.SetLogLevel("TRACE")

// Create Pact connecting to local Daemon
mockProvider := &v3.MockProvider{
mockProvider := &v3.HTTPMockProvider{
Consumer: "MyConsumer",
Provider: "MyProvider",
Host: "127.0.0.1",
Port: 8080,
LogLevel: "TRACE",
SpecificationVersion: v3.V2,
TLS: true,
}

mockProvider.Setup()

// Pass in test case
var test = func(config v3.MockServerConfig) error {
client := &http.Client{
Transport: v3.GetTLSConfigForTLSMockServer(),
Transport: &http.Transport{
TLSClientConfig: config.TLSConfig,
},
}
req := &http.Request{
Method: "POST",
URL: &url.URL{
Host: fmt.Sprintf("localhost:%d", mockProvider.Port),
Host: fmt.Sprintf("%s:%d", config.Host, config.Port),
Scheme: "https",
Path: "/foobar",
},
Expand Down
64 changes: 0 additions & 64 deletions examples/v3/pacts/consumer-provider.json

This file was deleted.

111 changes: 46 additions & 65 deletions v3/http.go
Original file line number Diff line number Diff line change
@@ -1,30 +1,36 @@
//package v3 contains the main Pact DSL used in the Consumer
// Package v3 contains the main Pact DSL used in the Consumer
// collaboration test cases, and Provider contract test verification.
package v3

// TODO: setup a proper state machine to prevent actions
// Current issues
// 1. Setup needs to be initialised to get a port
// 1. Setup needs to be initialised to get a port -> should be resolved by creating the server at the point of verification
// 2. Ensure that interactions are properly cleared
// 3. Need to ensure only v2 or v3 matchers are added

import (
"bytes"
"crypto/tls"
"encoding/json"
"errors"
"fmt"
"log"
"net/http"
"os"
"path/filepath"
"time"

"github.com/hashicorp/logutils"
"github.com/pact-foundation/pact-go/utils"
"github.com/pact-foundation/pact-go/v3/install"
"github.com/pact-foundation/pact-go/v3/native"
"github.com/pact-foundation/pact-go/v3/internal/native"
)

// MockProvider is the container structure to run the Consumer MockProvider test cases.
type MockProvider struct {
func init() {
initLogging()
native.Init()
}

// HTTPMockProvider is the entrypoint for http consumer tests
type HTTPMockProvider struct {
// Consumer is the name of the Consumer/Client.
Consumer string

Expand All @@ -34,9 +40,6 @@ type MockProvider struct {
// Interactions contains all of the Mock Service Interactions to be setup.
Interactions []*Interaction

// Log levels.
LogLevel logutils.LogLevel

// Location of Pact external service invocation output logging.
// Defaults to `<cwd>/logs`.
LogDir string
Expand Down Expand Up @@ -87,29 +90,27 @@ type MockProvider struct {
// TLS enables a mock service behind a self-signed certificate
// TODO: document and test this
TLS bool

// TODO: clean pact dir on run?
// CleanPactFile bool
}

// TODO: pass this into verification test func
type MockServerConfig struct{}
// MockServerConfig stores the address configuration details of the server for the current executing test
// This is most useful for the use of OS assigned, dynamic ports and parallel tests
type MockServerConfig struct {
Port int
Host string
TLSConfig *tls.Config
}

// AddInteraction creates a new Pact interaction, initialising all
// required things. Will automatically start a Mock Service if none running.
func (p *MockProvider) AddInteraction() *Interaction {
p.Setup()
func (p *HTTPMockProvider) AddInteraction() *Interaction {
log.Println("[DEBUG] pact add interaction")
i := &Interaction{}
p.Interactions = append(p.Interactions, i)
return i
}

// Setup starts the Pact Mock Server. This is usually called before each test
// suite begins. AddInteraction() will automatically call this if no Mock Server
// has been started.
func (p *MockProvider) Setup() (*MockProvider, error) {
setLogLevel(p.LogLevel)
// validateConfig validates the configuration for the consumer test
func (p *HTTPMockProvider) validateConfig() error {
log.Println("[DEBUG] pact setup")
dir, _ := os.Getwd()

Expand Down Expand Up @@ -147,62 +148,45 @@ func (p *MockProvider) Setup() (*MockProvider, error) {
}

if pErr != nil {
return nil, fmt.Errorf("error: unable to find free port, mock server will fail to start")
return fmt.Errorf("error: unable to find free port, mock server will fail to start")
}

native.Init()

return p, nil
return nil
}

// // TODO: do we still want this lifecycle method?
// Teardown stops the Pact Mock Server. This usually is called on completion
// // of each test suite.
// func (p *MockProvider) Teardown() error {
// log.Println("[DEBUG] teardown")

// if p.Port != 0 {
// err := native.WritePactFile(p.Port, p.PactDir)
// if err != nil {
// return err
// }

// if native.CleanupMockServer(p.Port) {
// p.Port = 0
// } else {
// log.Println("[DEBUG] unable to teardown server")
// }
// }
// return nil
// }

func (p *MockProvider) cleanInteractions() {
func (p *HTTPMockProvider) cleanInteractions() {
p.Interactions = make([]*Interaction, 0)
}

// ExecuteTest runs the current test case against a Mock Service.
// Will cleanup interactions between tests within a suite
// and write the pact file if successful
func (p *MockProvider) ExecuteTest(integrationTest func(MockServerConfig) error) error {
func (p *HTTPMockProvider) ExecuteTest(integrationTest func(MockServerConfig) error) error {
log.Println("[DEBUG] pact verify")
p.Setup()
err := p.validateConfig()
if err != nil {
return err
}

// Generate interactions for Pact file
serialisedPact := NewPactFile(p.Consumer, p.Provider, p.Interactions, p.SpecificationVersion)
fmt.Println("[DEBUG] Sending pact file:", formatJSONObject(serialisedPact))
log.Println("[DEBUG] Sending pact file:", formatJSONObject(serialisedPact))

// Clean interactions
p.cleanInteractions()

// TODO: wire this better
native.CreateMockServer(formatJSONObject(serialisedPact), fmt.Sprintf("%s:%d", p.Host, p.Port), p.TLS)

// TODO: use cases for having server running post integration test?
// Probably not...
port, err := native.CreateMockServer(formatJSONObject(serialisedPact), fmt.Sprintf("%s:%d", p.Host, p.Port), p.TLS)
defer native.CleanupMockServer(p.Port)
if err != nil {
return err
}

// Run the integration test
err := integrationTest(MockServerConfig{})
err = integrationTest(MockServerConfig{
Port: port,
Host: p.Host,
TLSConfig: GetTLSConfigForTLSMockServer(),
})

if err != nil {
return err
Expand All @@ -222,8 +206,7 @@ func (p *MockProvider) ExecuteTest(integrationTest func(MockServerConfig) error)

// TODO: pretty print this to make it really easy to understand the problems
// See existing Pact/Ruby code examples
// What about the Rust/Elm compiler feedback, they are pretty great too.
func (p *MockProvider) displayMismatches(mismatches []native.MismatchedRequest) {
func (p *HTTPMockProvider) displayMismatches(mismatches []native.MismatchedRequest) {
if len(mismatches) > 0 {
log.Println("[INFO] pact validation failed, errors: ")
for _, m := range mismatches {
Expand Down Expand Up @@ -254,7 +237,7 @@ func (p *MockProvider) displayMismatches(mismatches []native.MismatchedRequest)
// given Consumer <-> Provider pair. It will write out the Pact to the
// configured file. This is safe to call multiple times as the service is smart
// enough to merge pacts and avoid duplicates.
func (p *MockProvider) WritePact() error {
func (p *HTTPMockProvider) WritePact() error {
log.Println("[DEBUG] write pact file")
if p.Port != 0 {
return native.WritePactFile(p.Port, p.PactDir)
Expand Down Expand Up @@ -286,11 +269,9 @@ func formatJSONObject(object interface{}) string {
return formatJSONString(string(out))
}

// GetGetTLSConfigForTLSMockServer gets an http transport with
// GetTLSConfigForTLSMockServer gets an http transport with
// the certificates already trusted. Alternatively, simply set
// trust level to insecure
func GetTLSConfigForTLSMockServer() *http.Transport {
return &http.Transport{
TLSClientConfig: native.GetTLSConfig(),
}
func GetTLSConfigForTLSMockServer() *tls.Config {
return native.GetTLSConfig()
}

0 comments on commit 07c7fc9

Please sign in to comment.