Skip to content

Commit fabc249

Browse files
authored
test : add table-driven tests for PullSecrets with edge case coverage (#1506)
+ Added table-driven tests for the PullSecrets function + Used fake Kubernetes client and runtime scheme setup for isolated testing Signed-off-by: Rohan Kumar <[email protected]>
1 parent f0081c4 commit fabc249

File tree

1 file changed

+205
-0
lines changed

1 file changed

+205
-0
lines changed
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
// Copyright (c) 2019-2025 Red Hat, Inc.
2+
// Licensed under the Apache License, Version 2.0 (the "License");
3+
// you may not use this file except in compliance with the License.
4+
// You may obtain a copy of the License at
5+
//
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
package workspace
15+
16+
import (
17+
"testing"
18+
"time"
19+
20+
"github.com/devfile/devworkspace-operator/pkg/dwerrors"
21+
"github.com/devfile/devworkspace-operator/pkg/infrastructure"
22+
"github.com/devfile/devworkspace-operator/pkg/provision/sync"
23+
"k8s.io/apimachinery/pkg/runtime"
24+
"sigs.k8s.io/controller-runtime/pkg/client"
25+
26+
"github.com/devfile/devworkspace-operator/pkg/constants"
27+
corev1 "k8s.io/api/core/v1"
28+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29+
"sigs.k8s.io/controller-runtime/pkg/client/fake"
30+
31+
"github.com/stretchr/testify/assert"
32+
)
33+
34+
func TestPullSecrets_TableDriven(t *testing.T) {
35+
namespace := "test-ns"
36+
serviceAccountName := "test-sa"
37+
38+
tests := []struct {
39+
name string
40+
objects []client.Object
41+
setupInfra func()
42+
expectedError error
43+
expectedSecrets []string // expected PullSecret names
44+
}{
45+
{
46+
name: "ServiceAccount not found",
47+
objects: []client.Object{}, // No SA created
48+
setupInfra: func() {
49+
infrastructure.InitializeForTesting(infrastructure.Kubernetes)
50+
},
51+
expectedError: nil,
52+
expectedSecrets: []string{},
53+
},
54+
{
55+
name: "Secret with incorrect type is skipped",
56+
objects: []client.Object{
57+
&corev1.ServiceAccount{
58+
TypeMeta: metav1.TypeMeta{
59+
Kind: "ServiceAccount",
60+
APIVersion: corev1.SchemeGroupVersion.String(),
61+
},
62+
ObjectMeta: metav1.ObjectMeta{
63+
Name: serviceAccountName,
64+
Namespace: namespace,
65+
},
66+
},
67+
&corev1.Secret{
68+
TypeMeta: metav1.TypeMeta{
69+
Kind: "Secret",
70+
APIVersion: corev1.SchemeGroupVersion.String(),
71+
},
72+
ObjectMeta: metav1.ObjectMeta{
73+
Name: "bad-secret",
74+
Namespace: namespace,
75+
Labels: map[string]string{
76+
constants.DevWorkspacePullSecretLabel: "true",
77+
},
78+
},
79+
Type: corev1.SecretTypeOpaque, // Not a docker config type
80+
},
81+
},
82+
setupInfra: func() {
83+
infrastructure.InitializeForTesting(infrastructure.Kubernetes)
84+
},
85+
expectedError: nil,
86+
expectedSecrets: []string{},
87+
},
88+
{
89+
name: "RetryError when OpenShift SA is too new with no secrets",
90+
objects: []client.Object{
91+
&corev1.ServiceAccount{
92+
TypeMeta: metav1.TypeMeta{
93+
Kind: "ServiceAccount",
94+
APIVersion: corev1.SchemeGroupVersion.String(),
95+
},
96+
ObjectMeta: metav1.ObjectMeta{
97+
Name: serviceAccountName,
98+
Namespace: namespace,
99+
CreationTimestamp: metav1.NewTime(time.Now()), // Recent
100+
},
101+
},
102+
},
103+
setupInfra: func() {
104+
infrastructure.InitializeForTesting(infrastructure.OpenShiftv4)
105+
},
106+
expectedError: &dwerrors.RetryError{},
107+
expectedSecrets: nil,
108+
},
109+
{
110+
name: "Non-OpenShift: no retry even if SA is recent with no secrets",
111+
objects: []client.Object{
112+
&corev1.ServiceAccount{
113+
TypeMeta: metav1.TypeMeta{
114+
Kind: "ServiceAccount",
115+
APIVersion: corev1.SchemeGroupVersion.String(),
116+
},
117+
ObjectMeta: metav1.ObjectMeta{
118+
Name: serviceAccountName,
119+
Namespace: namespace,
120+
CreationTimestamp: metav1.NewTime(time.Now()),
121+
},
122+
},
123+
},
124+
setupInfra: func() {
125+
infrastructure.InitializeForTesting(infrastructure.Kubernetes)
126+
},
127+
expectedError: nil,
128+
expectedSecrets: []string{},
129+
},
130+
{
131+
name: "Multiple SA + labeled secrets merged and sorted",
132+
objects: []client.Object{
133+
&corev1.ServiceAccount{
134+
TypeMeta: metav1.TypeMeta{
135+
Kind: "ServiceAccount",
136+
APIVersion: corev1.SchemeGroupVersion.String(),
137+
},
138+
ObjectMeta: metav1.ObjectMeta{
139+
Name: serviceAccountName,
140+
Namespace: namespace,
141+
CreationTimestamp: metav1.NewTime(time.Now().Add(-10 * time.Minute)),
142+
},
143+
ImagePullSecrets: []corev1.LocalObjectReference{
144+
{Name: "z-sa-secret"},
145+
},
146+
},
147+
&corev1.Secret{
148+
TypeMeta: metav1.TypeMeta{
149+
Kind: "Secret",
150+
APIVersion: corev1.SchemeGroupVersion.String(),
151+
},
152+
ObjectMeta: metav1.ObjectMeta{
153+
Name: "a-labeled-secret",
154+
Namespace: namespace,
155+
Labels: map[string]string{
156+
constants.DevWorkspacePullSecretLabel: "true",
157+
},
158+
},
159+
Type: corev1.SecretTypeDockerConfigJson,
160+
},
161+
},
162+
setupInfra: func() {
163+
infrastructure.InitializeForTesting(infrastructure.OpenShiftv4)
164+
},
165+
expectedError: nil,
166+
expectedSecrets: []string{"a-labeled-secret", "z-sa-secret"},
167+
},
168+
}
169+
170+
for _, tt := range tests {
171+
t.Run(tt.name, func(t *testing.T) {
172+
// Given
173+
scheme := runtime.NewScheme()
174+
assert.NoError(t, corev1.AddToScheme(scheme))
175+
tt.setupInfra()
176+
177+
fakeClient := fake.NewClientBuilder().
178+
WithScheme(scheme).
179+
WithObjects(tt.objects...).
180+
Build()
181+
182+
clusterAPI := sync.ClusterAPI{
183+
Client: fakeClient,
184+
}
185+
186+
// When
187+
result, err := PullSecrets(clusterAPI, serviceAccountName, namespace)
188+
189+
// Then
190+
if tt.expectedError != nil {
191+
assert.Error(t, err)
192+
assert.IsType(t, tt.expectedError, err)
193+
return
194+
}
195+
196+
assert.NoError(t, err)
197+
assert.NotNil(t, result)
198+
actualNames := []string{}
199+
for _, ps := range result.PullSecrets {
200+
actualNames = append(actualNames, ps.Name)
201+
}
202+
assert.Equal(t, tt.expectedSecrets, actualNames)
203+
})
204+
}
205+
}

0 commit comments

Comments
 (0)