Skip to content

Commit 7e79591

Browse files
committed
Draft test drive testing flex 2 (no fakeatlas)
1 parent 1e205cd commit 7e79591

File tree

2 files changed

+269
-0
lines changed

2 files changed

+269
-0
lines changed
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
// Copyright 2025 MongoDB Inc
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package flexcluster_test
16+
17+
import (
18+
"context"
19+
"fmt"
20+
"net/http"
21+
"testing"
22+
23+
"github.com/crd2go/crd2go/k8s"
24+
"github.com/stretchr/testify/assert"
25+
"github.com/stretchr/testify/mock"
26+
"github.com/stretchr/testify/require"
27+
"go.mongodb.org/atlas-sdk/v20250312009/admin"
28+
v20250312sdk "go.mongodb.org/atlas-sdk/v20250312009/admin"
29+
"go.mongodb.org/atlas-sdk/v20250312009/mockadmin"
30+
corev1 "k8s.io/api/core/v1"
31+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
32+
"k8s.io/apimachinery/pkg/runtime"
33+
client "sigs.k8s.io/controller-runtime/pkg/client"
34+
"sigs.k8s.io/controller-runtime/pkg/client/fake"
35+
reconcile "sigs.k8s.io/controller-runtime/pkg/reconcile"
36+
37+
crapi "github.com/mongodb/mongodb-atlas-kubernetes/v2/internal/crapi"
38+
"github.com/mongodb/mongodb-atlas-kubernetes/v2/internal/generated/controller/flexcluster"
39+
crds "github.com/mongodb/mongodb-atlas-kubernetes/v2/internal/generated/crds"
40+
akov2generated "github.com/mongodb/mongodb-atlas-kubernetes/v2/internal/nextapi/generated/v1"
41+
"github.com/mongodb/mongodb-atlas-kubernetes/v2/internal/pointer"
42+
ctrlstate "github.com/mongodb/mongodb-atlas-kubernetes/v2/pkg/controller/state"
43+
result "github.com/mongodb/mongodb-atlas-kubernetes/v2/pkg/result"
44+
state "github.com/mongodb/mongodb-atlas-kubernetes/v2/pkg/state"
45+
)
46+
47+
func TestHandleInitial(t *testing.T) {
48+
tr, err := translatorFromEnbeddedCRD("FlexCluster", "v1", "v20250312")
49+
require.NoError(t, err)
50+
51+
deletionProtection := false
52+
53+
for _, tc := range []struct {
54+
title string
55+
flexCluster *akov2generated.FlexCluster
56+
kubeObjects []client.Object
57+
atlasCreateFlexClusterFunc func() (*v20250312sdk.FlexClusterDescription20241113, *http.Response, error)
58+
want ctrlstate.Result
59+
wantErr string
60+
}{
61+
{
62+
title: "create flex cluster",
63+
flexCluster: defaultTestFlexCluster("test-cluster", "ns1"),
64+
kubeObjects: []client.Object{
65+
defaultTestGroup("test-group", "ns1", nil),
66+
},
67+
atlasCreateFlexClusterFunc: func() (*v20250312sdk.FlexClusterDescription20241113, *http.Response, error) {
68+
return &v20250312sdk.FlexClusterDescription20241113{Id: pointer.MakePtr("test-cluster")}, nil, nil
69+
},
70+
want: reenqueueResult(state.StateCreating, "Creating Flex Cluster."),
71+
},
72+
{
73+
title: "missing group",
74+
flexCluster: setGroupRef(defaultTestFlexCluster("test-cluster1", "ns"), "not-found"),
75+
want: ctrlstate.Result{NextState: state.StateInitial},
76+
wantErr: "failed to get dependencies: failed to get group",
77+
},
78+
{
79+
title: "group without id",
80+
flexCluster: setGroupRef(defaultTestFlexCluster("test-cluster", "ns1"), "test-group"),
81+
kubeObjects: []client.Object{
82+
defaultTestGroup("test-group", "ns1", nil),
83+
},
84+
want: ctrlstate.Result{NextState: state.StateInitial},
85+
wantErr: "failed to fetch referenced value groupId",
86+
},
87+
{
88+
title: "atlas API error",
89+
flexCluster: setGroupRef(defaultTestFlexCluster("test-cluster2", "ns3"), "test-group"),
90+
kubeObjects: []client.Object{
91+
defaultTestGroup("test-group", "ns3", pointer.MakePtr("62b6e34b3d91647abb20e7b8")),
92+
},
93+
atlasCreateFlexClusterFunc: func() (*v20250312sdk.FlexClusterDescription20241113, *http.Response, error) {
94+
return nil, nil, fmt.Errorf("atlas API error: cluster creation failed")
95+
},
96+
want: ctrlstate.Result{NextState: state.StateInitial},
97+
wantErr: "failed to create flex cluster",
98+
},
99+
} {
100+
t.Run(tc.title, func(t *testing.T) {
101+
scheme := createTestScheme(t)
102+
allObjects := append([]client.Object{tc.flexCluster}, tc.kubeObjects...)
103+
fakeClient := fake.NewClientBuilder().
104+
WithScheme(scheme).
105+
WithObjects(allObjects...).
106+
WithStatusSubresource(tc.flexCluster).
107+
Build()
108+
kubeClient := newFakeClientWithGVK(fakeClient, scheme)
109+
110+
flexAPI := mockadmin.NewFlexClustersApi(t)
111+
if tc.atlasCreateFlexClusterFunc != nil {
112+
cluster, rsp, err := tc.atlasCreateFlexClusterFunc()
113+
req := admin.CreateFlexClusterApiRequest{ApiService: flexAPI}
114+
flexAPI.EXPECT().CreateFlexClusterWithParams(mock.Anything, mock.Anything).Return(req)
115+
flexAPI.EXPECT().CreateFlexClusterExecute(req).Return(cluster, rsp, err)
116+
}
117+
118+
atlasClient := &v20250312sdk.APIClient{
119+
FlexClustersApi: flexAPI,
120+
}
121+
122+
handler := flexcluster.NewHandlerv20250312(kubeClient, atlasClient, tr, deletionProtection)
123+
124+
ctx := context.Background()
125+
got, err := handler.HandleInitial(ctx, tc.flexCluster)
126+
if tc.wantErr != "" {
127+
assert.Error(t, err)
128+
} else {
129+
require.NoError(t, err)
130+
}
131+
require.Equal(t, tc.want, got)
132+
})
133+
}
134+
}
135+
136+
func setGroupRef(flexCluster *akov2generated.FlexCluster, groupRef string) *akov2generated.FlexCluster {
137+
flexCluster.Spec.V20250312.GroupRef = &k8s.LocalReference{Name: groupRef}
138+
return flexCluster
139+
}
140+
141+
func translatorFromEnbeddedCRD(kind, apiVersion, majorVersion string) (crapi.Translator, error) {
142+
crd, err := crds.EmbeddedCRD(kind)
143+
if err != nil {
144+
return nil, fmt.Errorf("failed to get embedded CRD for %s: %w", kind, err)
145+
}
146+
tr, err := crapi.NewTranslator(crd, apiVersion, majorVersion)
147+
if err != nil {
148+
return nil, fmt.Errorf("failed to get translator for %s: %w", kind, err)
149+
}
150+
return tr, nil
151+
}
152+
153+
// createTestScheme creates a runtime scheme for testing
154+
func createTestScheme(t *testing.T) *runtime.Scheme {
155+
scheme := runtime.NewScheme()
156+
require.NoError(t, akov2generated.AddToScheme(scheme))
157+
require.NoError(t, corev1.AddToScheme(scheme))
158+
return scheme
159+
}
160+
161+
// defaultTestGroup creates a basic Group for testing
162+
func defaultTestGroup(name, namespace string, id *string) *akov2generated.Group {
163+
return &akov2generated.Group{
164+
TypeMeta: metav1.TypeMeta{
165+
Kind: "Group",
166+
APIVersion: "atlas.generated.mongodb.com/v1",
167+
},
168+
ObjectMeta: metav1.ObjectMeta{
169+
Name: name,
170+
Namespace: namespace,
171+
},
172+
Status: akov2generated.GroupStatus{
173+
V20250312: &akov2generated.GroupStatusV20250312{
174+
Id: id,
175+
},
176+
},
177+
}
178+
}
179+
180+
// defaultTestFlexCluster creates a basic FlexCluster for testing
181+
func defaultTestFlexCluster(name, namespace string) *akov2generated.FlexCluster {
182+
return &akov2generated.FlexCluster{
183+
ObjectMeta: metav1.ObjectMeta{
184+
Name: name,
185+
Namespace: namespace,
186+
},
187+
Spec: akov2generated.FlexClusterSpec{
188+
V20250312: &akov2generated.FlexClusterSpecV20250312{
189+
Entry: &akov2generated.FlexClusterSpecV20250312Entry{
190+
Name: name,
191+
ProviderSettings: akov2generated.ProviderSettings{
192+
BackingProviderName: "AWS",
193+
RegionName: "us-east-1",
194+
},
195+
},
196+
},
197+
},
198+
}
199+
}
200+
201+
func reenqueueResult(state state.ResourceState, msg string) ctrlstate.Result {
202+
return ctrlstate.Result{
203+
Result: reconcile.Result{RequeueAfter: result.DefaultRequeueTIme},
204+
NextState: state,
205+
StateMsg: msg,
206+
}
207+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Copyright 2025 MongoDB Inc
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package flexcluster_test
16+
17+
import (
18+
"context"
19+
20+
"k8s.io/apimachinery/pkg/runtime"
21+
"k8s.io/apimachinery/pkg/runtime/schema"
22+
"sigs.k8s.io/controller-runtime/pkg/client"
23+
)
24+
25+
// gvkPreservingClient wraps a client.Client to set GVK on objects after retrieval.
26+
// This is needed because the fake client doesn't preserve TypeMeta when retrieving objects.
27+
type gvkPreservingClient struct {
28+
client.WithWatch
29+
scheme *runtime.Scheme
30+
}
31+
32+
// newFakeClientWithGVK creates a fake client that preserves GVK on retrieved objects.
33+
// The fake client from controller-runtime doesn't preserve TypeMeta by default, so we wrap it
34+
// to automatically set GVK after Get operations using the scheme.
35+
func newFakeClientWithGVK(fakeClient client.WithWatch, scheme *runtime.Scheme) client.Client {
36+
return &gvkPreservingClient{WithWatch: fakeClient, scheme: scheme}
37+
}
38+
39+
func (c *gvkPreservingClient) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error {
40+
if err := c.WithWatch.Get(ctx, key, obj, opts...); err != nil {
41+
return err
42+
}
43+
return setGVK(c.scheme, obj)
44+
}
45+
46+
// setGVK sets the GroupVersionKind on an object using the scheme.
47+
// This is necessary because fake clients don't preserve TypeMeta when retrieving objects.
48+
func setGVK(scheme *runtime.Scheme, obj runtime.Object) error {
49+
gvks, _, err := scheme.ObjectKinds(obj)
50+
if err != nil {
51+
return err
52+
}
53+
if len(gvks) == 0 {
54+
return nil
55+
}
56+
objectKind, ok := obj.(schema.ObjectKind)
57+
if !ok {
58+
return nil
59+
}
60+
objectKind.SetGroupVersionKind(gvks[0])
61+
return nil
62+
}

0 commit comments

Comments
 (0)