Skip to content

Commit

Permalink
CosmosDB Output Binding Stable Certification Tests (#1378)
Browse files Browse the repository at this point in the history
* cosmosdb output binding certification tests

Signed-off-by: GitHub <noreply@github.com>

* linter touchup

Signed-off-by: GitHub <noreply@github.com>

* Rename some remnants from copy paste

Signed-off-by: Bernd Verst <4535280+berndverst@users.noreply.github.com>

Co-authored-by: Looong Dai <long.dai@intel.com>
  • Loading branch information
berndverst and daixiang0 authored Dec 16, 2021
1 parent 870fedb commit 29848e1
Show file tree
Hide file tree
Showing 6 changed files with 225 additions and 19 deletions.
9 changes: 4 additions & 5 deletions tests/certification/bindings/azure/cosmosdb/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,10 @@ This project aims to test the Azure CosmosDB binding component under various con
* Authenticate with Master Key

### Other tests
- TODO: Verify data sent to output binding is written to Cosmos DB
- TODO: Expected failure for invalid partition key specified (Component Metadata Partition Key does not match Cosmos DB container)
- TODO: Expected failure for partition key missing from document
- TODO: Expected failure for `id` missing from document
- TODO: Graceful handling of connection resets / interruption (client connection only, not during Invoke/Create operation itself)
- Verify data sent to output binding is written to Cosmos DB
- Expected failure for invalid partition key specified (Component Metadata Partition Key does not match Cosmos DB container)
- Expected failure for partition key missing from document
- Expected failure for `id` missing from document

### Running the tests

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: azure-cosmosdb-binding
namespace: default
spec:
type: bindings.azure.cosmosdb
version: v1
metadata:
- name: url
secretKeyRef:
name: AzureCosmosDBUrl
key: AzureCosmosDBUrl
- name: database
secretKeyRef:
name: AzureCosmosDB
key: AzureCosmosDB
- name: collection
secretKeyRef:
name: AzureCosmosDBCollection
key: AzureCosmosDBCollection
- name: partitionKey
value: wrongPartitionKey
- name: masterKey
secretKeyRef:
name: AzureCosmosDBMasterKey
key: AzureCosmosDBMasterKey

auth:
secretStore: envvar-secret-store
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: envvar-secret-store
namespace: default
spec:
type: secretstores.local.env
version: v1
metadata:
2 changes: 1 addition & 1 deletion tests/certification/bindings/azure/cosmosdb/config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
apiVersion: dapr.io/v1alpha1
kind: Configuration
metadata:
name: keyvaultconfig
name: cosmosdbbindingconfig
spec:
features:
188 changes: 178 additions & 10 deletions tests/certification/bindings/azure/cosmosdb/cosmosdb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,16 @@
// Licensed under the MIT License.
// ------------------------------------------------------------

package keyvault_test
package cosmosdbbinding_test

import (
"encoding/json"
"fmt"
"os"
"testing"
"time"

"github.com/google/uuid"
"github.com/stretchr/testify/assert"

"github.com/dapr/components-contrib/bindings"
Expand All @@ -18,32 +23,167 @@ import (
secretstores_loader "github.com/dapr/dapr/pkg/components/secretstores"
"github.com/dapr/dapr/pkg/runtime"
dapr_testing "github.com/dapr/dapr/pkg/testing"
daprsdk "github.com/dapr/go-sdk/client"
"github.com/dapr/kit/logger"

"github.com/dapr/components-contrib/tests/certification/embedded"
"github.com/dapr/components-contrib/tests/certification/flow"
"github.com/dapr/components-contrib/tests/certification/flow/sidecar"

"github.com/a8m/documentdb"
)

const (
sidecarName = "cosmosdb-sidecar"
)

func TestKeyVault(t *testing.T) {
func createDocument(generateID bool, includePK bool) map[string]interface{} {
document := map[string]interface{}{
"orderid": "123abc456def",
"nestedproperty": map[string]interface{}{
"subproperty": "something of value for testing",
},
"description": "certification test item",
}
if generateID {
randomID := uuid.New().String()
document["id"] = randomID
}
if includePK {
document["partitionKey"] = "partitioniningOnThisValue"
}

return document
}

func TestCosmosDBBinding(t *testing.T) {
ports, err := dapr_testing.GetFreePorts(2)
assert.NoError(t, err)

currentGrpcPort := ports[0]
currentHttpPort := ports[1]
currentGRPCPort := ports[0]
currentHTTPPort := ports[1]

log := logger.NewLogger("dapr.components")

invokeCreateWithDocument := func(ctx flow.Context, document map[string]interface{}) error {
client, clientErr := daprsdk.NewClientWithPort(fmt.Sprint(currentGRPCPort))
if clientErr != nil {
panic(clientErr)
}
defer client.Close()

bytesDoc, marshalErr := json.Marshal(document)
if marshalErr != nil {
return marshalErr
}

invokeRequest := &daprsdk.InvokeBindingRequest{
Name: "azure-cosmosdb-binding",
Operation: "create",
Data: bytesDoc,
Metadata: nil,
}

err = client.InvokeOutputBinding(ctx, invokeRequest)
return err
}

testInvokeCreateAndVerify := func(ctx flow.Context) error {
document := createDocument(true, true)
invokeErr := invokeCreateWithDocument(ctx, document)
assert.NoError(t, invokeErr)

// sleep to avoid metdata request rate limit before initializing new client
flow.Sleep(3 * time.Second)

// all environment variables loaded here are also loaded in the component definition YAML files
// these are generated by the setup-azure-conf-test.sh script and injected by the GitHub Workflow, or by
// locally sourcing the generated .rc file
config := documentdb.NewConfig(&documentdb.Key{
Key: os.Getenv("AzureCosmosDBMasterKey"),
})
config.IdentificationHydrator = nil
dbclient := documentdb.New(os.Getenv("AzureCosmosDBUrl"), config)

dbs, queryDBErr := dbclient.QueryDatabases(&documentdb.Query{
Query: "SELECT * FROM ROOT r WHERE r.id=@id",
Parameters: []documentdb.Parameter{
{Name: "@id", Value: os.Getenv("AzureCosmosDB")},
},
})
assert.NoError(t, queryDBErr)
db := &dbs[0]
colls, queryCollErr := dbclient.QueryCollections(db.Self, &documentdb.Query{
Query: "SELECT * FROM ROOT r WHERE r.id=@id",
Parameters: []documentdb.Parameter{
{Name: "@id", Value: os.Getenv("AzureCosmosDBCollection")},
},
})
assert.NoError(t, queryCollErr)
collection := &colls[0]

var items []map[string]interface{}
_, queryErr := dbclient.QueryDocuments(
collection.Self,
documentdb.NewQuery("SELECT * FROM ROOT r WHERE r.id=@id", documentdb.P{Name: "@id", Value: document["id"].(string)}),
&items,
documentdb.CrossPartition(),
)

assert.NoError(t, queryErr)

result := items[0]
// verify the item retrieved from the database matches the item we inserted
assert.Equal(t, document["id"], result["id"])
assert.Equal(t, document["orderid"], result["orderid"])
assert.Equal(t, document["partitionKey"], result["partitionKey"])
assert.Equal(t, document["nestedproperty"].(map[string]interface{})["subproperty"],
result["nestedproperty"].(map[string]interface{})["subproperty"])

// cleanup
_, err = dbclient.DeleteDocument(result["_self"].(string), documentdb.PartitionKey(result["partitionKey"].(string)))
assert.NoError(t, err)

return nil
}

testInvokeCreateWithoutPartitionKey := func(ctx flow.Context) error {
document := createDocument(true, false)
invokeErr := invokeCreateWithDocument(ctx, document)

assert.Error(t, invokeErr)
assert.Contains(t, invokeErr.Error(), "missing partitionKey field")

return nil
}

testInvokeCreateWithoutID := func(ctx flow.Context) error {
document := createDocument(false, true)
invokeErr := invokeCreateWithDocument(ctx, document)

assert.Error(t, invokeErr)
assert.Contains(t, invokeErr.Error(), "the required properties - 'id; ' - are missing")

return nil
}

testInvokeCreateWithWrongPartitionKey := func(ctx flow.Context) error {
document := createDocument(true, false)
document["wrongPartitionKey"] = "somepkvalue"
invokeErr := invokeCreateWithDocument(ctx, document)

assert.Error(t, invokeErr)
assert.Contains(t, invokeErr.Error(), "PartitionKey extracted from document doesn't match the one specified in the header")

return nil
}

flow.New(t, "cosmosdb binding authentication using service principal").
Step(sidecar.Run(sidecarName,
embedded.WithoutApp(),
embedded.WithComponentsPath("./components/serviceprincipal"),
embedded.WithDaprGRPCPort(currentGrpcPort),
embedded.WithDaprHTTPPort(currentHttpPort),
embedded.WithDaprGRPCPort(currentGRPCPort),
embedded.WithDaprHTTPPort(currentHTTPPort),
runtime.WithSecretStores(
secretstores_loader.New("local.env", func() secretstores.SecretStore {
return secretstore_env.NewEnvSecretStore(log)
Expand All @@ -59,15 +199,42 @@ func TestKeyVault(t *testing.T) {
ports, err = dapr_testing.GetFreePorts(2)
assert.NoError(t, err)

currentGrpcPort = ports[0]
currentHttpPort = ports[1]
currentGRPCPort = ports[0]
currentHTTPPort = ports[1]

flow.New(t, "cosmosdb binding authentication using master key").
Step(sidecar.Run(sidecarName,
embedded.WithoutApp(),
embedded.WithComponentsPath("./components/masterkey"),
embedded.WithDaprGRPCPort(currentGrpcPort),
embedded.WithDaprHTTPPort(currentHttpPort),
embedded.WithDaprGRPCPort(currentGRPCPort),
embedded.WithDaprHTTPPort(currentHTTPPort),
runtime.WithSecretStores(
secretstores_loader.New("local.env", func() secretstores.SecretStore {
return secretstore_env.NewEnvSecretStore(log)
}),
),
runtime.WithOutputBindings(
bindings_loader.NewOutput("azure.cosmosdb", func() bindings.OutputBinding {
return cosmosdbbinding.NewCosmosDB(log)
}),
))).
Step("verify data sent to output binding is written to Cosmos DB", testInvokeCreateAndVerify).
Step("expect error if id is missing from document", testInvokeCreateWithoutID).
Step("expect error if partition key is missing from document", testInvokeCreateWithoutPartitionKey).
Run()

ports, err = dapr_testing.GetFreePorts(2)
assert.NoError(t, err)

currentGRPCPort = ports[0]
currentHTTPPort = ports[1]

flow.New(t, "cosmosdb binding with wrong partition key specified").
Step(sidecar.Run(sidecarName,
embedded.WithoutApp(),
embedded.WithComponentsPath("./components/wrongPartitionKey"),
embedded.WithDaprGRPCPort(currentGRPCPort),
embedded.WithDaprHTTPPort(currentHTTPPort),
runtime.WithSecretStores(
secretstores_loader.New("local.env", func() secretstores.SecretStore {
return secretstore_env.NewEnvSecretStore(log)
Expand All @@ -78,5 +245,6 @@ func TestKeyVault(t *testing.T) {
return cosmosdbbinding.NewCosmosDB(log)
}),
))).
Step("verify error when wrong partition key used", testInvokeCreateWithWrongPartitionKey).
Run()
}
6 changes: 3 additions & 3 deletions tests/certification/bindings/azure/cosmosdb/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ module github.com/dapr/components-contrib/tests/certification/bindings/azure/cos
go 1.17

require (
github.com/a8m/documentdb v1.3.1-0.20211026005403-13c3593b3c3a
github.com/dapr/components-contrib v1.5.1-rc.1
github.com/dapr/components-contrib/tests/certification v0.0.0-20211130185200-4918900c09e1
github.com/dapr/dapr v1.5.2-0.20211207220041-6296ceb58dec
github.com/dapr/go-sdk v1.3.0
github.com/dapr/kit v0.0.2-0.20210614175626-b9074b64d233
github.com/google/uuid v1.3.0
github.com/stretchr/testify v1.7.0
)

Expand All @@ -30,15 +33,13 @@ require (
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
github.com/PuerkitoBio/purell v1.1.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/a8m/documentdb v1.3.1-0.20211026005403-13c3593b3c3a // indirect
github.com/andybalholm/brotli v1.0.2 // indirect
github.com/antlr/antlr4 v0.0.0-20200503195918-621b933c7a7f // indirect
github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff v2.2.1+incompatible // indirect
github.com/cenkalti/backoff/v4 v4.1.1 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/dapr/go-sdk v1.3.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dimchansky/utfbom v1.1.1 // indirect
github.com/fasthttp/router v1.3.8 // indirect
Expand All @@ -52,7 +53,6 @@ require (
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/cel-go v0.7.3 // indirect
github.com/google/gofuzz v1.1.0 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/googleapis/gnostic v0.5.1 // indirect
github.com/grandcat/zeroconf v0.0.0-20190424104450-85eadb44205c // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.2.2 // indirect
Expand Down

0 comments on commit 29848e1

Please sign in to comment.