Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: setup pact in CI/CD #35

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 24 additions & 4 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,6 @@ jobs:
executor: default
steps:
- checkout
- run:
name: Install tools
command: make tools
- run:
name: Run unit tests
command: make test
Expand Down Expand Up @@ -71,6 +68,27 @@ jobs:
- prodsec/security_scans:
mode: auto
iac-scan: disabled
contract_test:
docker:
- image: cimg/go:1.21.1-node # the base image with a node variant for the provider
steps:
- checkout
- run:
name: Install tools
command: make tools
- run:
name: Run contract tests
command: make contract-test
- run:
name: Publish contract
command: make publish-contract
- run:
name: Verify contract # clones the provider, which requires node
command: make verify-contract
- run:
name: Check if contract is compatible
command: make is-contract-compatible

# Orchestrate our job run sequence
workflows:
version: 2
Expand All @@ -94,11 +112,13 @@ workflows:
- smoke_test:
name: Smoke tests
context:
- code-client-go-smoke-tests-token
- code-client-go-smoke-tests-token # SMOKE_TEST_TOKEN
requires:
- Unit tests
- contract_test:
name: Contract tests
context:
- code-client-go-contract-tests # PACT_BROKER_URL, PACT_BROKER_TOKEN
requires:
- Unit tests
- build:
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,5 @@ scripts/__pycache__/
internal/workspace/**/**/*.yaml
!internal/workspace/**/**/*.config.yaml
internal/orchestration/**/**/*.yaml
!internal/orchestration/**/**/*.config.yaml
!internal/orchestration/**/**/*.config.yaml
workspace-service
12 changes: 9 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,9 @@ make contract-test
These tests use `pact` and the name of each test should start with `TestPact`. The contracts end up in [https://snyk-dev.pactflow.io/](https://snyk-dev.pactflow.io/).

If writing contract tests, use the test implementations in [./internal/util/testutil](./internal/util/testutil) and
make sure that at the top of each file is the following line `//go:build CONTRACT`.
make sure that at the top of each file is the following line `//go:build contract`.

These tests are used then in the CI/CD as a gate for the PR.
These tests are used then in the CI/CD as a gate for the PR and for the release process in `main`.

### Smoke tests

Expand Down Expand Up @@ -192,7 +192,13 @@ Do not hold onto your changes for too long. Commit and push frequently and creat
We use a [GitHub Action](https://github.com/marketplace/actions/conventional-release-labels) to generate labels which are then used to generate the release notes when merging the PR.
The title of the PR is what is used to generate the labels.

The following steps run in the CI/CD of a PR and gate the merge:
![](PR pipeline.png)

## Merging PRs

We use a [GitHub Action](https://github.com/marketplace/actions/semver-conventional-commits) to compute the version based on conventional commit messages, push a tag with the computed version, then use
the [GitHub Release CLI](https://cli.github.com/manual/gh_release_create) to generate release notes based on labels.
the [GitHub Release CLI](https://cli.github.com/manual/gh_release_create) to generate release notes based on labels.

The following steps run in the CI/CD of the `main` branch and gate the release:
![](main pipeline.png)
37 changes: 28 additions & 9 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -44,24 +44,40 @@ clean:
@rm -rf $(TOOLS_BIN)

.PHONY: test
test:
test:
@echo "Testing..."
@go test -cover ./...
@go test -cover . ./...

.PHONY: testv
testv:
testv:
@echo "Testing verbosely..."
@go test -v
@go test -v . ./...

.PHONY: smoke-test
smoke-test:
@echo "Smoke testing..."
@go test -tags=smoke

.PHONY: contract-test
contract-test: $(TOOLS_BIN)
@echo "Contract testing..."
@go test -tags=CONTRACT ./...
@go test -tags=contract ./...

.PHONY: smoke-test
smoke-test:
@echo "Smoke testing..."
@go test -tags=SMOKE
.PHONY: publish-contract
publish-contract:
./scripts/publish-contract.sh

.PHONY: verify-contract
verify-contract:
./scripts/verify-contract.sh

.PHONY: is-contract-compatible
is-contract-compatible:
./scripts/is-contract-compatible.sh

.PHONY: deploy-contract
deploy-contract:
./scripts/deploy-contract.sh

.PHONY: generate
generate:
Expand Down Expand Up @@ -106,5 +122,8 @@ help:
@echo "$(LOG_PREFIX) download-apis"
@echo "$(LOG_PREFIX) download-workspace-api"
@echo "$(LOG_PREFIX) download-orchestration-api"
@echo "$(LOG_PREFIX) contract-test
@echo "$(LOG_PREFIX) smoke-test
@echo "$(LOG_PREFIX) publish-contract
@echo "$(LOG_PREFIX) GOOS Specify Operating System to compile for (see golang GOOS, default=$(GOOS))"
@echo "$(LOG_PREFIX) GOARCH Specify Architecture to compile for (see golang GOARCH, default=$(GOARCH))"
Binary file added PR pipeline.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions internal/deepcode/client_pact_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build CONTRACT
//go:build contract

/*
* © 2022-2024 Snyk Limited
Expand Down Expand Up @@ -51,7 +51,7 @@ var (
pact *consumer.V2HTTPMockProvider
)

func TestSnykCodeClientPact(t *testing.T) {
func TestPact_DeepcodeClient(t *testing.T) {
setupPact(t)

t.Run("Create bundle", func(t *testing.T) {
Expand Down
4 changes: 2 additions & 2 deletions internal/orchestration/2024-02-16/client_pact_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build CONTRACT
//go:build contract

/*
* © 2022-2024 Snyk Limited
Expand Down Expand Up @@ -258,7 +258,7 @@ func setupPact(t *testing.T) {
func() *http.Client {
return http.DefaultClient
},
codeClientHTTP.WithRetryCount(3),
codeClientHTTP.WithRetryCount(1),
codeClientHTTP.WithInstrumentor(instrumentor),
codeClientHTTP.WithErrorReporter(errorReporter),
codeClientHTTP.WithLogger(&logger),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@
"providerState": "Scan ID",
"request": {
"method": "GET",
"path": "/orgs/e7ea34c9-de0f-422c-bf2c-4654c2e2da90/scans/c4a998ce-be47-4bca-b470-6a1f88becdda",
"path": "/orgs/e7ea34c9-de0f-422c-bf2c-4654c2e2da90/scans/91f17350-13e0-4d45-959f-7f853ef715a4",
"query": "version=2024-02-16%7eexperimental"
},
"response": {
Expand Down
1 change: 0 additions & 1 deletion internal/workspace/2024-05-14/client.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

73 changes: 58 additions & 15 deletions internal/workspace/2024-05-14/client_pact_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build CONTRACT
// go:build contract

/*
* © 2022-2024 Snyk Limited
Expand All @@ -21,6 +21,8 @@ package v20240514_test
import (
"context"
"fmt"
v202405142 "github.com/snyk/code-client-go/internal/workspace/2024-05-14/common"
externalRef1 "github.com/snyk/code-client-go/internal/workspace/2024-05-14/links"
"net/http"
"testing"

Expand All @@ -42,9 +44,11 @@ const (
pactDir = "./pacts"
pactProvider = "WorkspaceApi"

orgUUID = "e7ea34c9-de0f-422c-bf2c-4654c2e2da90"
requestId = "b6ea34c9-de0f-422c-bf2c-4654c2e2da90"
uuidRegex = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"
orgUUID = "e7ea34c9-de0f-422c-bf2c-4654c2e2da90"
requestId = "b6ea34c9-de0f-422c-bf2c-4654c2e2da90"
fakeApiToken = "a330e51f-1234-467d-8728-15e7aaea9b1a"
uuidRegex = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"
sessionTokenMatcher = "^Bearer [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"
)

// Common test data
Expand All @@ -53,36 +57,51 @@ var (
httpClient v20240216.HttpRequestDoer
)

type Data struct {
Id string `json:"id"`
Type string `json:"type"`
}

func TestWorkspaceClientPact(t *testing.T) {
setupPact(t)

// https://snyk.roadie.so/catalog/default/api/workspace-service_2024-05-14_experimental
t.Run("Create workspace", func(t *testing.T) {
pact.AddInteraction().Given("New workspace").UponReceiving("Create workspace").WithCompleteRequest(consumer.Request{
Method: "POST",
Path: matchers.String(fmt.Sprintf("/orgs/%s/workspaces", orgUUID)),
Path: matchers.String(fmt.Sprintf("/hidden/orgs/%s/workspaces", orgUUID)),
Query: matchers.MapMatcher{
"version": matchers.String("2024-05-14~experimental"),
},
Headers: getHeaderMatcher(),
Body: getBodyMatcher(),
}).WithCompleteResponse(consumer.Response{
Status: 200,
Status: 201,
Headers: matchers.MapMatcher{
"Content-Type": matchers.String("application/vnd.api+json"),
"Content-Type": matchers.String("application/vnd.api+json; charset=utf-8"),
}, Body: map[string]interface{}{
"data": matchers.Like(Data{
Id: "9c2c14da-7035-4280-bafb-d3e874ebd4af",
Type: "file_bundle_workspace",
}),
"jsonapi": matchers.MatchV2(&v202405142.JsonApi{}),
"links": matchers.MatchV2(&externalRef1.LinkSelf{}),
},
Body: matchers.MatchV2(workspaces.WorkspacePostResponse{}),
// dsl.Match(&externalRef3.WorkspacePostResponse{}), // not working due to uuid deserialisation https://github.com/pact-foundation/pact-go/issues/179
})

test := func(config consumer.MockServerConfig) error {
client, err := v20240514.NewClientWithResponses(fmt.Sprintf("http://localhost:%d", config.Port), v20240514.WithHTTPClient(httpClient))
require.NoError(t, err)
client, err := v20240514.NewClientWithResponses(fmt.Sprintf("http://localhost:%d/hidden", config.Port), v20240514.WithHTTPClient(httpClient))
if err != nil {
return err
}
_, err = client.CreateWorkspaceWithApplicationVndAPIPlusJSONBodyWithResponse(
context.Background(),
uuid.MustParse(orgUUID),
&v20240514.CreateWorkspaceParams{
Version: "2024-05-14~experimental",
SnykRequestId: uuid.MustParse(requestId),
UserAgent: "code-client-go",
ContentType: "application/vnd.api+json",
},
v20240514.CreateWorkspaceApplicationVndAPIPlusJSONRequestBody{
Expand Down Expand Up @@ -116,10 +135,7 @@ func TestWorkspaceClientPact(t *testing.T) {
}

err := pact.ExecuteTest(t, test)

if err != nil {
t.Fatalf("Error on verify: %v", err)
}
require.NoError(t, err)
})
}

Expand Down Expand Up @@ -153,6 +169,7 @@ func getHeaderMatcher() matchers.MapMatcher {
return matchers.MapMatcher{
"Snyk-Request-Id": getSnykRequestIdMatcher(),
"Content-Type": matchers.S("application/vnd.api+json"),
"User-Agent": matchers.Regex("go-http-client/1.1", ".*"),
}
}

Expand All @@ -161,5 +178,31 @@ func getSnykRequestIdMatcher() matchers.Matcher {
}

func getBodyMatcher() matchers.Matcher {
return matchers.MatchV2(v20240514.CreateWorkspaceApplicationVndAPIPlusJSONRequestBody{})
return matchers.Like(v20240514.CreateWorkspaceApplicationVndAPIPlusJSONRequestBody{
Data: struct {
Attributes struct {
BundleId string `json:"bundle_id"`
RepositoryUri string `json:"repository_uri"`
RootFolderId string `json:"root_folder_id"`
WorkspaceType workspaces.WorkspacePostRequestDataAttributesWorkspaceType `json:"workspace_type"`
} `json:"attributes"`
Type workspaces.WorkspacePostRequestDataType `json:"type"`
}{Attributes: struct {
BundleId string `json:"bundle_id"`
RepositoryUri string `json:"repository_uri"`
RootFolderId string `json:"root_folder_id"`
WorkspaceType workspaces.WorkspacePostRequestDataAttributesWorkspaceType `json:"workspace_type"`
}(struct {
BundleId string
RepositoryUri string
RootFolderId string
WorkspaceType workspaces.WorkspacePostRequestDataAttributesWorkspaceType
}{
BundleId: "sampleYnVuZGxlSWQK",
RepositoryUri: "https://url.invalid/code-client-go.git",
WorkspaceType: "file_bundle_workspace",
}),
Type: "workspace",
},
})
}
Loading