Skip to content

Commit 62da49a

Browse files
committed
test(api/v1): Add unit tests for Token, ClusterToken, Permissions
Add comprehensive unit tests for Token, ClusterToken, Permissions, and ManagedSecret types. Improves coverage for CRD methods and status logic.
1 parent 92993ac commit 62da49a

6 files changed

Lines changed: 1156 additions & 0 deletions

File tree

api/v1/clustertoken_types_test.go

Lines changed: 358 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,358 @@
1+
package v1_test
2+
3+
import (
4+
"testing"
5+
"time"
6+
7+
"github.com/google/go-github/v80/github"
8+
v1 "github.com/isometry/github-token-manager/api/v1"
9+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
10+
)
11+
12+
func TestClusterToken_GetSecretNamespace(t *testing.T) {
13+
tests := []struct {
14+
name string
15+
token *v1.ClusterToken
16+
wantNamespace string
17+
}{
18+
{
19+
name: "namespace from spec",
20+
token: &v1.ClusterToken{
21+
ObjectMeta: metav1.ObjectMeta{
22+
Name: "cluster-token",
23+
},
24+
Spec: v1.ClusterTokenSpec{
25+
Secret: v1.ClusterTokenSecretSpec{
26+
Namespace: "target-namespace",
27+
},
28+
},
29+
},
30+
wantNamespace: "target-namespace",
31+
},
32+
}
33+
34+
for _, tt := range tests {
35+
t.Run(tt.name, func(t *testing.T) {
36+
if got := tt.token.GetSecretNamespace(); got != tt.wantNamespace {
37+
t.Errorf("GetSecretNamespace() = %v, want %v", got, tt.wantNamespace)
38+
}
39+
})
40+
}
41+
}
42+
43+
func TestClusterToken_GetSecretName(t *testing.T) {
44+
tests := []struct {
45+
name string
46+
token *v1.ClusterToken
47+
wantSecret string
48+
}{
49+
{
50+
name: "default to cluster token name",
51+
token: &v1.ClusterToken{
52+
ObjectMeta: metav1.ObjectMeta{Name: "my-cluster-token"},
53+
Spec: v1.ClusterTokenSpec{
54+
Secret: v1.ClusterTokenSecretSpec{
55+
Namespace: "default",
56+
},
57+
},
58+
},
59+
wantSecret: "my-cluster-token",
60+
},
61+
{
62+
name: "custom secret name",
63+
token: &v1.ClusterToken{
64+
ObjectMeta: metav1.ObjectMeta{Name: "my-cluster-token"},
65+
Spec: v1.ClusterTokenSpec{
66+
Secret: v1.ClusterTokenSecretSpec{
67+
Namespace: "default",
68+
Name: "custom-secret",
69+
},
70+
},
71+
},
72+
wantSecret: "custom-secret",
73+
},
74+
}
75+
76+
for _, tt := range tests {
77+
t.Run(tt.name, func(t *testing.T) {
78+
if got := tt.token.GetSecretName(); got != tt.wantSecret {
79+
t.Errorf("GetSecretName() = %v, want %v", got, tt.wantSecret)
80+
}
81+
})
82+
}
83+
}
84+
85+
func TestClusterToken_GetInstallationTokenOptions(t *testing.T) {
86+
read := "read"
87+
write := "write"
88+
89+
tests := []struct {
90+
name string
91+
token *v1.ClusterToken
92+
want *github.InstallationTokenOptions
93+
}{
94+
{
95+
name: "with permissions and repositories",
96+
token: &v1.ClusterToken{
97+
Spec: v1.ClusterTokenSpec{
98+
Secret: v1.ClusterTokenSecretSpec{
99+
Namespace: "default",
100+
},
101+
Permissions: &v1.Permissions{
102+
Contents: &read,
103+
PullRequests: &write,
104+
},
105+
Repositories: []string{"repo1", "repo2"},
106+
},
107+
},
108+
want: &github.InstallationTokenOptions{
109+
Permissions: &github.InstallationPermissions{
110+
Contents: &read,
111+
PullRequests: &write,
112+
},
113+
Repositories: []string{"repo1", "repo2"},
114+
},
115+
},
116+
{
117+
name: "with repository IDs",
118+
token: &v1.ClusterToken{
119+
Spec: v1.ClusterTokenSpec{
120+
Secret: v1.ClusterTokenSecretSpec{
121+
Namespace: "default",
122+
},
123+
RepositoryIDs: []int64{999, 888},
124+
},
125+
},
126+
want: &github.InstallationTokenOptions{
127+
RepositoryIDs: []int64{999, 888},
128+
},
129+
},
130+
{
131+
name: "minimal options",
132+
token: &v1.ClusterToken{
133+
Spec: v1.ClusterTokenSpec{
134+
Secret: v1.ClusterTokenSecretSpec{
135+
Namespace: "default",
136+
},
137+
},
138+
},
139+
want: &github.InstallationTokenOptions{},
140+
},
141+
}
142+
143+
for _, tt := range tests {
144+
t.Run(tt.name, func(t *testing.T) {
145+
got := tt.token.GetInstallationTokenOptions()
146+
assertInstallationTokenOptions(t, got, tt.want)
147+
})
148+
}
149+
}
150+
151+
func TestClusterToken_UpdateManagedSecret(t *testing.T) {
152+
tests := []struct {
153+
name string
154+
token *v1.ClusterToken
155+
wantChanged bool
156+
wantNamespace string
157+
wantName string
158+
wantBasicAuth bool
159+
}{
160+
{
161+
name: "initial update - unset to set",
162+
token: &v1.ClusterToken{
163+
ObjectMeta: metav1.ObjectMeta{
164+
Name: "test-cluster-token",
165+
},
166+
Spec: v1.ClusterTokenSpec{
167+
Secret: v1.ClusterTokenSecretSpec{
168+
Namespace: "production",
169+
Name: "github-token",
170+
BasicAuth: true,
171+
},
172+
},
173+
Status: v1.ClusterTokenStatus{
174+
ManagedSecret: v1.ManagedSecret{},
175+
},
176+
},
177+
wantChanged: true,
178+
wantNamespace: "production",
179+
wantName: "github-token",
180+
wantBasicAuth: true,
181+
},
182+
{
183+
name: "no change needed",
184+
token: &v1.ClusterToken{
185+
ObjectMeta: metav1.ObjectMeta{
186+
Name: "test-cluster-token",
187+
},
188+
Spec: v1.ClusterTokenSpec{
189+
Secret: v1.ClusterTokenSecretSpec{
190+
Namespace: "default",
191+
},
192+
},
193+
Status: v1.ClusterTokenStatus{
194+
ManagedSecret: v1.ManagedSecret{
195+
Namespace: "default",
196+
Name: "test-cluster-token",
197+
BasicAuth: false,
198+
},
199+
},
200+
},
201+
wantChanged: false,
202+
wantNamespace: "default",
203+
wantName: "test-cluster-token",
204+
wantBasicAuth: false,
205+
},
206+
{
207+
name: "namespace changed",
208+
token: &v1.ClusterToken{
209+
ObjectMeta: metav1.ObjectMeta{
210+
Name: "test-cluster-token",
211+
},
212+
Spec: v1.ClusterTokenSpec{
213+
Secret: v1.ClusterTokenSecretSpec{
214+
Namespace: "staging",
215+
},
216+
},
217+
Status: v1.ClusterTokenStatus{
218+
ManagedSecret: v1.ManagedSecret{
219+
Namespace: "production",
220+
Name: "test-cluster-token",
221+
BasicAuth: false,
222+
},
223+
},
224+
},
225+
wantChanged: true,
226+
wantNamespace: "staging",
227+
wantName: "test-cluster-token",
228+
wantBasicAuth: false,
229+
},
230+
}
231+
232+
for _, tt := range tests {
233+
t.Run(tt.name, func(t *testing.T) {
234+
gotChanged := tt.token.UpdateManagedSecret()
235+
if gotChanged != tt.wantChanged {
236+
t.Errorf("UpdateManagedSecret() changed = %v, want %v", gotChanged, tt.wantChanged)
237+
}
238+
assertManagedSecret(t, tt.token.Status.ManagedSecret, tt.wantNamespace, tt.wantName, tt.wantBasicAuth)
239+
})
240+
}
241+
}
242+
243+
func TestClusterToken_SetStatusCondition(t *testing.T) {
244+
tests := []struct {
245+
name string
246+
initialConditions []metav1.Condition
247+
newCondition metav1.Condition
248+
wantChanged bool
249+
wantCount int
250+
}{
251+
{
252+
name: "add first condition",
253+
initialConditions: []metav1.Condition{},
254+
newCondition: metav1.Condition{
255+
Type: v1.ConditionTypeReady,
256+
Status: metav1.ConditionTrue,
257+
Reason: "Success",
258+
Message: "ClusterToken is ready",
259+
},
260+
wantChanged: true,
261+
wantCount: 1,
262+
},
263+
{
264+
name: "update existing condition",
265+
initialConditions: []metav1.Condition{
266+
{
267+
Type: v1.ConditionTypeReady,
268+
Status: metav1.ConditionFalse,
269+
Reason: "Pending",
270+
Message: "Waiting",
271+
},
272+
},
273+
newCondition: metav1.Condition{
274+
Type: v1.ConditionTypeReady,
275+
Status: metav1.ConditionTrue,
276+
Reason: "Success",
277+
Message: "ClusterToken is ready",
278+
},
279+
wantChanged: true,
280+
wantCount: 1,
281+
},
282+
{
283+
name: "no change - same condition",
284+
initialConditions: []metav1.Condition{
285+
{
286+
Type: v1.ConditionTypeReady,
287+
Status: metav1.ConditionTrue,
288+
Reason: "Success",
289+
Message: "ClusterToken is ready",
290+
LastTransitionTime: metav1.Now(),
291+
},
292+
},
293+
newCondition: metav1.Condition{
294+
Type: v1.ConditionTypeReady,
295+
Status: metav1.ConditionTrue,
296+
Reason: "Success",
297+
Message: "ClusterToken is ready",
298+
},
299+
wantChanged: false,
300+
wantCount: 1,
301+
},
302+
}
303+
304+
for _, tt := range tests {
305+
t.Run(tt.name, func(t *testing.T) {
306+
token := &v1.ClusterToken{
307+
Status: v1.ClusterTokenStatus{
308+
Conditions: tt.initialConditions,
309+
},
310+
}
311+
gotChanged := token.SetStatusCondition(tt.newCondition)
312+
assertConditionResult(t, token.Status.Conditions, gotChanged, tt.wantChanged, tt.wantCount, tt.newCondition)
313+
})
314+
}
315+
}
316+
317+
func TestClusterToken_GetStatusTimestamps(t *testing.T) {
318+
createdAt := time.Now().Add(-30 * time.Minute)
319+
expiresAt := time.Now()
320+
321+
token := &v1.ClusterToken{
322+
Status: v1.ClusterTokenStatus{
323+
IAT: v1.InstallationAccessToken{
324+
CreatedAt: metav1.NewTime(createdAt),
325+
ExpiresAt: metav1.NewTime(expiresAt),
326+
},
327+
},
328+
}
329+
330+
gotCreated, gotExpires := token.GetStatusTimestamps()
331+
332+
if !gotCreated.Equal(createdAt) {
333+
t.Errorf("GetStatusTimestamps() createdAt = %v, want %v", gotCreated, createdAt)
334+
}
335+
if !gotExpires.Equal(expiresAt) {
336+
t.Errorf("GetStatusTimestamps() expiresAt = %v, want %v", gotExpires, expiresAt)
337+
}
338+
}
339+
340+
func TestClusterToken_SetStatusTimestamps(t *testing.T) {
341+
expiresAt := time.Now().Add(45 * time.Minute)
342+
343+
token := &v1.ClusterToken{}
344+
token.SetStatusTimestamps(expiresAt)
345+
346+
gotCreated, gotExpires := token.GetStatusTimestamps()
347+
348+
if !gotExpires.Equal(expiresAt) {
349+
t.Errorf("ExpiresAt = %v, want %v", gotExpires, expiresAt)
350+
}
351+
352+
if gotCreated.IsZero() {
353+
t.Error("CreatedAt should not be zero")
354+
}
355+
if !gotCreated.Before(gotExpires) {
356+
t.Error("CreatedAt should be before ExpiresAt")
357+
}
358+
}

0 commit comments

Comments
 (0)