Skip to content

Commit

Permalink
Add unit tests for traverser.maxItemsSync
Browse files Browse the repository at this point in the history
Signed-off-by: Alper Rifat Ulucinar <ulucinar@users.noreply.github.com>
  • Loading branch information
ulucinar committed Jun 12, 2024
1 parent f4f87ba commit 361331e
Show file tree
Hide file tree
Showing 4 changed files with 333 additions and 4 deletions.
186 changes: 185 additions & 1 deletion pkg/config/resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const (
provider = "ACoolProvider"
)

func TestTagger_Initialize(t *testing.T) {
func TestTaggerInitialize(t *testing.T) {
errBoom := errors.New("boom")

type args struct {
Expand Down Expand Up @@ -112,3 +112,187 @@ func TestSetExternalTagsWithPaved(t *testing.T) {
})
}
}

func TestAddSingletonListConversion(t *testing.T) {
type args struct {
r func() *Resource
tfPath string
crdPath string
}
type want struct {
r func() *Resource
}
cases := map[string]struct {
reason string
args
want
}{
"AddNonWildcardTFPath": {
reason: "A non-wildcard TF path of a singleton list should successfully be configured to be converted into an embedded object.",
args: args{
tfPath: "singleton_list",
crdPath: "singletonList",
r: func() *Resource {
r := DefaultResource("test_resource", nil, nil, nil)
r.AddSingletonListConversion("singleton_list", "singletonList")
return r
},
},
want: want{
r: func() *Resource {
r := DefaultResource("test_resource", nil, nil, nil)
r.SchemaElementOptions = SchemaElementOptions{}
r.SchemaElementOptions["singleton_list"] = &SchemaElementOption{
EmbeddedObject: true,
}
r.listConversionPaths["singleton_list"] = "singletonList"
return r
},
},
},
"AddWildcardTFPath": {
reason: "A wildcard TF path of a singleton list should successfully be configured to be converted into an embedded object.",
args: args{
tfPath: "parent[*].singleton_list",
crdPath: "parent[*].singletonList",
r: func() *Resource {
r := DefaultResource("test_resource", nil, nil, nil)
r.AddSingletonListConversion("parent[*].singleton_list", "parent[*].singletonList")
return r
},
},
want: want{
r: func() *Resource {
r := DefaultResource("test_resource", nil, nil, nil)
r.SchemaElementOptions = SchemaElementOptions{}
r.SchemaElementOptions["parent.singleton_list"] = &SchemaElementOption{
EmbeddedObject: true,
}
r.listConversionPaths["parent[*].singleton_list"] = "parent[*].singletonList"
return r
},
},
},
"AddIndexedTFPath": {
reason: "An indexed TF path of a singleton list should successfully be configured to be converted into an embedded object.",
args: args{
tfPath: "parent[0].singleton_list",
crdPath: "parent[0].singletonList",
r: func() *Resource {
r := DefaultResource("test_resource", nil, nil, nil)
r.AddSingletonListConversion("parent[0].singleton_list", "parent[0].singletonList")
return r
},
},
want: want{
r: func() *Resource {
r := DefaultResource("test_resource", nil, nil, nil)
r.SchemaElementOptions = SchemaElementOptions{}
r.SchemaElementOptions["parent.singleton_list"] = &SchemaElementOption{
EmbeddedObject: true,
}
r.listConversionPaths["parent[0].singleton_list"] = "parent[0].singletonList"
return r
},
},
},
}
for n, tc := range cases {
t.Run(n, func(t *testing.T) {
r := tc.args.r()
r.AddSingletonListConversion(tc.args.tfPath, tc.args.crdPath)
wantR := tc.want.r()
if diff := cmp.Diff(wantR.listConversionPaths, r.listConversionPaths); diff != "" {
t.Errorf("%s\nAddSingletonListConversion(tfPath): -wantConversionPaths, +gotConversionPaths: \n%s", tc.reason, diff)
}
if diff := cmp.Diff(wantR.SchemaElementOptions, r.SchemaElementOptions); diff != "" {
t.Errorf("%s\nAddSingletonListConversion(tfPath): -wantSchemaElementOptions, +gotSchemaElementOptions: \n%s", tc.reason, diff)
}
})
}
}

