package edgebsl import ( "context" "database/sql" "encoding/json" "errors" "testing" "time" "cloud.google.com/go/secretmanager/apiv1/secretmanagerpb" "github.com/DATA-DOG/go-sqlmock" "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" "edge-infra.dev/pkg/edge/api/graph/model" "edge-infra.dev/pkg/edge/api/mocks" "edge-infra.dev/pkg/edge/api/testutils/seededpostgres" btypes "edge-infra.dev/pkg/edge/api/types" "edge-infra.dev/pkg/edge/bsl" "edge-infra.dev/pkg/edge/constants" secretMgrApi "edge-infra.dev/pkg/lib/gcp/secretmanager" logger "edge-infra.dev/pkg/lib/logging" ) const ( testRootOrg = "/test_root_org/" testOrganizationPrefix = "test-prefix" databaseHost = "localhost" databaseName = "bsl_reconciler" databaseUser = "test" databasePassword = "12345" ) var ( databasePort int testTenant = &model.TenantInput{ TenantBSLId: "testingTenantBSLId", OrgName: "testingTenantBSLName", } ) func TestBSLController(t *testing.T) { ctx := context.Background() srv := createBSLServer(t, false) defer srv.Close() mockCtrl := gomock.NewController(t) mockSecretManager := mocks.NewMockSecretManagerService(mockCtrl) log := logger.NewLogger().Logger fn := func(_ context.Context, _ string) (btypes.SecretManagerService, error) { return mockSecretManager, nil } config := BslConfig{ RootOrg: testRootOrg, OrgPrefix: testOrganizationPrefix, RootURI: srv.URL, TopLevelProjectID: testForemanProject, BslReconcilesInterval: DefaultReconcileInterval, SQL: SQLConfig{ ConnectionName: databaseHost, User: databaseUser, DatabaseName: databaseName, }, AccessKey: &bsl.AccessKey{ SharedKey: testSharedKey, SecretKey: testSecretKey, }, Client: nil, } db, sqlMock := setup(t) sqlMock.MatchExpectationsInOrder(false) defer db.Close() setUpSecretManagerMocks(t, mockSecretManager) bslReconciler := New(fn, log, config, db) err := bslReconciler.Reconcile(ctx) assert.NoError(t, err) } func TestBSLControllerWithSQLConnection(t *testing.T) { ctx := context.Background() srv := createBSLServer(t, true) defer srv.Close() mockCtrl := gomock.NewController(t) mockSecretManager := mocks.NewMockSecretManagerService(mockCtrl) log := logger.NewLogger().Logger fn := func(_ context.Context, _ string) (btypes.SecretManagerService, error) { return mockSecretManager, nil } config := BslConfig{ RootOrg: testRootOrg, OrgPrefix: testOrganizationPrefix, RootURI: srv.URL, TopLevelProjectID: testForemanProject, BslReconcilesInterval: DefaultReconcileInterval, SQL: SQLConfig{ ConnectionName: databaseHost, User: databaseUser, DatabaseName: databaseName, }, AccessKey: &bsl.AccessKey{ SharedKey: testSharedKey, SecretKey: testSecretKey, }, Client: nil, } setUpSecretManagerMocks(t, mockSecretManager) dbConnection, sp, err := SetupSQLDB() assert.NoError(t, err) bslReconciler := New(fn, log, config, dbConnection) reconcileErr := bslReconciler.Reconcile(ctx) assert.NoError(t, reconcileErr) tenants, err := bslReconciler.TenantService.List(ctx) assert.Equal(t, 2, len(tenants)) assert.NoError(t, err) _, err = bslReconciler.TenantService.Create(ctx, testTenant) assert.NoError(t, err) tenants, err = bslReconciler.TenantService.List(ctx) assert.Equal(t, 3, len(tenants)) assert.NoError(t, err) t.Cleanup(func() { _ = dbConnection.Close() _ = sp.Close() }) } func setUpSecretManagerMocks(t *testing.T, mockSecretManager *mocks.MockSecretManagerService) { mockSecretManager.EXPECT().AddSecret(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). //nolint: dupl DoAndReturn(func(_ context.Context, secretID string, secretValue []byte, labels map[string]string, _ bool, _ *time.Time, _ string) error { assert.Contains(t, []string{"test-prefix-test-1", "test-prefix-test-1-org-admin", "test-prefix-test-2", "test-prefix-test-2-org-admin", "test-prefix-test-3", "test-prefix-test-3-org-admin"}, secretID) assert.Len(t, labels, 4) assert.Equal(t, labels[secretMgrApi.SecretLabel], "platform") assert.Equal(t, labels[secretMgrApi.SecretTypeLabel], "bsl-secret") assert.Equal(t, labels[secretMgrApi.SecretOwnerLabel], "edge") assert.Equal(t, labels[secretMgrApi.SecretNamespaceSelectorLabel], string(constants.PlatformNamespaceSelector)) var userPassword struct { Username string `json:"username"` Password string `json:"password"` SharedKey string `json:"shared_key"` SecretKey string `json:"secret_key"` } err := json.Unmarshal(secretValue, &userPassword) assert.NoError(t, err) assert.NotEmpty(t, userPassword.Username) assert.NotEmpty(t, userPassword.Password) assert.NotEmpty(t, userPassword.SharedKey) assert.NotEmpty(t, userPassword.SecretKey) return nil }).AnyTimes() mockSecretManager.EXPECT().GetSecret(gomock.Any(), gomock.Any()). //nolint: dupl DoAndReturn(func(_ context.Context, secretID string) (*secretmanagerpb.Secret, error) { assert.Contains(t, []string{"test-prefix-test-1", "test-prefix-test-1-org-admin", "test-prefix-test-2", "test-prefix-test-2-org-admin", "test-prefix-test-3", "test-prefix-test-3-org-admin"}, secretID) return &secretmanagerpb.Secret{}, errors.New("not found") }).AnyTimes() } func SetupSQLDB() (*sql.DB, *seededpostgres.SeededPostgres, error) { sp, err := seededpostgres.NewWithUser(databaseName, databaseUser, databasePassword) if err != nil { return nil, nil, err } db, err := sp.DB() if err != nil { _ = sp.Close() return nil, nil, err } databasePort = sp.Port() return db, sp, nil } func setup(t *testing.T) (*sql.DB, sqlmock.Sqlmock) { 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) } return db, mock }