package services import ( "context" "encoding/json" "fmt" "io" "net/http" "net/http/httptest" "strings" "testing" "time" "github.com/DATA-DOG/go-sqlmock" "github.com/google/uuid" "github.com/stretchr/testify/assert" bslerror "edge-infra.dev/pkg/edge/api/apierror/bsl" "edge-infra.dev/pkg/edge/api/bsl/types" "edge-infra.dev/pkg/edge/api/graph/mapper" "edge-infra.dev/pkg/edge/api/graph/model" "edge-infra.dev/pkg/edge/api/middleware" sqlquery "edge-infra.dev/pkg/edge/api/sql" edgetypes "edge-infra.dev/pkg/edge/api/types" "edge-infra.dev/pkg/edge/bsl" ) const ( testStoreName = "test-store" testEnterpriseUnitName = "test-enterprise-unit-name" testEuid = "test-euid" testEuidNotExist = "test-euid-not-exist" testUsername = "test-user" testBSLToken = "test-token" testOrganization = "test-org" testBanner = "test-banner" testReferenceID = "test-reference-id" testBannerID = "test-banner" ) func TestCreateBSLSite(t *testing.T) { srv := getMockBspServer(t) defer srv.Close() appConfig := edgetypes.Config{ BSP: types.BSPConfig{ Endpoint: srv.URL, }, } bslClient := bsl.NewBSLClient(appConfig.BSP, nil) bslSiteService := NewBSLSiteService(bslClient, nil) contextWithAuth := middleware.NewContext(context.Background(), &types.AuthUser{ Username: testUsername, Token: testBSLToken, Organization: testOrganization, }) bslsite, err := bslSiteService.CreateBSLSite(contextWithAuth, testStoreName, testEnterpriseUnitName, ActiveStatus, DefaultLatitude, DefaultLongitude, &model.Banner{BannerType: "eu"}, testReferenceID) assert.Nil(t, err) assert.NotEmpty(t, bslsite) assert.Equal(t, bslsite.ID, testEuid) assert.Equal(t, bslsite.SiteName, testStoreName) assert.Equal(t, bslsite.EnterpriseUnitName, testEnterpriseUnitName) assert.Equal(t, bslsite.Status, ActiveStatus) assert.Equal(t, bslsite.Coordinates.Latitude, DefaultLatitude) assert.Equal(t, bslsite.Coordinates.Longitude, DefaultLongitude) assert.Equal(t, bslsite.ReferenceID, testReferenceID) } func TestGetBSLSite(t *testing.T) { srv := getMockBspServer(t) defer srv.Close() appConfig := edgetypes.Config{ BSP: types.BSPConfig{ Endpoint: srv.URL, }, } contextWithAuth := middleware.NewContext(context.Background(), &types.AuthUser{ Username: testUsername, Token: testBSLToken, Organization: testOrganization, }) siteID := "site-id" secret := &bsl.AccessKey{SecretKey: "testSecretKey", SharedKey: "testSharedKey"} bslClient := bsl.NewBSLClient(appConfig.BSP, func(_ context.Context, _ string) (*bsl.AccessKey, error) { return secret, nil }) bslSiteService := NewBSLSiteService(bslClient, nil) bslInfo, err := bslSiteService.GetBSLSiteByID(contextWithAuth, siteID, &model.Banner{BannerType: "org", Name: "test-banner", BannerBSLId: "test-id"}) assert.Nil(t, err) assert.NotEmpty(t, bslInfo) assert.Equal(t, testStoreName, bslInfo.SiteName) assert.Equal(t, testEnterpriseUnitName, bslInfo.EnterpriseUnitName) assert.Equal(t, ActiveStatus, bslInfo.Status) assert.Equal(t, DefaultLatitude, bslInfo.Coordinates.Latitude) assert.Equal(t, DefaultLongitude, bslInfo.Coordinates.Longitude) } func TestDoesSiteHaveAttachedCluster(t *testing.T) { db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual)) if err != nil { t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) } defer db.Close() mock.ExpectQuery(sqlquery.DoesClusterWithSiteIDExist).WithArgs("123456789"). WillReturnRows(mock.NewRows([]string{""}).AddRow(true)) bslClient := bsl.NewBSLClient(appConfig.BSP, nil) service := NewBSLSiteService(bslClient, db) exists, err := service.DoesSiteHaveAttachedCluster(context.Background(), "123456789") assert.NoError(t, err) assert.True(t, exists) } func TestDeleteBSLSite(t *testing.T) { srv := getMockBspServer(t) defer srv.Close() appConfig := edgetypes.Config{ BSP: types.BSPConfig{ Endpoint: srv.URL, }, } contextWithAuth := middleware.NewContext(context.Background(), &types.AuthUser{ Username: testUsername, Token: testBSLToken, Organization: testOrganization, }) bslSite := &bsl.BSLInfo{ ID: testEuid, EnterpriseUnitName: testEnterpriseUnitName, SiteName: testStoreName, Status: ActiveStatus, Coordinates: bsl.Coordinates{ Latitude: DefaultLatitude, Longitude: DefaultLongitude, }, } secret := &bsl.AccessKey{SecretKey: "testPassword", SharedKey: "testUsername"} bslClient := bsl.NewBSLClient(appConfig.BSP, func(_ context.Context, _ string) (*bsl.AccessKey, error) { return secret, nil }) bslSiteService := NewBSLSiteService(bslClient, nil) errs := bslSiteService.DeleteBSLSite(contextWithAuth, bslSite, &model.Banner{BannerType: "eu"}) assert.Nil(t, errs) } func TestUpdateBSLSite(t *testing.T) { srv := getMockBspServer(t) defer srv.Close() appConfig := edgetypes.Config{ BSP: types.BSPConfig{ Endpoint: srv.URL, }, } contextWithAuth := middleware.NewContext(context.Background(), &types.AuthUser{ Username: testUsername, Token: testBSLToken, Organization: testOrganization, }) siteName := "test-site" site := model.Site{ SiteName: &siteName, } secret := &bsl.AccessKey{SecretKey: "testSecretKey", SharedKey: "testSharedKey"} bslClient := bsl.NewBSLClient(appConfig.BSP, func(_ context.Context, _ string) (*bsl.AccessKey, error) { return secret, nil }) bslSiteService := NewBSLSiteService(bslClient, nil) bslsite, err := bslSiteService.UpdateBSLSiteByID(contextWithAuth, &model.Banner{BannerType: "eu", Name: "test-banner"}, "site-id", site) assert.Nil(t, err) assert.NotEmpty(t, bslsite) assert.Equal(t, *bslsite.ID, testEuid) assert.Equal(t, bslsite.SiteName, siteName) assert.Equal(t, bslsite.EnterpriseUnitName, testEnterpriseUnitName) assert.Equal(t, bslsite.Status, InActiveStatus) assert.Equal(t, bslsite.Coordinates.Latitude, DefaultLatitude) assert.Equal(t, bslsite.Coordinates.Longitude, DefaultLongitude) assert.Equal(t, *bslsite.ReferenceID, testReferenceID) //org bslsite, err = bslSiteService.UpdateBSLSiteByID(contextWithAuth, &model.Banner{BannerType: "org", Name: "test-banner", BannerBSLId: "test-id"}, "site-id", site) assert.Nil(t, err) assert.NotEmpty(t, bslsite) assert.Equal(t, *bslsite.ID, testEuid) assert.Equal(t, bslsite.SiteName, siteName) assert.Equal(t, bslsite.EnterpriseUnitName, testEnterpriseUnitName) assert.Equal(t, bslsite.Status, InActiveStatus) assert.Equal(t, bslsite.Coordinates.Latitude, DefaultLatitude) assert.Equal(t, bslsite.Coordinates.Longitude, DefaultLongitude) assert.Equal(t, *bslsite.ReferenceID, testReferenceID) //org bad _, err = bslSiteService.UpdateBSLSiteByID(contextWithAuth, &model.Banner{BannerType: "org", Name: "test-banner", BannerEdgeID: "test-id"}, "site-id", site) assert.Error(t, err) } func TestBSLSiteService_GetOrCreateSite(t *testing.T) { srv := getMockBspServer(t) defer srv.Close() appConfig := edgetypes.Config{ BSP: types.BSPConfig{ Endpoint: srv.URL, }, } db, mock, err := sqlmock.New(sqlmock.QueryMatcherOption(sqlmock.QueryMatcherEqual)) if err != nil { t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) } defer db.Close() mock.ExpectQuery(sqlquery.DoesClusterWithSiteIDExist).WithArgs(testEuid). WillReturnRows(mock.NewRows([]string{""}).AddRow(true)) mock.ExpectQuery(sqlquery.DoesClusterWithSiteIDExist).WithArgs(testEuidNotExist). WillReturnRows(mock.NewRows([]string{""}).AddRow(false)) mock.ExpectQuery(sqlquery.DoesClusterWithSiteIDExist).WithArgs(testEuid). WillReturnRows(mock.NewRows([]string{""}).AddRow(false)) mock.ExpectQuery(sqlquery.DoesClusterWithSiteIDExist).WithArgs(testEuid). WillReturnRows(mock.NewRows([]string{""}).AddRow(false)) mock.ExpectQuery(sqlquery.DoesClusterWithSiteIDExist).WithArgs(testEuid). WillReturnRows(mock.NewRows([]string{""}).AddRow(false)) mock.ExpectQuery(sqlquery.DoesClusterWithSiteIDExist).WithArgs(testEuid). WillReturnRows(mock.NewRows([]string{""}).AddRow(false)) mock.ExpectQuery(sqlquery.DoesClusterWithSiteIDExist).WithArgs(testEuid). WillReturnRows(mock.NewRows([]string{""}).AddRow(false)) bslClient := bsl.NewBSLClient(appConfig.BSP, nil) service := NewBSLSiteService(bslClient, db) contextWithAuth := middleware.NewContext(context.Background(), &types.AuthUser{ Username: testUsername, Token: testBSLToken, Organization: testOrganization, }) // possible input for create site createSiteFalse := false createSiteTrue := true euid := testEuid euidNotExist := testEuidNotExist clusterEdgeID := uuid.NewString() b := &model.Banner{BannerType: "eu"} payload := getRegistrationStoreInfo(&createSiteTrue, &euid) bslSite, siteCreated, errs := service.GetOrCreateSite(contextWithAuth, payload, b, testStoreName, clusterEdgeID) assert.NotNil(t, errs) assert.Equal(t, "bsl site id has already been registered with a cluster", errs.Error()) assert.Nil(t, bslSite) assert.Equal(t, false, siteCreated) payload = getRegistrationStoreInfo(&createSiteFalse, &euidNotExist) bslSite, siteCreated, errs = service.GetOrCreateSite(contextWithAuth, payload, b, testStoreName, clusterEdgeID) assert.NotNil(t, errs) e, ok := errs.(*bslerror.Error) assert.True(t, ok, "%T", errs) assert.Equal(t, e.Extensions()["statusCode"], 404) assert.NotNil(t, bslSite) assert.Equal(t, "", bslSite.ID) assert.Equal(t, false, siteCreated) payload = getRegistrationStoreInfo(&createSiteFalse, &euid) bslSite, siteCreated, errs = service.GetOrCreateSite(contextWithAuth, payload, b, testStoreName, clusterEdgeID) assert.Nil(t, errs) assert.NotNil(t, bslSite) assert.Equal(t, false, siteCreated) assert.Equal(t, testEuid, bslSite.ID) payload = getRegistrationStoreInfo(nil, &euid) bslSite, siteCreated, err = service.GetOrCreateSite(contextWithAuth, payload, b, testStoreName, clusterEdgeID) assert.Nil(t, err) assert.NotNil(t, bslSite) assert.Equal(t, false, siteCreated) assert.Equal(t, testEuid, bslSite.ID) payload = getRegistrationStoreInfo(&createSiteTrue, nil) bslSite, siteCreated, err = service.GetOrCreateSite(contextWithAuth, payload, b, testStoreName, clusterEdgeID) assert.Nil(t, err) assert.NotNil(t, bslSite) assert.Equal(t, true, siteCreated) assert.Equal(t, testEuid, bslSite.ID) payload = getRegistrationStoreInfo(&createSiteFalse, nil) bslSite, siteCreated, err = service.GetOrCreateSite(contextWithAuth, payload, b, testStoreName, clusterEdgeID) assert.Nil(t, err) assert.NotNil(t, bslSite) assert.Equal(t, false, siteCreated) assert.Empty(t, bslSite.ID) payload = getRegistrationStoreInfo(nil, nil) bslSite, siteCreated, err = service.GetOrCreateSite(contextWithAuth, payload, b, testStoreName, clusterEdgeID) assert.Nil(t, err) assert.NotNil(t, bslSite) assert.Equal(t, false, siteCreated) assert.Empty(t, bslSite.ID) } func getMockBspServer(t *testing.T) *httptest.Server { return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { assert.Equal(t, r.Header.Get("Content-Type"), "application/json") fmt.Printf("getMockBspServer url: %s\n", r.URL.String()) if strings.Contains(r.URL.String(), BslSitePath) { //nolint: nestif if r.Method == http.MethodPost || r.Method == http.MethodPatch { w.WriteHeader(200) body, err := io.ReadAll(r.Body) assert.NoError(t, err) bslSite := &types.BSLSite{} err = json.Unmarshal(body, bslSite) assert.NoError(t, err) bslSite.ID = testEuid response, err := json.Marshal(bslSite) assert.NoError(t, err) _, err = w.Write(response) assert.NoError(t, err) } else if r.Method == http.MethodGet { bslSiteID := getBSLSiteID(r.URL.Path) org := r.Header["Nep-Organization"] if org[0] == "" { w.WriteHeader(403) } if strings.Trim(bslSiteID, "/") == testEuidNotExist { w.WriteHeader(404) } else { w.WriteHeader(200) bslSite := getMockBSLSiteAPI(strings.Trim(bslSiteID, "/")) response, err := json.Marshal(bslSite) assert.NoError(t, err) _, err = w.Write(response) assert.NoError(t, err) } } else if r.Method == http.MethodPut { body, err := io.ReadAll(r.Body) assert.NoError(t, err) bslSite := &types.BSLSite{} err = json.Unmarshal(body, bslSite) assert.NoError(t, err) if strings.Contains(bslSite.SiteName, deletedSuffix) { expectedBslSite := getMockInactiveBSLSite(testEuid) response, err := json.Marshal(bslSite) assert.NoError(t, err) assert.Equal(t, expectedBslSite.SiteName, bslSite.SiteName) assert.Equal(t, expectedBslSite.EnterpriseUnitName, bslSite.EnterpriseUnitName) assert.Equal(t, expectedBslSite.Status, bslSite.Status) _, err = w.Write(response) assert.NoError(t, err) } else { bslSite.ID = testEuid bslSite.Status = InActiveStatus bslSite.ReferenceID = testReferenceID response, err := json.Marshal(bslSite) assert.NoError(t, err) _, err = w.Write(response) assert.NoError(t, err) } } else { w.WriteHeader(404) } } else if strings.Contains(r.URL.String(), "/provisioning/organizations/") && r.Method == http.MethodDelete { w.WriteHeader(200) _, err := io.ReadAll(r.Body) assert.NoError(t, err) bannerID := strings.Split(strings.TrimPrefix(r.URL.String(), "/provisioning/organizations/"), "/")[0] assert.Equal(t, bannerID, testBannerID) } else if strings.Contains(r.URL.String(), "/provisioning/organizations/") && r.Method == http.MethodPut { w.WriteHeader(200) body, err := io.ReadAll(r.Body) assert.NoError(t, err) payload := &types.UpdateOrganizationData{} err = json.Unmarshal(body, payload) assert.NoError(t, err) assert.Equal(t, "new-name", payload.DisplayName) } else if strings.Contains(r.URL.String(), BspEnterpriseUnitPath) && r.Method == http.MethodPut { w.WriteHeader(200) body, err := io.ReadAll(r.Body) assert.NoError(t, err) payload := &types.EnterpriseUnitData{} err = json.Unmarshal(body, payload) assert.NoError(t, err) assert.Equal(t, testBannerID, payload.EnterpriseUnitID) response, err := json.Marshal(payload) assert.NoError(t, err) _, err = w.Write(response) assert.NoError(t, err) } else if strings.HasSuffix(r.URL.String(), "/security/authentication/login") { authResponse := &types.SecurityTokenData{Token: "anything", Username: "test-user", Authorities: []string{"NEP_IDENTITY_VIEWER"}} writeResponse(w, authResponse) } else if strings.Contains(r.URL.String(), "/provisioning/users/external/") && r.Method == http.MethodGet { orgs := getMockExternalUserDate(testOrg, testOrgID) res, _ := json.Marshal(orgs) w.WriteHeader(http.StatusOK) _, _ = w.Write(res) } else if strings.Contains(r.URL.String(), "/provisioning/enterprise-unit-grants/unit-grants") && r.Method == http.MethodGet { data := getMockEUData() res, _ := json.Marshal(data) w.WriteHeader(http.StatusOK) _, _ = w.Write(res) } else if strings.Contains(r.URL.String(), "/provisioning/enterprise-units/eu-banner-bsl-id") && r.Method == http.MethodGet { data := getSingleMockEUdata() res, _ := json.Marshal(data) w.WriteHeader(http.StatusOK) _, _ = w.Write(res) } else { w.WriteHeader(404) } })) } // getMockBSLSiteAPI returns a mock response of a BSL Site func getMockBSLSiteAPI(id string) *types.BSLSite { return &types.BSLSite{ ID: id, EnterpriseUnitName: testEnterpriseUnitName, SiteName: testStoreName, Status: ActiveStatus, Coordinates: types.Coordinates{ Latitude: DefaultLatitude, Longitude: DefaultLongitude, }, } } func getMockInactiveBSLSite(id string) *types.BSLSite { suffix := deletedSuffix + time.Now().UTC().Format(mapper.TimeFormat) return &types.BSLSite{ ID: id, EnterpriseUnitName: testEnterpriseUnitName, SiteName: testStoreName + suffix, Status: InActiveStatus, Coordinates: types.Coordinates{ Latitude: DefaultLatitude, Longitude: DefaultLongitude, }, } } func getRegistrationStoreInfo(createSite *bool, euid *string) *model.StoreInfo { lat := DefaultLatitude lnt := DefaultLongitude storeID := "anything" referenceID := "reference-id" return &model.StoreInfo{ StoreID: &storeID, CreateSite: createSite, SiteID: euid, Latitude: &lat, Longitude: &lnt, ReferenceID: &referenceID, } } func getMockExternalUserDate(testOrg, testOrgID string) *types.GetExternalUserResponse { return &types.GetExternalUserResponse{ LastPage: true, PageNumber: 0, TotalPages: 1, TotalResults: 1, PageContent: []*types.ExternalUserData{ { ID: testOrgID, OrganizationName: testOrg, DisplayName: testOrg, Description: "test_description", Primary: false, }, { ID: "test-org-banner", OrganizationName: "test-org-banner", DisplayName: "test-org-banner", Description: "test_description", Primary: false, }, }, } } func getMockEUData() *types.FindEnterpriseUnitsResponse { return &types.FindEnterpriseUnitsResponse{ LastPage: true, PageNumber: 0, TotalPages: 1, TotalResults: 1, PageContent: []*types.EnterpriseUnitData{ { Active: true, Description: "test_description", EnterpriseTypeName: types.IsOrganization, EnterpriseUnitID: "eu-banner-bsl-id", Name: "test-banner-eu", }, }, } } func getSingleMockEUdata() *types.EnterpriseUnitData { return &types.EnterpriseUnitData{ Active: true, Description: "test_description", EnterpriseTypeName: types.IsOrganization, EnterpriseUnitID: "eu-banner-bsl-id", Name: "test-banner-eu", } }