Skip to content

Commit

Permalink
feat: add tests for resolver + modif config
Browse files Browse the repository at this point in the history
implement ENG-4738
  • Loading branch information
cowan-macady committed Oct 7, 2024
1 parent 9769370 commit b9b5665
Show file tree
Hide file tree
Showing 9 changed files with 254 additions and 14 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/pr-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ env:
INDYKITE_APPLICATION_CREDENTIALS: ${{ secrets.APP_AGENT_CREDENTIALS }}
INDYKITE_SERVICE_ACCOUNT_CREDENTIALS: ${{ secrets.SERVICE_ACCOUNT_CREDENTIALS }}
TENANT_ID: ${{ secrets.TENANT_ID }}
CUSTOMER_ID: ${{ secrets.CUSTOMER_ID }}
LOCATION_ID: ${{ secrets.LOCATION_ID }}
PROJECT_ID: jarvis-dev-268314
PROJ_NUMBER: 699926043561
RUN_ENV: staging
Expand Down
1 change: 0 additions & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ linters:
- errname
- errorlint
- exhaustive
- exportloopref
- forbidigo
- gci
- ginkgolinter
Expand Down
216 changes: 216 additions & 0 deletions config/config_integration_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
// Copyright (c) 2024 IndyKite
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//go:build integration

package config_test

import (
"context"
"fmt"
"log"
"time"

"google.golang.org/protobuf/types/known/wrapperspb"

"github.com/indykite/indykite-sdk-go/config"
configpb "github.com/indykite/indykite-sdk-go/gen/indykite/config/v1beta1"
integration "github.com/indykite/indykite-sdk-go/test"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
. "github.com/onsi/gomega/gstruct"
)

