Skip to content

Commit 7e63f02

Browse files
committed
Add first sample flex cluster hendler unit test
Signed-off-by: jose.vazquez <[email protected]>
1 parent f3a9380 commit 7e63f02

File tree

1 file changed

+236
-0
lines changed

1 file changed

+236
-0
lines changed
Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
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+
scheme := createTestScheme(t)
49+
tr, err := translatorFromEnbeddedCRD(scheme, "FlexCluster", "v1", "v20250312")
50+
require.NoError(t, err)
51+
52+
deletionProtection := false
53+
54+
for _, tc := range []struct {
55+
title string
56+
flexCluster *akov2generated.FlexCluster
57+
kubeObjects []client.Object
58+
atlasCreateFlexClusterFunc func() (*v20250312sdk.FlexClusterDescription20241113, *http.Response, error)
59+
want ctrlstate.Result
60+
wantErr string
61+
}{
62+
{
63+
title: "create flex cluster",
64+
flexCluster: defaultTestFlexCluster("test-cluster", "ns1"),
65+
kubeObjects: []client.Object{
66+
defaultTestGroup("test-group", "ns1", nil),
67+
},
68+
atlasCreateFlexClusterFunc: func() (*v20250312sdk.FlexClusterDescription20241113, *http.Response, error) {
69+
return &v20250312sdk.FlexClusterDescription20241113{Id: pointer.MakePtr("test-cluster")}, nil, nil
70+
},
71+
want: reenqueueResult(state.StateCreating, "Creating Flex Cluster."),
72+
},
73+
{
74+
title: "corrupt flex type cluster",
75+
flexCluster: withFlexGVK(defaultTestFlexCluster("test-cluster1", "ns"), "bad-kind", "bad-api-version"),
76+
kubeObjects: []client.Object{
77+
defaultTestGroup("some-group", "ns1", nil),
78+
},
79+
want: ctrlstate.Result{NextState: state.StateInitial},
80+
wantErr: "failed to get dependencies: nil flex cluster",
81+
},
82+
{
83+
title: "corrupt api reply",
84+
flexCluster: defaultTestFlexCluster("test-cluster", "ns1"),
85+
kubeObjects: []client.Object{
86+
defaultTestGroup("test-group", "ns1", nil),
87+
},
88+
atlasCreateFlexClusterFunc: func() (*v20250312sdk.FlexClusterDescription20241113, *http.Response, error) {
89+
return nil, nil, nil
90+
},
91+
want: ctrlstate.Result{NextState: state.StateInitial},
92+
wantErr: "failed to get dependencies: nil flex cluster",
93+
},
94+
{
95+
title: "missing group",
96+
flexCluster: setGroupRef(defaultTestFlexCluster("test-cluster1", "ns"), "not-found"),
97+
want: ctrlstate.Result{NextState: state.StateInitial},
98+
wantErr: "failed to get dependencies: failed to get group",
99+
},
100+
{
101+
title: "group without id",
102+
flexCluster: setGroupRef(defaultTestFlexCluster("test-cluster", "ns1"), "test-group"),
103+
kubeObjects: []client.Object{
104+
defaultTestGroup("test-group", "ns1", nil),
105+
},
106+
want: ctrlstate.Result{NextState: state.StateInitial},
107+
wantErr: "failed to fetch referenced value groupId",
108+
},
109+
{
110+
title: "atlas API error",
111+
flexCluster: setGroupRef(defaultTestFlexCluster("test-cluster2", "ns3"), "test-group"),
112+
kubeObjects: []client.Object{
113+
defaultTestGroup("test-group", "ns3", pointer.MakePtr("62b6e34b3d91647abb20e7b8")),
114+
},
115+
atlasCreateFlexClusterFunc: func() (*v20250312sdk.FlexClusterDescription20241113, *http.Response, error) {
116+
return nil, nil, fmt.Errorf("atlas API error: cluster creation failed")
117+
},
118+
want: ctrlstate.Result{NextState: state.StateInitial},
119+
wantErr: "failed to create flex cluster",
120+
},
121+
} {
122+
t.Run(tc.title, func(t *testing.T) {
123+
allObjects := tc.kubeObjects
124+
clientBuilder := fake.NewClientBuilder().WithScheme(scheme)
125+
if tc.flexCluster != nil {
126+
allObjects = append([]client.Object{tc.flexCluster}, tc.kubeObjects...)
127+
clientBuilder = clientBuilder.WithObjects(allObjects...).WithStatusSubresource(tc.flexCluster)
128+
} else {
129+
clientBuilder = clientBuilder.WithObjects(allObjects...)
130+
}
131+
fakeClient := clientBuilder.Build()
132+
133+
flexAPI := mockadmin.NewFlexClustersApi(t)
134+
if tc.atlasCreateFlexClusterFunc != nil {
135+
cluster, rsp, err := tc.atlasCreateFlexClusterFunc()
136+
req := admin.CreateFlexClusterApiRequest{ApiService: flexAPI}
137+
flexAPI.EXPECT().CreateFlexClusterWithParams(mock.Anything, mock.Anything).Return(req)
138+
flexAPI.EXPECT().CreateFlexClusterExecute(req).Return(cluster, rsp, err)
139+
}
140+
141+
atlasClient := &v20250312sdk.APIClient{
142+
FlexClustersApi: flexAPI,
143+
}
144+
145+
handler := flexcluster.NewHandlerv20250312(fakeClient, atlasClient, tr, deletionProtection)
146+
147+
ctx := context.Background()
148+
got, err := handler.HandleInitial(ctx, tc.flexCluster)
149+
if tc.wantErr != "" {
150+
assert.Error(t, err)
151+
} else {
152+
require.NoError(t, err)
153+
}
154+
require.Equal(t, tc.want, got)
155+
})
156+
}
157+
}
158+
159+
func setGroupRef(flexCluster *akov2generated.FlexCluster, groupRef string) *akov2generated.FlexCluster {
160+
flexCluster.Spec.V20250312.GroupRef = &k8s.LocalReference{Name: groupRef}
161+
return flexCluster
162+
}
163+
164+
func translatorFromEnbeddedCRD(scheme *runtime.Scheme, kind, apiVersion, majorVersion string) (crapi.Translator, error) {
165+
crd, err := crds.EmbeddedCRD(kind)
166+
if err != nil {
167+
return nil, fmt.Errorf("failed to get embedded CRD for %s: %w", kind, err)
168+
}
169+
tr, err := crapi.NewTranslator(scheme, crd, apiVersion, majorVersion)
170+
if err != nil {
171+
return nil, fmt.Errorf("failed to get translator for %s: %w", kind, err)
172+
}
173+
return tr, nil
174+
}
175+
176+
// createTestScheme creates a runtime scheme for testing
177+
func createTestScheme(t *testing.T) *runtime.Scheme {
178+
scheme := runtime.NewScheme()
179+
require.NoError(t, akov2generated.AddToScheme(scheme))
180+
require.NoError(t, corev1.AddToScheme(scheme))
181+
return scheme
182+
}
183+
184+
// defaultTestGroup creates a basic Group for testing
185+
func defaultTestGroup(name, namespace string, id *string) *akov2generated.Group {
186+
return &akov2generated.Group{
187+
TypeMeta: metav1.TypeMeta{
188+
Kind: "Group",
189+
APIVersion: "atlas.generated.mongodb.com/v1",
190+
},
191+
ObjectMeta: metav1.ObjectMeta{
192+
Name: name,
193+
Namespace: namespace,
194+
},
195+
Status: akov2generated.GroupStatus{
196+
V20250312: &akov2generated.GroupStatusV20250312{
197+
Id: id,
198+
},
199+
},
200+
}
201+
}
202+
203+
// defaultTestFlexCluster creates a basic FlexCluster for testing
204+
func defaultTestFlexCluster(name, namespace string) *akov2generated.FlexCluster {
205+
return &akov2generated.FlexCluster{
206+
ObjectMeta: metav1.ObjectMeta{
207+
Name: name,
208+
Namespace: namespace,
209+
},
210+
Spec: akov2generated.FlexClusterSpec{
211+
V20250312: &akov2generated.FlexClusterSpecV20250312{
212+
Entry: &akov2generated.FlexClusterSpecV20250312Entry{
213+
Name: name,
214+
ProviderSettings: akov2generated.ProviderSettings{
215+
BackingProviderName: "AWS",
216+
RegionName: "us-east-1",
217+
},
218+
},
219+
},
220+
},
221+
}
222+
}
223+
224+
func withFlexGVK(flexCluster *akov2generated.FlexCluster, kind string, apiVersion string) *akov2generated.FlexCluster {
225+
flexCluster.TypeMeta.Kind = kind
226+
flexCluster.TypeMeta.APIVersion = apiVersion
227+
return flexCluster
228+
}
229+
230+
func reenqueueResult(state state.ResourceState, msg string) ctrlstate.Result {
231+
return ctrlstate.Result{
232+
Result: reconcile.Result{RequeueAfter: result.DefaultRequeueTIme},
233+
NextState: state,
234+
StateMsg: msg,
235+
}
236+
}

0 commit comments

Comments
 (0)