1 package services
2
3 import (
4 "context"
5 "encoding/json"
6 "net/http"
7 "net/http/httptest"
8 "strings"
9 "testing"
10
11 "github.com/gin-gonic/gin"
12 "github.com/stretchr/testify/assert"
13 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
14 "k8s.io/apimachinery/pkg/runtime"
15 "sigs.k8s.io/controller-runtime/pkg/client"
16
17 "edge-infra.dev/pkg/edge/device-registrar/config"
18
19 api "edge-infra.dev/pkg/edge/device-registrar/api/v1alpha1"
20 test "edge-infra.dev/pkg/edge/device-registrar/utils/test"
21 iamApi "edge-infra.dev/pkg/edge/iam/api/v1alpha1"
22 ienApi "edge-infra.dev/pkg/sds/ien/k8s/apis/v1"
23 )
24
25
26
27 func TestListApplications(t *testing.T) {
28 router, scheme := setupTest()
29 app1, app2 := getApps()
30
31 mockClient := test.NewErrorInjectingFakeClient(scheme, false, &app1, &app2)
32 router.Use(injectMockClientMiddleware(t, mockClient))
33
34 w := serveApplicationRequest(t, router)
35
36
37 assert.Equal(t, http.StatusOK, w.Code)
38
39 var applications []map[string]interface{}
40 err := json.Unmarshal(w.Body.Bytes(), &applications)
41 assert.NoError(t, err)
42
43
44 appNames := make(map[string]bool)
45 for _, app := range applications {
46 appNames[app["name"].(string)] = true
47 }
48
49 assert.True(t, appNames["app1"], "app1 should be in the response")
50 assert.True(t, appNames["app2"], "app2 should be in the response")
51
52
53 assert.Contains(t, w.Body.String(), "123e4567-e89b-12d3-a456-426614174000")
54 assert.Contains(t, w.Body.String(), "123e4567-e89b-12d3-a456-426614174001")
55
56
57 assert.Contains(t, w.Body.String(), "app1")
58 assert.Contains(t, w.Body.String(), "app2")
59
60
61 assert.Contains(t, w.Body.String(), "Connected associates mobile application definition")
62 assert.Contains(t, w.Body.String(), "Connected associates mobile application 2 definition")
63 }
64
65 func TestListApplicationsNoApps(t *testing.T) {
66 router, scheme := setupTest()
67
68 mockClient := test.NewErrorInjectingFakeClient(scheme, false)
69 router.Use(injectMockClientMiddleware(t, mockClient))
70
71 w := serveApplicationRequest(t, router)
72
73
74 assert.Equal(t, http.StatusOK, w.Code)
75
76
77 assert.Equal(t, "[]", w.Body.String())
78 }
79
80 func TestListApplicationsWithErrors(t *testing.T) {
81 router, scheme := setupTest()
82
83 mockClient := test.NewErrorInjectingFakeClient(scheme, false)
84
85
86 mockClient.SetFailOnList()
87 router.Use(injectMockClientMiddleware(t, mockClient))
88
89
90 w := serveApplicationRequest(t, router)
91
92
93 assert.Equal(t, http.StatusInternalServerError, w.Code)
94
95
96 assert.Contains(t, w.Body.String(), "failed to retrieve applications: unsupported list type")
97 }
98
99 func TestRegisterDeviceNoVIP(t *testing.T) {
100 router, scheme := setupTest()
101 config.VIP = ""
102
103 app1, _ := getApps()
104 mockClient := test.NewErrorInjectingFakeClient(scheme, true, &app1)
105
106 router.Use(injectMockClientMiddleware(t, mockClient))
107 w := serveRegistrationRequest(t, router, "")
108
109
110 assert.Equal(t, http.StatusInternalServerError, w.Code, "Expected status code 500")
111
112
113 assert.Contains(t, w.Body.String(), "no IENodes found")
114 }
115
116 func TestRegisterDevice(t *testing.T) {
117 router, scheme := setupTest()
118
119 app1, _ := getApps()
120
121 node := ienApi.IENode{
122 ObjectMeta: metav1.ObjectMeta{
123 Name: "test-node",
124 Namespace: "device-registrar-clients",
125 },
126 Spec: ienApi.IENodeSpec{
127 NetworkServices: ienApi.NetworkServices{
128 KubeVip: "172.0.0.1",
129 },
130 },
131 }
132
133 mockClient := test.NewErrorInjectingFakeClient(scheme, true, &app1, &node)
134
135 router.Use(injectMockClientMiddleware(t, mockClient))
136 w := serveRegistrationRequest(t, router, "")
137
138
139 assert.Equal(t, http.StatusOK, w.Code, "Expected status code 200")
140
141
142 extDev := &api.ExternalDevice{}
143 err := mockClient.Get(context.TODO(), client.ObjectKey{Name: "test-device-name", Namespace: "device-registrar-clients"}, extDev)
144 assert.NoError(t, err)
145
146
147 assert.NotEmpty(t, extDev.Spec.Name)
148
149
150 devBinding := &api.DeviceBinding{}
151 err = mockClient.Get(context.TODO(), client.ObjectKey{Name: "test-device-name-device-binding", Namespace: "device-registrar-clients"}, devBinding)
152 assert.NoError(t, err)
153
154
155 assert.Equal(t, "test-device-name", devBinding.Spec.Device.Name)
156 assert.Equal(t, "123e4567-e89b-12d3-a456-426614174000", devBinding.Spec.Applications[0].ID)
157 assert.Equal(t, "ExternalApplication", devBinding.Spec.Applications[0].Kind)
158 assert.Equal(t, "app1", devBinding.Spec.Applications[0].Name)
159 assert.NotEmpty(t, devBinding.Status.ActivationCode)
160
161
162 var iamClients iamApi.ClientList
163 err = mockClient.List(context.TODO(), &iamClients, client.InNamespace("device-registrar-clients"))
164 assert.NoError(t, err)
165
166 var foundIAMClient *iamApi.Client
167 for _, iamClient := range iamClients.Items {
168 if strings.HasPrefix(iamClient.Name, "test-device-name-app1-client") {
169 foundIAMClient = &iamClient
170 break
171 }
172 }
173
174 assert.NotNil(t, foundIAMClient, "IAMClient with the specified prefix not found")
175
176
177 assert.Equal(t, "test-device-name-app1-secret", foundIAMClient.Spec.SecretName)
178
179
180 assert.Contains(t, w.Body.String(), "bootstrapURL")
181 assert.Contains(t, w.Body.String(), "hostMapping")
182
183
184 assert.Contains(t, w.Body.String(), "edge-bootstrap.store.ncr.corp")
185
186
187 assert.Contains(t, w.Body.String(), devBinding.Status.ActivationCode)
188
189
190 assert.Contains(t, w.Body.String(), node.Spec.NetworkServices.KubeVip)
191 }
192
193 func TestRegisterDeviceWithCreateError(t *testing.T) {
194 router, scheme := setupTest()
195
196 app1, _ := getApps()
197
198 mockClient := test.NewErrorInjectingFakeClient(scheme, true, &app1)
199 mockClient.SetFailOnCreate()
200
201 router.Use(injectMockClientMiddleware(t, mockClient))
202 w := serveRegistrationRequest(t, router, "")
203
204
205 assert.Equal(t, http.StatusInternalServerError, w.Code)
206
207
208 assert.Contains(t, w.Body.String(), "failed to create external device")
209 }
210
211 func TestRegisterDeviceWithGetError(t *testing.T) {
212 router, scheme := setupTest()
213 app1, _ := getApps()
214
215 mockClient := test.NewErrorInjectingFakeClient(scheme, true, &app1)
216 mockClient.SetFailOnGet()
217 router.Use(injectMockClientMiddleware(t, mockClient))
218
219 w := serveRegistrationRequest(t, router, "")
220
221
222 assert.Equal(t, http.StatusInternalServerError, w.Code)
223
224
225 assert.Contains(t, w.Body.String(), "failed to create external device: unsupported get type")
226 }
227
228 func TestGetExternalApplicationByID(t *testing.T) {
229 app1, _ := getApps()
230
231 scheme := runtime.NewScheme()
232 config.AddToScheme(scheme)
233
234 mockClient := test.NewErrorInjectingFakeClient(scheme, true, &app1)
235
236
237 c, _ := gin.CreateTestContext(httptest.NewRecorder())
238
239 app, err := getExternalApplicationByID(context.TODO(), c, mockClient, "123e4567-e89b-12d3-a456-426614174000")
240 assert.NoError(t, err)
241 assert.Equal(t, "app1", app.GetName())
242
243
244 _, err = getExternalApplicationByID(context.TODO(), c, mockClient, "123")
245 assert.Error(t, err)
246 }
247
248 func TestRegisterDeviceWithStatusUpdateError(t *testing.T) {
249 router, scheme := setupTest()
250
251 app1, _ := getApps()
252
253 mockClient := test.NewErrorInjectingFakeClient(scheme, true, &app1)
254 mockClient.SetFailOnStatus()
255 mockClient.NewCustomSubResourceWriter().SetFailOnUpdate()
256
257 router.Use(injectMockClientMiddleware(t, mockClient))
258 w := serveRegistrationRequest(t, router, "")
259
260
261 assert.Equal(t, http.StatusInternalServerError, w.Code)
262
263
264 assert.Contains(t, w.Body.String(), "failed to update deviceBinding status: update error")
265 }
266
267 func TestRegisterDevice_InvalidJSON(t *testing.T) {
268 router := gin.Default()
269
270
271 payload := `{
272 "device": "test-device-name",
273 "applicationIDs": ["123e4567-e89b-12d3-a456-426614174000",`
274
275 w := serveRegistrationRequest(t, router, payload)
276
277
278 assert.Equal(t, http.StatusBadRequest, w.Code)
279 assert.Contains(t, w.Body.String(), "error")
280 }
281
282 func getApps() (api.ExternalApplication, api.ExternalApplication) {
283 app1 := api.ExternalApplication{
284 ObjectMeta: metav1.ObjectMeta{
285 Name: "app1",
286 Namespace: "device-registrar-clients",
287 },
288 Spec: api.ExternalApplicationSpec{
289 Description: "Connected associates mobile application definition",
290 ID: "123e4567-e89b-12d3-a456-426614174000",
291 },
292 }
293 app2 := api.ExternalApplication{
294 ObjectMeta: metav1.ObjectMeta{
295 Name: "app2",
296 Namespace: "device-registrar-clients",
297 },
298 Spec: api.ExternalApplicationSpec{
299 Description: "Connected associates mobile application 2 definition",
300 ID: "123e4567-e89b-12d3-a456-426614174001",
301 },
302 }
303 return app1, app2
304 }
305
306
307 func injectMockClientMiddleware(t *testing.T, mockClient client.Client) gin.HandlerFunc {
308 return func(c *gin.Context) {
309 c.Set("k8sClient", mockClient)
310 c.Next()
311
312 k8sClient, exists := c.Get("k8sClient")
313
314
315 assert.True(t, exists, "k8sClient should exist in the context")
316 assert.NotNil(t, k8sClient, "k8sClient should not be nil")
317 }
318 }
319
320 func serveRegistrationRequest(t *testing.T, router *gin.Engine, payload string) *httptest.ResponseRecorder {
321
322 router.POST("/register", RegisterDevice)
323
324
325 if payload == "" {
326 payload = `{
327 "device": {
328 "name": "test-device-name",
329 "description": "my test device",
330 "serialNumber": "DSFE7DSCXX90"
331 },
332 "applicationIDs": ["123e4567-e89b-12d3-a456-426614174000"]
333 }`
334 }
335
336
337 req, err := http.NewRequest(http.MethodPost, "/register", strings.NewReader(payload))
338 assert.NoError(t, err)
339 req.Header.Set("Content-Type", "application/json")
340
341 w := httptest.NewRecorder()
342 router.ServeHTTP(w, req)
343
344 return w
345 }
346
347 func serveApplicationRequest(t *testing.T, router *gin.Engine) *httptest.ResponseRecorder {
348
349 router.GET("/applications", ListApplications)
350
351
352 req, err := http.NewRequest(http.MethodGet, "/applications", nil)
353 assert.NoError(t, err)
354 w := httptest.NewRecorder()
355 router.ServeHTTP(w, req)
356
357 return w
358 }
359
View as plain text