var _ = Describe("Configuration", func() {
Describe("ExternalDataResolver", func() {
It("CreateExternalDataResolver", func() {
var (
err error
timeNow = fmt.Sprintf("%v", time.Now().UnixNano())
)

configClient, err := integration.InitConfigConfig()
Expect(err).To(Succeed())

displayNamePb := &wrapperspb.StringValue{Value: "AppSpace " + timeNow}
createAppSpaceReq := &configpb.CreateApplicationSpaceRequest{
CustomerId: integration.CustomerID,
Name: "appspace-" + timeNow,
DisplayName: displayNamePb,
Region: "europe-west1",
}
respAppSpace, err := configClient.CreateApplicationSpace(context.Background(), createAppSpaceReq)
Expect(err).To(Succeed())
Expect(respAppSpace).NotTo(BeNil())
appSpaceID := respAppSpace.Id
appSpaceEtag := respAppSpace.Etag

configuration := &configpb.ExternalDataResolverConfig{
Url: integration.URL,
Method: integration.Method1,
Headers: integration.Headers,
RequestType: integration.RequestType,
RequestPayload: integration.RequestPayload,
ResponseType: integration.ResponseType,
ResponseSelector: integration.ResponseSelector,
}
createReq, _ := config.NewCreate("resolver-" + timeNow)
createReq.ForLocation(appSpaceID)
createReq.WithDisplayName("Resolver" + timeNow)
createReq.WithExternalDataResolverConfig(configuration)

resp, err := configClient.CreateConfigNode(context.Background(), createReq)
if err != nil {
log.Fatalf("failed to invoke operation on IndyKite creation config node %v", err)
}
Expect(resp).NotTo(BeNil())
configID := resp.Id
configEtag := resp.Etag
Expect(resp.LocationId).To(Equal(appSpaceID))

readReq, _ := config.NewRead(configID)
respRead, err := configClient.ReadConfigNode(context.Background(), readReq)
Expect(err).To(Succeed())
Expect(respRead).NotTo(BeNil())
configNode := respRead.ConfigNode
Expect(configNode).To(PointTo(MatchFields(IgnoreExtras, Fields{
"Id": Equal(configID),
"Name": Equal("resolver-" + timeNow),
"Config": PointTo(MatchFields(IgnoreExtras, Fields{
"ExternalDataResolverConfig": integration.EqualProto(configuration),
})),
})))

configurationUpd := &configpb.ExternalDataResolverConfig{
Url: integration.URLUpd,
Method: integration.Method1,
Headers: integration.HeadersUpd,
RequestType: integration.RequestType,
RequestPayload: integration.RequestPayload,
ResponseType: integration.ResponseType,
ResponseSelector: integration.ResponseSelector,
}
updateReq, _ := config.NewUpdate(configID)
updateReq.WithDisplayName("Resolver2" + timeNow)
updateReq.WithExternalDataResolverConfig(configurationUpd)
respUpd, err := configClient.UpdateConfigNode(context.Background(), updateReq)
if err != nil {
log.Fatalf("failed to invoke operation on IndyKite update config node Client %v", err)
}
Expect(respUpd).NotTo(BeNil())
configUpdEtag := respUpd.Etag
Expect(respUpd.Id).To(Equal(configID))
Expect(respUpd.LocationId).To(Equal(appSpaceID))
Expect(configUpdEtag).NotTo(Equal(configEtag))

deleteReq, _ := config.NewDelete(configID)
respDel, err := configClient.DeleteConfigNode(context.Background(), deleteReq)
Expect(err).To(Succeed())
Expect(respDel).NotTo(BeNil())

time.Sleep(3 * time.Second)
etagPb := &wrapperspb.StringValue{Value: appSpaceEtag}
reqDelAS := &configpb.DeleteApplicationSpaceRequest{
Id: appSpaceID,
Etag: etagPb,
Bookmarks: []string{},
}
respDelAS, err := configClient.DeleteApplicationSpace(context.Background(), reqDelAS)
Expect(err).To(Succeed())
Expect(respDelAS).NotTo(BeNil())
})

It("CreateExternalDataResolverErrorLocation", func() {
var (
err error
timeNow = fmt.Sprintf("%v", time.Now().UnixNano())
)

configClient, err := integration.InitConfigConfig()
Expect(err).To(Succeed())

configuration := &configpb.ExternalDataResolverConfig{
Url: integration.URL,
Method: integration.Method1,
Headers: integration.Headers,
RequestType: integration.RequestType,
RequestPayload: integration.RequestPayload,
ResponseType: integration.ResponseType,
ResponseSelector: integration.ResponseSelector,
}
createReq, _ := config.NewCreate("resolver-" + timeNow)
createReq.ForLocation(integration.WrongAppSpace)
createReq.WithDisplayName("Resolver" + timeNow)
createReq.WithExternalDataResolverConfig(configuration)

resp, err := configClient.CreateConfigNode(context.Background(), createReq)
Expect(err).To(MatchError(ContainSubstring(
"insufficient permission to perform requested action")))
Expect(resp).To(BeNil())
})

It("CreateExternalDataResolverWrongMethod", func() {
var (
err error
timeNow = fmt.Sprintf("%v", time.Now().UnixNano())
)

configClient, err := integration.InitConfigConfig()
Expect(err).To(Succeed())

displayNamePb := &wrapperspb.StringValue{Value: "AppSpace " + timeNow}
createAppSpaceReq := &configpb.CreateApplicationSpaceRequest{
CustomerId: integration.CustomerID,
Name: "appspace-" + timeNow,
DisplayName: displayNamePb,
Region: "europe-west1",
}
respAppSpace, err := configClient.CreateApplicationSpace(context.Background(), createAppSpaceReq)
Expect(err).To(Succeed())
Expect(respAppSpace).NotTo(BeNil())
appSpaceID := respAppSpace.Id
appSpaceEtag := respAppSpace.Etag

configuration := &configpb.ExternalDataResolverConfig{
Url: integration.URL,
Method: integration.Method3,
RequestType: integration.RequestType,
RequestPayload: integration.RequestPayload,
ResponseType: integration.ResponseType,
ResponseSelector: integration.ResponseSelector,
}
createReq, _ := config.NewCreate("resolver-" + timeNow)
createReq.ForLocation(appSpaceID)
createReq.WithDisplayName("Resolver" + timeNow)
createReq.WithExternalDataResolverConfig(configuration)

resp, err := configClient.CreateConfigNode(context.Background(), createReq)
Expect(err).To(MatchError(ContainSubstring(
"invalid ExternalDataResolverConfig.Method: value must be in list [GET POST PUT PATCH]")))
Expect(resp).To(BeNil())

time.Sleep(3 * time.Second)
etagPb := &wrapperspb.StringValue{Value: appSpaceEtag}
reqDelAS := &configpb.DeleteApplicationSpaceRequest{
Id: appSpaceID,
Etag: etagPb,
Bookmarks: []string{},
}
respDelAS, err := configClient.DeleteApplicationSpace(context.Background(), reqDelAS)
Expect(err).To(Succeed())
Expect(respDelAS).NotTo(BeNil())
})
})
})
2 changes: 1 addition & 1 deletion examples/config/cmd/external_data_resolver_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ var createExternalDataResolverConfigCmd = &cobra.Command{
ResponseSelector: ".",
}
createReq, _ := config.NewCreate("like-real-config-node-name2")
createReq.ForLocation("gid:AAAAAvFyVpD_1kd8k2kpNY9rjFM")
createReq.ForLocation("gid:AAAAABBBBB_uiuiu144KNUI1245")
createReq.WithDisplayName("Like real ConfigNode Name2")
createReq.WithExternalDataResolverConfig(configuration)

