Skip to content

Commit

Permalink
Merge pull request #8 from sergenyalcin/no-fork-unit-tests
Browse files Browse the repository at this point in the history
Add unit tests for no-fork arch
  • Loading branch information
sergenyalcin authored Nov 13, 2023
2 parents 9cb226a + 7e3c1a3 commit 9fd3224
Show file tree
Hide file tree
Showing 5 changed files with 704 additions and 4 deletions.
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ require (
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/go-errors/errors v1.4.2 // indirect
github.com/go-logr/logr v1.2.4 // indirect
github.com/go-logr/zapr v1.2.4 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/swag v0.22.3 // indirect
Expand Down Expand Up @@ -104,6 +105,8 @@ require (
github.com/vmihailenco/tagparser v0.1.1 // indirect
github.com/xlab/treeprint v1.2.0 // indirect
go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.26.0 // indirect
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/oauth2 v0.10.0 // indirect
Expand Down
9 changes: 9 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/
github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec=
github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw=
github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
Expand Down Expand Up @@ -108,6 +109,7 @@ github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbV
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/zapr v1.2.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo=
github.com/go-logr/zapr v1.2.4/go.mod h1:FyHWQIzQORZ0QVE1BtVHv3cKtNLuXsbNLtpuhNapBOA=
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
Expand Down Expand Up @@ -372,9 +374,15 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.starlark.net v0.0.0-20230525235612-a134d8f9ddca h1:VdD38733bfYv5tUZwEIskMM93VanwNIi5bIKnDrJdEY=
go.starlark.net v0.0.0-20230525235612-a134d8f9ddca/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
Expand Down Expand Up @@ -599,6 +607,7 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
Expand Down
336 changes: 336 additions & 0 deletions pkg/controller/external_async_nofork_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,336 @@
// SPDX-FileCopyrightText: 2023 The Crossplane Authors <https://crossplane.io>
//
// SPDX-License-Identifier: Apache-2.0

package controller

import (
"context"
"testing"

"github.com/crossplane/crossplane-runtime/pkg/reconciler/managed"
xpresource "github.com/crossplane/crossplane-runtime/pkg/resource"
"github.com/crossplane/crossplane-runtime/pkg/test"
"github.com/google/go-cmp/cmp"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
tf "github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
"sigs.k8s.io/controller-runtime/pkg/client"

"github.com/crossplane/upjet/pkg/config"
"github.com/crossplane/upjet/pkg/resource/fake"
"github.com/crossplane/upjet/pkg/terraform"
)

var (
cfgAsync = &config.Resource{
TerraformResource: &schema.Resource{
Timeouts: &schema.ResourceTimeout{
Create: &timeout,
Read: &timeout,
Update: &timeout,
Delete: &timeout,
},
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
},
"id": {
Type: schema.TypeString,
Computed: true,
Required: false,
},
"map": {
Type: schema.TypeMap,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"list": {
Type: schema.TypeList,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
},
},
ExternalName: config.IdentifierFromProvider,
Sensitive: config.Sensitive{AdditionalConnectionDetailsFn: func(attr map[string]any) (map[string][]byte, error) {
return nil, nil
}},
}
objAsync = &fake.Terraformed{
Parameterizable: fake.Parameterizable{
Parameters: map[string]any{
"name": "example",
"map": map[string]any{
"key": "value",
},
"list": []any{"elem1", "elem2"},
},
},
Observable: fake.Observable{
Observation: map[string]any{},
},
}
)

func prepareNoForkAsyncExternal(r Resource, cfg *config.Resource, fns CallbackFns) *noForkAsyncExternal {
schemaBlock := cfg.TerraformResource.CoreConfigSchema()
rawConfig, err := schema.JSONMapToStateValue(map[string]any{"name": "example"}, schemaBlock)
if err != nil {
panic(err)
}
return &noForkAsyncExternal{
noForkExternal: &noForkExternal{
ts: terraform.Setup{},
resourceSchema: r,
config: cfg,
params: map[string]any{
"name": "example",
},
rawConfig: rawConfig,
logger: logTest,
opTracker: NewAsyncTracker(),
},
callback: fns,
}
}

func TestAsyncNoForkConnect(t *testing.T) {
type args struct {
setupFn terraform.SetupFn
cfg *config.Resource
ots *OperationTrackerStore
obj xpresource.Managed
}
type want struct {
err error
}
cases := map[string]struct {
args
want
}{
"Successful": {
args: args{
setupFn: func(_ context.Context, _ client.Client, _ xpresource.Managed) (terraform.Setup, error) {
return terraform.Setup{}, nil
},
cfg: cfgAsync,
obj: objAsync,
ots: ots,
},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
c := NewNoForkAsyncConnector(nil, tc.args.ots, tc.args.setupFn, tc.args.cfg, WithNoForkAsyncLogger(logTest))
_, err := c.Connect(context.TODO(), tc.args.obj)
if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" {
t.Errorf("\n%s\nConnect(...): -want error, +got error:\n", diff)
}
})
}
}

