Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitaly.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjohn.mcdonnell <jmcdonnell@gitlab.com>2022-04-01 06:32:31 +0300
committerjohn.mcdonnell <jmcdonnell@gitlab.com>2022-04-01 06:32:31 +0300
commitcf490eb06969efd7bfb16c341adb98f986fd8aad (patch)
tree94ad43feee3953e977c198fc2a41e11030f16d20
parentffa6861c2b9f66ed22ebdfa6bc7a7319c47a1ea3 (diff)
Adding POC pact test for gitalyjmd-pact-testing-poc
-rw-r--r--testing/pacts/README.MD33
-rw-r--r--testing/pacts/consumer_test.go104
-rw-r--r--testing/pacts/pacts/createrepository-createrepository.json38
-rw-r--r--testing/pacts/provider_test.go56
-rw-r--r--testing/pacts/server.go90
5 files changed, 321 insertions, 0 deletions
diff --git a/testing/pacts/README.MD b/testing/pacts/README.MD
new file mode 100644
index 000000000..89879211e
--- /dev/null
+++ b/testing/pacts/README.MD
@@ -0,0 +1,33 @@
+We can use the [grpc-gateway](https://grpc-ecosystem.github.io/grpc-gateway/) which is a reverse-proxy to translate HTTP API into gRPC.
+That would give use a HTTP endpoint that would be more suited to use with the curent tooling available in Pact.
+
+To generate HTTP endpoints run the following command from the `proto` directory
+
+```
+protoc -I . --grpc-gateway_out go/gitalypb \
+ --grpc-gateway_opt logtostderr=true \
+ --grpc-gateway_opt paths=source_relative \
+ --grpc-gateway_opt generate_unbound_methods=true \
+ *.proto
+```
+
+
+When running thest tests, you must have a praefect server running.
+For initial simplicity, we can use the GDK on own localhost. This can be improved later.
+
+- Configure praefect to run on localhost using `listen_addr` as opposed to `socket_path` in {GITALY_DIR}/praefect.config.toml, and restart GDK
+```
+# # TCP address to listen on
+listen_addr = "192.168.178.33:2305"
+
+# # Praefect can listen on a socket when placed on the same machine as all clients
+# socket_path = "/Users/john/dev/gdk/praefect.socket"
+```
+
+- Modify `testing/pacts/server.go grpcServerEndpoint` accordingly to ensure you point at your praefect endpoint.
+
+- Run the tests
+ ```
+ cd {GITALY_DIR}/testing/pacts
+ go test -v
+ ``` \ No newline at end of file
diff --git a/testing/pacts/consumer_test.go b/testing/pacts/consumer_test.go
new file mode 100644
index 000000000..6f749afb5
--- /dev/null
+++ b/testing/pacts/consumer_test.go
@@ -0,0 +1,104 @@
+package pacts
+
+import (
+ "encoding/base64"
+ "encoding/json"
+ "fmt"
+ "log"
+ "math/rand"
+ "net/http"
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/google/uuid"
+ "github.com/pact-foundation/pact-go/dsl"
+)
+
+func init() {
+ rand.Seed(time.Now().UnixNano())
+}
+
+type Repository struct {
+ RelativePath string `json:"relativePath" pact:"relativePath=@hashed/ab/cd/abcdefghij.git"`
+ StorageName string `json:"storageName" pact:"storageName=default"`
+}
+
+type CreateRepository struct {
+ Repository Repository `json:"repository"`
+ DefaultBranch string `json:"defaultBranch" pact:"defaultBranch=bWFpbg=="`
+}
+
+func (c *CreateRepository) String() string {
+ j, _ := json.Marshal(c)
+ return string(j)
+}
+
+func TestConsumer(t *testing.T) {
+ uniqueProjectPath := uuid.New().String()
+
+ // Create Pact connecting to local Daemon
+ pact := &dsl.Pact{
+ Consumer: "CreateRepository",
+ Provider: "CreateRepository",
+ Host: "localhost",
+ }
+ defer pact.Teardown()
+
+ // Pass in test case
+ test := func() error {
+ u := fmt.Sprintf("http://localhost:%d/gitaly.RepositoryService/CreateRepository", pact.Server.Port)
+ req, err := http.NewRequest(http.MethodPost, u, strings.NewReader(
+ (&CreateRepository{
+ Repository: Repository{
+ RelativePath: uniqueProjectPath,
+ StorageName: "default",
+ },
+ DefaultBranch: base64.StdEncoding.EncodeToString([]byte("main")),
+ }).String(),
+ ))
+
+ // NOTE: by default, request bodies are expected to be sent with a Content-Type
+ // of application/json. If you don't explicitly set the content-type, you
+ // will get a mismatch during Verification.
+ req.Header.Set("Content-Type", "application/json")
+
+ if err != nil {
+ return err
+ }
+ if _, err = http.DefaultClient.Do(req); err != nil {
+ return err
+ }
+
+ return err
+ }
+
+ // Set up our expected interactions.
+ pact.
+ AddInteraction().
+ Given("Repository does not exist").
+ UponReceiving("A request to CreateRepository").
+ WithRequest(dsl.Request{
+ Method: http.MethodPost,
+ Path: dsl.String("/gitaly.RepositoryService/CreateRepository"),
+ Body: map[string]interface{}{
+ "defaultBranch": base64.StdEncoding.EncodeToString([]byte("main")),
+ "repository": map[string]string{
+ "relativePath": uniqueProjectPath,
+ "storageName": "default",
+ },
+ },
+ }).
+ WillRespondWith(dsl.Response{
+ Status: 200,
+ Headers: dsl.MapMatcher{
+ "Content-Type": dsl.String("application/json"),
+ },
+ Body: map[string]interface{}{},
+ })
+
+ // Verify
+ if err := pact.Verify(test); err != nil {
+ log.Fatalf("Error on Verify: %v", err)
+ }
+}
diff --git a/testing/pacts/pacts/createrepository-createrepository.json b/testing/pacts/pacts/createrepository-createrepository.json
new file mode 100644
index 000000000..96d06c23c
--- /dev/null
+++ b/testing/pacts/pacts/createrepository-createrepository.json
@@ -0,0 +1,38 @@
+{
+ "consumer": {
+ "name": "CreateRepository"
+ },
+ "provider": {
+ "name": "CreateRepository"
+ },
+ "interactions": [
+ {
+ "description": "A request to CreateRepository",
+ "providerState": "Repository does not exist",
+ "request": {
+ "method": "POST",
+ "path": "/gitaly.RepositoryService/CreateRepository",
+ "body": {
+ "defaultBranch": "bWFpbg==",
+ "repository": {
+ "relativePath": "ae300491-9af0-49a8-9d52-c8e685524fb4",
+ "storageName": "default"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "headers": {
+ "Content-Type": "application/json"
+ },
+ "body": {
+ }
+ }
+ }
+ ],
+ "metadata": {
+ "pactSpecification": {
+ "version": "2.0.0"
+ }
+ }
+} \ No newline at end of file
diff --git a/testing/pacts/provider_test.go b/testing/pacts/provider_test.go
new file mode 100644
index 000000000..4aefe772e
--- /dev/null
+++ b/testing/pacts/provider_test.go
@@ -0,0 +1,56 @@
+package pacts
+
+import (
+ "fmt"
+ "log"
+ "net/http"
+ "os"
+ "path/filepath"
+ "testing"
+
+ "github.com/pact-foundation/pact-go/dsl"
+ "github.com/pact-foundation/pact-go/types"
+)
+
+var (
+ dir, _ = os.Getwd()
+ pactDir = fmt.Sprintf("%s/pacts", dir)
+)
+
+func TestProvider(t *testing.T) {
+ // Create Pact connecting to local Daemon
+ pact := &dsl.Pact{
+ Consumer: "MyConsumer",
+ Provider: "MyProvider",
+ }
+
+ // Start provider API in the background
+ go startServer()
+
+ // Authorization middleware
+ // This is your chance to modify the request before it hits your provider
+ // NOTE: this should be used very carefully, as it has the potential to
+ // _change_ the contract
+ f := func(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ next.ServeHTTP(w, r)
+ })
+ }
+
+ // Verify the Provider with local Pact Files
+ pact.VerifyProvider(t, types.VerifyRequest{
+ ProviderBaseURL: "http://localhost:8081",
+ PactURLs: []string{filepath.ToSlash(fmt.Sprintf("%s/createrepository-createrepository.json", pactDir))},
+ CustomProviderHeaders: []string{"X-API-Token: abcd"},
+ RequestFilter: f,
+ StateHandlers: types.StateHandlers{
+ "User foo exists": func() error {
+ return nil
+ },
+ },
+ })
+}
+
+func startServer() {
+ log.Fatal(PactServerRun())
+}
diff --git a/testing/pacts/server.go b/testing/pacts/server.go
new file mode 100644
index 000000000..3c1bea00d
--- /dev/null
+++ b/testing/pacts/server.go
@@ -0,0 +1,90 @@
+package pacts
+
+import (
+ "context"
+ "flag"
+ "log"
+ "net/http"
+
+ "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
+ "gitlab.com/gitlab-org/gitaly/v14/proto/go/gitalypb"
+ "google.golang.org/grpc"
+ "google.golang.org/grpc/credentials/insecure"
+ // Update
+)
+
+// command-line options:
+// gRPC server endpoint
+var grpcServerEndpoint = flag.String("grpc-server-endpoint", "192.168.178.33:2305", "gRPC server endpoint")
+
+func PactServerRun() error {
+ log.Println(("Running grpc-server"))
+ ctx := context.Background()
+ ctx, cancel := context.WithCancel(ctx)
+ defer cancel()
+
+ // Register gRPC server endpoint
+ // Note: Make sure the gRPC server is running properly and accessible
+ mux := runtime.NewServeMux()
+ opts := []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())}
+ if err := gitalypb.RegisterConflictsServiceHandlerFromEndpoint(ctx, mux, *grpcServerEndpoint, opts); err != nil {
+ panic(err)
+ }
+ if err := gitalypb.RegisterSSHServiceHandlerFromEndpoint(ctx, mux, *grpcServerEndpoint, opts); err != nil {
+ panic(err)
+ }
+ if err := gitalypb.RegisterServerServiceHandlerFromEndpoint(ctx, mux, *grpcServerEndpoint, opts); err != nil {
+ panic(err)
+ }
+ if err := gitalypb.RegisterRepositoryServiceHandlerFromEndpoint(ctx, mux, *grpcServerEndpoint, opts); err != nil {
+ panic(err)
+ }
+ if err := gitalypb.RegisterHookServiceHandlerFromEndpoint(ctx, mux, *grpcServerEndpoint, opts); err != nil {
+ panic(err)
+ }
+ if err := gitalypb.RegisterDiffServiceHandlerFromEndpoint(ctx, mux, *grpcServerEndpoint, opts); err != nil {
+ panic(err)
+ }
+ if err := gitalypb.RegisterObjectPoolServiceHandlerFromEndpoint(ctx, mux, *grpcServerEndpoint, opts); err != nil {
+ panic(err)
+ }
+ if err := gitalypb.RegisterInternalGitalyHandlerFromEndpoint(ctx, mux, *grpcServerEndpoint, opts); err != nil {
+ panic(err)
+ }
+ if err := gitalypb.RegisterBlobServiceHandlerFromEndpoint(ctx, mux, *grpcServerEndpoint, opts); err != nil {
+ panic(err)
+ }
+ if err := gitalypb.RegisterOperationServiceHandlerFromEndpoint(ctx, mux, *grpcServerEndpoint, opts); err != nil {
+ panic(err)
+ }
+ if err := gitalypb.RegisterNamespaceServiceHandlerFromEndpoint(ctx, mux, *grpcServerEndpoint, opts); err != nil {
+ panic(err)
+ }
+ if err := gitalypb.RegisterCleanupServiceHandlerFromEndpoint(ctx, mux, *grpcServerEndpoint, opts); err != nil {
+ panic(err)
+ }
+ if err := gitalypb.RegisterWikiServiceHandlerFromEndpoint(ctx, mux, *grpcServerEndpoint, opts); err != nil {
+ panic(err)
+ }
+ if err := gitalypb.RegisterSmartHTTPServiceHandlerFromEndpoint(ctx, mux, *grpcServerEndpoint, opts); err != nil {
+ panic(err)
+ }
+ if err := gitalypb.RegisterRefTransactionHandlerFromEndpoint(ctx, mux, *grpcServerEndpoint, opts); err != nil {
+ panic(err)
+ }
+ if err := gitalypb.RegisterCommitServiceHandlerFromEndpoint(ctx, mux, *grpcServerEndpoint, opts); err != nil {
+ panic(err)
+ }
+ if err := gitalypb.RegisterRefServiceHandlerFromEndpoint(ctx, mux, *grpcServerEndpoint, opts); err != nil {
+ panic(err)
+ }
+ if err := gitalypb.RegisterRemoteServiceHandlerFromEndpoint(ctx, mux, *grpcServerEndpoint, opts); err != nil {
+ panic(err)
+ }
+ if err := gitalypb.RegisterPraefectInfoServiceHandlerFromEndpoint(ctx, mux, *grpcServerEndpoint, opts); err != nil {
+ panic(err)
+ }
+
+ // Start HTTP server (and proxy calls to gRPC server endpoint)
+ return http.ListenAndServe(":8081", mux)
+}