func TestRemoveSingletonListConversion(t *testing.T) {
type args struct {
r func() *Resource
tfPath string
}
type want struct {
removed bool
r func() *Resource
}
cases := map[string]struct {
reason string
args
want
}{
"RemoveWildcardListConversion": {
reason: "An existing wildcard list conversion can successfully be removed.",
args: args{
tfPath: "parent[*].singleton_list",
r: func() *Resource {
r := DefaultResource("test_resource", nil, nil, nil)
r.AddSingletonListConversion("parent[*].singleton_list", "parent[*].singletonList")
return r
},
},
want: want{
removed: true,
r: func() *Resource {
r := DefaultResource("test_resource", nil, nil, nil)
return r
},
},
},
"RemoveIndexedListConversion": {
reason: "An existing indexed list conversion can successfully be removed.",
args: args{
tfPath: "parent[0].singleton_list",
r: func() *Resource {
r := DefaultResource("test_resource", nil, nil, nil)
r.AddSingletonListConversion("parent[0].singleton_list", "parent[0].singletonList")
return r
},
},
want: want{
removed: true,
r: func() *Resource {
r := DefaultResource("test_resource", nil, nil, nil)
return r
},
},
},
"NonExistingListConversion": {
reason: "A list conversion path that does not exist cannot be removed.",
args: args{
tfPath: "non-existent",
r: func() *Resource {
r := DefaultResource("test_resource", nil, nil, nil)
r.AddSingletonListConversion("parent[*].singleton_list", "parent[*].singletonList")
return r
},
},
want: want{
removed: false,
r: func() *Resource {
r := DefaultResource("test_resource", nil, nil, nil)
r.AddSingletonListConversion("parent[*].singleton_list", "parent[*].singletonList")
return r
},
},
},
}
for n, tc := range cases {
t.Run(n, func(t *testing.T) {
r := tc.args.r()
got := r.RemoveSingletonListConversion(tc.args.tfPath)
if diff := cmp.Diff(tc.want.removed, got); diff != "" {
t.Errorf("%s\nRemoveSingletonListConversion(tfPath): -wantRemoved, +gotRemoved: \n%s", tc.reason, diff)
}

if diff := cmp.Diff(tc.want.r().listConversionPaths, r.listConversionPaths); diff != "" {
t.Errorf("%s\nRemoveSingletonListConversion(tfPath): -wantConversionPaths, +gotConversionPaths: \n%s", tc.reason, diff)
}
})
}
}
5 changes: 4 additions & 1 deletion pkg/config/schema_conversions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,10 @@ func TestSingletonListEmbedder(t *testing.T) {
t.Run(n, func(t *testing.T) {
e := &SingletonListEmbedder{}
r := DefaultResource(tt.args.name, tt.args.resource, nil, nil)
err := TraverseSchemas(tt.args.name, r, e)
s := ResourceSchema{
tt.args.name: r,
}
err := s.TraverseTFSchemas(e)
if diff := cmp.Diff(tt.want.err, err, test.EquateErrors()); diff != "" {
t.Fatalf("\n%s\ntraverseSchemas(name, schema, ...): -wantErr, +gotErr:\n%s", tt.reason, diff)
}
Expand Down
142 changes: 142 additions & 0 deletions pkg/schema/traverser/maxitemssync_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
// SPDX-FileCopyrightText: 2024 The Crossplane Authors <https://crossplane.io>
//
// SPDX-License-Identifier: Apache-2.0

package traverser

import (
"testing"

"github.com/crossplane/crossplane-runtime/pkg/test"
"github.com/google/go-cmp/cmp"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func TestMaxItemsSync(t *testing.T) {
type args struct {
srcSchema TFResourceSchema
targetSchema TFResourceSchema
}
type want struct {
targetSchema TFResourceSchema
err error
}
cases := map[string]struct {
reason string
args
want
}{
"SyncMaxItemsConstraints": {
reason: `maxItemsSync traverser can successfully sync the "MaxItems = 1" constraints from the source schema to the target schema.`,
args: args{
srcSchema: map[string]*schema.Resource{
"test_resource": {
Schema: map[string]*schema.Schema{
"argument": {
MaxItems: 1,
Type: schema.TypeList,
Elem: &schema.Resource{},
},
},
},
},
targetSchema: map[string]*schema.Resource{
"test_resource": {
Schema: map[string]*schema.Schema{
"argument": {
MaxItems: 0,
Type: schema.TypeList,
Elem: &schema.Resource{},
},
},
},
},
},
want: want{
targetSchema: map[string]*schema.Resource{
"test_resource": {
Schema: map[string]*schema.Schema{
"argument": {
MaxItems: 1,
Type: schema.TypeList,
Elem: &schema.Resource{},
},
},
},
},
},
},
"NoSyncMaxItems": {
reason: "If the MaxItems constraint is greater than 1, then maxItemsSync should not sync the constraint.",
args: args{
srcSchema: map[string]*schema.Resource{
"test_resource": {
Schema: map[string]*schema.Schema{
"argument": {
MaxItems: 2,
Type: schema.TypeList,
Elem: &schema.Resource{},
},
},
},
},
targetSchema: map[string]*schema.Resource{
"test_resource": {
Schema: map[string]*schema.Schema{
"argument": {
MaxItems: 0,
Type: schema.TypeList,
Elem: &schema.Resource{},
},
},
},
},
},
want: want{
targetSchema: map[string]*schema.Resource{
"test_resource": {
Schema: map[string]*schema.Schema{
"argument": {
MaxItems: 0,
Type: schema.TypeList,
Elem: &schema.Resource{},
},
},
},
},
},
},
}
for n, tc := range cases {
t.Run(n, func(t *testing.T) {
copySrc := copySchema(tc.args.srcSchema)
got := tc.args.srcSchema.Traverse(NewMaxItemsSync(tc.args.targetSchema))
if diff := cmp.Diff(tc.want.err, got, test.EquateErrors()); diff != "" {
t.Errorf("%s\nMaxItemsSync: -wantErr, +gotErr: \n%s", tc.reason, diff)
}
if diff := cmp.Diff(tc.want.err, got, test.EquateErrors()); diff != "" {
t.Errorf("%s\nMaxItemsSync: -wantErr, +gotErr: \n%s", tc.reason, diff)
}
if diff := cmp.Diff(copySrc, tc.srcSchema); diff != "" {
t.Errorf("%s\nMaxItemsSync: -wantSourceSchema, +gotSourceSchema: \n%s", tc.reason, diff)
}
if diff := cmp.Diff(tc.want.targetSchema, tc.args.targetSchema); diff != "" {
t.Errorf("%s\nMaxItemsSync: -wantTargetSchema, +gotTargetSchema: \n%s", tc.reason, diff)
}
})
}
}

func copySchema(s TFResourceSchema) TFResourceSchema {
result := make(TFResourceSchema)
for k, v := range s {
c := *v
for n, s := range v.Schema {
// not a deep copy but sufficient to check the schema constraints
s := *s
c.Schema[n] = &s
}
result[k] = &c
}
return result
}
4 changes: 2 additions & 2 deletions pkg/schema/traverser/traverse.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,9 @@ func (n NoopTraverser) VisitResource(*ResourceNode) error {
// TFResourceSchema represents a provider's Terraform resource schema.
type TFResourceSchema map[string]*schema.Resource

// TraverseTFSchemas traverses the receiver schema using the specified
// Traverse traverses the receiver schema using the specified
// visitors. Reports any errors encountered by the visitors.
func (s TFResourceSchema) TraverseTFSchemas(visitors ...SchemaTraverser) error {
func (s TFResourceSchema) Traverse(visitors ...SchemaTraverser) error {
for n, r := range s {
if err := Traverse(n, r, visitors...); err != nil {
return errors.Wrapf(err, "failed to traverse the schema of the Terraform resource with name %q", n)
Expand Down

0 comments on commit 361331e

Please sign in to comment.