func TestAsyncNoForkObserve(t *testing.T) {
type args struct {
r Resource
cfg *config.Resource
obj xpresource.Managed
}
type want struct {
obs managed.ExternalObservation
err error
}
cases := map[string]struct {
args
want
}{
"NotExists": {
args: args{
r: mockResource{
RefreshWithoutUpgradeFn: func(ctx context.Context, s *tf.InstanceState, meta interface{}) (*tf.InstanceState, diag.Diagnostics) {
return nil, nil
},
},
cfg: cfgAsync,
obj: objAsync,
},
want: want{
obs: managed.ExternalObservation{
ResourceExists: false,
ResourceUpToDate: false,
ResourceLateInitialized: false,
ConnectionDetails: nil,
Diff: "",
},
},
},
"UpToDate": {
args: args{
r: mockResource{
RefreshWithoutUpgradeFn: func(ctx context.Context, s *tf.InstanceState, meta interface{}) (*tf.InstanceState, diag.Diagnostics) {
return &tf.InstanceState{ID: "example-id", Attributes: map[string]string{"name": "example"}}, nil
},
},
cfg: cfgAsync,
obj: objAsync,
},
want: want{
obs: managed.ExternalObservation{
ResourceExists: true,
ResourceUpToDate: true,
ResourceLateInitialized: true,
ConnectionDetails: nil,
Diff: "",
},
},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
noForkAsyncExternal := prepareNoForkAsyncExternal(tc.args.r, tc.args.cfg, CallbackFns{})
observation, err := noForkAsyncExternal.Observe(context.TODO(), tc.args.obj)
if diff := cmp.Diff(tc.want.obs, observation); diff != "" {
t.Errorf("\n%s\nObserve(...): -want observation, +got observation:\n", diff)
}
if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" {
t.Errorf("\n%s\nConnect(...): -want error, +got error:\n", diff)
}
})
}
}

func TestAsyncNoForkCreate(t *testing.T) {
type args struct {
r Resource
cfg *config.Resource
obj xpresource.Managed
fns CallbackFns
}
type want struct {
err error
}
cases := map[string]struct {
args
want
}{
"Successful": {
args: args{
r: mockResource{
ApplyFn: func(ctx context.Context, s *tf.InstanceState, d *tf.InstanceDiff, meta interface{}) (*tf.InstanceState, diag.Diagnostics) {
return &tf.InstanceState{ID: "example-id"}, nil
},
},
cfg: cfgAsync,
obj: objAsync,
fns: CallbackFns{
CreateFn: func(s string) terraform.CallbackFn {
return func(err error, ctx context.Context) error {
return nil
}
},
},
},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
noForkAsyncExternal := prepareNoForkAsyncExternal(tc.args.r, tc.args.cfg, tc.args.fns)
_, err := noForkAsyncExternal.Create(context.TODO(), tc.args.obj)
if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" {
t.Errorf("\n%s\nConnect(...): -want error, +got error:\n", diff)
}
})
}
}

func TestAsyncNoForkUpdate(t *testing.T) {
type args struct {
r Resource
cfg *config.Resource
obj xpresource.Managed
fns CallbackFns
}
type want struct {
err error
}
cases := map[string]struct {
args
want
}{
"Successful": {
args: args{
r: mockResource{
ApplyFn: func(ctx context.Context, s *tf.InstanceState, d *tf.InstanceDiff, meta interface{}) (*tf.InstanceState, diag.Diagnostics) {
return &tf.InstanceState{ID: "example-id"}, nil
},
},
cfg: cfgAsync,
obj: objAsync,
fns: CallbackFns{
UpdateFn: func(s string) terraform.CallbackFn {
return func(err error, ctx context.Context) error {
return nil
}
},
},
},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
noForkAsyncExternal := prepareNoForkAsyncExternal(tc.args.r, tc.args.cfg, tc.args.fns)
_, err := noForkAsyncExternal.Update(context.TODO(), tc.args.obj)
if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" {
t.Errorf("\n%s\nConnect(...): -want error, +got error:\n", diff)
}
})
}
}

func TestAsyncNoForkDelete(t *testing.T) {
type args struct {
r Resource
cfg *config.Resource
obj xpresource.Managed
fns CallbackFns
}
type want struct {
err error
}
cases := map[string]struct {
args
want
}{
"Successful": {
args: args{
r: mockResource{
ApplyFn: func(ctx context.Context, s *tf.InstanceState, d *tf.InstanceDiff, meta interface{}) (*tf.InstanceState, diag.Diagnostics) {
return &tf.InstanceState{ID: "example-id"}, nil
},
},
cfg: cfgAsync,
obj: objAsync,
fns: CallbackFns{
DestroyFn: func(s string) terraform.CallbackFn {
return func(err error, ctx context.Context) error {
return nil
}
},
},
},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
noForkAsyncExternal := prepareNoForkAsyncExternal(tc.args.r, tc.args.cfg, tc.args.fns)
err := noForkAsyncExternal.Delete(context.TODO(), tc.args.obj)
if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" {
t.Errorf("\n%s\nConnect(...): -want error, +got error:\n", diff)
}
})
}
}
Loading

0 comments on commit 9fd3224

Please sign in to comment.