1 package services
2
3 import (
4 "context"
5 "encoding/json"
6 "fmt"
7 "io"
8 "net/http"
9 "net/http/httptest"
10 "strings"
11 "testing"
12 "time"
13
14 "github.com/DATA-DOG/go-sqlmock"
15 "github.com/google/uuid"
16 "github.com/stretchr/testify/assert"
17
18 bslerror "edge-infra.dev/pkg/edge/api/apierror/bsl"
19 "edge-infra.dev/pkg/edge/api/bsl/types"
20 "edge-infra.dev/pkg/edge/api/graph/mapper"
21 "edge-infra.dev/pkg/edge/api/graph/model"
22 "edge-infra.dev/pkg/edge/api/middleware"
23 sqlquery "edge-infra.dev/pkg/edge/api/sql"
24 edgetypes "edge-infra.dev/pkg/edge/api/types"
25 "edge-infra.dev/pkg/edge/bsl"
26 )
27
28 const (
29 testStoreName = "test-store"
30 testEnterpriseUnitName = "test-enterprise-unit-name"
31 testEuid = "test-euid"
32 testEuidNotExist = "test-euid-not-exist"
33 testUsername = "test-user"
34 testBSLToken = "test-token"
35 testOrganization = "test-org"
36 testBanner = "test-banner"
37 testReferenceID = "test-reference-id"
38 testBannerID = "test-banner"
39 )
40
41 func TestCreateBSLSite(t *testing.T) {
42 srv := getMockBspServer(t)
43 defer srv.Close()
44 appConfig := edgetypes.Config{
45 BSP: types.BSPConfig{
46 Endpoint: srv.URL,
47 },
48 }
49 bslClient := bsl.NewBSLClient(appConfig.BSP, nil)
50 bslSiteService := NewBSLSiteService(bslClient, nil)
51 contextWithAuth := middleware.NewContext(context.Background(), &types.AuthUser{
52 Username: testUsername,
53 Token: testBSLToken,
54 Organization: testOrganization,
55 })
56 bslsite, err := bslSiteService.CreateBSLSite(contextWithAuth, testStoreName, testEnterpriseUnitName, ActiveStatus, DefaultLatitude, DefaultLongitude, &model.Banner{BannerType: "eu"}, testReferenceID)
57 assert.Nil(t, err)
58 assert.NotEmpty(t, bslsite)
59 assert.Equal(t, bslsite.ID, testEuid)
60 assert.Equal(t, bslsite.SiteName, testStoreName)
61 assert.Equal(t, bslsite.EnterpriseUnitName, testEnterpriseUnitName)
62 assert.Equal(t, bslsite.Status, ActiveStatus)
63 assert.Equal(t, bslsite.Coordinates.Latitude, DefaultLatitude)
64 assert.Equal(t, bslsite.Coordinates.Longitude, DefaultLongitude)
65 assert.Equal(t, bslsite.ReferenceID, testReferenceID)
66 }
67
68 func TestGetBSLSite(t *testing.T) {
69 srv := getMockBspServer(t)
70 defer srv.Close()
71 appConfig := edgetypes.Config{
72 BSP: types.BSPConfig{
73 Endpoint: srv.URL,
74 },
75 }
76 contextWithAuth := middleware.NewContext(context.Background(), &types.AuthUser{
77 Username: testUsername,
78 Token: testBSLToken,
79 Organization: testOrganization,
80 })
81 siteID := "site-id"
82 secret := &bsl.AccessKey{SecretKey: "testSecretKey", SharedKey: "testSharedKey"}
83 bslClient := bsl.NewBSLClient(appConfig.BSP,
84 func(_ context.Context, _ string) (*bsl.AccessKey, error) {
85 return secret, nil
86 })
87 bslSiteService := NewBSLSiteService(bslClient, nil)
88 bslInfo, err := bslSiteService.GetBSLSiteByID(contextWithAuth, siteID, &model.Banner{BannerType: "org", Name: "test-banner", BannerBSLId: "test-id"})
89 assert.Nil(t, err)
90 assert.NotEmpty(t, bslInfo)
91 assert.Equal(t, testStoreName, bslInfo.SiteName)
92 assert.Equal(t, testEnterpriseUnitName, bslInfo.EnterpriseUnitName)
93 assert.Equal(t, ActiveStatus, bslInfo.Status)
94 assert.Equal(t, DefaultLatitude, bslInfo.Coordinates.Latitude)
95 assert.Equal(t, DefaultLongitude, bslInfo.Coordinates.Longitude)
96 }
97
98 func TestDoesSiteHaveAttachedCluster(t *testing.T) {
99 db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
100 if err != nil {
101 t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
102 }
103 defer db.Close()
104 mock.ExpectQuery(sqlquery.DoesClusterWithSiteIDExist).WithArgs("123456789").
105 WillReturnRows(mock.NewRows([]string{""}).AddRow(true))
106 bslClient := bsl.NewBSLClient(appConfig.BSP, nil)
107 service := NewBSLSiteService(bslClient, db)
108 exists, err := service.DoesSiteHaveAttachedCluster(context.Background(), "123456789")
109 assert.NoError(t, err)
110 assert.True(t, exists)
111 }
112
113 func TestDeleteBSLSite(t *testing.T) {
114 srv := getMockBspServer(t)
115 defer srv.Close()
116 appConfig := edgetypes.Config{
117 BSP: types.BSPConfig{
118 Endpoint: srv.URL,
119 },
120 }
121 contextWithAuth := middleware.NewContext(context.Background(), &types.AuthUser{
122 Username: testUsername,
123 Token: testBSLToken,
124 Organization: testOrganization,
125 })
126 bslSite := &bsl.BSLInfo{
127 ID: testEuid,
128 EnterpriseUnitName: testEnterpriseUnitName,
129 SiteName: testStoreName,
130 Status: ActiveStatus,
131 Coordinates: bsl.Coordinates{
132 Latitude: DefaultLatitude,
133 Longitude: DefaultLongitude,
134 },
135 }
136 secret := &bsl.AccessKey{SecretKey: "testPassword", SharedKey: "testUsername"}
137 bslClient := bsl.NewBSLClient(appConfig.BSP,
138 func(_ context.Context, _ string) (*bsl.AccessKey, error) { return secret, nil })
139 bslSiteService := NewBSLSiteService(bslClient, nil)
140 errs := bslSiteService.DeleteBSLSite(contextWithAuth, bslSite, &model.Banner{BannerType: "eu"})
141 assert.Nil(t, errs)
142 }
143
144 func TestUpdateBSLSite(t *testing.T) {
145 srv := getMockBspServer(t)
146 defer srv.Close()
147 appConfig := edgetypes.Config{
148 BSP: types.BSPConfig{
149 Endpoint: srv.URL,
150 },
151 }
152 contextWithAuth := middleware.NewContext(context.Background(), &types.AuthUser{
153 Username: testUsername,
154 Token: testBSLToken,
155 Organization: testOrganization,
156 })
157 siteName := "test-site"
158 site := model.Site{
159 SiteName: &siteName,
160 }
161 secret := &bsl.AccessKey{SecretKey: "testSecretKey", SharedKey: "testSharedKey"}
162 bslClient := bsl.NewBSLClient(appConfig.BSP,
163 func(_ context.Context, _ string) (*bsl.AccessKey, error) {
164 return secret, nil
165 })
166 bslSiteService := NewBSLSiteService(bslClient, nil)
167 bslsite, err := bslSiteService.UpdateBSLSiteByID(contextWithAuth, &model.Banner{BannerType: "eu", Name: "test-banner"}, "site-id", site)
168 assert.Nil(t, err)
169 assert.NotEmpty(t, bslsite)
170 assert.Equal(t, *bslsite.ID, testEuid)
171 assert.Equal(t, bslsite.SiteName, siteName)
172 assert.Equal(t, bslsite.EnterpriseUnitName, testEnterpriseUnitName)
173 assert.Equal(t, bslsite.Status, InActiveStatus)
174 assert.Equal(t, bslsite.Coordinates.Latitude, DefaultLatitude)
175 assert.Equal(t, bslsite.Coordinates.Longitude, DefaultLongitude)
176 assert.Equal(t, *bslsite.ReferenceID, testReferenceID)
177
178 bslsite, err = bslSiteService.UpdateBSLSiteByID(contextWithAuth, &model.Banner{BannerType: "org", Name: "test-banner", BannerBSLId: "test-id"}, "site-id", site)
179 assert.Nil(t, err)
180 assert.NotEmpty(t, bslsite)
181 assert.Equal(t, *bslsite.ID, testEuid)
182 assert.Equal(t, bslsite.SiteName, siteName)
183 assert.Equal(t, bslsite.EnterpriseUnitName, testEnterpriseUnitName)
184 assert.Equal(t, bslsite.Status, InActiveStatus)
185 assert.Equal(t, bslsite.Coordinates.Latitude, DefaultLatitude)
186 assert.Equal(t, bslsite.Coordinates.Longitude, DefaultLongitude)
187 assert.Equal(t, *bslsite.ReferenceID, testReferenceID)
188
189 _, err = bslSiteService.UpdateBSLSiteByID(contextWithAuth, &model.Banner{BannerType: "org", Name: "test-banner", BannerEdgeID: "test-id"}, "site-id", site)
190 assert.Error(t, err)
191 }
192
193 func TestBSLSiteService_GetOrCreateSite(t *testing.T) {
194 srv := getMockBspServer(t)
195 defer srv.Close()
196 appConfig := edgetypes.Config{
197 BSP: types.BSPConfig{
198 Endpoint: srv.URL,
199 },
200 }
201 db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual))
202 if err != nil {
203 t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
204 }
205 defer db.Close()
206 mock.ExpectQuery(sqlquery.DoesClusterWithSiteIDExist).WithArgs(testEuid).
207 WillReturnRows(mock.NewRows([]string{""}).AddRow(true))
208 mock.ExpectQuery(sqlquery.DoesClusterWithSiteIDExist).WithArgs(testEuidNotExist).
209 WillReturnRows(mock.NewRows([]string{""}).AddRow(false))
210 mock.ExpectQuery(sqlquery.DoesClusterWithSiteIDExist).WithArgs(testEuid).
211 WillReturnRows(mock.NewRows([]string{""}).AddRow(false))
212 mock.ExpectQuery(sqlquery.DoesClusterWithSiteIDExist).WithArgs(testEuid).
213 WillReturnRows(mock.NewRows([]string{""}).AddRow(false))
214 mock.ExpectQuery(sqlquery.DoesClusterWithSiteIDExist).WithArgs(testEuid).
215 WillReturnRows(mock.NewRows([]string{""}).AddRow(false))
216 mock.ExpectQuery(sqlquery.DoesClusterWithSiteIDExist).WithArgs(testEuid).
217 WillReturnRows(mock.NewRows([]string{""}).AddRow(false))
218 mock.ExpectQuery(sqlquery.DoesClusterWithSiteIDExist).WithArgs(testEuid).
219 WillReturnRows(mock.NewRows([]string{""}).AddRow(false))
220 bslClient := bsl.NewBSLClient(appConfig.BSP, nil)
221 service := NewBSLSiteService(bslClient, db)
222 contextWithAuth := middleware.NewContext(context.Background(), &types.AuthUser{
223 Username: testUsername,
224 Token: testBSLToken,
225 Organization: testOrganization,
226 })
227
228
229 createSiteFalse := false
230 createSiteTrue := true
231 euid := testEuid
232 euidNotExist := testEuidNotExist
233 clusterEdgeID := uuid.NewString()
234 b := &model.Banner{BannerType: "eu"}
235
236 payload := getRegistrationStoreInfo(&createSiteTrue, &euid)
237 bslSite, siteCreated, errs := service.GetOrCreateSite(contextWithAuth, payload, b, testStoreName, clusterEdgeID)
238 assert.NotNil(t, errs)
239 assert.Equal(t, "bsl site id has already been registered with a cluster", errs.Error())
240 assert.Nil(t, bslSite)
241 assert.Equal(t, false, siteCreated)
242
243 payload = getRegistrationStoreInfo(&createSiteFalse, &euidNotExist)
244 bslSite, siteCreated, errs = service.GetOrCreateSite(contextWithAuth, payload, b, testStoreName, clusterEdgeID)
245 assert.NotNil(t, errs)
246 e, ok := errs.(*bslerror.Error)
247 assert.True(t, ok, "%T", errs)
248 assert.Equal(t, e.Extensions()["statusCode"], 404)
249 assert.NotNil(t, bslSite)
250 assert.Equal(t, "", bslSite.ID)
251 assert.Equal(t, false, siteCreated)
252
253 payload = getRegistrationStoreInfo(&createSiteFalse, &euid)
254 bslSite, siteCreated, errs = service.GetOrCreateSite(contextWithAuth, payload, b, testStoreName, clusterEdgeID)
255 assert.Nil(t, errs)
256 assert.NotNil(t, bslSite)
257 assert.Equal(t, false, siteCreated)
258 assert.Equal(t, testEuid, bslSite.ID)
259
260 payload = getRegistrationStoreInfo(nil, &euid)
261 bslSite, siteCreated, err = service.GetOrCreateSite(contextWithAuth, payload, b, testStoreName, clusterEdgeID)
262 assert.Nil(t, err)
263 assert.NotNil(t, bslSite)
264 assert.Equal(t, false, siteCreated)
265 assert.Equal(t, testEuid, bslSite.ID)
266
267 payload = getRegistrationStoreInfo(&createSiteTrue, nil)
268 bslSite, siteCreated, err = service.GetOrCreateSite(contextWithAuth, payload, b, testStoreName, clusterEdgeID)
269 assert.Nil(t, err)
270 assert.NotNil(t, bslSite)
271 assert.Equal(t, true, siteCreated)
272 assert.Equal(t, testEuid, bslSite.ID)
273
274 payload = getRegistrationStoreInfo(&createSiteFalse, nil)
275 bslSite, siteCreated, err = service.GetOrCreateSite(contextWithAuth, payload, b, testStoreName, clusterEdgeID)
276 assert.Nil(t, err)
277 assert.NotNil(t, bslSite)
278 assert.Equal(t, false, siteCreated)
279 assert.Empty(t, bslSite.ID)
280
281 payload = getRegistrationStoreInfo(nil, nil)
282 bslSite, siteCreated, err = service.GetOrCreateSite(contextWithAuth, payload, b, testStoreName, clusterEdgeID)
283 assert.Nil(t, err)
284 assert.NotNil(t, bslSite)
285 assert.Equal(t, false, siteCreated)
286 assert.Empty(t, bslSite.ID)
287 }
288
289 func getMockBspServer(t *testing.T) *httptest.Server {
290 return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
291 assert.Equal(t, r.Header.Get("Content-Type"), "application/json")
292 fmt.Printf("getMockBspServer url: %s\n", r.URL.String())
293 if strings.Contains(r.URL.String(), BslSitePath) {
294 if r.Method == http.MethodPost || r.Method == http.MethodPatch {
295 w.WriteHeader(200)
296 body, err := io.ReadAll(r.Body)
297 assert.NoError(t, err)
298 bslSite := &types.BSLSite{}
299 err = json.Unmarshal(body, bslSite)
300 assert.NoError(t, err)
301 bslSite.ID = testEuid
302 response, err := json.Marshal(bslSite)
303 assert.NoError(t, err)
304 _, err = w.Write(response)
305 assert.NoError(t, err)
306 } else if r.Method == http.MethodGet {
307 bslSiteID := getBSLSiteID(r.URL.Path)
308 org := r.Header["Nep-Organization"]
309 if org[0] == "" {
310 w.WriteHeader(403)
311 }
312 if strings.Trim(bslSiteID, "/") == testEuidNotExist {
313 w.WriteHeader(404)
314 } else {
315 w.WriteHeader(200)
316 bslSite := getMockBSLSiteAPI(strings.Trim(bslSiteID, "/"))
317 response, err := json.Marshal(bslSite)
318 assert.NoError(t, err)
319 _, err = w.Write(response)
320 assert.NoError(t, err)
321 }
322 } else if r.Method == http.MethodPut {
323 body, err := io.ReadAll(r.Body)
324 assert.NoError(t, err)
325 bslSite := &types.BSLSite{}
326 err = json.Unmarshal(body, bslSite)
327 assert.NoError(t, err)
328 if strings.Contains(bslSite.SiteName, deletedSuffix) {
329 expectedBslSite := getMockInactiveBSLSite(testEuid)
330 response, err := json.Marshal(bslSite)
331 assert.NoError(t, err)
332 assert.Equal(t, expectedBslSite.SiteName, bslSite.SiteName)
333 assert.Equal(t, expectedBslSite.EnterpriseUnitName, bslSite.EnterpriseUnitName)
334 assert.Equal(t, expectedBslSite.Status, bslSite.Status)
335 _, err = w.Write(response)
336 assert.NoError(t, err)
337 } else {
338 bslSite.ID = testEuid
339 bslSite.Status = InActiveStatus
340 bslSite.ReferenceID = testReferenceID
341 response, err := json.Marshal(bslSite)
342 assert.NoError(t, err)
343 _, err = w.Write(response)
344 assert.NoError(t, err)
345 }
346 } else {
347 w.WriteHeader(404)
348 }
349 } else if strings.Contains(r.URL.String(), "/provisioning/organizations/") && r.Method == http.MethodDelete {
350 w.WriteHeader(200)
351 _, err := io.ReadAll(r.Body)
352 assert.NoError(t, err)
353 bannerID := strings.Split(strings.TrimPrefix(r.URL.String(), "/provisioning/organizations/"), "/")[0]
354 assert.Equal(t, bannerID, testBannerID)
355 } else if strings.Contains(r.URL.String(), "/provisioning/organizations/") && r.Method == http.MethodPut {
356 w.WriteHeader(200)
357 body, err := io.ReadAll(r.Body)
358 assert.NoError(t, err)
359 payload := &types.UpdateOrganizationData{}
360 err = json.Unmarshal(body, payload)
361 assert.NoError(t, err)
362 assert.Equal(t, "new-name", payload.DisplayName)
363 } else if strings.Contains(r.URL.String(), BspEnterpriseUnitPath) && r.Method == http.MethodPut {
364 w.WriteHeader(200)
365 body, err := io.ReadAll(r.Body)
366 assert.NoError(t, err)
367 payload := &types.EnterpriseUnitData{}
368 err = json.Unmarshal(body, payload)
369 assert.NoError(t, err)
370 assert.Equal(t, testBannerID, payload.EnterpriseUnitID)
371 response, err := json.Marshal(payload)
372 assert.NoError(t, err)
373 _, err = w.Write(response)
374 assert.NoError(t, err)
375 } else if strings.HasSuffix(r.URL.String(), "/security/authentication/login") {
376 authResponse := &types.SecurityTokenData{Token: "anything", Username: "test-user", Authorities: []string{"NEP_IDENTITY_VIEWER"}}
377 writeResponse(w, authResponse)
378 } else if strings.Contains(r.URL.String(), "/provisioning/users/external/") && r.Method == http.MethodGet {
379 orgs := getMockExternalUserDate(testOrg, testOrgID)
380 res, _ := json.Marshal(orgs)
381 w.WriteHeader(http.StatusOK)
382 _, _ = w.Write(res)
383 } else if strings.Contains(r.URL.String(), "/provisioning/enterprise-unit-grants/unit-grants") && r.Method == http.MethodGet {
384 data := getMockEUData()
385 res, _ := json.Marshal(data)
386 w.WriteHeader(http.StatusOK)
387 _, _ = w.Write(res)
388 } else if strings.Contains(r.URL.String(), "/provisioning/enterprise-units/eu-banner-bsl-id") && r.Method == http.MethodGet {
389 data := getSingleMockEUdata()
390 res, _ := json.Marshal(data)
391 w.WriteHeader(http.StatusOK)
392 _, _ = w.Write(res)
393 } else {
394 w.WriteHeader(404)
395 }
396 }))
397 }
398
399
400 func getMockBSLSiteAPI(id string) *types.BSLSite {
401 return &types.BSLSite{
402 ID: id,
403 EnterpriseUnitName: testEnterpriseUnitName,
404 SiteName: testStoreName,
405 Status: ActiveStatus,
406 Coordinates: types.Coordinates{
407 Latitude: DefaultLatitude,
408 Longitude: DefaultLongitude,
409 },
410 }
411 }
412
413 func getMockInactiveBSLSite(id string) *types.BSLSite {
414 suffix := deletedSuffix + time.Now().UTC().Format(mapper.TimeFormat)
415 return &types.BSLSite{
416 ID: id,
417 EnterpriseUnitName: testEnterpriseUnitName,
418 SiteName: testStoreName + suffix,
419 Status: InActiveStatus,
420 Coordinates: types.Coordinates{
421 Latitude: DefaultLatitude,
422 Longitude: DefaultLongitude,
423 },
424 }
425 }
426
427 func getRegistrationStoreInfo(createSite *bool, euid *string) *model.StoreInfo {
428 lat := DefaultLatitude
429 lnt := DefaultLongitude
430 storeID := "anything"
431 referenceID := "reference-id"
432 return &model.StoreInfo{
433 StoreID: &storeID,
434 CreateSite: createSite,
435 SiteID: euid,
436 Latitude: &lat,
437 Longitude: &lnt,
438 ReferenceID: &referenceID,
439 }
440 }
441
442 func getMockExternalUserDate(testOrg, testOrgID string) *types.GetExternalUserResponse {
443 return &types.GetExternalUserResponse{
444 LastPage: true,
445 PageNumber: 0,
446 TotalPages: 1,
447 TotalResults: 1,
448 PageContent: []*types.ExternalUserData{
449 {
450 ID: testOrgID,
451 OrganizationName: testOrg,
452 DisplayName: testOrg,
453 Description: "test_description",
454 Primary: false,
455 },
456 {
457 ID: "test-org-banner",
458 OrganizationName: "test-org-banner",
459 DisplayName: "test-org-banner",
460 Description: "test_description",
461 Primary: false,
462 },
463 },
464 }
465 }
466
467 func getMockEUData() *types.FindEnterpriseUnitsResponse {
468 return &types.FindEnterpriseUnitsResponse{
469 LastPage: true,
470 PageNumber: 0,
471 TotalPages: 1,
472 TotalResults: 1,
473 PageContent: []*types.EnterpriseUnitData{
474 {
475 Active: true,
476 Description: "test_description",
477 EnterpriseTypeName: types.IsOrganization,
478 EnterpriseUnitID: "eu-banner-bsl-id",
479 Name: "test-banner-eu",
480 },
481 },
482 }
483 }
484
485 func getSingleMockEUdata() *types.EnterpriseUnitData {
486 return &types.EnterpriseUnitData{
487 Active: true,
488 Description: "test_description",
489 EnterpriseTypeName: types.IsOrganization,
490 EnterpriseUnitID: "eu-banner-bsl-id",
491 Name: "test-banner-eu",
492 }
493 }
494
View as plain text