From 2465e9ffd13df12bb453adbe4f77ac3978998e68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Mac=C3=ADk?= Date: Fri, 11 Jan 2019 17:30:03 +0100 Subject: [PATCH] contract-tests: Implement consumer of fabric8auth service. --- .make/test.mk | 25 +- Gopkg.lock | 7 +- test/contracts/consumer/api_space.go | 82 ----- test/contracts/consumer/consumer_test.go | 63 ---- .../consumer/{ => fabric8auth}/api_status.go | 14 +- .../consumer/fabric8auth/api_token.go | 55 +++ .../consumer/fabric8auth/api_user.go | 257 +++++++++++++ .../consumer/fabric8auth/consumer_test.go | 69 ++++ test/contracts/main.go | 188 +--------- test/contracts/provider/provider_test.go | 83 ----- test/contracts/provider/setup.go | 345 ------------------ 11 files changed, 404 insertions(+), 784 deletions(-) delete mode 100644 test/contracts/consumer/api_space.go delete mode 100644 test/contracts/consumer/consumer_test.go rename test/contracts/consumer/{ => fabric8auth}/api_status.go (74%) create mode 100644 test/contracts/consumer/fabric8auth/api_token.go create mode 100644 test/contracts/consumer/fabric8auth/api_user.go create mode 100644 test/contracts/consumer/fabric8auth/consumer_test.go delete mode 100644 test/contracts/provider/provider_test.go delete mode 100644 test/contracts/provider/setup.go diff --git a/.make/test.mk b/.make/test.mk index 29ca9bd4da..93143c017c 100644 --- a/.make/test.mk +++ b/.make/test.mk @@ -180,21 +180,20 @@ test-integration-benchmark: prebuild-check migrate-database $(SOURCES) $(eval TEST_PACKAGES:=$(shell go list ./... | grep -v $(ALL_PKGS_EXCLUDE_PATTERN))) F8_DEVELOPER_MODE_ENABLED=1 F8_RESOURCE_DATABASE=1 F8_RESOURCE_UNIT_TEST=0 F8_LOG_LEVEL=$(F8_LOG_LEVEL) go test -run=^$$ -bench=. -cpu 1,2,4 -test.benchmem $(GO_TEST_VERBOSITY_FLAG) $(TEST_PACKAGES) | grep -E "Bench|allocs" -.PHONY: test-contracts-no-coverage -## Runs the contract tests WITHOUT producing coverage files for each package. -## Make sure you ran "make integration-test-env-prepare" before you run this target. -test-contracts-no-coverage: prebuild-check migrate-database $(SOURCES) +.PHONY: test-contracts-consumer-no-coverage +## Runs the consumer side of contract tests WITHOUT producing coverage files for each package. +## and publish generated Pact file (the contract) to the Pact broker. +## The following env variables needs to be set in environment: +## - Pact broker for storing pact files +## PACT_BROKER_URL +## PACT_BROKER_USERNAME +## PACT_BROKER_PASSWORD +test-contracts-consumer-no-coverage: $(call log-info,"Running test: $@") - $(eval TEST_PACKAGES:=$(shell go list ./... | grep contracts | grep 'consumer\|provider')) + $(eval TEST_PACKAGES:=$(shell go list ./... | grep -e 'contracts/consumer')) PACT_DIR=$(PWD)/test/contracts/pacts \ - PACT_CONSUMER=Fabric8WitConsumer \ - PACT_PROVIDER=Fabric8Wit \ - PACT_VERSION=1.0.0 \ - PACT_PROVIDER_BASE_URL=http://localhost:8080 \ - PACT_PROVIDER_AUTH_BASE_URL=http://localhost:8089 \ - OSIO_USERNAME="$(OSIO_USERNAME)" \ - OSIO_PASSWORD="$(OSIO_PASSWORD)" \ - go test -count=1 $(GO_TEST_VERBOSITY_FLAG) $(TEST_PACKAGES) + PACT_VERSION="latest" \ + go test $(GO_TEST_VERBOSITY_FLAG) -count=1 $(TEST_PACKAGES) .PHONY: test-remote ## Runs the remote tests and produces coverage files for each package. diff --git a/Gopkg.lock b/Gopkg.lock index 52d145841a..a5ad8b460e 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -434,7 +434,9 @@ [[projects]] name = "github.com/pact-foundation/pact-go" packages = [ + "client", "dsl", + "install", "types", "utils" ] @@ -604,11 +606,6 @@ revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71" version = "v1.2.1" -[[projects]] - name = "github.com/tebeka/selenium" - packages = ["."] - revision = "master" - [[projects]] name = "github.com/trivago/tgo" packages = ["tcontainer"] diff --git a/test/contracts/consumer/api_space.go b/test/contracts/consumer/api_space.go deleted file mode 100644 index 36768e910f..0000000000 --- a/test/contracts/consumer/api_space.go +++ /dev/null @@ -1,82 +0,0 @@ -package consumer - -import ( - "bytes" - "encoding/json" - "fmt" - "log" - "net/http" - "testing" - - "github.com/fabric8-services/fabric8-wit/test/contracts" - "github.com/pact-foundation/pact-go/dsl" -) - -// APISpacesCreate defines contract of /api/spaces endpoint to create a new space -func APISpacesCreate(t *testing.T, pact *dsl.Pact, spaceName string) { - - log.Printf("Invoking APISpaces now\n") - - // Pass in test case - var test = func() error { - u := fmt.Sprintf("http://localhost:%d/api/spaces", pact.Server.Port) - - reqBody, err := json.Marshal(contracts.CreateSpaceRequest{ - Data: contracts.CreateSpaceRequestData{ - Name: spaceName, - Path: "", - Attributes: contracts.CreateSpaceRequestAttributes{ - Name: spaceName, - Description: "Space created by the contract tests to test new space creation.", - }, - Type: "spaces", - PrivateSpace: false, - }, - }) - - if err != nil { - return err - } - req, err := http.NewRequest("POST", u, bytes.NewBuffer(reqBody)) - - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", contracts.TestJWSToken)) - if err != nil { - return err - } - - _, err = http.DefaultClient.Do(req) - if err != nil { - return err - } - return err - } - - // Set up our expected interactions. - pact. - AddInteraction(). - Given("WIT service is up and running."). - UponReceiving("A request to create a new space with a given name"). - WithRequest(dsl.Request{ - Method: "POST", - Path: dsl.String("/api/spaces"), - Headers: dsl.MapMatcher{ - "Content-Type": dsl.String("application/json"), - "Authorization": dsl.Term( - fmt.Sprintf("Bearer %s", contracts.TestJWSToken), - fmt.Sprintf("^Bearer %s$", contracts.JWSRegex), - ), - }, - Body: dsl.Match(contracts.CreateSpaceRequest{}), - }). - WillRespondWith(dsl.Response{ - Status: 201, - Headers: dsl.MapMatcher{"Content-Type": dsl.String("application/vnd.api+json")}, - Body: dsl.Match(contracts.Space{}), - }) - - // Verify - if err := pact.Verify(test); err != nil { - log.Fatalf("Error on Verify: %v", err) - } -} diff --git a/test/contracts/consumer/consumer_test.go b/test/contracts/consumer/consumer_test.go deleted file mode 100644 index c96d4d7663..0000000000 --- a/test/contracts/consumer/consumer_test.go +++ /dev/null @@ -1,63 +0,0 @@ -package consumer - -import ( - "log" - "os" - "testing" - - "github.com/fabric8-services/fabric8-wit/test/contracts" - "github.com/pact-foundation/pact-go/dsl" -) - -// TestWitAPIConsumer runs all user related tests -func TestWitAPIConsumer(t *testing.T) { - - log.SetOutput(os.Stdout) - - var pactDir = os.Getenv("PACT_DIR") - var pactConsumer = os.Getenv("PACT_CONSUMER") - var pactProvider = os.Getenv("PACT_PROVIDER") - - //var pactBrokerURL = os.Getenv("PACT_BROKER_URL") - //var pactBrokerUsername = os.Getenv("PACT_BROKER_USERNAME") - //var pactBrokerPassword = os.Getenv("PACT_BROKER_PASSWORD") - - //var userName = os.Getenv("OSIO_USERNAME") - - // Create Pact connecting to local Daemon - pact := &dsl.Pact{ - Consumer: pactConsumer, - Provider: pactProvider, - PactDir: pactDir, - Host: "localhost", - LogLevel: "DEBUG", - PactFileWriteMode: "overwrite", - SpecificationVersion: 2, - } - defer pact.Teardown() - - // Test interactions - APIStatus(t, pact) - APISpacesCreate(t, pact, contracts.TestSpaceName) - - // Write a pact file - pactFile := contracts.PactFile() - log.Printf("All tests done, writting a pact file to %s.\n", pactFile) - pact.WritePact() - - /*log.Printf("Publishing pact to a broker %s\n", pactBrokerURL) - - p := dsl.Publisher{} - err := p.Publish(types.PublishRequest{ - PactURLs: []string{}, - PactBroker: pactBrokerURL, - BrokerUsername: pactBrokerUsername, - BrokerPassword: pactBrokerPassword, - ConsumerVersion: pactVersion, - Tags: []string{"latest"}, - }) - - if err != nil { - log.Fatalf("Unable to publish pact to a broker %s:\n%q\n", pactBrokerURL, err) - }*/ -} diff --git a/test/contracts/consumer/api_status.go b/test/contracts/consumer/fabric8auth/api_status.go similarity index 74% rename from test/contracts/consumer/api_status.go rename to test/contracts/consumer/fabric8auth/api_status.go index 8c9a27d6de..a757f1b48e 100644 --- a/test/contracts/consumer/api_status.go +++ b/test/contracts/consumer/fabric8auth/api_status.go @@ -1,4 +1,4 @@ -package consumer +package fabric8auth import ( "fmt" @@ -6,14 +6,14 @@ import ( "net/http" "testing" - "github.com/fabric8-services/fabric8-wit/test/contracts" + "github.com/fabric8-services/fabric8-auth/test/contracts/model" "github.com/pact-foundation/pact-go/dsl" ) -// APIStatus defines contract of /api/status endpoint -func APIStatus(t *testing.T, pact *dsl.Pact) { +// AuthAPIStatus defines contract of /api/status endpoint +func AuthAPIStatus(t *testing.T, pact *dsl.Pact) { - log.Printf("Invoking APIStatus now\n") + log.Printf("Invoking AuthAPIStatus now\n") // Pass in test case var test = func() error { @@ -35,7 +35,7 @@ func APIStatus(t *testing.T, pact *dsl.Pact) { // Set up our expected interactions. pact. AddInteraction(). - Given("WIT service is up and running."). + Given("Auth service is up and running."). UponReceiving("A request to get status"). WithRequest(dsl.Request{ Method: "GET", @@ -45,7 +45,7 @@ func APIStatus(t *testing.T, pact *dsl.Pact) { WillRespondWith(dsl.Response{ Status: 200, Headers: dsl.MapMatcher{"Content-Type": dsl.String("application/vnd.status+json")}, - Body: dsl.Match(contracts.APIStatusResponse{}), + Body: dsl.Match(model.APIStatusMessage{}), }) // Verify diff --git a/test/contracts/consumer/fabric8auth/api_token.go b/test/contracts/consumer/fabric8auth/api_token.go new file mode 100644 index 0000000000..26aa70f244 --- /dev/null +++ b/test/contracts/consumer/fabric8auth/api_token.go @@ -0,0 +1,55 @@ +package fabric8auth + +import ( + "fmt" + "log" + "net/http" + "testing" + + "github.com/fabric8-services/fabric8-auth/test/contracts/model" + "github.com/pact-foundation/pact-go/dsl" +) + +// AuthAPITokenKeys defines contract of /api/status endpoint +func AuthAPITokenKeys(t *testing.T, pact *dsl.Pact) { + + log.Printf("Invoking AuthAPITokenKeys now\n") + + // Pass in test case + var test = func() error { + u := fmt.Sprintf("http://localhost:%d/api/token/keys", pact.Server.Port) + req, err := http.NewRequest("GET", u, nil) + + req.Header.Set("Accept", "application/json") + if err != nil { + return err + } + + _, err = http.DefaultClient.Do(req) + if err != nil { + return err + } + return err + } + + // Set up our expected interactions. + pact. + AddInteraction(). + Given("Auth service is up and running."). + UponReceiving("A request to get public keys"). + WithRequest(dsl.Request{ + Method: "GET", + Path: dsl.String("/api/token/keys"), + Headers: dsl.MapMatcher{"Accept": dsl.String("application/json")}, + }). + WillRespondWith(dsl.Response{ + Status: 200, + Headers: dsl.MapMatcher{"Content-Type": dsl.String("application/vnd.publickeys+json")}, + Body: dsl.Match(model.TokenKeys{}), + }) + + // Verify + if err := pact.Verify(test); err != nil { + log.Fatalf("Error on Verify: %v", err) + } +} diff --git a/test/contracts/consumer/fabric8auth/api_user.go b/test/contracts/consumer/fabric8auth/api_user.go new file mode 100644 index 0000000000..193c7ee81d --- /dev/null +++ b/test/contracts/consumer/fabric8auth/api_user.go @@ -0,0 +1,257 @@ +package fabric8auth + +import ( + "fmt" + "log" + "net/http" + "testing" + + "github.com/fabric8-services/fabric8-auth/test/contracts/model" + "github.com/pact-foundation/pact-go/dsl" +) + +// AuthAPIUserByName defines contract of /api/users?filter[username]= endpoint +func AuthAPIUserByName(t *testing.T, pact *dsl.Pact, userName string) { + + log.Println("Invoking AuthAPIUserByName test interaction now") + + // Pass in test case + var test = func() error { + url := fmt.Sprintf("http://localhost:%d/api/users?filter[username]=%s", pact.Server.Port, userName) + req, err := http.NewRequest("GET", url, nil) + + req.Header.Set("Content-Type", "application/json") + if err != nil { + return err + } + + _, err = http.DefaultClient.Do(req) + if err != nil { + return err + } + return err + } + + // Set up our expected interactions. + pact. + AddInteraction(). + Given("User with a given username exists."). + UponReceiving("A request to get user's information by username"). + WithRequest(dsl.Request{ + Method: "GET", + Path: dsl.String("/api/users"), + Query: dsl.MapMatcher{ + "filter[username]": dsl.Term( + userName, + model.UserNameRegex, + ), + }, + Headers: dsl.MapMatcher{"Content-Type": dsl.String("application/json")}, + }). + WillRespondWith(dsl.Response{ + Status: 200, + Headers: dsl.MapMatcher{"Content-Type": dsl.String("application/vnd.api+json")}, + Body: dsl.Match(model.Users{}), + }) + + // Verify + if err := pact.Verify(test); err != nil { + log.Fatalf("Error on Verify: %v", err) + } +} + +// AuthAPIUserByID defines contract of /api/users/ endpoint +func AuthAPIUserByID(t *testing.T, pact *dsl.Pact, userID string) { + + log.Printf("Invoking AuthAPIUserByID test interaction now\n") + + // Pass in test case + var test = func() error { + url := fmt.Sprintf("http://localhost:%d/api/users/%s", pact.Server.Port, userID) + req, err := http.NewRequest("GET", url, nil) + + req.Header.Set("Content-Type", "application/json") + if err != nil { + return err + } + + _, err = http.DefaultClient.Do(req) + if err != nil { + return err + } + return err + } + + // Set up our expected interactions. + pact. + AddInteraction(). + Given("User with a given ID exists."). + UponReceiving("A request to get user's information by ID"). + WithRequest(dsl.Request{ + Method: "GET", + Path: dsl.Term( + fmt.Sprintf("/api/users/%s", userID), + fmt.Sprintf("/api/users/%s", model.UserIDRegex), + ), + Headers: dsl.MapMatcher{"Content-Type": dsl.String("application/json")}, + }). + WillRespondWith(dsl.Response{ + Status: 200, + Headers: dsl.MapMatcher{"Content-Type": dsl.String("application/vnd.api+json")}, + Body: dsl.Match(model.User{}), + }) + + // Verify + if err := pact.Verify(test); err != nil { + log.Fatalf("Error on Verify: %v", err) + } +} + +// AuthAPIUserByToken defines contract of /api/user endpoint with valid auth token +// passed as 'Authorization: Bearer ...' header +func AuthAPIUserByToken(t *testing.T, pact *dsl.Pact, userToken string) { + + log.Printf("Invoking AuthAPIUserByToken test interaction now\n") + + // Pass in test case + var test = func() error { + url := fmt.Sprintf("http://localhost:%d/api/user", pact.Server.Port) + req, err := http.NewRequest("GET", url, nil) + + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", userToken)) + if err != nil { + return err + } + + _, err = http.DefaultClient.Do(req) + if err != nil { + return err + } + return err + } + + // Set up our expected interactions. + pact. + AddInteraction(). + Given("A user exists with the given valid token."). + UponReceiving("A request to get user's information with valid auth token "). + WithRequest(dsl.Request{ + Method: "GET", + Path: dsl.String("/api/user"), + Headers: dsl.MapMatcher{ + "Content-Type": dsl.String("application/json"), + "Authorization": dsl.Term( + fmt.Sprintf("Bearer %s", userToken), + fmt.Sprintf("^Bearer %s$", model.JWSRegex), + ), + }, + }). + WillRespondWith(dsl.Response{ + Status: 200, + Headers: dsl.MapMatcher{"Content-Type": dsl.String("application/vnd.api+json")}, + Body: dsl.Match(model.User{}), + }) + + // Verify + if err := pact.Verify(test); err != nil { + log.Fatalf("Error on Verify: %v", err) + } +} + +// AuthAPIUserInvalidToken defines contract of /api/user endpoint with invalid auth token +func AuthAPIUserInvalidToken(t *testing.T, pact *dsl.Pact, invalidToken string) { + + log.Printf("Invoking AuthAPIUserInvalidToken test interaction now\n") + + // Pass in test case + var test = func() error { + url := fmt.Sprintf("http://localhost:%d/api/user", pact.Server.Port) + req, err := http.NewRequest("GET", url, nil) + + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", invalidToken)) + if err != nil { + return err + } + + _, err = http.DefaultClient.Do(req) + if err != nil { + return err + } + return err + } + + // Set up our expected interactions. + pact. + AddInteraction(). + Given("No user exists with the given token valid."). + UponReceiving("A request to get user's information with invalid auth token "). + WithRequest(dsl.Request{ + Method: "GET", + Path: dsl.String("/api/user"), + Headers: dsl.MapMatcher{ + "Content-Type": dsl.String("application/json"), + "Authorization": dsl.Term( + fmt.Sprintf("Bearer %s", invalidToken), + fmt.Sprintf("^Bearer %s$", model.JWSRegex), + ), + }, + }). + WillRespondWith(dsl.Response{ + Status: 401, + Headers: dsl.MapMatcher{"Content-Type": dsl.String("application/vnd.api+json")}, + Body: dsl.Match(model.InvalidTokenMessage{}), + }) + + // Verify + if err := pact.Verify(test); err != nil { + log.Fatalf("Error on Verify: %v", err) + } +} + +// AuthAPIUserNoToken defines contract of /api/user endpoint with missing auth token +func AuthAPIUserNoToken(t *testing.T, pact *dsl.Pact) { + + log.Printf("Invoking AuthAPIUserNoToken test interaction now\n") + + // Pass in test case + var test = func() error { + url := fmt.Sprintf("http://localhost:%d/api/user", pact.Server.Port) + req, err := http.NewRequest("GET", url, nil) + + req.Header.Set("Content-Type", "application/json") + if err != nil { + return err + } + + _, err = http.DefaultClient.Do(req) + if err != nil { + return err + } + return err + } + + // Set up our expected interactions. + pact. + AddInteraction(). + Given("Any user exists but no auth token was provided."). + UponReceiving("A request to get user's information with no auth token "). + WithRequest(dsl.Request{ + Method: "GET", + Path: dsl.String("/api/user"), + Headers: dsl.MapMatcher{ + "Content-Type": dsl.String("application/json"), + }, + }). + WillRespondWith(dsl.Response{ + Status: 401, + Headers: dsl.MapMatcher{"Content-Type": dsl.String("application/vnd.api+json")}, + Body: dsl.Match(model.MissingTokenMessage{}), + }) + + // Verify + if err := pact.Verify(test); err != nil { + log.Fatalf("Error on Verify: %v", err) + } +} diff --git a/test/contracts/consumer/fabric8auth/consumer_test.go b/test/contracts/consumer/fabric8auth/consumer_test.go new file mode 100644 index 0000000000..104960ad12 --- /dev/null +++ b/test/contracts/consumer/fabric8auth/consumer_test.go @@ -0,0 +1,69 @@ +package fabric8auth + +import ( + "log" + "os" + "testing" + + "github.com/fabric8-services/fabric8-auth/test/contracts/model" + "github.com/fabric8-services/fabric8-wit/test/contracts" + "github.com/pact-foundation/pact-go/dsl" + "github.com/pact-foundation/pact-go/types" +) + +// TestWitAPIConsumer runs all user related tests +func TestWitAPIConsumer(t *testing.T) { + log.SetOutput(os.Stdout) + + var pactDir = os.Getenv("PACT_DIR") + var pactVersion = os.Getenv("PACT_VERSION") + + var pactConsumer = "Fabric8Wit" + var pactProvider = "Fabric8Auth" + + var pactBrokerURL = os.Getenv("PACT_BROKER_URL") + var pactBrokerUsername = os.Getenv("PACT_BROKER_USERNAME") + var pactBrokerPassword = os.Getenv("PACT_BROKER_PASSWORD") + + // Create Pact connecting to local Daemon + pact := &dsl.Pact{ + Consumer: pactConsumer, + Provider: pactProvider, + PactDir: pactDir, + Host: "localhost", + LogLevel: "INFO", + PactFileWriteMode: "overwrite", + SpecificationVersion: 2, + } + defer pact.Teardown() + + // Test interactions + AuthAPIStatus(t, pact) + + AuthAPIUserByName(t, pact, model.TestUserName) + AuthAPIUserByID(t, pact, model.TestUserID) + AuthAPIUserByToken(t, pact, model.TestJWSToken) + + AuthAPITokenKeys(t, pact) + + // Write a pact file + pactFile := contracts.PactFile(pactConsumer, pactProvider) + log.Printf("All tests done, writting a pact file (%s).\n", pactFile) + pact.WritePact() + + log.Printf("Publishing pact to a broker (%s)...\n", pactBrokerURL) + + p := dsl.Publisher{} + err := p.Publish(types.PublishRequest{ + PactURLs: []string{pactFile}, + PactBroker: pactBrokerURL, + BrokerUsername: pactBrokerUsername, + BrokerPassword: pactBrokerPassword, + ConsumerVersion: pactVersion, + Tags: []string{"latest"}, + }) + + if err != nil { + log.Fatalf("Unable to publish pact to a broker (%s):\n%q\n", pactBrokerURL, err) + } +} diff --git a/test/contracts/main.go b/test/contracts/main.go index d8853d6444..d6571c4bfa 100644 --- a/test/contracts/main.go +++ b/test/contracts/main.go @@ -1,205 +1,21 @@ package contracts import ( - "encoding/base64" "fmt" - "io/ioutil" - "log" - "net/http" "os" "strings" - "time" ) func main() { // nop } -// APIStatusResponse represents a service status message returned by /api/status endpoint. -type APIStatusResponse struct { - BuildTime string `json:"buildTime" pact:"example=2018-10-05T10:03:04Z"` - Commit string `json:"commit" pact:"example=0f9921980549b2baeb43f6f16cbe794f430f498c"` - StartTime string `json:"startTime" pact:"example=2018-10-09T15:04:50Z"` -} - -// UserData represents a JSON object containing user's info. -type UserData struct { - Attributes struct { - Bio string `json:"bio" pact:"example=n/a,regex=^[ a-zA-Z0-9,\\./]*$"` - Cluster string `json:"cluster" pact:"example=openshift.developer.osio/"` - Company string `json:"company" pact:"example=n/a,regex=^[ a-zA-Z0-9,\\./]*$"` - CreatedAt string `json:"created-at" pact:"example=2018-03-16T14:34:31.615511Z"` - Email string `json:"email" pact:"example=osio-developer@email.com"` - EmailPrivate bool `json:"emailPrivate" pact:"example=false,regex=^[(true)(false)]$"` - EmailVerified bool `json:"emailVerified" pact:"example=true,regex=^[(true)(false)]$"` - FeatureLevel string `json:"featureLevel" pact:"example=internal"` - FullName string `json:"fullName" pact:"example=Osio Developer"` - IdentityID string `json:"identityID" pact:"example=00000000-0000-4000-a000-000000000000"` - ImageURL string `json:"imageURL" pact:"example=n/a"` - ProviderType string `json:"providerType" pact:"example=kc"` - RegistrationCompleted bool `json:"registrationCompleted" pact:"example=true,regex=^[(true)(false)]$"` - UpdatedAt string `json:"updated-at" pact:"example=2018-05-30T11:05:23.513612Z"` - URL string `json:"url" pact:"example=n/a"` - UserID string `json:"userID" pact:"example=5f41b66e-6f84-42b3-ab5f-8d9ef21149b1"` - Username string `json:"username" pact:"example=developer"` - } `json:"attributes"` - ID string `json:"id" pact:"example=00000000-0000-4000-a000-000000000000"` - Links struct { - Related string `json:"related" pact:"example=http://localhost:8089/api/users/00000000-0000-4000-a000-000000000000"` - Self string `json:"self" pact:"example=http://localhost:8089/api/users/00000000-0000-4000-a000-000000000000"` - } `json:"links"` - Type string `json:"type" pact:"example=identities"` -} - -// User represents a JSON object of a single user. -type User struct { - Data UserData `json:"data"` -} - -// Users represents a JSON object of a collection of users. -type Users struct { - Data []UserData `json:"data"` -} - -// EmptyData represents an empty message returned by API -type EmptyData struct { - Data []interface{} `json:"data"` -} - -// Space represents JSON description of space -type Space struct { - Data SpaceData `json:"data"` -} - -// Spaces represents JSON object of space list -type Spaces struct { - Data []SpaceData `json:"data"` -} - -// SpaceAttributes represents JSON description of space attributes -type SpaceAttributes struct { - CreatedAt string `json:"created-at"` - Description string `json:"description"` - Name string `json:"name"` - UpdatedAt string `json:"updated-at"` - Version int `json:"version"` -} - -// SpaceData represents JSON description of space data -type SpaceData struct { - Attributes SpaceAttributes `json:"attributes"` - ID string `json:"id"` - Type string `json:"type"` -} - -//CreateSpaceRequestAttributes represents attributes of a JSON request message to create a new space -type CreateSpaceRequestAttributes struct { - Name string `json:"name"` - Description string `json:"description"` -} - -//CreateSpaceRequestData represents payload of a JSON request message to create a new space -type CreateSpaceRequestData struct { - Name string `json:"name"` - Path string `json:"path"` - Attributes CreateSpaceRequestAttributes `json:"attributes"` - Type string `json:"type"` - PrivateSpace bool `json:"privateSpace"` -} - -//CreateSpaceRequest represents JSON request message to create a new space -type CreateSpaceRequest struct { - Data CreateSpaceRequestData `json:"data"` -} - -// JWSRegex is a regular expression for matching JWS tokens -const JWSRegex = "[a-zA-Z0-9\\-_]+?\\.?[a-zA-Z0-9\\-_]+?\\.?([a-zA-Z0-9\\-_]+)?" - -// TestInvalidJWSToken Base64 encoded '{"alg":"RS256","kid":"1111111111111111111111111111111111111111111","typ":"JWT"}somerandombytes' -const TestInvalidJWSToken = "eyJhbGciOiJSUzI1NiIsImtpZCI6IjExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTExMTEiLCJ0eXAiOiJKV1QifXNvbWVyYW5kb21ieXRlcw" - -// TestJWSToken contains Base64 encoded '{"alg":"RS256","kid":"0000000000000000000000000000000000000000000","typ":"JWT"}somerandombytes' -const TestJWSToken = "eyJhbGciOiJSUzI1NiIsImtpZCI6IjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAiLCJ0eXAiOiJKV1QifXNvbWVyYW5kb21ieXRlcw" - -// UserNameRegex is a regular expression for matching usernames. -const UserNameRegex = "[a-zA-Z\\-0-9]+" - -// UserIDRegex is a regular expression for matching user IDs. -const UserIDRegex = "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[8|9|aA|bB][a-fA-F0-9]{3}-[a-fA-F0-9]{12}" - -//TestUserID contains user id placeholder -const TestUserID = "00000000-0000-4000-a000-000000000000" - -//TestUserName contains username placeholder -const TestUserName = "testuser00000000" - -//TestSpaceName contains space name placeholder -const TestSpaceName = "testspace11111111" - -//TestSpaceID contains user id placeholder -const TestSpaceID = "11111111-0000-4000-a000-000000000000" - -type ProviderInitialState struct { - User User - Space Space - UserToken string -} - -//NewSpaceName returns a name of the new space -func NewSpaceName() string { - return "test-space" -} - // PactDir returns a path to the directory to store pact files (taken from PACT_DIR env variable) func PactDir() string { return os.Getenv("PACT_DIR") } // PactFile returns a path to the generated pact file -func PactFile() string { - return fmt.Sprintf("%s/%s-%s.json", PactDir(), strings.ToLower(PactConsumer()), strings.ToLower(PactProvider())) -} - -// PactConsumer returns a name of the pact consumer (taken from PACT_CONSUMER env variable) -func PactConsumer() string { - return os.Getenv("PACT_CONSUMER") -} - -// PactProvider returns a name of the pact provider (taken from PACT_PROVIDER env variable) -func PactProvider() string { - return os.Getenv("PACT_PROVIDER") -} - -// PactFromFile reads a pact from a given file and returns as string -func PactFromFile(pactFile string) string { - f, err := ioutil.ReadFile(pactFile) - if err != nil { - log.Fatalf("Unable to read pact file: %s", pactFile) - } - return string(f) -} - -// PactFromBroker reads a pact from a given pact broker and returns as string -func PactFromBroker(pactBrokerURL string, pactBrokerUsername string, pactBrokerPassword string) string { - var httpClient = &http.Client{ - Timeout: time.Second * 30, - } - pactURL := fmt.Sprintf("%s/pacts/provider/%s/consumer/%s/latest", pactBrokerURL, PactProvider(), PactConsumer()) - request, err := http.NewRequest("GET", pactURL, nil) - if err != nil { - log.Fatal(err) - } - request.Header.Set("Accept", "application/json") - request.Header.Set("Authorization", fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", pactBrokerUsername, pactBrokerPassword))))) - - log.Printf("Downloading a pact file from pact broker: %s", pactURL) - response, err := httpClient.Do(request) - if err != nil { - log.Fatal(err) - } - defer response.Body.Close() - - responseBody, err := ioutil.ReadAll(response.Body) - - return string(responseBody) +func PactFile(pactConsumer string, pactProvider string) string { + return fmt.Sprintf("%s/%s-%s.json", PactDir(), strings.ToLower(pactConsumer), strings.ToLower(pactProvider)) } diff --git a/test/contracts/provider/provider_test.go b/test/contracts/provider/provider_test.go deleted file mode 100644 index eb05ce98b4..0000000000 --- a/test/contracts/provider/provider_test.go +++ /dev/null @@ -1,83 +0,0 @@ -package provider - -import ( - "fmt" - "log" - "os" - "strings" - "testing" - - "github.com/fabric8-services/fabric8-wit/test/contracts" - "github.com/pact-foundation/pact-go/dsl" - "github.com/pact-foundation/pact-go/types" -) - -// TestWitAPIProvider verifies the provider -func TestWitAPIProvider(t *testing.T) { - - pactProviderBaseURL := os.Getenv("PACT_PROVIDER_BASE_URL") - pactConsumer := contracts.PactConsumer() - pactProvider := contracts.PactProvider() - pactDir := contracts.PactDir() - - // Create Pact connecting to local Daemon - pact := &dsl.Pact{ - Consumer: pactConsumer, - Provider: pactProvider, - PactDir: pactDir, - Host: "localhost", - LogLevel: "INFO", - SpecificationVersion: 2, - } - defer pact.Teardown() - - var providerSetupHost = "localhost" // this should ultimately be part of the provider api (developer mode: on) - var providerSetupPort = 8888 - - // Create user to get userid - var userName = os.Getenv("OSIO_USERNAME") - var userPassword = os.Getenv("OSIO_PASSWORD") - var spaceName = contracts.NewSpaceName() - var pactProviderAuthBaseURL = os.Getenv("PACT_PROVIDER_AUTH_BASE_URL") - - var initialState = Setup(providerSetupHost, providerSetupPort, map[string]string{ - "pactProviderBaseURL": pactProviderBaseURL, - "pactProviderAuthBaseURL": pactProviderAuthBaseURL, - "userName": userName, - "userPassword": userPassword, - "spaceName": spaceName, - }) - - if initialState == nil { - log.Fatalf("Error returning user") - } - - pactFile := contracts.PactFile() - pactContent := contracts.PactFromFile(pactFile) - providerPactFilePath := fmt.Sprintf("%s/provider-%s-%s.json", pactDir, strings.ToLower(pactConsumer), strings.ToLower(pactProvider)) - - //log.Printf("Pact taken from broker:\n%s\n", pactContent) - pactContent = strings.Replace(pactContent, contracts.TestUserName, initialState.User.Data.Attributes.Username, -1) - pactContent = strings.Replace(pactContent, contracts.TestUserID, initialState.User.Data.ID, -1) - pactContent = strings.Replace(pactContent, contracts.TestJWSToken, initialState.UserToken, -1) - pactContent = strings.Replace(pactContent, contracts.TestSpaceName, initialState.Space.Data.Attributes.Name, -1) - pactContent = strings.Replace(pactContent, contracts.TestSpaceID, initialState.Space.Data.ID, -1) - //log.Printf("Pact filtered:\n%s\n", pactContent) - - providerPactFile, err := os.Create(providerPactFilePath) - if err != nil { - log.Fatal(err) - } - defer providerPactFile.Close() - - _, err = providerPactFile.WriteString(pactContent) - - // Verify the Provider with local Pact Files - pact.VerifyProvider(t, types.VerifyRequest{ - ProviderBaseURL: pactProviderBaseURL, - PactURLs: []string{providerPactFilePath}, - ProviderStatesSetupURL: fmt.Sprintf("http://%s:%d/pact/setup", providerSetupHost, providerSetupPort), - }) - - log.Println("Test Passed!") -} diff --git a/test/contracts/provider/setup.go b/test/contracts/provider/setup.go deleted file mode 100644 index afb9a95f4c..0000000000 --- a/test/contracts/provider/setup.go +++ /dev/null @@ -1,345 +0,0 @@ -package provider - -import ( - "bytes" - "encoding/json" - "fmt" - "io/ioutil" - "log" - "net/http" - "os" - "time" - - "github.com/fabric8-services/fabric8-wit/test/contracts" - "github.com/google/uuid" - "github.com/pmacik/loginusers-go/config" - "github.com/pmacik/loginusers-go/loginusers" -) - -// State represents JSON request for 'state setup' from Pact -type State struct { - // Consumer name - Consumer string `json:"consumer"` - // State - State string `json:"state"` - // States - States []string `json:"states"` -} - -type createUserAttributes struct { - Bio string `json:"bio"` - Cluster string `json:"cluster"` - Username string `json:"username"` - Email string `json:"email"` - RhdUserID string `json:"rhd_user_id"` -} - -type createUserData struct { - createUserAttributes `json:"attributes"` - Type string `json:"type" pact:"example=identities"` -} - -type createUserRequest struct { - createUserData `json:"data"` -} - -// Setup starts a setup service for a provider - should be replaced by a provider setup endpoint -func Setup(setupHost string, setupPort int, params map[string]string) *contracts.ProviderInitialState { - log.SetOutput(os.Stdout) - - userName := params["userName"] - userPassword := params["userPassword"] - - spaceName := params["spaceName"] - - pactProviderBaseURL := params["pactProviderBaseURL"] - pactProviderAuthBaseURL := params["pactProviderAuthBaseURL"] - - // Create test user in Auth and retun user info (such as id) - log.Printf("Making sure user %s is created...", userName) - var user = createUser(pactProviderAuthBaseURL, userName) - if user == nil { - log.Fatalf("Error creating/getting user") - return nil - } - - loginUsersConfig := config.DefaultConfig() - loginUsersConfig.Auth.ServerAddress = pactProviderAuthBaseURL - // Login user to get tokens - userTokens, err := loginusers.OAuth2(userName, userPassword, loginUsersConfig) - if err != nil { - log.Fatalf("Unable to login user: %s", err) - return nil - } - log.Printf("Provider setup with user ID: %s", user.Data.ID) - - //Create userspace - space := createSpace(pactProviderBaseURL, spaceName, user, userTokens.AccessToken) - if space == nil { - log.Fatalf("Error creating/getting space") - return nil - } - go setupEndpoint(setupHost, setupPort) - - return &contracts.ProviderInitialState{ - User: *user, - Space: *space, - UserToken: userTokens.AccessToken, - } -} - -func setupEndpoint(setupHost string, setupPort int) { - http.HandleFunc("/pact/setup", func(w http.ResponseWriter, r *http.Request) { - body, err := ioutil.ReadAll(r.Body) - if err != nil { - log.Fatalf(">>> ERROR: Unable to read request body.\n %q", err) - return - } - //log.Printf("\nBody: %s\n", body) - //log.Printf("\nHeaders: %s\n", r.Header) - - var providerState State - json.Unmarshal(body, &providerState) - - switch providerState.State { - case "Space with a given name does not exist.", - "Space with a given ID exists.", - "WIT service is up and running.": - log.Printf(">>>> %s\n", providerState.State) - default: - errorMessage(w, fmt.Sprintf("State '%s' not impemented.", providerState.State)) - return - } - fmt.Fprintf(w, "Provider states has ben set up.\n") - }) - - var setupURL = fmt.Sprintf("%s:%d", setupHost, setupPort) - log.Printf(">>> Starting ProviderSetup and listening at %s\n", setupURL) - log.Fatal(http.ListenAndServe(setupURL, nil)) -} - -func errorMessage(w http.ResponseWriter, errorMessage string) { - w.WriteHeader(500) - fmt.Fprintf(w, `{"error": "%s"}`, errorMessage) -} - -func createUser(providerAuthBaseURL string, userName string) *contracts.User { - - var httpClient = &http.Client{ - Timeout: time.Second * 10, - } - - log.Println("Getting the auth service account token") - authServiceAccountToken := serviceAccountToken(providerAuthBaseURL) - // log.Printf("Auth Service Token: %s", authServiceAccountToken) - - rhdUserUUID, _ := uuid.NewUUID() - message := &createUserRequest{ - createUserData: createUserData{ - createUserAttributes: createUserAttributes{ - Bio: "Contract testing user account", - Cluster: "localhost", - Email: fmt.Sprintf("%s@email.com", userName), - Username: userName, - RhdUserID: rhdUserUUID.String(), - }, - }, - } - - messageBytes, err := json.Marshal(message) - if err != nil { - log.Fatalf("createUser: Error marshalling JSON object:\n%q", err) - } - - request, err := http.NewRequest("POST", fmt.Sprintf("%s/api/users", providerAuthBaseURL), bytes.NewBuffer(messageBytes)) - if err != nil { - log.Fatalf("createUser: Error creating HTTP request:\n%q", err) - } - request.Header.Add("Content-Type", "application/json") - request.Header.Add("Authorization", fmt.Sprintf("Bearer %s", authServiceAccountToken)) - - log.Println("Sending a request to create a user") - response, err := httpClient.Do(request) - if err != nil { - log.Fatalf("createUser: Error sending HTTP request:\n%q", err) - } - defer response.Body.Close() - - responseBody, err := ioutil.ReadAll(response.Body) - - if response.StatusCode != 200 { - if response.StatusCode == 409 { //user already exists - log.Printf("User %s already exists, getting user info.", userName) - response2, err := http.Get(fmt.Sprintf("%s/api/users?filter[username]=%s", providerAuthBaseURL, userName)) - if err != nil { - log.Fatalf("userExists: Error creating HTTP request:\n%q", err) - } - defer response2.Body.Close() - - responseBody, err := ioutil.ReadAll(response2.Body) - // log.Printf("User info:\n%s\n", responseBody) - if response2.StatusCode != 200 { - log.Fatalf("userExists: Something went wrong with reading response body: %s", responseBody) - } - var users contracts.Users - err = json.Unmarshal(responseBody, &users) - if err != nil { - log.Fatalf("userExists: Unable to unmarshal response body: %s", err) - } - var user = &contracts.User{ - Data: users.Data[0], - } - log.Printf("User found with ID: %s", user.Data.ID) - return user - } - log.Fatalf("createUser: Something went wrong with reading response body: %s", responseBody) - } - - var user contracts.User - err = json.Unmarshal(responseBody, &user) - if err != nil { - log.Fatalf("createUser: Unable to unmarshal response body: %s", err) - } - log.Printf("User created with ID: %s", user.Data.ID) - return &user -} - -// ServiceAccountTokenRequest represents a request JSON body -type ServiceAccountTokenRequest struct { - GrantType string `json:"grant_type"` - ClientID string `json:"client_id"` - ClientSecret string `json:"client_secret"` -} - -// ServiceAccountTokenResponse represents a response JSON body -type ServiceAccountTokenResponse struct { - AccessToken string `json:"access_token"` - TokenType string `json:"token_type"` -} - -func serviceAccountToken(providerBaseURL string) string { - var httpClient = &http.Client{ - Timeout: time.Second * 10, - } - authClientID := "f867ec72-3171-4b8f-8eec-90a32eab6e0b" - authClienSecret := "secret" - - message, err := json.Marshal(&ServiceAccountTokenRequest{ - GrantType: "client_credentials", - ClientID: authClientID, - ClientSecret: authClienSecret, - }) - - // log.Printf("Message: %s", string(message)) - - if err != nil { - log.Fatalf("serviceAccountToken: Error marshalling json object: %q\n", err) - } - request, err := http.NewRequest("POST", fmt.Sprintf("%s/api/token", providerBaseURL), bytes.NewBuffer(message)) - request.Header.Add("Content-Type", "application/json") - - response, err := httpClient.Do(request) - if err != nil { - log.Fatalf("serviceAccountToken: Error sending HTTP request: %q\n", err) - } - defer response.Body.Close() - - responseBody, err := ioutil.ReadAll(response.Body) - - if response.StatusCode != 200 { - log.Fatalf("serviceAccountToken: Something went wrong with reading response body: %s", responseBody) - } - - var tokenResponse ServiceAccountTokenResponse - err = json.Unmarshal(responseBody, &tokenResponse) - if err != nil { - log.Fatalf("serviceAccountToken: Unable to unmarshal response body: %s", err) - } - return tokenResponse.AccessToken -} - -func createSpace(providerBaseURL string, spaceName string, user *contracts.User, userToken string) *contracts.Space { - - var httpClient = &http.Client{ - Timeout: time.Second * 10, - } - - message := &contracts.CreateSpaceRequest{ - Data: contracts.CreateSpaceRequestData{ - Name: spaceName, - Path: "", - Attributes: contracts.CreateSpaceRequestAttributes{ - Name: spaceName, - Description: "Space created by the contract tests to test new space creation.", - }, - Type: "spaces", - PrivateSpace: false, - }, - } - - messageBytes, err := json.Marshal(message) - if err != nil { - log.Fatalf("createSpace: Error marshalling JSON object:\n%q", err) - return nil - } - - request, err := http.NewRequest("POST", fmt.Sprintf("%s/api/spaces", providerBaseURL), bytes.NewBuffer(messageBytes)) - if err != nil { - log.Fatalf("createSpace: Error creating HTTP request:\n%q", err) - } - request.Header.Add("Content-Type", "application/json") - request.Header.Add("Authorization", fmt.Sprintf("Bearer %s", userToken)) - - log.Println("Sending a request to create a space") - response, err := httpClient.Do(request) - if err != nil { - log.Fatalf("createSpace: Error sending HTTP request:\n%q", err) - } - defer response.Body.Close() - - responseBody, err := ioutil.ReadAll(response.Body) - - if response.StatusCode != 201 { - if response.StatusCode == 409 { //space already exists - userName := user.Data.Attributes.Username - log.Printf("Space %s already exists, getting space info.", spaceName) - response2, err := http.Get(fmt.Sprintf("%s/api/namedspaces/%s", providerBaseURL, userName)) - if err != nil { - log.Fatalf("spaceExists: Error creating HTTP request:\n%q", err) - } - defer response2.Body.Close() - log.Println("2") - responseBody, err := ioutil.ReadAll(response2.Body) - // log.Printf("User info:\n%s\n", responseBody) - if response2.StatusCode != 200 { - log.Fatalf("spaceExists: Something went wrong with reading response body: %s", responseBody) - } - log.Println("3") - var spaces contracts.Spaces - err = json.Unmarshal(responseBody, &spaces) - if err != nil { - log.Fatalf("spaceExists: Unable to unmarshal response body: %s", err) - } - log.Println("4") - for _, space := range spaces.Data { - log.Println("5") - if space.Attributes.Name == spaceName { - log.Printf("Space found with ID: %s", space.ID) - return &contracts.Space{ - Data: space, - } - } - } - } - log.Fatalf("createSpace: Something went wrong with reading response body: %s", responseBody) - return nil - } - - var space contracts.Space - err = json.Unmarshal(responseBody, &space) - if err != nil { - log.Fatalf("createSpace: Unable to unmarshal response body: %s", err) - } - log.Printf("Space created with ID: %s", user.Data.ID) - return &space -}