Skip to content

Commit

Permalink
Support deep comparison of intents cloud object for testing (#120)
Browse files Browse the repository at this point in the history
  • Loading branch information
NetanelBollag authored Jan 24, 2023
1 parent 089f5ae commit 72d5eaa
Show file tree
Hide file tree
Showing 2 changed files with 142 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,29 @@ func (s *CloudReconcilerTestSuite) TestAppliedIntentsUpload() {
server := "test-server"
_, err := s.AddIntents(intentsObjectName, clientName, []otterizev1alpha2.Intent{{
Name: server,
Type: otterizev1alpha2.IntentTypeKafka,
Topics: []otterizev1alpha2.KafkaTopic{{
Name: "test-topic",
Operations: []otterizev1alpha2.KafkaOperation{
otterizev1alpha2.KafkaOperationCreate,
otterizev1alpha2.KafkaOperationDelete,
},
}},
},
})
s.Require().NoError(err)
s.Require().True(s.Mgr.GetCache().WaitForCacheSync(context.Background()))

expectedIntent := intentInput(clientName, s.TestNamespace, server, s.TestNamespace)

expectedIntent.Type = lo.ToPtr(graphqlclient.IntentTypeKafka)
kafkaConfigInput := graphqlclient.KafkaConfigInput{
Name: lo.ToPtr("test-topic"),
Operations: []*graphqlclient.KafkaOperation{
lo.ToPtr(graphqlclient.KafkaOperationDelete),
lo.ToPtr(graphqlclient.KafkaOperationCreate),
},
}
expectedIntent.Topics = []*graphqlclient.KafkaConfigInput{&kafkaConfigInput}
expectedIntents := []graphqlclient.IntentInput{expectedIntent}
expectedNamespace := lo.ToPtr(s.TestNamespace)
s.mockCloudClient.EXPECT().ReportAppliedIntents(gomock.Any(), expectedNamespace, GetMatcher(expectedIntents)).Return(nil).Times(1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,97 @@ package otterizecloud

import (
"fmt"
"github.com/google/go-cmp/cmp"
"github.com/otterize/intents-operator/src/shared/otterizecloud/graphqlclient"
"golang.org/x/exp/constraints"
"sort"
)

// IntentsMatcher Implement gomock.Matcher interface for []graphqlclient.IntentInput
type IntentsMatcher struct {
expected []graphqlclient.IntentInput
}

func NilCompare[T comparable](a *T, b *T) bool {
return (a == nil && b == nil) || (a != nil && b != nil && *a == *b)
func NilCompare[T constraints.Ordered](a *T, b *T) int {
if a == nil && b == nil {
return 0
}
if a == nil {
return -1
}
if b == nil {
return 1
}
if *a > *b {
return 1
}
if *a < *b {
return -1
}
return 0
}

func compareIntentInput(a graphqlclient.IntentInput, b graphqlclient.IntentInput) bool {
return NilCompare(a.ClientName, b.ClientName) &&
NilCompare(a.Namespace, b.Namespace) &&
NilCompare(a.ServerName, b.ServerName) &&
NilCompare(a.ServerNamespace, b.ServerNamespace)
func intentInputSort(intents []graphqlclient.IntentInput) {
for _, intent := range intents {
if intent.Type == nil {
continue
}

switch *intent.Type {
case graphqlclient.IntentTypeKafka:
for _, topic := range intent.Topics {
sort.Slice(topic.Operations, func(i, j int) bool {
return NilCompare(topic.Operations[i], topic.Operations[j]) < 0
})
}
sort.Slice(intent.Topics, func(i, j int) bool {
res := NilCompare(intent.Topics[i].Name, intent.Topics[j].Name)
if res != 0 {
return res < 0
}

return len(intent.Topics[i].Operations) < len(intent.Topics[j].Operations)
})
case graphqlclient.IntentTypeHttp:
sort.Slice(intent.Resources, func(i, j int) bool {
res := NilCompare(intent.Resources[i].Path, intent.Resources[j].Path)
if res != 0 {
return res < 0
}
return NilCompare(intent.Resources[i].Method, intent.Resources[j].Method) < 0
})
}
}
sort.Slice(intents, func(i, j int) bool {
res := NilCompare(intents[i].Namespace, intents[j].Namespace)
if res != 0 {
return res < 0
}
res = NilCompare(intents[i].ClientName, intents[j].ClientName)
if res != 0 {
return res < 0
}
res = NilCompare(intents[i].ServerName, intents[j].ServerName)
if res != 0 {
return res < 0
}
res = NilCompare(intents[i].ServerNamespace, intents[j].ServerNamespace)
if res != 0 {
return res < 0
}
res = NilCompare(intents[i].Type, intents[j].Type)
if res != 0 {
return res < 0
}
switch *intents[i].Type {
case graphqlclient.IntentTypeKafka:
return len(intents[i].Topics) < len(intents[j].Topics)
case graphqlclient.IntentTypeHttp:
return len(intents[i].Resources) < len(intents[j].Resources)
default:
panic("Unimplemented intent type")
}
})
}

func (m IntentsMatcher) Matches(x interface{}) bool {
Expand All @@ -32,35 +106,53 @@ func (m IntentsMatcher) Matches(x interface{}) bool {
matched := getMatcherFromPtrArr(actualIntentsPtr)
actualIntents := matched.expected
expectedIntents := m.expected
if len(actualIntents) != len(expectedIntents) {
return false
}

for _, expected := range expectedIntents {
found := false
for _, actual := range actualIntents {
if compareIntentInput(actual, expected) {
found = true
break
}
}
if !found {
return false
}
intentInputSort(expectedIntents)
intentInputSort(actualIntents)

diff := cmp.Diff(expectedIntents, actualIntents)
if diff != "" {
fmt.Println(diff)
}
return true
return cmp.Equal(expectedIntents, actualIntents)
}

func (m IntentsMatcher) String() string {
return prettyPrint(m)
}

func printKafkaConfigInput(input []*graphqlclient.KafkaConfigInput) string {
var result string
for _, item := range input {
var name, operations string
if item == nil {
result += "nil"
continue
}
if item.Name != nil {
name = *item.Name
} else {
name = "nil"
}

for _, op := range item.Operations {
if op == nil {
operations += "nil,"
continue
}
operations += fmt.Sprintf("%s,", *op)
}
result += fmt.Sprintf("KafkaConfigInput{Name: %s, Operations: %v}", name, operations)
}
return result
}

func prettyPrint(m IntentsMatcher) string {
expected := m.expected
var result string
itemFormat := "IntentInput{ClientName: %s, ServerName: %s, Namespace: %s, ServerNamespace: %s, Type: %s},"
itemFormat := "IntentInput{ClientName: %s, ServerName: %s, Namespace: %s, ServerNamespace: %s, Type: %s, Resource: %s},"
for _, intent := range expected {
var clientName, namespace, serverName, serverNamespace, intentType string
var clientName, namespace, serverName, serverNamespace, intentType, resource string
if intent.ClientName != nil {
clientName = *intent.ClientName
}
Expand All @@ -75,8 +167,16 @@ func prettyPrint(m IntentsMatcher) string {
}
if intent.Type != nil {
intentType = *intent.ServerNamespace
switch *intent.Type {
case graphqlclient.IntentTypeKafka:
resource = printKafkaConfigInput(intent.Topics)
case graphqlclient.IntentTypeHttp:
// Fallthrough
default:
resource = "Unimplemented type"
}
}
result += fmt.Sprintf(itemFormat, clientName, serverName, namespace, serverNamespace, intentType)
result += fmt.Sprintf(itemFormat, clientName, serverName, namespace, serverNamespace, intentType, resource)
}

return result
Expand Down

0 comments on commit 72d5eaa

Please sign in to comment.