Skip to content
This repository has been archived by the owner on Mar 11, 2021. It is now read-only.

Commit

Permalink
test: Create contract tests for interaction with Auth service.
Browse files Browse the repository at this point in the history
  • Loading branch information
pmacik committed Oct 12, 2018
1 parent df67bff commit 6d5fcbc
Show file tree
Hide file tree
Showing 10 changed files with 454 additions and 0 deletions.
46 changes: 46 additions & 0 deletions .make/test.mk
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,14 @@ ALL_PKGS_EXCLUDE_PATTERN = 'vendor\|app\|tool\/cli\|design\|client\|test'
GOANALYSIS_PKGS_EXCLUDE_PATTERN="vendor|app|client|tool/cli"
GOANALYSIS_DIRS=$(shell go list -f {{.Dir}} ./... | grep -v -E $(GOANALYSIS_PKGS_EXCLUDE_PATTERN))

# Folder with contract tests
CONTRACT_TESTS=$(CUR_DIR)/test/contracts

# Configuration of contract tests
PACT_VERSION ?= 1.0.0
PACT_BROKER_URL ?= http://pact-broker-pact-broker.193b.starter-ca-central-1.openshiftapps.com
PACT_PROVIDER_BASE_URL ?= https://auth.openshift.io

#-------------------------------------------------------------------------------
# Normal test targets
#
Expand Down Expand Up @@ -159,6 +167,7 @@ test-templates-flags:
test-unit: test-templates-flags prebuild-check clean-coverage-unit $(COV_PATH_UNIT)

.PHONY: test-unit-no-coverage

## Runs the unit tests and WITHOUT producing coverage files for each package.
test-unit-no-coverage: test-templates-flags prebuild-check $(SOURCES)
$(call log-info,"Running test: $@")
Expand All @@ -183,6 +192,43 @@ test-integration-no-coverage: 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_POSTGRES_DATABASE=postgres go test -v $(TEST_PACKAGES)

.PHONY: test-contract-auth-consumer
## Runs the consumer side contract tests of the Auth service and produces pact files.
test-contract-auth-consumers:
cd $(CONTRACT_TESTS)/auth && \
PACT_DIR=$(CONTRACT_TESTS)/pacts \
./consumer-contracts.sh

.PHONY: test-contract-auth-publish
## Publishes the generated files to a Pact broker.
test-contract-auth-publish:
cd $(CONTRACT_TESTS)/auth && \
PACT_VERSION=$(PACT_VERSION) \
PACT_BROKER_URL=$(PACT_BROKER_URL) \
./publish-contracts.sh

.PHONY: test-contract-auth-verify
## Verifies the contracts against the living provider. The pact files are taken from pact directory.
test-contract-auth-verify:
cd $(CONTRACT_TESTS)/auth && \
PACT_PROVIDER_BASE_URL=$(PACT_PROVIDER_BASE_URL) \
./verify-contracts.sh

.PHONY: test-contract-auth-verify-broker
## Verifies the contracts against the living provider. The pact files are taken from the Pact broker.
test-contract-auth-verify-broker:
cd $(CONTRACT_TESTS)/auth && \
PACT_VERSION=$(PACT_VERSION) \
PACT_BROKER_URL=$(PACT_BROKER_URL) \
PACT_PROVIDER_BASE_URL=$(PACT_PROVIDER_BASE_URL) \
./verify-contracts-broker.sh

.PHONY: clean-test-contract-auth
## Runs the consumer side contract tests and produces pact files.
clean-test-contract-auth:
cd $(CONTRACT_TESTS)/auth && \
rm -rvf pacts log logs

