From ea2c80735197b6ae79db2760a6979a7037c49e5c Mon Sep 17 00:00:00 2001 From: Matt Fellows Date: Sat, 11 Jun 2016 23:18:26 +1000 Subject: [PATCH] feat(writepact): allow client to control when pact file is written Create new WritePact() method in dsl package, remove 'Pact' prefix from structs, remove 'pact_' prefix from files and update E2E tests. --- README.md | 4 +- command/daemon.go | 2 +- command/root.go | 6 +- daemon/daemon.go | 22 +++--- daemon/daemon_test.go | 28 ++++---- .../{pact_mock_service.go => mock_service.go} | 17 ++--- ...k_service_test.go => mock_service_test.go} | 6 +- doc.go | 3 + dsl/client.go | 14 ++-- dsl/client_test.go | 20 +++--- dsl/interaction.go | 11 +++ dsl/interaction_test.go | 48 +++++++++++++ dsl/mock_service.go | 14 ++-- dsl/mock_service_test.go | 14 ++-- dsl/pact.go | 68 ++++++++++++++++--- dsl/pact_test.go | 49 ++++++++++++- ...pact_list_response.go => list_response.go} | 2 +- types/mock_server.go | 9 +++ types/pact_mock_server.go | 9 --- 19 files changed, 250 insertions(+), 96 deletions(-) rename daemon/{pact_mock_service.go => mock_service.go} (52%) rename daemon/{pact_mock_service_test.go => mock_service_test.go} (73%) rename types/{pact_list_response.go => list_response.go} (84%) create mode 100644 types/mock_server.go delete mode 100644 types/pact_mock_server.go diff --git a/README.md b/README.md index 785f45ebf..b691eb526 100644 --- a/README.md +++ b/README.md @@ -111,7 +111,9 @@ func TestLogin(t *testing.T) { if err != nil { t.Fatalf("Error on Verify: %v", err) } - // You should now have a pact file in the file `/pacts/my_consumer-my_provider.json` + + // Write pact to file `/pacts/my_consumer-my_provider.json` + pact.WritePact() } ``` diff --git a/command/daemon.go b/command/daemon.go index 01ac364ff..a453f192f 100644 --- a/command/daemon.go +++ b/command/daemon.go @@ -14,7 +14,7 @@ var daemonCmd = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { setLogLevel(verbose, logLevel) - mock := &daemon.PactMockService{} + mock := &daemon.MockService{} mock.Setup() verifier := &daemon.VerificationService{} verifier.Setup() diff --git a/command/root.go b/command/root.go index c90441c20..d51eff742 100644 --- a/command/root.go +++ b/command/root.go @@ -21,9 +21,9 @@ var RootCmd = &cobra.Command{ Use: "pact-go", Short: "Pact Go makes it easier to work with Pact with Golang projects", Long: `Pact Go is a utility that wraps a number of external applications into - an idiomatic Golang interface and CLI, providing a mock service and DSL for - the consumer project, and interaction playback and verification for the - service provider project.`, +an idiomatic Golang interface and CLI, providing a mock service and DSL for +the consumer project, and interaction playback and verification for the +service provider project.`, } // Execute adds all child commands to the root command sets flags appropriately. diff --git a/daemon/daemon.go b/daemon/daemon.go index 892a50555..b6cd066ea 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -34,12 +34,12 @@ type Daemon struct { } // NewDaemon returns a new Daemon with all instance variables initialised. -func NewDaemon(pactMockServiceManager Service, verificationServiceManager Service) *Daemon { - pactMockServiceManager.Setup() +func NewDaemon(MockServiceManager Service, verificationServiceManager Service) *Daemon { + MockServiceManager.Setup() verificationServiceManager.Setup() return &Daemon{ - pactMockSvcManager: pactMockServiceManager, + pactMockSvcManager: MockServiceManager, verificationSvcManager: verificationServiceManager, signalChan: make(chan os.Signal, 1), } @@ -94,11 +94,11 @@ func (d Daemon) Shutdown() { } } -// StartServer starts a mock server and returns a pointer to atypes.PactMockServer +// StartServer starts a mock server and returns a pointer to atypes.MockServer // struct. -func (d Daemon) StartServer(request types.PactMockServer, reply *types.PactMockServer) error { +func (d Daemon) StartServer(request types.MockServer, reply *types.MockServer) error { log.Println("[DEBUG] daemon - starting mock server") - server := &types.PactMockServer{} + server := &types.MockServer{} port, svc := d.pactMockSvcManager.NewService(request.Args) server.Port = port server.Status = -1 @@ -139,13 +139,13 @@ func (d Daemon) VerifyProvider(request types.VerifyRequest, reply *types.Command return nil } -// ListServers returns a slice of all running types.PactMockServers. -func (d Daemon) ListServers(request types.PactMockServer, reply *types.PactListResponse) error { +// ListServers returns a slice of all running types.MockServers. +func (d Daemon) ListServers(request types.MockServer, reply *types.PactListResponse) error { log.Println("[DEBUG] daemon - listing mock servers") - var servers []*types.PactMockServer + var servers []*types.MockServer for port, s := range d.pactMockSvcManager.List() { - servers = append(servers, &types.PactMockServer{ + servers = append(servers, &types.MockServer{ Pid: s.Process.Pid, Port: port, }) @@ -159,7 +159,7 @@ func (d Daemon) ListServers(request types.PactMockServer, reply *types.PactListR } // StopServer stops the given mock server. -func (d Daemon) StopServer(request types.PactMockServer, reply *types.PactMockServer) error { +func (d Daemon) StopServer(request types.MockServer, reply *types.MockServer) error { log.Println("[DEBUG] daemon - stopping mock server") success, err := d.pactMockSvcManager.Stop(request.Pid) if success == true && err == nil { diff --git a/daemon/daemon_test.go b/daemon/daemon_test.go index 7c8174946..0a10d6f3f 100644 --- a/daemon/daemon_test.go +++ b/daemon/daemon_test.go @@ -145,8 +145,8 @@ func TestDaemonShutdown(t *testing.T) { func TestStartServer(t *testing.T) { daemon, _ := createMockedDaemon(true) - req := types.PactMockServer{Pid: 1234} - res := types.PactMockServer{} + req := types.MockServer{Pid: 1234} + res := types.MockServer{} err := daemon.StartServer(req, &res) if err != nil { t.Fatalf("Error: %v", err) @@ -164,7 +164,7 @@ func TestStartServer(t *testing.T) { func TestListServers(t *testing.T) { daemon, _ := createMockedDaemon(true) var res types.PactListResponse - err := daemon.ListServers(types.PactMockServer{}, &res) + err := daemon.ListServers(types.MockServer{}, &res) if err != nil { t.Fatalf("Error: %v", err) @@ -178,12 +178,12 @@ func TestListServers(t *testing.T) { func TestStopServer(t *testing.T) { daemon, manager := createMockedDaemon(true) var cmd *exec.Cmd - var res types.PactMockServer + var res types.MockServer for _, s := range manager.List() { cmd = s } - request := types.PactMockServer{ + request := types.MockServer{ Pid: cmd.Process.Pid, } @@ -204,12 +204,12 @@ func TestStopServer(t *testing.T) { func TestStopServer_Fail(t *testing.T) { daemon, manager := createMockedDaemon(true) var cmd *exec.Cmd - var res types.PactMockServer + var res types.MockServer for _, s := range manager.List() { cmd = s } - request := types.PactMockServer{ + request := types.MockServer{ Pid: cmd.Process.Pid, } @@ -224,12 +224,12 @@ func TestStopServer_Fail(t *testing.T) { func TestStopServer_FailedStatus(t *testing.T) { daemon, manager := createMockedDaemon(true) var cmd *exec.Cmd - var res types.PactMockServer + var res types.MockServer for _, s := range manager.List() { cmd = s } - request := types.PactMockServer{ + request := types.MockServer{ Pid: cmd.Process.Pid, } @@ -344,7 +344,7 @@ func TestRPCClient_List(t *testing.T) { client, err := rpc.DialHTTP("tcp", fmt.Sprintf(":%d", port)) var res types.PactListResponse - err = client.Call("Daemon.ListServers", types.PactMockServer{}, &res) + err = client.Call("Daemon.ListServers", types.MockServer{}, &res) if err != nil { log.Fatal("rpc error:", err) } @@ -362,8 +362,8 @@ func TestRPCClient_StartServer(t *testing.T) { connectToDaemon(port, t) client, err := rpc.DialHTTP("tcp", fmt.Sprintf(":%d", port)) - var res types.PactMockServer - err = client.Call("Daemon.StartServer", types.PactMockServer{}, &res) + var res types.MockServer + err = client.Call("Daemon.StartServer", types.MockServer{}, &res) if err != nil { log.Fatal("rpc error:", err) } @@ -388,12 +388,12 @@ func TestRPCClient_StopServer(t *testing.T) { for _, s := range manager.List() { cmd = s } - request := types.PactMockServer{ + request := types.MockServer{ Pid: cmd.Process.Pid, } client, err := rpc.DialHTTP("tcp", fmt.Sprintf(":%d", port)) - var res *types.PactMockServer + var res *types.MockServer err = client.Call("Daemon.StopServer", request, &res) if err != nil { log.Fatal("rpc error:", err) diff --git a/daemon/pact_mock_service.go b/daemon/mock_service.go similarity index 52% rename from daemon/pact_mock_service.go rename to daemon/mock_service.go index 2b8a8745c..b55b7aa7d 100644 --- a/daemon/pact_mock_service.go +++ b/daemon/mock_service.go @@ -3,34 +3,27 @@ package daemon import ( "fmt" "log" - "os" "path/filepath" "github.com/kardianos/osext" "github.com/pact-foundation/pact-go/utils" ) -// PactMockService is a wrapper for the Pact Mock Service. -type PactMockService struct { +// MockService is a wrapper for the Pact Mock Service. +type MockService struct { ServiceManager } -// NewService creates a new PactMockService with default settings. -func (m *PactMockService) NewService(args []string) (int, Service) { +// NewService creates a new MockService with default settings. +func (m *MockService) NewService(args []string) (int, Service) { port, _ := utils.GetFreePort() - version := 2 - dir, _ := os.Getwd() - logDir := fmt.Sprintf(filepath.Join(dir, "logs")) - dir = fmt.Sprintf(filepath.Join(dir, "pacts")) log.Println("[DEBUG] starting mock service on port:", port) m.Args = []string{ fmt.Sprintf("--port %d", port), - fmt.Sprintf("--pact-specification-version %d", version), - fmt.Sprintf("--pact-dir %s", dir), - fmt.Sprintf("--log %s/pact.log", logDir), } m.Args = append(m.Args, args...) + m.Command = getMockServiceCommandPath() return port, m } diff --git a/daemon/pact_mock_service_test.go b/daemon/mock_service_test.go similarity index 73% rename from daemon/pact_mock_service_test.go rename to daemon/mock_service_test.go index 920f94091..110855e13 100644 --- a/daemon/pact_mock_service_test.go +++ b/daemon/mock_service_test.go @@ -2,8 +2,8 @@ package daemon import "testing" -func TestPactMockService_NewService(t *testing.T) { - s := &PactMockService{} +func TestMockService_NewService(t *testing.T) { + s := &MockService{} port, svc := s.NewService([]string{"--foo bar"}) if port <= 0 { @@ -14,7 +14,7 @@ func TestPactMockService_NewService(t *testing.T) { t.Fatalf("Expected a non-nil object but got nil") } - if s.Args[4] != "--foo bar" { + if s.Args[1] != "--foo bar" { t.Fatalf("Expected '--foo bar' argument to be passed") } } diff --git a/doc.go b/doc.go index 4c83c6946..45e9cc377 100644 --- a/doc.go +++ b/doc.go @@ -52,6 +52,9 @@ A typical consumer-side test would look something like this: if err != nil { t.Fatalf("Error on Verify: %v", err) } + + // Write pact to file + pact.WritePact() } If this test completed successfully, a Pact file should have been written to diff --git a/dsl/client.go b/dsl/client.go index df10142b0..795c6eb1c 100644 --- a/dsl/client.go +++ b/dsl/client.go @@ -24,7 +24,7 @@ var ( // Client is the simplified remote interface to the Pact Daemon. type Client interface { - StartServer() *types.PactMockServer + StartServer() *types.MockServer } // PactClient is the default implementation of the Client interface. @@ -83,12 +83,12 @@ var waitForPort = func(port int, message string) error { } // StartServer starts a remote Pact Mock Server. -func (p *PactClient) StartServer() *types.PactMockServer { +func (p *PactClient) StartServer(args []string) *types.MockServer { log.Println("[DEBUG] client: starting a server") - var res types.PactMockServer + var res types.MockServer client, err := getHTTPClient(p.Port) if err == nil { - err = client.Call(commandStartServer, types.PactMockServer{}, &res) + err = client.Call(commandStartServer, types.MockServer{Args: args}, &res) if err != nil { log.Println("[ERROR] rpc:", err.Error()) } @@ -129,7 +129,7 @@ func (p *PactClient) ListServers() *types.PactListResponse { var res types.PactListResponse client, err := getHTTPClient(p.Port) if err == nil { - err = client.Call(commandListServers, types.PactMockServer{}, &res) + err = client.Call(commandListServers, types.MockServer{}, &res) if err != nil { log.Println("[ERROR] rpc:", err.Error()) } @@ -138,9 +138,9 @@ func (p *PactClient) ListServers() *types.PactListResponse { } // StopServer stops a remote Pact Mock Server. -func (p *PactClient) StopServer(server *types.PactMockServer) *types.PactMockServer { +func (p *PactClient) StopServer(server *types.MockServer) *types.MockServer { log.Println("[DEBUG] client: stop server") - var res types.PactMockServer + var res types.MockServer client, err := getHTTPClient(p.Port) if err == nil { err = client.Call(commandStopServer, server, &res) diff --git a/dsl/client_test.go b/dsl/client_test.go index ef1fc098b..ef3ed34e2 100644 --- a/dsl/client_test.go +++ b/dsl/client_test.go @@ -130,7 +130,7 @@ func TestClient_List(t *testing.T) { func TestClient_ListFail(t *testing.T) { timeoutDuration = 50 * time.Millisecond client := &PactClient{ /* don't supply port */ } - client.StartServer() + client.StartServer([]string{}) list := client.ListServers() if len(list.Servers) != 0 { @@ -146,7 +146,7 @@ func TestClient_StartServer(t *testing.T) { defer waitForDaemonToShutdown(port, t) client := &PactClient{Port: port} - client.StartServer() + client.StartServer([]string{}) if svc.ServiceStartCount != 1 { t.Fatalf("Expected 1 server to have been started, got %d", svc.ServiceStartCount) } @@ -185,11 +185,11 @@ func TestClient_StartServerRPCError(t *testing.T) { "rpc: service/method request ill-formed: failcommand": func() interface{} { return client.StopDaemon().Error() }, - &types.PactMockServer{}: func() interface{} { - return client.StopServer(&types.PactMockServer{}) + &types.MockServer{}: func() interface{} { + return client.StopServer(&types.MockServer{}) }, - &types.PactMockServer{}: func() interface{} { - return client.StartServer() + &types.MockServer{}: func() interface{} { + return client.StartServer([]string{}) }, &types.PactListResponse{}: func() interface{} { return client.ListServers() @@ -300,7 +300,7 @@ func TestClient_StartServerFail(t *testing.T) { timeoutDuration = 50 * time.Millisecond client := &PactClient{ /* don't supply port */ } - server := client.StartServer() + server := client.StartServer([]string{}) if server.Port != 0 { t.Fatalf("Expected server to be empty %v", server) } @@ -314,7 +314,7 @@ func TestClient_StopServer(t *testing.T) { defer waitForDaemonToShutdown(port, t) client := &PactClient{Port: port} - client.StopServer(&types.PactMockServer{}) + client.StopServer(&types.MockServer{}) if svc.ServiceStopCount != 1 { t.Fatalf("Expected 1 server to have been stopped, got %d", svc.ServiceStartCount) } @@ -323,8 +323,8 @@ func TestClient_StopServer(t *testing.T) { func TestClient_StopServerFail(t *testing.T) { timeoutDuration = 50 * time.Millisecond client := &PactClient{ /* don't supply port */ } - res := client.StopServer(&types.PactMockServer{}) - should := &types.PactMockServer{} + res := client.StopServer(&types.MockServer{}) + should := &types.MockServer{} if !reflect.DeepEqual(res, should) { t.Fatalf("Expected nil object but got a difference: %v != %v", res, should) } diff --git a/dsl/interaction.go b/dsl/interaction.go index d05c79ada..8fdec58ab 100644 --- a/dsl/interaction.go +++ b/dsl/interaction.go @@ -38,6 +38,17 @@ func (p *Interaction) UponReceiving(description string) *Interaction { // Mandatory. func (p *Interaction) WithRequest(request *Request) *Interaction { p.Request = request + + // Need to fix any weird JSON marshalling issues with the body Here + // If body is a string, not an object, we need to put it back into an object + // so that it's not double encoded + switch content := request.Body.(type) { + case string: + p.Request.Body = toObject([]byte(content)) + default: + // leave alone + } + return p } diff --git a/dsl/interaction_test.go b/dsl/interaction_test.go index 92a5271a4..bed801a62 100644 --- a/dsl/interaction_test.go +++ b/dsl/interaction_test.go @@ -21,6 +21,54 @@ func TestInteraction_NewInteraction(t *testing.T) { } } +func TestInteraction_WithRequest(t *testing.T) { + // Pass in plain string, should be left alone + i := (&Interaction{}). + Given("Some state"). + UponReceiving("Some name for the test"). + WithRequest(&Request{ + Body: "somestring", + }) + + content, ok := i.Request.Body.(string) + + if !ok { + t.Fatalf("must be a string") + } + + if content != "somestring" { + t.Fatalf("Expected 'somestring' but got '%s'", content) + } + + // structured string should be changed to an interface{} + i = (&Interaction{}). + Given("Some state"). + UponReceiving("Some name for the test"). + WithRequest(&Request{ + Body: `{ + "foo": "bar", + "baz": "bat" + }`, + }) + + obj := map[string]string{ + "foo": "bar", + "baz": "bat", + } + + var expect interface{} + body, _ := json.Marshal(obj) + json.Unmarshal(body, &expect) + + if _, ok := i.Request.Body.(map[string]interface{}); !ok { + t.Fatalf("Expected response to be of type 'map[string]string'") + } + + if !reflect.DeepEqual(i.Request.Body, expect) { + t.Fatalf("Expected response object body '%v' to match '%v'", i.Request.Body, expect) + } +} + func TestInteraction_WillRespondWith(t *testing.T) { // Pass in plain string, should be left alone i := (&Interaction{}). diff --git a/dsl/mock_service.go b/dsl/mock_service.go index ab27e5dbc..f222e6afc 100644 --- a/dsl/mock_service.go +++ b/dsl/mock_service.go @@ -10,10 +10,10 @@ import ( "net/http" ) -// PactMockService is the HTTP interface to setup the Pact Mock Service +// MockService is the HTTP interface to setup the Pact Mock Service // See https://github.com/bethesque/pact-mock_service and // https://gist.github.com/bethesque/9d81f21d6f77650811f4. -type PactMockService struct { +type MockService struct { // BaseURL is the base host for the Pact Mock Service. BaseURL string @@ -25,7 +25,7 @@ type PactMockService struct { } // call sends a message to the Pact service -func (m *PactMockService) call(method string, url string, content interface{}) error { +func (m *MockService) call(method string, url string, content interface{}) error { body, err := json.Marshal(content) if err != nil { return err @@ -60,28 +60,28 @@ func (m *PactMockService) call(method string, url string, content interface{}) e } // DeleteInteractions removes any previous Mock Service Interactions. -func (m *PactMockService) DeleteInteractions() error { +func (m *MockService) DeleteInteractions() error { log.Println("[DEBUG] mock service delete interactions") url := fmt.Sprintf("%s/interactions", m.BaseURL) return m.call("DELETE", url, nil) } // AddInteraction adds a new Pact Mock Service interaction. -func (m *PactMockService) AddInteraction(interaction *Interaction) error { +func (m *MockService) AddInteraction(interaction *Interaction) error { log.Println("[DEBUG] mock service add interaction") url := fmt.Sprintf("%s/interactions", m.BaseURL) return m.call("POST", url, interaction) } // Verify confirms that all interactions were called. -func (m *PactMockService) Verify() error { +func (m *MockService) Verify() error { log.Println("[DEBUG] mock service verify") url := fmt.Sprintf("%s/interactions/verification", m.BaseURL) return m.call("GET", url, nil) } // WritePact writes the pact file to disk. -func (m *PactMockService) WritePact() error { +func (m *MockService) WritePact() error { log.Println("[DEBUG] mock service write pact") if m.Consumer == "" || m.Provider == "" { return errors.New("Consumer and Provider name need to be provided") diff --git a/dsl/mock_service_test.go b/dsl/mock_service_test.go index 2d32025df..ce6c77494 100644 --- a/dsl/mock_service_test.go +++ b/dsl/mock_service_test.go @@ -35,7 +35,7 @@ func TestMockService_AddInteraction(t *testing.T) { ms := setupMockServer(true, t) defer ms.Close() - mockService := &PactMockService{ + mockService := &MockService{ BaseURL: ms.URL, } i := (&Interaction{}). @@ -54,7 +54,7 @@ func TestMockService_AddInteractionFail(t *testing.T) { ms := setupMockServer(false, t) defer ms.Close() - mockService := &PactMockService{ + mockService := &MockService{ BaseURL: ms.URL, } i := (&Interaction{}). @@ -73,7 +73,7 @@ func TestMockService_DeleteInteractions(t *testing.T) { ms := setupMockServer(true, t) defer ms.Close() - mockService := &PactMockService{ + mockService := &MockService{ BaseURL: ms.URL, } err := mockService.DeleteInteractions() @@ -87,7 +87,7 @@ func TestMockService_WritePact(t *testing.T) { ms := setupMockServer(true, t) defer ms.Close() - mockService := &PactMockService{ + mockService := &MockService{ BaseURL: ms.URL, Consumer: "Foo Consumer", Provider: "Bar Provider", @@ -104,7 +104,7 @@ func TestMockService_WritePactFail(t *testing.T) { ms := setupMockServer(true, t) defer ms.Close() - mockService := &PactMockService{ + mockService := &MockService{ BaseURL: ms.URL, } @@ -119,7 +119,7 @@ func TestMockService_Verify(t *testing.T) { ms := setupMockServer(true, t) defer ms.Close() - mockService := &PactMockService{ + mockService := &MockService{ BaseURL: ms.URL, } @@ -134,7 +134,7 @@ func TestMockService_VerifyFail(t *testing.T) { ms := setupMockServer(false, t) defer ms.Close() - mockService := &PactMockService{ + mockService := &MockService{ BaseURL: ms.URL, } diff --git a/dsl/pact.go b/dsl/pact.go index 7c680a3bf..52bd71a9e 100644 --- a/dsl/pact.go +++ b/dsl/pact.go @@ -8,6 +8,7 @@ import ( "fmt" "log" "os" + "path/filepath" "github.com/hashicorp/logutils" "github.com/pact-foundation/pact-go/types" @@ -16,7 +17,7 @@ import ( // Pact is the container structure to run the Consumer Pact test cases. type Pact struct { // Current server for the consumer. - Server *types.PactMockServer + Server *types.MockServer // Port the Pact Daemon is running on. Port int @@ -36,8 +37,20 @@ type Pact struct { // Log levels. LogLevel string - // Used to detect if logging has been configured + // Used to detect if logging has been configured. logFilter *logutils.LevelFilter + + // Location of Pact external service invocation output logging. + // Defaults to `/logs`. + LogDir string + + // Pact files will be saved in this folder. + // Defaults to `/pacts`. + PactDir string + + // Specify which version of the Pact Specification should be used (1 or 2). + // Defaults to 2. + SpecificationVersion int } // AddInteraction creates a new Pact interaction, initialising all @@ -56,10 +69,29 @@ func (p *Pact) AddInteraction() *Interaction { func (p *Pact) Setup() *Pact { p.setupLogging() log.Printf("[DEBUG] pact setup") + dir, _ := os.Getwd() + + if p.LogDir == "" { + p.LogDir = fmt.Sprintf(filepath.Join(dir, "logs")) + } + + if p.PactDir == "" { + p.PactDir = fmt.Sprintf(filepath.Join(dir, "pacts")) + } + + if p.SpecificationVersion == 0 { + p.SpecificationVersion = 2 + } + if p.Server == nil { + args := []string{ + fmt.Sprintf("--pact-specification-version %d", p.SpecificationVersion), + fmt.Sprintf("--pact-dir %s", p.PactDir), + fmt.Sprintf("--log %s/pact.log", p.LogDir), + } client := &PactClient{Port: p.Port} p.pactClient = client - p.Server = client.StartServer() + p.Server = client.StartServer(args) } return p @@ -85,16 +117,18 @@ func (p *Pact) setupLogging() { // of each test suite. func (p *Pact) Teardown() *Pact { log.Printf("[DEBUG] teardown") - p.Server = p.pactClient.StopServer(p.Server) - + if p.Server != nil { + p.Server = p.pactClient.StopServer(p.Server) + } return p } // Verify runs the current test case against a Mock Service. // Will cleanup interactions between tests within a suite. func (p *Pact) Verify(integrationTest func() error) error { + p.Setup() log.Printf("[DEBUG] pact verify") - mockServer := &PactMockService{ + mockServer := &MockService{ BaseURL: fmt.Sprintf("http://localhost:%d", p.Server.Port), Consumer: p.Consumer, Provider: p.Provider, @@ -116,17 +150,35 @@ func (p *Pact) Verify(integrationTest func() error) error { return err } - err = mockServer.WritePact() + // Clear out interations + p.Interactions = make([]*Interaction, 0) + + return mockServer.DeleteInteractions() +} + +// WritePact should be called writes when all tests have been performed for a +// given Consumer <-> Provider pair. It will write out the Pact to the +// configured file. +func (p *Pact) WritePact() error { + p.Setup() + log.Printf("[DEBUG] pact write Pact file") + mockServer := &MockService{ + BaseURL: fmt.Sprintf("http://localhost:%d", p.Server.Port), + Consumer: p.Consumer, + Provider: p.Provider, + } + err := mockServer.WritePact() if err != nil { return err } - return mockServer.DeleteInteractions() + return nil } // VerifyProvider reads the provided pact files and runs verification against // a running Provider API. func (p *Pact) VerifyProvider(request *types.VerifyRequest) *types.CommandResponse { + p.Setup() log.Printf("[DEBUG] pact provider verification") return p.pactClient.VerifyProvider(request) } diff --git a/dsl/pact_test.go b/dsl/pact_test.go index c9ab2b721..dc7c57e20 100644 --- a/dsl/pact_test.go +++ b/dsl/pact_test.go @@ -13,6 +13,10 @@ import ( "github.com/pact-foundation/pact-go/utils" ) +var dir, _ = os.Getwd() +var pactDir = fmt.Sprintf("%s/../pacts", dir) +var logDir = fmt.Sprintf("%s/../logs", dir) + func TestPact_setupLogging(t *testing.T) { res := captureOutput(func() { (&Pact{LogLevel: "DEBUG"}).setupLogging() @@ -67,7 +71,7 @@ func TestPact_Verify(t *testing.T) { } pact := &Pact{ - Server: &types.PactMockServer{ + Server: &types.MockServer{ Port: getPort(ms.URL), }, Consumer: "My Consumer", @@ -91,13 +95,49 @@ func TestPact_Verify(t *testing.T) { } } +func TestPact_WritePact(t *testing.T) { + ms := setupMockServer(true, t) + defer ms.Close() + + pact := &Pact{ + Server: &types.MockServer{ + Port: getPort(ms.URL), + }, + Consumer: "My Consumer", + Provider: "My Provider", + } + + err := pact.WritePact() + if err != nil { + t.Fatalf("Error: %v", err) + } +} + +func TestPact_WritePactFail(t *testing.T) { + ms := setupMockServer(false, t) + defer ms.Close() + + pact := &Pact{ + Server: &types.MockServer{ + Port: getPort(ms.URL), + }, + Consumer: "My Consumer", + Provider: "My Provider", + } + + err := pact.WritePact() + if err == nil { + t.Fatalf("Expected error but got none") + } +} + func TestPact_VerifyFail(t *testing.T) { ms := setupMockServer(false, t) defer ms.Close() var testFunc = func() error { return nil } pact := &Pact{ - Server: &types.PactMockServer{ + Server: &types.MockServer{ Port: getPort(ms.URL), }, } @@ -226,6 +266,8 @@ func TestPact_Integration(t *testing.T) { Consumer: "billy", Provider: "bobby", LogLevel: "DEBUG", + LogDir: logDir, + PactDir: pactDir, } defer pact.Teardown() @@ -297,6 +339,9 @@ func TestPact_Integration(t *testing.T) { t.Fatalf("Error on Verify: %v", err) } + // Write pact to file `/pacts/my_consumer-my_provider.json` + pact.WritePact() + // Publish the Pacts... p := &Publisher{} brokerHost := os.Getenv("PACT_BROKER_HOST") diff --git a/types/pact_list_response.go b/types/list_response.go similarity index 84% rename from types/pact_list_response.go rename to types/list_response.go index 843986fda..d4836045c 100644 --- a/types/pact_list_response.go +++ b/types/list_response.go @@ -3,5 +3,5 @@ package types // PactListResponse contains a list of all running Servers. type PactListResponse struct { // System exit code from the Publish task. - Servers []*PactMockServer + Servers []*MockServer } diff --git a/types/mock_server.go b/types/mock_server.go new file mode 100644 index 000000000..70d6704e7 --- /dev/null +++ b/types/mock_server.go @@ -0,0 +1,9 @@ +package types + +// MockServer contains the RPC client interface to a Mock Server +type MockServer struct { + Pid int + Port int + Status int + Args []string +} diff --git a/types/pact_mock_server.go b/types/pact_mock_server.go deleted file mode 100644 index 8e90bcea8..000000000 --- a/types/pact_mock_server.go +++ /dev/null @@ -1,9 +0,0 @@ -package types - -// PactMockServer contains the RPC client interface to a Mock Server -type PactMockServer struct { - Pid int - Port int - Status int - Args []string -}