Expand Down
9 changes: 0 additions & 9 deletions grpc/config/credentials.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,6 @@ func DefaultEnvironmentLoaderConfig(_ context.Context) (*CredentialsConfig, erro
return UnmarshalCredentialConfig(data)
}

// EntityMatchingEnvironmentLoader for backward compatibility with old environment variables.
func EntityMatchingEnvironmentLoader(_ context.Context) (*CredentialsConfig, error) {
data, err := lookupEnvCredentialVariables("INDYKITE_ENTITY_MATCHING_APPLICATION_CREDENTIALS")
if err != nil {
return nil, err
}
return UnmarshalCredentialConfig(data)
}

func StaticCredentialsJSON(credentialsJSON []byte) CredentialsLoader {
return func(_ context.Context) (*CredentialsConfig, error) {
return UnmarshalCredentialConfig(credentialsJSON)
Expand Down
12 changes: 11 additions & 1 deletion grpc/pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ package grpc
import (
"context"
"fmt"
"math"
"sync/atomic"

"google.golang.org/grpc"
Expand Down Expand Up @@ -75,7 +76,16 @@ func (p *roundRobinConnPool) Num() int {

func (p *roundRobinConnPool) Conn() *grpc.ClientConn {
i := atomic.AddUint32(&p.idx, 1)
return p.conns[i%uint32(len(p.conns))]
v := len(p.conns)
// Check for negative values
if v < 0 {
return nil
}
// Check for overflow
if v > int(math.MaxUint32) {
return nil
}
return p.conns[i%uint32(v)]
}

func (p *roundRobinConnPool) Close() error {
Expand Down
2 changes: 1 addition & 1 deletion helpers/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func (c *Client) DeleteNodes(
}
records = append(records, record)
}
responses, err := c.ClientIngest.StreamRecords(records) //nolint: contextcheck // against StreamRecords
responses, err := c.ClientIngest.StreamRecords(records) //nolint:contextcheck // against StreamRecords
if err != nil {
return nil, err
}
Expand Down
22 changes: 22 additions & 0 deletions test/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@ package test
import (
"crypto/rand"
"encoding/base64"
"os"

"github.com/google/uuid"
"google.golang.org/protobuf/types/known/timestamppb"

authorizationpb "github.com/indykite/indykite-sdk-go/gen/indykite/authorization/v1beta1"
configpb "github.com/indykite/indykite-sdk-go/gen/indykite/config/v1beta1"
ingestpb "github.com/indykite/indykite-sdk-go/gen/indykite/ingest/v1beta3"
knowledgeobjects "github.com/indykite/indykite-sdk-go/gen/indykite/knowledge/objects/v1beta1"
objects "github.com/indykite/indykite-sdk-go/gen/indykite/objects/v1beta2"
Expand Down Expand Up @@ -191,6 +193,26 @@ var (
ConsentAllow = "gid:AAAAHf5ZnwufDUK-tnCjoSsw-cQ"

Resolver = "gid:AAAAIcrOChFSj0R5sFm1V8JXhiE"
URL = "https://example.com/source2"
URLUpd = "https://example.com/sourceupd"
Method1 = "GET"
Method2 = "POST"
Method3 = "ACTION"
Headers = map[string]*configpb.ExternalDataResolverConfig_Header{
"Authorization": {Values: []string{"Bearer edolkUTY"}},
"Content-Type": {Values: []string{"application/json"}},
}
HeadersUpd = map[string]*configpb.ExternalDataResolverConfig_Header{
"Authorization": {Values: []string{"Bearer pdnYhjui"}},
"Content-Type": {Values: []string{"application/json"}},
}
RequestType = configpb.ExternalDataResolverConfig_CONTENT_TYPE_JSON
RequestPayload = []byte(`{"key": "value"}`)
ResponseType = configpb.ExternalDataResolverConfig_CONTENT_TYPE_JSON
ResponseSelector = "."

CustomerID = os.Getenv("CUSTOMER_ID")
WrongAppSpace = "gid:AAAAAgDRZxyY6Ecrjhj2GMCtgVI"
)

func GenerateRandomString(length int) string {
Expand Down
2 changes: 1 addition & 1 deletion test/integration_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ func InitConfigIngestRetry() (*ingest.RetryClient, error) {
func InitConfigConfig() (*config.Client, error) {
clientConfig, err = config.NewClient(context.Background(),
grpc.WithCredentialsLoader(apicfg.DefaultEnvironmentLoaderConfig),
grpc.WithRetryOptions(retry.Disable()),
grpc.WithServiceAccount(),
)
if err != nil {
er(fmt.Sprintf("failed to create IndyKite Config Client: %v", err))
Expand Down

0 comments on commit b9b5665

Please sign in to comment.