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  }