.PHONY: test-remote
## Runs the remote tests and produces coverage files for each package.
test-remote: prebuild-check clean-coverage-remote $(COV_PATH_REMOTE)
Expand Down
3 changes: 3 additions & 0 deletions test/contracts/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
**/*log
**/*logs
**/*pacts
59 changes: 59 additions & 0 deletions test/contracts/auth/auth_api_status.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Package contracts contains a runnable Consumer Pact test example.
package contracts

import (
"fmt"
"log"
"net/http"
"testing"

"github.com/pact-foundation/pact-go/dsl"
)

// AuthAPIStatus defines contract of /api/status endpoint
func AuthAPIStatus(t *testing.T, pact *dsl.Pact) {
// Pass in test case
var test = func() error {
u := fmt.Sprintf("http://localhost:%d/api/status", pact.Server.Port)
req, err := http.NewRequest("GET", u, 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
}

type STATUS struct {
buildTime string `json:"buildTime" pact:"example=2018-10-05T10:03:04Z"`
commit string `json:"commit" pact:"example=0f9921980549b2baeb43f6f16cbe794f430f498c"`
configurationStatus string `json:"configurationStatus" pact:"example=OK"`
databaseStatus string `json:"databaseStatus" pact:"example=OK"`
startTime string `json:"startTime" pact:"example=2018-10-09T15:04:50Z"`
}

// Set up our expected interactions.
pact.
AddInteraction().
UponReceiving("A request to get status").
WithRequest(dsl.Request{
Method: "GET",
Path: dsl.String("/api/status"),
Headers: dsl.MapMatcher{"Content-Type": dsl.String("application/json")},
}).
WillRespondWith(dsl.Response{
Status: 200,
Headers: dsl.MapMatcher{"Content-Type": dsl.String("application/vnd.status+json")},
Body: dsl.Match(STATUS{}),
})

// Verify
if err := pact.Verify(test); err != nil {
log.Fatalf("Error on Verify: %v", err)
}
}
263 changes: 263 additions & 0 deletions test/contracts/auth/auth_api_user.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
// Package contracts contains a runnable Consumer Pact test example.
package contracts

import (
"fmt"
"log"
"net/http"
"os"
"testing"

"github.com/pact-foundation/pact-go/dsl"
)

type Data struct {
Attributes struct {
Bio string `json:"bio" pact:"example=n/a"`
Cluster string `json:"cluster" pact:"example=https://api.starter-us-east-2a.openshift.com/"`
Company string `json:"company" pact:"example=n/a"`
ContextInformation struct {
RecentContexts []struct {
User string `json:"user" pact:"example=c46445eb-2448-4c91-916a-2c1de3e6f63e"`
} `json:"recentContexts"`
RecentSpaces []string `json:"recentSpaces"`
} `json:"contextInformation"`
CreatedAt string `json:"created-at" pact:"example=2018-03-16T14:34:31.615511Z"`
Email string `json:"email" pact:"example=osio-ci+ee10@redhat.com"`
EmailPrivate bool `json:"emailPrivate" pact:"example=false"`
EmailVerified bool `json:"emailVerified" pact:"example=true"`
FeatureLevel string `json:"featureLevel" pact:"example=internal"`
FullName string `json:"fullName" pact:"example=Osio10 Automated Tests"`
IdentityID string `json:"identityID" pact:"example=c46445eb-2448-4c91-916a-2c1de3e6f63e"`
ImageURL string `json:"imageURL" pact:"example=n/a"`
ProviderType string `json:"providerType" pact:"example=kc"`
RegistrationCompleted bool `json:"registrationCompleted" pact:"example=true"`
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=osio-ci-ee10"`
} `json:"attributes"`
ID string `json:"id" pact:"example=c46445eb-2448-4c91-916a-2c1de3e6f63e"`
Links struct {
Related string `json:"related" pact:"example=https://auth.openshift.io/api/users/c46445eb-2448-4c91-916a-2c1de3e6f63e"`
Self string `json:"self" pact:"example=https://auth.openshift.io/api/users/c46445eb-2448-4c91-916a-2c1de3e6f63e"`
} `json:"links"`
Type string `json:"type" pact:"example=identities"`
}

type User struct {
data Data `json:"data"`
}

type Users struct {
data []Data `json:"data"`
}

type InvalidToken struct {
Errors []struct {
Code string `json:"code" pact:"example=token_validation_failed"`
Detail string `json:"detail" pact:"example=token is invalid"`
ID string `json:"id" pact:"example=76J0ww+6"`
Status string `json:"status" pact:"example=401"`
Title string `json:"title" pact:"example=Unauthorized"`
} `json:"errors"`
}

type MissingToken struct {
Errors []struct {
Code string `json:"code" pact:"example=jwt_security_error"`
Detail string `json:"detail" pact:"example=missing header \"Authorization\""`
ID string `json:"id" pact:"example=FRzHbogQ"`
Status string `json:"status" pact:"example=401"`
Title string `json:"title" pact:"example=Unauthorized"`
} `json:"errors"`
}

const JWSRegex = "[a-zA-Z0-9\\-_]+?\\.?[a-zA-Z0-9\\-_]+?\\.?([a-zA-Z0-9\\-_]+)?"

// AuthAPIUserByNameConsumer defines contract of /api/users?filter[username]=<user_name> endpoint
func AuthAPIUserByNameConsumer(t *testing.T, pact *dsl.Pact) {
userName := os.Getenv("OSIO_USERNAME")

// 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().
UponReceiving("A request to get user's information by name").
WithRequest(dsl.Request{
Method: "GET",
Path: dsl.String("/api/users"),
Query: dsl.MapMatcher{
"filter[username]": dsl.Term(
userName,
".*",
),
},
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(Users{}),
})

// Verify
if err := pact.Verify(test); err != nil {
log.Fatalf("Error on Verify: %v", err)
}
}

// AuthAPIUserByIDConsumer defines contract of /api/users/<user_id> endpoint
func AuthAPIUserByIDConsumer(t *testing.T, pact *dsl.Pact) {
userID := os.Getenv("OSIO_USER_ID")

// 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().
UponReceiving("A request to get user's information by ID").
WithRequest(dsl.Request{
Method: "GET",
Path: dsl.Term(
fmt.Sprintf("/api/users/%s", userID),
"/api/users/.*",
),
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(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) {

// Base64 encoded '{"alg":"RS256","kid":"1aA2bBc3CDDdEEefff7gGHH_ii9jJjkkkLl2mmm4NNO","typ":"JWT"}somerandombytes'
var invalidToken = "eyJhbGciOiJSUzI1NiIsImtpZCI6IjFhQTJiQmMzQ0REZEVFZWZmZjdnR0hIX2lpOWpKamtra0xsMm1tbTROTk8iLCJ0eXAiOiJKV1QifXNvbWVyYW5kb21ieXRlcw"

// 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().
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$", JWSRegex),
),
},
}).
WillRespondWith(dsl.Response{
Status: 401,
Headers: dsl.MapMatcher{"Content-Type": dsl.String("application/vnd.api+json")},
Body: dsl.Match(InvalidToken{}),
})

// Verify
if err := pact.Verify(test); err != nil {
log.Fatalf("Error on Verify: %v", err)
}
}

// AuthAPIUserNoToken defines contract of /api/user endpoint with invalid auth token
func AuthAPIUserNoToken(t *testing.T, pact *dsl.Pact) {

// 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().
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(MissingToken{}),
})

// Verify
if err := pact.Verify(test); err != nil {
log.Fatalf("Error on Verify: %v", err)
}
}
Loading

0 comments on commit 6d5fcbc

Please sign in to comment.