Skip to content

Commit 3528369

Browse files
committed
feat: unit tests for listener/apischema/crd_resolver
1 parent aa8da35 commit 3528369

File tree

1 file changed

+228
-0
lines changed

1 file changed

+228
-0
lines changed
Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
package apischema
2+
3+
import (
4+
"encoding/json"
5+
"errors"
6+
"testing"
7+
8+
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
9+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
10+
"k8s.io/client-go/openapi"
11+
"k8s.io/kube-openapi/pkg/validation/spec"
12+
)
13+
14+
type fakeGV struct {
15+
data []byte
16+
err error
17+
}
18+
19+
func (f fakeGV) Schema(mime string) ([]byte, error) {
20+
return f.data, f.err
21+
}
22+
23+
func (f fakeGV) ServerRelativeURL() string {
24+
return ""
25+
}
26+
27+
// TestGetCRDGroupKindVersions tests the getCRDGroupKindVersions function. It checks if the
28+
// function correctly extracts the Group, Kind, and Versions from the CRD spec.
29+
func TestGetCRDGroupKindVersions(t *testing.T) {
30+
tests := []struct {
31+
name string
32+
spec apiextensionsv1.CustomResourceDefinitionSpec
33+
wantG string
34+
wantKind string
35+
wantVers []string
36+
}{
37+
{
38+
name: "basic",
39+
spec: apiextensionsv1.CustomResourceDefinitionSpec{Group: "test.group", Versions: []apiextensionsv1.CustomResourceDefinitionVersion{{Name: "v1"}, {Name: "v2"}}, Names: apiextensionsv1.CustomResourceDefinitionNames{Kind: "MyKind"}},
40+
wantG: "test.group",
41+
wantKind: "MyKind",
42+
wantVers: []string{"v1", "v2"},
43+
},
44+
{
45+
name: "single version",
46+
spec: apiextensionsv1.CustomResourceDefinitionSpec{Group: "g", Versions: []apiextensionsv1.CustomResourceDefinitionVersion{{Name: "v1"}}, Names: apiextensionsv1.CustomResourceDefinitionNames{Kind: "K"}},
47+
wantG: "g",
48+
wantKind: "K",
49+
wantVers: []string{"v1"},
50+
},
51+
}
52+
for _, tc := range tests {
53+
t.Run(tc.name, func(t *testing.T) {
54+
gkv := getCRDGroupKindVersions(tc.spec)
55+
if gkv.Group != tc.wantG || gkv.Kind != tc.wantKind {
56+
t.Errorf("GroupKind mismatch: got %v/%v, want %v/%v", gkv.Group, gkv.Kind, tc.wantG, tc.wantKind)
57+
}
58+
if len(gkv.Versions) != len(tc.wantVers) {
59+
t.Fatalf("Versions length: got %d, want %d", len(gkv.Versions), len(tc.wantVers))
60+
}
61+
for i, v := range tc.wantVers {
62+
if gkv.Versions[i] != v {
63+
t.Errorf("Versions[%d]: got %q, want %q", i, gkv.Versions[i], v)
64+
}
65+
}
66+
})
67+
}
68+
}
69+
70+
// TestIsCRDKindIncluded tests the isCRDKindIncluded function. It checks if the function correctly
71+
// determines if a specific kind is included in the APIResourceList.
72+
func TestIsCRDKindIncluded(t *testing.T) {
73+
tests := []struct {
74+
name string
75+
gkv *GroupKindVersions
76+
apiList *metav1.APIResourceList
77+
want bool
78+
}{
79+
{
80+
name: "kind present",
81+
gkv: &GroupKindVersions{GroupKind: &metav1.GroupKind{Group: "g", Kind: "KindA"}, Versions: []string{"v1"}},
82+
apiList: &metav1.APIResourceList{GroupVersion: "g/v1", APIResources: []metav1.APIResource{{Kind: "KindA"}, {Kind: "Other"}}},
83+
want: true,
84+
},
85+
{
86+
name: "kind absent",
87+
gkv: &GroupKindVersions{GroupKind: &metav1.GroupKind{Group: "g", Kind: "KindA"}, Versions: []string{"v1"}},
88+
apiList: &metav1.APIResourceList{GroupVersion: "g/v1", APIResources: []metav1.APIResource{{Kind: "Different"}}},
89+
want: false,
90+
},
91+
}
92+
for _, tc := range tests {
93+
t.Run(tc.name, func(t *testing.T) {
94+
got := isCRDKindIncluded(tc.gkv, tc.apiList)
95+
if got != tc.want {
96+
t.Errorf("expected %v, got %v", tc.want, got)
97+
}
98+
})
99+
}
100+
}
101+
102+
// TestErrorIfCRDNotInPreferredApiGroups tests the errorIfCRDNotInPreferredApiGroups function.
103+
// It checks if the function correctly identifies if a CRD is not in the preferred API groups.
104+
func TestErrorIfCRDNotInPreferredApiGroups(t *testing.T) {
105+
gkv := &GroupKindVersions{
106+
GroupKind: &metav1.GroupKind{Group: "g", Kind: "K"},
107+
Versions: []string{"v1", "v2"},
108+
}
109+
cases := []struct {
110+
name string
111+
lists []*metav1.APIResourceList
112+
wantErr error
113+
wantGroup []string
114+
}{
115+
{
116+
name: "kind found",
117+
lists: []*metav1.APIResourceList{
118+
{
119+
GroupVersion: "g/v2",
120+
APIResources: []metav1.APIResource{{Kind: "K"}},
121+
},
122+
{
123+
GroupVersion: "g/v3",
124+
APIResources: []metav1.APIResource{{Kind: "Other"}},
125+
},
126+
},
127+
wantErr: nil,
128+
wantGroup: []string{"g/v2", "g/v3"},
129+
},
130+
{
131+
name: "kind not found",
132+
lists: []*metav1.APIResourceList{{GroupVersion: "g/v1", APIResources: []metav1.APIResource{{Kind: "X"}}}},
133+
wantErr: ErrGVKNotPreferred,
134+
},
135+
}
136+
137+
for _, tc := range cases {
138+
t.Run(tc.name, func(t *testing.T) {
139+
groups, err := errorIfCRDNotInPreferredApiGroups(gkv, tc.lists)
140+
if tc.wantErr != nil {
141+
if !errors.Is(err, tc.wantErr) {
142+
t.Fatalf("expected error %v, got %v", tc.wantErr, err)
143+
}
144+
return
145+
}
146+
if err != nil {
147+
t.Fatalf("unexpected error: %v", err)
148+
}
149+
if len(groups) != len(tc.wantGroup) {
150+
t.Fatalf("group count: got %d, want %d", len(groups), len(tc.wantGroup))
151+
}
152+
for i := range groups {
153+
if groups[i] != tc.wantGroup[i] {
154+
t.Errorf("groups[%d]: got %q, want %q", i, groups[i], tc.wantGroup[i])
155+
}
156+
}
157+
})
158+
}
159+
}
160+
161+
// TestGetSchemaForPath tests the getSchemaForPath function. It checks if the function
162+
// correctly retrieves the schema for a given path and handles various error cases.
163+
func TestGetSchemaForPath(t *testing.T) {
164+
// prepare a valid schemaResponse JSON
165+
validSchemas := map[string]*spec.Schema{"a.v1.K": {}}
166+
resp := schemaResponse{Components: schemasComponentsWrapper{Schemas: validSchemas}}
167+
validJSON, err := json.Marshal(&resp)
168+
if err != nil {
169+
t.Fatalf("failed to marshal valid response: %v", err)
170+
}
171+
172+
tests := []struct {
173+
name string
174+
preferred []string
175+
path string
176+
gv openapi.GroupVersion
177+
wantErr error
178+
wantCount int
179+
}{
180+
{
181+
name: "invalid path",
182+
preferred: []string{"g/v1"},
183+
path: "noSlash",
184+
gv: fakeGV{},
185+
wantErr: ErrInvalidPath,
186+
},
187+
{
188+
name: "not preferred",
189+
preferred: []string{"x/y"},
190+
path: "/g/v1",
191+
gv: fakeGV{},
192+
wantErr: ErrNotPreferred,
193+
},
194+
{
195+
name: "unmarshal error",
196+
preferred: []string{"g/v1"},
197+
path: "/g/v1",
198+
gv: fakeGV{data: []byte("bad json"), err: nil},
199+
wantErr: ErrUnmarshalSchemaForPath,
200+
},
201+
{
202+
name: "success",
203+
preferred: []string{"g/v1"},
204+
path: "/g/v1",
205+
gv: fakeGV{data: validJSON, err: nil},
206+
wantErr: nil,
207+
wantCount: len(validSchemas),
208+
},
209+
}
210+
211+
for _, tc := range tests {
212+
t.Run(tc.name, func(t *testing.T) {
213+
got, err := getSchemaForPath(tc.preferred, tc.path, tc.gv)
214+
if tc.wantErr != nil {
215+
if !errors.Is(err, tc.wantErr) {
216+
t.Fatalf("expected error %v, got %v", tc.wantErr, err)
217+
}
218+
return
219+
}
220+
if err != nil {
221+
t.Fatalf("unexpected error: %v", err)
222+
}
223+
if len(got) != tc.wantCount {
224+
t.Errorf("schema count: got %d, want %d", len(got), tc.wantCount)
225+
}
226+
})
227+
}
228+
}

0 commit comments

Comments
 (0)