github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/internal/repo/testdb/generic_repo_upsert_tests.go (about) 1 package testdb 2 3 import ( 4 "context" 5 "fmt" 6 "reflect" 7 "testing" 8 9 "github.com/kyma-incubator/compass/components/director/pkg/apperrors" 10 "github.com/kyma-incubator/compass/components/director/pkg/persistence" 11 "github.com/pkg/errors" 12 "github.com/stretchr/testify/assert" 13 "github.com/stretchr/testify/require" 14 ) 15 16 // RepoUpsertTestSuite represents a generic test suite for repository Upsert, TrustedUpsert and UpsertGlobal methods of any entity that has externally managed tenants in m2m table/view. 17 // This test suite is not suitable for global entities or entities with embedded tenant in them. 18 type RepoUpsertTestSuite struct { 19 Name string 20 SQLQueryDetails []SQLQueryDetails 21 ConverterMockProvider func() Mock 22 RepoConstructorFunc interface{} 23 ModelEntity interface{} 24 DBEntity interface{} 25 NilModelEntity interface{} 26 TenantID string 27 DisableConverterErrorTest bool 28 UpsertMethodName string 29 } 30 31 // Run runs the generic repo upsert test suite 32 func (suite *RepoUpsertTestSuite) Run(t *testing.T) bool { 33 if len(suite.UpsertMethodName) == 0 { 34 suite.UpsertMethodName = "Upsert" 35 } 36 37 return t.Run(suite.Name, func(t *testing.T) { 38 testErr := errors.New("test error") 39 40 t.Run("success", func(t *testing.T) { 41 sqlxDB, sqlMock := MockDatabase(t) 42 ctx := persistence.SaveToContext(context.TODO(), sqlxDB) 43 44 configureValidSQLQueries(sqlMock, suite.SQLQueryDetails) 45 46 convMock := suite.ConverterMockProvider() 47 convMock.On("ToEntity", suite.ModelEntity).Return(suite.DBEntity, nil).Once() 48 pgRepository := createRepo(suite.RepoConstructorFunc, convMock) 49 // WHEN 50 err := callUpsert(pgRepository, ctx, suite.TenantID, suite.ModelEntity, suite.UpsertMethodName) 51 // THEN 52 require.NoError(t, err) 53 sqlMock.AssertExpectations(t) 54 convMock.AssertExpectations(t) 55 }) 56 57 for i := range suite.SQLQueryDetails { 58 t.Run(fmt.Sprintf("error if SQL query %d fail", i), func(t *testing.T) { 59 sqlxDB, sqlMock := MockDatabase(t) 60 ctx := persistence.SaveToContext(context.TODO(), sqlxDB) 61 62 configureFailureForSQLQueryOnIndex(sqlMock, suite.SQLQueryDetails, i, testErr) 63 64 convMock := suite.ConverterMockProvider() 65 convMock.On("ToEntity", suite.ModelEntity).Return(suite.DBEntity, nil).Once() 66 pgRepository := createRepo(suite.RepoConstructorFunc, convMock) 67 // WHEN 68 err := callUpsert(pgRepository, ctx, suite.TenantID, suite.ModelEntity, suite.UpsertMethodName) 69 // THEN 70 require.Error(t, err) 71 require.Equal(t, apperrors.InternalError, apperrors.ErrorCode(err)) 72 require.Contains(t, err.Error(), "Internal Server Error: Unexpected error while executing SQL query") 73 74 sqlMock.AssertExpectations(t) 75 convMock.AssertExpectations(t) 76 }) 77 } 78 79 if !suite.DisableConverterErrorTest { 80 t.Run("error when conversion fail", func(t *testing.T) { 81 sqlxDB, sqlMock := MockDatabase(t) 82 ctx := persistence.SaveToContext(context.TODO(), sqlxDB) 83 84 convMock := suite.ConverterMockProvider() 85 convMock.On("ToEntity", suite.ModelEntity).Return(nil, testErr).Once() 86 pgRepository := createRepo(suite.RepoConstructorFunc, convMock) 87 // WHEN 88 err := callUpsert(pgRepository, ctx, suite.TenantID, suite.ModelEntity, suite.UpsertMethodName) 89 // THEN 90 require.Error(t, err) 91 require.Contains(t, err.Error(), testErr.Error()) 92 93 sqlMock.AssertExpectations(t) 94 convMock.AssertExpectations(t) 95 }) 96 } 97 98 t.Run("returns error when item is nil", func(t *testing.T) { 99 ctx := context.TODO() 100 convMock := suite.ConverterMockProvider() 101 pgRepository := createRepo(suite.RepoConstructorFunc, convMock) 102 // WHEN 103 err := callUpsert(pgRepository, ctx, suite.TenantID, suite.NilModelEntity, suite.UpsertMethodName) 104 // THEN 105 require.Error(t, err) 106 assert.Contains(t, err.Error(), "Internal Server Error") 107 convMock.AssertExpectations(t) 108 }) 109 }) 110 } 111 112 func callUpsert(repo interface{}, ctx context.Context, tenant string, modelEntity interface{}, methodName string) error { 113 results := reflect.ValueOf(repo).MethodByName(methodName).Call([]reflect.Value{reflect.ValueOf(ctx), reflect.ValueOf(tenant), reflect.ValueOf(modelEntity)}) 114 if len(results) != 2 { 115 panic("Update should return two arguments") 116 } 117 result := results[1].Interface() 118 if result == nil { 119 return nil 120 } 121 err, ok := result.(error) 122 if !ok { 123 panic("Expected result to be an error") 124 } 125 return err 126 }