Skip to content

Commit 661d6ba

Browse files
authored
Add unit tests to protection primitives (#1064)
1 parent c051b89 commit 661d6ba

File tree

2 files changed

+191
-0
lines changed

2 files changed

+191
-0
lines changed

pkg/controller/customresource/customresource.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,3 +135,13 @@ func ReconciliationShouldBeSkipped(resource mdbv1.AtlasCustomResource) bool {
135135
}
136136
return false
137137
}
138+
139+
// SetAnnotation sets an annotation in resource while respecting the rest of annotations.
140+
func SetAnnotation(resource mdbv1.AtlasCustomResource, key, value string) {
141+
annot := resource.GetAnnotations()
142+
if annot == nil {
143+
annot = map[string]string{}
144+
}
145+
annot[key] = value
146+
resource.SetAnnotations(annot)
147+
}
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
package customresource_test
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"testing"
7+
8+
"github.com/stretchr/testify/assert"
9+
"github.com/stretchr/testify/require"
10+
11+
"sigs.k8s.io/controller-runtime/pkg/client/fake"
12+
13+
mdbv1 "github.com/mongodb/mongodb-atlas-kubernetes/pkg/api/v1"
14+
"github.com/mongodb/mongodb-atlas-kubernetes/pkg/controller/customresource"
15+
)
16+
17+
func sampleResource() *mdbv1.AtlasDatabaseUser {
18+
return &mdbv1.AtlasDatabaseUser{
19+
Spec: mdbv1.AtlasDatabaseUserSpec{},
20+
}
21+
}
22+
23+
func taggedResource(tag, value string) *mdbv1.AtlasDatabaseUser {
24+
dbUser := sampleResource()
25+
annot := map[string]string{}
26+
annot[tag] = value
27+
dbUser.SetAnnotations(annot)
28+
return dbUser
29+
}
30+
31+
func testOpChecker(reply bool) customresource.OperatorChecker {
32+
return func(resource mdbv1.AtlasCustomResource) (bool, error) {
33+
return reply, nil
34+
}
35+
}
36+
37+
func testAtlasChecker(reply bool) customresource.AtlasChecker {
38+
return func(resource mdbv1.AtlasCustomResource) (bool, error) {
39+
return reply, nil
40+
}
41+
}
42+
43+
var ErrOpChecker = fmt.Errorf("operator checker failed")
44+
45+
func failedOpChecker(err error) customresource.OperatorChecker {
46+
return func(resource mdbv1.AtlasCustomResource) (bool, error) {
47+
return false, err
48+
}
49+
}
50+
51+
var ErrAtlasChecker = fmt.Errorf("atlas checker failed")
52+
53+
func failedAtlasChecker(err error) customresource.AtlasChecker {
54+
return func(resource mdbv1.AtlasCustomResource) (bool, error) {
55+
return false, err
56+
}
57+
}
58+
59+
func TestWithoutProtectionIsOwned(t *testing.T) {
60+
owned, err := customresource.IsOwner(sampleResource(), false, nil, nil)
61+
assert.NoError(t, err)
62+
assert.Equal(t, owned, true)
63+
}
64+
65+
func TestProtected(t *testing.T) {
66+
tests := []struct {
67+
title string
68+
opChecker customresource.OperatorChecker
69+
atlasChecker customresource.AtlasChecker
70+
expectedOwned bool
71+
}{
72+
{"managed is owned", testOpChecker(true), nil, true},
73+
{"unmanaged but not in Atlas is owned", testOpChecker(false), testAtlasChecker(false), true},
74+
{"unmanaged but in Atlas is NOT owned", testOpChecker(false), testAtlasChecker(true), false},
75+
}
76+
for _, tc := range tests {
77+
t.Run(fmt.Sprintf("Protected and %s", tc.title), func(t *testing.T) {
78+
owned, err := customresource.IsOwner(sampleResource(), true, tc.opChecker, tc.atlasChecker)
79+
assert.NoError(t, err)
80+
assert.Equal(t, tc.expectedOwned, owned)
81+
})
82+
}
83+
}
84+
85+
func TestProtectedFailures(t *testing.T) {
86+
tests := []struct {
87+
title string
88+
opChecker customresource.OperatorChecker
89+
atlasChecker customresource.AtlasChecker
90+
expectedFailure error
91+
}{
92+
{"When all checkers fail, operator checker fails first", failedOpChecker(ErrOpChecker), failedAtlasChecker(ErrAtlasChecker), ErrOpChecker},
93+
{"When unamanaged and atlas checker fails we get that its failure", testOpChecker(false), failedAtlasChecker(ErrAtlasChecker), ErrAtlasChecker},
94+
}
95+
for _, tc := range tests {
96+
t.Run(tc.title, func(t *testing.T) {
97+
_, err := customresource.IsOwner(sampleResource(), true, tc.opChecker, tc.atlasChecker)
98+
assert.Equal(t, tc.expectedFailure, err)
99+
})
100+
}
101+
}
102+
103+
func TestIsResourceProtected(t *testing.T) {
104+
tests := []struct {
105+
title string
106+
protectionFlag bool
107+
resource mdbv1.AtlasCustomResource
108+
expectedProtected bool
109+
}{
110+
{"Resource without tags with the flag set is protected", true, sampleResource(), true},
111+
{"Resource without tags with the flag unset isn't protected", false, sampleResource(), false},
112+
{
113+
"Resource with keep tag is protected",
114+
false,
115+
taggedResource(customresource.ResourcePolicyAnnotation, customresource.ResourcePolicyKeep),
116+
true,
117+
},
118+
{
119+
"Resource with delete tag and protected flag set is NOT protected",
120+
true,
121+
taggedResource(customresource.ResourcePolicyAnnotation, customresource.ResourcePolicyDelete),
122+
false,
123+
},
124+
{
125+
"Resource with delete tag and protected flag unset isn't protected",
126+
false,
127+
taggedResource(customresource.ResourcePolicyAnnotation, customresource.ResourcePolicyDelete),
128+
false,
129+
},
130+
}
131+
for _, tc := range tests {
132+
t.Run(tc.title, func(t *testing.T) {
133+
assert.Equal(t, tc.expectedProtected, customresource.IsResourceProtected(tc.resource, tc.protectionFlag))
134+
})
135+
}
136+
}
137+
138+
func TestApplyLastConfigApplied(t *testing.T) {
139+
resource := sampleResource()
140+
resource.Spec.Username = "test-user"
141+
142+
// ignore the error due to not configuring the fake client
143+
// we are not checking that, we are only interested on a new annotation in resource
144+
_ = customresource.ApplyLastConfigApplied(context.Background(), resource, fake.NewClientBuilder().Build())
145+
146+
annot := resource.GetAnnotations()
147+
assert.NotEmpty(t, annot)
148+
expectedConfig := `{"projectRef":{"name":"","namespace":""},"roles":null,"username":"test-user"}`
149+
assert.Equal(t, annot[customresource.AnnotationLastAppliedConfiguration], expectedConfig)
150+
}
151+
152+
func TestIsResourceManagedByOperator(t *testing.T) {
153+
testCases := []struct {
154+
title string
155+
annotated bool
156+
expectManaged bool
157+
}{
158+
{
159+
title: "If the resource is annotated with last applied config, then it is managed",
160+
annotated: true,
161+
expectManaged: true,
162+
},
163+
{
164+
title: "If the resource is NOT annotated with last applied config, then it is NOT managed",
165+
annotated: true,
166+
expectManaged: true,
167+
},
168+
}
169+
for _, tc := range testCases {
170+
t.Run(tc.title, func(t *testing.T) {
171+
resource := sampleResource()
172+
if tc.annotated {
173+
customresource.SetAnnotation(resource, customresource.AnnotationLastAppliedConfiguration, "")
174+
}
175+
176+
managed, err := customresource.IsResourceManagedByOperator(resource)
177+
require.NoError(t, err)
178+
assert.Equal(t, tc.expectManaged, managed)
179+
})
180+
}
181+
}

0 commit comments

Comments
 (0)