diff --git a/.circleci/config.yml b/.circleci/config.yml index 4cccc1cb1..7975409ba 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -158,6 +158,7 @@ jobs: name: make test command: | mkdir -p $GOCACHE + make build-deps citest if [ -n "$CC_TEST_REPORTER_ID" ]; then make ci-upload-coverage @@ -167,6 +168,15 @@ jobs: - /tmp/go/cache key: ship-unit-test-build-cache-{{ epoch }} + pacts: + docker: + - image: circleci/golang:1.12 + working_directory: /go/src/github.com/replicatedhq/ship + steps: + - checkout + - setup_remote_docker + - run: make build-deps pacts + windows_build_test: docker: - image: golang:1.12 @@ -592,6 +602,7 @@ workflows: - e2e_setup - test + - pacts - windows_build_test - docs - integration_base @@ -627,6 +638,7 @@ workflows: - e2e_setup - e2e_init - test + - pacts - windows_build_test - integration_base - integration_init @@ -695,6 +707,13 @@ workflows: branches: ignore: /.*/ + - pacts: + filters: + tags: + only: /^v[0-9]+(\.[0-9]+)*(-.*)*/ + branches: + ignore: /.*/ + - windows_build_test: filters: tags: @@ -765,6 +784,7 @@ workflows: - e2e_setup - e2e_init - test + - pacts - windows_build_test - integration_base - integration_init diff --git a/.gitignore b/.gitignore index ec141d0d9..f5bdf5075 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,5 @@ cmd/ship/overlays # local circle builds altconfig.yaml +logs/ +pacts/ diff --git a/Makefile b/Makefile index e7c061ee4..b6b85cae5 100644 --- a/Makefile +++ b/Makefile @@ -73,6 +73,14 @@ githooks: echo 'make fmt; git add `git diff --name-only --cached`' > .git/hooks/pre-commit chmod +x .git/hooks/pre-commit +.PHONY: pacts +pacts: + docker build -t ship-contract-tests -f contracts/Dockerfile.testing . + docker run --rm --name ship-contract-tests \ + -v `pwd`:/go/src/github.com/replicatedhq/ship \ + ship-contract-tests \ + pwd && ls && go test -v ./contracts/... + _mockgen: rm -rf pkg/test-mocks mkdir -p pkg/test-mocks/ui diff --git a/contracts/Dockerfile.testing b/contracts/Dockerfile.testing new file mode 100644 index 000000000..2d71bb283 --- /dev/null +++ b/contracts/Dockerfile.testing @@ -0,0 +1,6 @@ +FROM golang:1.12 + +RUN cd /opt && curl -fsSL https://raw.githubusercontent.com/pact-foundation/pact-ruby-standalone/v1.66.0/install.sh | bash +ENV PATH="/opt/pact/bin:${PATH}" + +WORKDIR /go/src/github.com/replicatedhq/ship diff --git a/contracts/replicatedapp/graphql_test.go b/contracts/replicatedapp/graphql_test.go new file mode 100644 index 000000000..41cd0b384 --- /dev/null +++ b/contracts/replicatedapp/graphql_test.go @@ -0,0 +1,104 @@ +package replicatedapp + +import ( + "encoding/base64" + "fmt" + "net/http" + "testing" + + "github.com/pact-foundation/pact-go/dsl" + replapp "github.com/replicatedhq/ship/pkg/specs/replicatedapp" + "github.com/spf13/viper" + "github.com/stretchr/testify/require" +) + +func Test_GetReleaseFromSemver(t *testing.T) { + customerID := "ship-fetch-release-customer-0" + installationID := "ship-fetch-release-installation-0" + semver := "1.0.2" + spec := `assets: + v1: + - inline: + contents: | + #!/bin/bash + echo "installing nothing" + echo "config option: {{repl ConfigOption "test_option" }}" + dest: ./scripts/install.sh + mode: 0777 + - inline: + contents: | + #!/bin/bash + echo "tested nothing" + echo "customer {{repl Installation "customer_id" }}" + echo "install {{repl Installation "installation_id" }}" + dest: ./scripts/test.sh + mode: 0777 +config: + v1: + - name: test_options + title: Test Options + description: testing testing 123 + items: + - name: test_option + title: Test Option + default: abc123_test-option-value + type: text + +lifecycle: + v1: + - render: {}` + var test = func() (err error) { + req := require.New(t) + + v := viper.New() + v.Set("customer-endpoint", fmt.Sprintf("http://localhost:%d/graphql", pact.Server.Port)) + + gqlClient, err := replapp.NewGraphqlClient(v, http.DefaultClient) + req.NoError(err) + + selector := replapp.Selector{ + CustomerID: customerID, + InstallationID: installationID, + ReleaseSemver: semver, + } + + _, err = gqlClient.GetRelease(&selector) + req.NoError(err) + + return nil + } + + pact.AddInteraction(). + Given("A request to get a single app release"). + UponReceiving("A request to get the amn app release from a semver and customer id"). + WithRequest(dsl.Request{ + Method: "POST", + Path: dsl.String("/graphql"), + Headers: dsl.MapMatcher{ + "Authorization": dsl.String(fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", customerID, installationID))))), + "Content-Type": dsl.String("application/json"), + }, + Body: map[string]interface{}{ + "operationName": "", + "query": replapp.GetAppspecQuery, + "variables": map[string]interface{}{ + "semver": semver, + }, + }, + }). + WillRespondWith(dsl.Response{ + Status: 200, + Body: map[string]interface{}{ + "data": map[string]interface{}{ + "shipRelease": map[string]interface{}{ + "id": dsl.Like(dsl.String("generated")), + "spec": dsl.Like(dsl.String(spec)), + }, + }, + }, + }) + + if err := pact.Verify(test); err != nil { + t.Fatalf("Error on Verify: %v", err) + } +} diff --git a/contracts/replicatedapp/prem_graphql_test.go b/contracts/replicatedapp/prem_graphql_test.go new file mode 100644 index 000000000..d0e4e64f1 --- /dev/null +++ b/contracts/replicatedapp/prem_graphql_test.go @@ -0,0 +1,42 @@ +package replicatedapp + +import ( + "os" + "path" + "testing" + + "github.com/pact-foundation/pact-go/dsl" +) + +var ( + pact dsl.Pact +) + +func TestMain(m *testing.M) { + pact = createPact() + + pact.Setup(true) + + code := m.Run() + + pact.WritePact() + pact.Teardown() + + os.Exit(code) +} + +func createPact() dsl.Pact { + dir, _ := os.Getwd() + + pactDir := path.Join(dir, "..", "..", "..", "pacts") + logDir := path.Join(dir, "..", "..", "..", "logs") + + return dsl.Pact{ + Consumer: "ship", + Provider: "prem-graphql-api", + LogDir: logDir, + PactDir: pactDir, + LogLevel: "debug", + Host: "0.0.0.0", + } +} diff --git a/pkg/specs/replicatedapp/graphql.go b/pkg/specs/replicatedapp/graphql.go index 2533da44d..ce33f2cc7 100644 --- a/pkg/specs/replicatedapp/graphql.go +++ b/pkg/specs/replicatedapp/graphql.go @@ -17,7 +17,7 @@ import ( "github.com/spf13/viper" ) -const getAppspecQuery = ` +const GetAppspecQuery = ` query($semver: String) { shipRelease (semver: $semver) { id @@ -248,7 +248,7 @@ func NewGraphqlClient(v *viper.Viper, client *http.Client) (*GraphQLClient, erro // GetRelease gets a payload from the graphql server func (c *GraphQLClient) GetRelease(selector *Selector) (*state.ShipRelease, error) { requestObj := GraphQLRequest{ - Query: getAppspecQuery, + Query: GetAppspecQuery, Variables: map[string]string{ "semver": selector.ReleaseSemver, }, @@ -396,6 +396,7 @@ func (c *GraphQLClient) callGQL(ci callInfo, result interface{}) error { if ci.upstream != "" { gqlServer = ci.upstream } + graphQLRequest, err := http.NewRequest(http.MethodPost, gqlServer, bodyReader) if err != nil { return errors.Wrap(err, "create new request")