github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/internal/domain/runtime_context/repository_test.go (about)

     1  package runtimectx_test
     2  
     3  import (
     4  	"context"
     5  	"database/sql/driver"
     6  	"regexp"
     7  	"testing"
     8  
     9  	"github.com/stretchr/testify/assert"
    10  
    11  	"github.com/kyma-incubator/compass/components/director/pkg/pagination"
    12  
    13  	"github.com/DATA-DOG/go-sqlmock"
    14  	runtimectx "github.com/kyma-incubator/compass/components/director/internal/domain/runtime_context"
    15  	"github.com/kyma-incubator/compass/components/director/internal/domain/runtime_context/automock"
    16  	"github.com/kyma-incubator/compass/components/director/internal/labelfilter"
    17  	"github.com/kyma-incubator/compass/components/director/internal/model"
    18  	"github.com/kyma-incubator/compass/components/director/internal/repo/testdb"
    19  	"github.com/kyma-incubator/compass/components/director/pkg/persistence"
    20  	"github.com/stretchr/testify/require"
    21  )
    22  
    23  func TestPgRepository_GetByID(t *testing.T) {
    24  	runtimeCtxModel := fixModelRuntimeCtx()
    25  	runtimeCtxEntity := fixEntityRuntimeCtx()
    26  
    27  	suite := testdb.RepoGetTestSuite{
    28  		Name: "Get Runtime Context By ID",
    29  		SQLQueryDetails: []testdb.SQLQueryDetails{
    30  			{
    31  				Query:    regexp.QuoteMeta(`SELECT id, runtime_id, key, value FROM public.runtime_contexts WHERE id = $1 AND (id IN (SELECT id FROM tenant_runtime_contexts WHERE tenant_id = $2))`),
    32  				Args:     []driver.Value{runtimeCtxID, tenantID},
    33  				IsSelect: true,
    34  				ValidRowsProvider: func() []*sqlmock.Rows {
    35  					return []*sqlmock.Rows{sqlmock.NewRows(fixColumns).AddRow(runtimeCtxModel.ID, runtimeCtxModel.RuntimeID, runtimeCtxModel.Key, runtimeCtxModel.Value)}
    36  				},
    37  				InvalidRowsProvider: func() []*sqlmock.Rows {
    38  					return []*sqlmock.Rows{sqlmock.NewRows(fixColumns)}
    39  				},
    40  			},
    41  		},
    42  		ConverterMockProvider: func() testdb.Mock {
    43  			return &automock.EntityConverter{}
    44  		},
    45  		RepoConstructorFunc:       runtimectx.NewRepository,
    46  		ExpectedModelEntity:       runtimeCtxModel,
    47  		ExpectedDBEntity:          runtimeCtxEntity,
    48  		MethodArgs:                []interface{}{tenantID, runtimeCtxID},
    49  		DisableConverterErrorTest: true,
    50  	}
    51  
    52  	suite.Run(t)
    53  }
    54  
    55  func TestPgRepository_GetGlobalByID(t *testing.T) {
    56  	runtimeCtxModel := fixModelRuntimeCtx()
    57  	runtimeCtxEntity := fixEntityRuntimeCtx()
    58  
    59  	suite := testdb.RepoGetTestSuite{
    60  		Name: "Get Runtime Context Globally by ID",
    61  		SQLQueryDetails: []testdb.SQLQueryDetails{
    62  			{
    63  				Query:    regexp.QuoteMeta(`SELECT id, runtime_id, key, value FROM public.runtime_contexts WHERE id = $1`),
    64  				Args:     []driver.Value{runtimeCtxID},
    65  				IsSelect: true,
    66  				ValidRowsProvider: func() []*sqlmock.Rows {
    67  					return []*sqlmock.Rows{sqlmock.NewRows(fixColumns).AddRow(runtimeCtxModel.ID, runtimeCtxModel.RuntimeID, runtimeCtxModel.Key, runtimeCtxModel.Value)}
    68  				},
    69  				InvalidRowsProvider: func() []*sqlmock.Rows {
    70  					return []*sqlmock.Rows{sqlmock.NewRows(fixColumns)}
    71  				},
    72  			},
    73  		},
    74  		ConverterMockProvider: func() testdb.Mock {
    75  			return &automock.EntityConverter{}
    76  		},
    77  		RepoConstructorFunc:       runtimectx.NewRepository,
    78  		ExpectedModelEntity:       runtimeCtxModel,
    79  		ExpectedDBEntity:          runtimeCtxEntity,
    80  		MethodName:                "GetGlobalByID",
    81  		MethodArgs:                []interface{}{runtimeCtxID},
    82  		DisableConverterErrorTest: true,
    83  	}
    84  
    85  	suite.Run(t)
    86  }
    87  
    88  func TestPgRepository_GetByRuntimeID(t *testing.T) {
    89  	runtimeCtxModel := fixModelRuntimeCtx()
    90  	runtimeCtxEntity := fixEntityRuntimeCtx()
    91  
    92  	suite := testdb.RepoGetTestSuite{
    93  		Name: "Get Runtime Context By runtime ID",
    94  		SQLQueryDetails: []testdb.SQLQueryDetails{
    95  			{
    96  				Query:    regexp.QuoteMeta(`SELECT id, runtime_id, key, value FROM public.runtime_contexts WHERE runtime_id = $1 AND (id IN (SELECT id FROM tenant_runtime_contexts WHERE tenant_id = $2))`),
    97  				Args:     []driver.Value{runtimeID, tenantID},
    98  				IsSelect: true,
    99  				ValidRowsProvider: func() []*sqlmock.Rows {
   100  					return []*sqlmock.Rows{sqlmock.NewRows(fixColumns).AddRow(runtimeCtxModel.ID, runtimeCtxModel.RuntimeID, runtimeCtxModel.Key, runtimeCtxModel.Value)}
   101  				},
   102  				InvalidRowsProvider: func() []*sqlmock.Rows {
   103  					return []*sqlmock.Rows{sqlmock.NewRows(fixColumns)}
   104  				},
   105  			},
   106  		},
   107  		ConverterMockProvider: func() testdb.Mock {
   108  			return &automock.EntityConverter{}
   109  		},
   110  		RepoConstructorFunc:       runtimectx.NewRepository,
   111  		ExpectedModelEntity:       runtimeCtxModel,
   112  		ExpectedDBEntity:          runtimeCtxEntity,
   113  		MethodName:                "GetByRuntimeID",
   114  		MethodArgs:                []interface{}{tenantID, runtimeID},
   115  		DisableConverterErrorTest: true,
   116  	}
   117  
   118  	suite.Run(t)
   119  }
   120  
   121  func TestPgRepository_GetForRuntime(t *testing.T) {
   122  	runtimeCtxModel := fixModelRuntimeCtx()
   123  	runtimeCtxEntity := fixEntityRuntimeCtx()
   124  
   125  	suite := testdb.RepoGetTestSuite{
   126  		Name: "Get RuntimeContext For Runtime",
   127  		SQLQueryDetails: []testdb.SQLQueryDetails{
   128  			{
   129  				Query:    regexp.QuoteMeta(`SELECT id, runtime_id, key, value FROM public.runtime_contexts WHERE id = $1 AND runtime_id = $2 AND (id IN (SELECT id FROM tenant_runtime_contexts WHERE tenant_id = $3))`),
   130  				Args:     []driver.Value{runtimeCtxID, runtimeID, tenantID},
   131  				IsSelect: true,
   132  				ValidRowsProvider: func() []*sqlmock.Rows {
   133  					return []*sqlmock.Rows{
   134  						sqlmock.NewRows(fixColumns).
   135  							AddRow(runtimeCtxModel.ID, runtimeCtxModel.RuntimeID, runtimeCtxModel.Key, runtimeCtxModel.Value),
   136  					}
   137  				},
   138  				InvalidRowsProvider: func() []*sqlmock.Rows {
   139  					return []*sqlmock.Rows{
   140  						sqlmock.NewRows(fixColumns),
   141  					}
   142  				},
   143  			},
   144  		},
   145  		ConverterMockProvider: func() testdb.Mock {
   146  			return &automock.EntityConverter{}
   147  		},
   148  		RepoConstructorFunc:       runtimectx.NewRepository,
   149  		ExpectedModelEntity:       runtimeCtxModel,
   150  		ExpectedDBEntity:          runtimeCtxEntity,
   151  		MethodArgs:                []interface{}{tenantID, runtimeCtxID, runtimeID},
   152  		MethodName:                "GetForRuntime",
   153  		DisableConverterErrorTest: true,
   154  	}
   155  
   156  	suite.Run(t)
   157  }
   158  
   159  func TestPgRepository_GetByFiltersAndID(t *testing.T) {
   160  	runtimeCtxModel := fixModelRuntimeCtx()
   161  	runtimeCtxEntity := fixEntityRuntimeCtx()
   162  
   163  	suite := testdb.RepoGetTestSuite{
   164  		Name: "Get Runtime Context By Filters and ID",
   165  		SQLQueryDetails: []testdb.SQLQueryDetails{
   166  			{
   167  				Query: regexp.QuoteMeta(`SELECT id, runtime_id, key, value FROM public.runtime_contexts WHERE id = $1
   168  												AND id IN (SELECT "runtime_context_id" FROM public.labels WHERE "runtime_context_id" IS NOT NULL AND (id IN (SELECT id FROM runtime_contexts_labels_tenants WHERE tenant_id = $2)) AND "key" = $3 AND "value" ?| array[$4])
   169  												AND (id IN (SELECT id FROM tenant_runtime_contexts WHERE tenant_id = $5))`),
   170  				Args:     []driver.Value{runtimeCtxID, tenantID, model.ScenariosKey, "scenario", tenantID},
   171  				IsSelect: true,
   172  				ValidRowsProvider: func() []*sqlmock.Rows {
   173  					return []*sqlmock.Rows{sqlmock.NewRows(fixColumns).AddRow(runtimeCtxModel.ID, runtimeCtxModel.RuntimeID, runtimeCtxModel.Key, runtimeCtxModel.Value)}
   174  				},
   175  				InvalidRowsProvider: func() []*sqlmock.Rows {
   176  					return []*sqlmock.Rows{sqlmock.NewRows(fixColumns)}
   177  				},
   178  			},
   179  		},
   180  		ConverterMockProvider: func() testdb.Mock {
   181  			return &automock.EntityConverter{}
   182  		},
   183  		RepoConstructorFunc:       runtimectx.NewRepository,
   184  		ExpectedModelEntity:       runtimeCtxModel,
   185  		ExpectedDBEntity:          runtimeCtxEntity,
   186  		MethodName:                "GetByFiltersAndID",
   187  		MethodArgs:                []interface{}{tenantID, runtimeCtxID, []*labelfilter.LabelFilter{labelfilter.NewForKeyWithQuery(model.ScenariosKey, `$[*] ? ( @ == "scenario" )`)}},
   188  		DisableConverterErrorTest: true,
   189  	}
   190  
   191  	suite.Run(t)
   192  }
   193  
   194  func TestPgRepository_GetByFiltersGlobal_ShouldReturnRuntimeContextModelForRuntimeContextEntity(t *testing.T) {
   195  	// GIVEN
   196  	runtimeCtxModel := fixModelRuntimeCtx()
   197  	runtimeCtxEntity := fixEntityRuntimeCtx()
   198  
   199  	sqlxDB, sqlMock := testdb.MockDatabase(t)
   200  	defer sqlMock.AssertExpectations(t)
   201  
   202  	rows := sqlmock.NewRows(fixColumns).AddRow(runtimeCtxModel.ID, runtimeCtxModel.RuntimeID, runtimeCtxModel.Key, runtimeCtxModel.Value)
   203  
   204  	sqlMock.ExpectQuery(`^SELECT (.+) FROM public.runtime_contexts WHERE id IN \(SELECT "runtime_context_id" FROM public\.labels WHERE "runtime_context_id" IS NOT NULL AND "key" = \$1\)$`).
   205  		WithArgs("someKey").
   206  		WillReturnRows(rows)
   207  
   208  	ctx := persistence.SaveToContext(context.TODO(), sqlxDB)
   209  
   210  	mockConverter := &automock.EntityConverter{}
   211  	mockConverter.On("FromEntity", runtimeCtxEntity).Return(runtimeCtxModel, nil).Once()
   212  	pgRepository := runtimectx.NewRepository(mockConverter)
   213  
   214  	// WHEN
   215  	filters := []*labelfilter.LabelFilter{labelfilter.NewForKey("someKey")}
   216  	modelRuntimeCtx, err := pgRepository.GetByFiltersGlobal(ctx, filters)
   217  
   218  	// THEN
   219  	require.NoError(t, err)
   220  	require.Equal(t, runtimeCtxModel, modelRuntimeCtx)
   221  	mockConverter.AssertExpectations(t)
   222  }
   223  
   224  func TestPgRepository_List(t *testing.T) {
   225  	runtimeCtx1ID := "aec0e9c5-06da-4625-9f8a-bda17ab8c3b9"
   226  	runtimeCtx2ID := "ccdbef8f-b97a-490c-86e2-2bab2862a6e4"
   227  	runtimeCtxEntity1 := fixEntityRuntimeCtxWithID(runtimeCtx1ID)
   228  	runtimeCtxEntity2 := fixEntityRuntimeCtxWithID(runtimeCtx2ID)
   229  
   230  	runtimeCtxModel1 := fixModelRuntimeCtxWithID(runtimeCtx1ID)
   231  	runtimeCtxModel2 := fixModelRuntimeCtxWithID(runtimeCtx2ID)
   232  
   233  	suite := testdb.RepoListPageableTestSuite{
   234  		Name: "List Runtime Contexts",
   235  		SQLQueryDetails: []testdb.SQLQueryDetails{
   236  			{
   237  				Query: regexp.QuoteMeta(`SELECT id, runtime_id, key, value FROM public.runtime_contexts WHERE (runtime_id = $1
   238  												AND id IN (SELECT "runtime_context_id" FROM public.labels WHERE "runtime_context_id" IS NOT NULL AND (id IN (SELECT id FROM runtime_contexts_labels_tenants WHERE tenant_id = $2)) AND "key" = $3 AND "value" ?| array[$4])
   239  												AND (id IN (SELECT id FROM tenant_runtime_contexts WHERE tenant_id = $5))) ORDER BY id LIMIT 2 OFFSET 0`),
   240  				Args:     []driver.Value{runtimeID, tenantID, model.ScenariosKey, "scenario", tenantID},
   241  				IsSelect: true,
   242  				ValidRowsProvider: func() []*sqlmock.Rows {
   243  					return []*sqlmock.Rows{sqlmock.NewRows(fixColumns).
   244  						AddRow(runtimeCtxEntity1.ID, runtimeCtxEntity1.RuntimeID, runtimeCtxEntity1.Key, runtimeCtxEntity1.Value).
   245  						AddRow(runtimeCtxEntity2.ID, runtimeCtxEntity2.RuntimeID, runtimeCtxEntity2.Key, runtimeCtxEntity2.Value),
   246  					}
   247  				},
   248  			},
   249  			{
   250  				Query: regexp.QuoteMeta(`SELECT COUNT(*) FROM public.runtime_contexts WHERE (runtime_id = $1
   251  												AND id IN (SELECT "runtime_context_id" FROM public.labels WHERE "runtime_context_id" IS NOT NULL AND (id IN (SELECT id FROM runtime_contexts_labels_tenants WHERE tenant_id = $2)) AND "key" = $3 AND "value" ?| array[$4])
   252  												AND (id IN (SELECT id FROM tenant_runtime_contexts WHERE tenant_id = $5)))`),
   253  				Args:     []driver.Value{runtimeID, tenantID, model.ScenariosKey, "scenario", tenantID},
   254  				IsSelect: true,
   255  				ValidRowsProvider: func() []*sqlmock.Rows {
   256  					return []*sqlmock.Rows{sqlmock.NewRows([]string{"count"}).AddRow(2)}
   257  				},
   258  			},
   259  		},
   260  		Pages: []testdb.PageDetails{
   261  			{
   262  				ExpectedModelEntities: []interface{}{runtimeCtxModel1, runtimeCtxModel2},
   263  				ExpectedDBEntities:    []interface{}{runtimeCtxEntity1, runtimeCtxEntity2},
   264  				ExpectedPage: &model.RuntimeContextPage{
   265  					Data: []*model.RuntimeContext{runtimeCtxModel1, runtimeCtxModel2},
   266  					PageInfo: &pagination.Page{
   267  						StartCursor: "",
   268  						EndCursor:   "",
   269  						HasNextPage: false,
   270  					},
   271  					TotalCount: 2,
   272  				},
   273  			},
   274  		},
   275  		ConverterMockProvider: func() testdb.Mock {
   276  			return &automock.EntityConverter{}
   277  		},
   278  		RepoConstructorFunc:       runtimectx.NewRepository,
   279  		MethodArgs:                []interface{}{runtimeID, tenantID, []*labelfilter.LabelFilter{labelfilter.NewForKeyWithQuery(model.ScenariosKey, `$[*] ? ( @ == "scenario" )`)}, 2, ""},
   280  		MethodName:                "List",
   281  		DisableConverterErrorTest: true,
   282  	}
   283  
   284  	suite.Run(t)
   285  }
   286  
   287  func TestPgRepository_ListByRuntimeIDs(t *testing.T) {
   288  	pageSize := 1
   289  	cursor := ""
   290  
   291  	runtimeCtx1ID := "aec0e9c5-06da-4625-9f8a-bda17ab8c3b9"
   292  	runtimeCtx2ID := "ccdbef8f-b97a-490c-86e2-2bab2862a6e4"
   293  	runtimeCtxEntity1 := fixEntityRuntimeCtxWithIDAndRuntimeID(runtimeCtx1ID, onePageRuntimeID)
   294  	runtimeCtxEntity2 := fixEntityRuntimeCtxWithIDAndRuntimeID(runtimeCtx2ID, multiplePagesRuntimeID)
   295  
   296  	runtimeCtxModel1 := fixModelRuntimeCtxWithIDAndRuntimeID(runtimeCtx1ID, onePageRuntimeID)
   297  	runtimeCtxModel2 := fixModelRuntimeCtxWithIDAndRuntimeID(runtimeCtx2ID, multiplePagesRuntimeID)
   298  
   299  	suite := testdb.RepoListPageableTestSuite{
   300  		Name: "ListByRuntimeIDs Runtime Contexts",
   301  		SQLQueryDetails: []testdb.SQLQueryDetails{
   302  			{
   303  				Query: regexp.QuoteMeta(`(SELECT id, runtime_id, key, value FROM public.runtime_contexts WHERE (id IN (SELECT id FROM tenant_runtime_contexts WHERE tenant_id = $1)) AND runtime_id = $2 ORDER BY runtime_id ASC, id ASC LIMIT $3 OFFSET $4)
   304  												UNION
   305  												(SELECT id, runtime_id, key, value FROM public.runtime_contexts WHERE (id IN (SELECT id FROM tenant_runtime_contexts WHERE tenant_id = $5)) AND runtime_id = $6 ORDER BY runtime_id ASC, id ASC LIMIT $7 OFFSET $8)
   306  												UNION
   307  												(SELECT id, runtime_id, key, value FROM public.runtime_contexts WHERE (id IN (SELECT id FROM tenant_runtime_contexts WHERE tenant_id = $9)) AND runtime_id = $10 ORDER BY runtime_id ASC, id ASC LIMIT $11 OFFSET $12)`),
   308  				Args:     []driver.Value{tenantID, emptyPageRuntimeID, pageSize, 0, tenantID, onePageRuntimeID, pageSize, 0, tenantID, multiplePagesRuntimeID, pageSize, 0},
   309  				IsSelect: true,
   310  				ValidRowsProvider: func() []*sqlmock.Rows {
   311  					return []*sqlmock.Rows{sqlmock.NewRows(fixColumns).
   312  						AddRow(runtimeCtxEntity1.ID, runtimeCtxEntity1.RuntimeID, runtimeCtxEntity1.Key, runtimeCtxEntity1.Value).
   313  						AddRow(runtimeCtxEntity2.ID, runtimeCtxEntity2.RuntimeID, runtimeCtxEntity2.Key, runtimeCtxEntity2.Value),
   314  					}
   315  				},
   316  			},
   317  			{
   318  				Query:    regexp.QuoteMeta(`SELECT runtime_id AS id, COUNT(*) AS total_count FROM public.runtime_contexts WHERE (id IN (SELECT id FROM tenant_runtime_contexts WHERE tenant_id = $1)) GROUP BY runtime_id ORDER BY runtime_id ASC`),
   319  				Args:     []driver.Value{tenantID},
   320  				IsSelect: true,
   321  				ValidRowsProvider: func() []*sqlmock.Rows {
   322  					return []*sqlmock.Rows{sqlmock.NewRows([]string{"id", "total_count"}).AddRow(emptyPageRuntimeID, 0).AddRow(onePageRuntimeID, 1).AddRow(multiplePagesRuntimeID, 2)}
   323  				},
   324  			},
   325  		},
   326  		Pages: []testdb.PageDetails{
   327  			{
   328  				ExpectedModelEntities: nil,
   329  				ExpectedDBEntities:    nil,
   330  				ExpectedPage: &model.RuntimeContextPage{
   331  					Data: nil,
   332  					PageInfo: &pagination.Page{
   333  						StartCursor: "",
   334  						EndCursor:   "",
   335  						HasNextPage: false,
   336  					},
   337  					TotalCount: 0,
   338  				},
   339  			},
   340  			{
   341  				ExpectedModelEntities: []interface{}{runtimeCtxModel1},
   342  				ExpectedDBEntities:    []interface{}{runtimeCtxEntity1},
   343  				ExpectedPage: &model.RuntimeContextPage{
   344  					Data: []*model.RuntimeContext{runtimeCtxModel1},
   345  					PageInfo: &pagination.Page{
   346  						StartCursor: "",
   347  						EndCursor:   "",
   348  						HasNextPage: false,
   349  					},
   350  					TotalCount: 1,
   351  				},
   352  			},
   353  			{
   354  				ExpectedModelEntities: []interface{}{runtimeCtxModel2},
   355  				ExpectedDBEntities:    []interface{}{runtimeCtxEntity2},
   356  				ExpectedPage: &model.RuntimeContextPage{
   357  					Data: []*model.RuntimeContext{runtimeCtxModel2},
   358  					PageInfo: &pagination.Page{
   359  						StartCursor: "",
   360  						EndCursor:   pagination.EncodeNextOffsetCursor(0, pageSize),
   361  						HasNextPage: true,
   362  					},
   363  					TotalCount: 2,
   364  				},
   365  			},
   366  		},
   367  		ConverterMockProvider: func() testdb.Mock {
   368  			return &automock.EntityConverter{}
   369  		},
   370  		RepoConstructorFunc:       runtimectx.NewRepository,
   371  		MethodArgs:                []interface{}{tenantID, []string{emptyPageRuntimeID, onePageRuntimeID, multiplePagesRuntimeID}, pageSize, cursor},
   372  		MethodName:                "ListByRuntimeIDs",
   373  		DisableConverterErrorTest: true,
   374  	}
   375  
   376  	suite.Run(t)
   377  }
   378  
   379  func TestPgRepository_ListByScenariosAndRuntimeIDs(t *testing.T) {
   380  	scenario1 := "scenario-1"
   381  	scenario2 := "scenario-2"
   382  	runtimeCtx1ID := "aec0e9c5-06da-4625-9f8a-bda17ab8c3b9"
   383  	runtimeCtx2ID := "ccdbef8f-b97a-490c-86e2-2bab2862a6e4"
   384  	runtimeCtxEntity1 := fixEntityRuntimeCtxWithIDAndRuntimeID(runtimeCtx1ID, runtimeID)
   385  	runtimeCtxEntity2 := fixEntityRuntimeCtxWithIDAndRuntimeID(runtimeCtx2ID, runtimeID2)
   386  
   387  	runtimeCtxModel1 := fixModelRuntimeCtxWithIDAndRuntimeID(runtimeCtx1ID, runtimeID)
   388  	runtimeCtxModel2 := fixModelRuntimeCtxWithIDAndRuntimeID(runtimeCtx2ID, runtimeID2)
   389  
   390  	suite := testdb.RepoListTestSuite{
   391  		Name: "ListByScenariosAndRuntimeIDs Runtime Contexts",
   392  		SQLQueryDetails: []testdb.SQLQueryDetails{
   393  			{
   394  				Query: regexp.QuoteMeta(`SELECT id, runtime_id, key, value FROM public.runtime_contexts 
   395  											  WHERE id IN (SELECT "runtime_context_id" FROM public.labels 
   396  											  WHERE "runtime_context_id" IS NOT NULL 
   397  											  AND (id IN (SELECT id FROM runtime_contexts_labels_tenants WHERE tenant_id = $1)) 
   398  											  AND "key" = $2 AND "value" ?| array[$3] UNION SELECT "runtime_context_id" FROM public.labels WHERE "runtime_context_id" IS NOT NULL 
   399  											  AND (id IN (SELECT id FROM runtime_contexts_labels_tenants WHERE tenant_id = $4)) AND "key" = $5 
   400  											  AND "value" ?| array[$6]) AND runtime_id IN ($7, $8) 
   401  											  AND (id IN (SELECT id FROM tenant_runtime_contexts WHERE tenant_id = $9))`),
   402  				Args:     []driver.Value{tenantID, model.ScenariosKey, scenario1, tenantID, model.ScenariosKey, scenario2, runtimeID, runtimeID2, tenantID},
   403  				IsSelect: true,
   404  				ValidRowsProvider: func() []*sqlmock.Rows {
   405  					return []*sqlmock.Rows{sqlmock.NewRows(fixColumns).
   406  						AddRow(runtimeCtxEntity1.ID, runtimeCtxEntity1.RuntimeID, runtimeCtxEntity1.Key, runtimeCtxEntity1.Value).
   407  						AddRow(runtimeCtxEntity2.ID, runtimeCtxEntity2.RuntimeID, runtimeCtxEntity2.Key, runtimeCtxEntity2.Value),
   408  					}
   409  				},
   410  				InvalidRowsProvider: func() []*sqlmock.Rows {
   411  					return []*sqlmock.Rows{sqlmock.NewRows(fixColumns)}
   412  				},
   413  			},
   414  		},
   415  		ExpectedModelEntities: []interface{}{runtimeCtxModel1, runtimeCtxModel2},
   416  		ExpectedDBEntities:    []interface{}{runtimeCtxEntity1, runtimeCtxEntity2},
   417  		ConverterMockProvider: func() testdb.Mock {
   418  			return &automock.EntityConverter{}
   419  		},
   420  		RepoConstructorFunc:       runtimectx.NewRepository,
   421  		MethodArgs:                []interface{}{tenantID, []string{scenario1, scenario2}, []string{runtimeID, runtimeID2}},
   422  		MethodName:                "ListByScenariosAndRuntimeIDs",
   423  		DisableConverterErrorTest: true,
   424  	}
   425  
   426  	suite.Run(t)
   427  
   428  	// Additional test - empty slice because test suite returns empty result given valid query
   429  	t.Run("returns empty slice given no scenarios", func(t *testing.T) {
   430  		// GIVEN
   431  		ctx := context.TODO()
   432  		repository := runtimectx.NewRepository(nil)
   433  
   434  		// WHEN
   435  		actual, err := repository.ListByScenariosAndRuntimeIDs(ctx, tenantID, []string{}, []string{})
   436  
   437  		// THEN
   438  		assert.NoError(t, err)
   439  		assert.Nil(t, actual)
   440  	})
   441  }
   442  
   443  func TestPgRepository_ListByScenarios(t *testing.T) {
   444  	scenario1 := "scenario-1"
   445  	scenario2 := "scenario-2"
   446  	runtimeCtx1ID := "aec0e9c5-06da-4625-9f8a-bda17ab8c3b9"
   447  	runtimeCtx2ID := "ccdbef8f-b97a-490c-86e2-2bab2862a6e4"
   448  	runtimeCtxEntity1 := fixEntityRuntimeCtxWithIDAndRuntimeID(runtimeCtx1ID, runtimeID)
   449  	runtimeCtxEntity2 := fixEntityRuntimeCtxWithIDAndRuntimeID(runtimeCtx2ID, runtimeID2)
   450  
   451  	runtimeCtxModel1 := fixModelRuntimeCtxWithIDAndRuntimeID(runtimeCtx1ID, runtimeID)
   452  	runtimeCtxModel2 := fixModelRuntimeCtxWithIDAndRuntimeID(runtimeCtx2ID, runtimeID2)
   453  
   454  	suite := testdb.RepoListTestSuite{
   455  		Name: "ListByScenarios Runtime Contexts",
   456  		SQLQueryDetails: []testdb.SQLQueryDetails{
   457  			{
   458  				Query: regexp.QuoteMeta(`SELECT id, runtime_id, key, value FROM public.runtime_contexts 
   459  											  WHERE id IN (SELECT "runtime_context_id" FROM public.labels 
   460  											  WHERE "runtime_context_id" IS NOT NULL 
   461  											  AND (id IN (SELECT id FROM runtime_contexts_labels_tenants WHERE tenant_id = $1)) 
   462  											  AND "key" = $2 AND "value" ?| array[$3] UNION SELECT "runtime_context_id" FROM public.labels WHERE "runtime_context_id" IS NOT NULL 
   463  											  AND (id IN (SELECT id FROM runtime_contexts_labels_tenants WHERE tenant_id = $4)) AND "key" = $5 
   464  											  AND "value" ?| array[$6]) 
   465  											  AND (id IN (SELECT id FROM tenant_runtime_contexts WHERE tenant_id = $7))`),
   466  				Args:     []driver.Value{tenantID, model.ScenariosKey, scenario1, tenantID, model.ScenariosKey, scenario2, tenantID},
   467  				IsSelect: true,
   468  				ValidRowsProvider: func() []*sqlmock.Rows {
   469  					return []*sqlmock.Rows{sqlmock.NewRows(fixColumns).
   470  						AddRow(runtimeCtxEntity1.ID, runtimeCtxEntity1.RuntimeID, runtimeCtxEntity1.Key, runtimeCtxEntity1.Value).
   471  						AddRow(runtimeCtxEntity2.ID, runtimeCtxEntity2.RuntimeID, runtimeCtxEntity2.Key, runtimeCtxEntity2.Value),
   472  					}
   473  				},
   474  				InvalidRowsProvider: func() []*sqlmock.Rows {
   475  					return []*sqlmock.Rows{sqlmock.NewRows(fixColumns)}
   476  				},
   477  			},
   478  		},
   479  		ExpectedModelEntities: []interface{}{runtimeCtxModel1, runtimeCtxModel2},
   480  		ExpectedDBEntities:    []interface{}{runtimeCtxEntity1, runtimeCtxEntity2},
   481  		ConverterMockProvider: func() testdb.Mock {
   482  			return &automock.EntityConverter{}
   483  		},
   484  		RepoConstructorFunc:       runtimectx.NewRepository,
   485  		MethodArgs:                []interface{}{tenantID, []string{scenario1, scenario2}},
   486  		MethodName:                "ListByScenarios",
   487  		DisableConverterErrorTest: true,
   488  	}
   489  
   490  	suite.Run(t)
   491  
   492  	// Additional test - empty slice because test suite returns empty result given valid query
   493  	t.Run("returns empty slice given no scenarios", func(t *testing.T) {
   494  		// GIVEN
   495  		ctx := context.TODO()
   496  		repository := runtimectx.NewRepository(nil)
   497  
   498  		// WHEN
   499  		actual, err := repository.ListByScenarios(ctx, tenantID, []string{})
   500  
   501  		// THEN
   502  		assert.NoError(t, err)
   503  		assert.Nil(t, actual)
   504  	})
   505  }
   506  
   507  func TestPgRepository_ListByIDs(t *testing.T) {
   508  	runtimeCtx1ID := "aec0e9c5-06da-4625-9f8a-bda17ab8c3b9"
   509  	runtimeCtx2ID := "ccdbef8f-b97a-490c-86e2-2bab2862a6e4"
   510  	runtimeCtxEntity1 := fixEntityRuntimeCtxWithIDAndRuntimeID(runtimeCtx1ID, runtimeID)
   511  	runtimeCtxEntity2 := fixEntityRuntimeCtxWithIDAndRuntimeID(runtimeCtx2ID, runtimeID2)
   512  
   513  	runtimeCtxModel1 := fixModelRuntimeCtxWithIDAndRuntimeID(runtimeCtx1ID, runtimeID)
   514  	runtimeCtxModel2 := fixModelRuntimeCtxWithIDAndRuntimeID(runtimeCtx2ID, runtimeID2)
   515  
   516  	suite := testdb.RepoListTestSuite{
   517  		Name: "ListByIDs Runtime Contexts",
   518  		SQLQueryDetails: []testdb.SQLQueryDetails{
   519  			{
   520  				Query:    regexp.QuoteMeta(`SELECT id, runtime_id, key, value FROM public.runtime_contexts WHERE id IN ($1, $2) AND (id IN (SELECT id FROM tenant_runtime_contexts WHERE tenant_id = $3))`),
   521  				Args:     []driver.Value{runtimeCtx1ID, runtimeCtx2ID, tenantID},
   522  				IsSelect: true,
   523  				ValidRowsProvider: func() []*sqlmock.Rows {
   524  					return []*sqlmock.Rows{sqlmock.NewRows(fixColumns).
   525  						AddRow(runtimeCtxEntity1.ID, runtimeCtxEntity1.RuntimeID, runtimeCtxEntity1.Key, runtimeCtxEntity1.Value).
   526  						AddRow(runtimeCtxEntity2.ID, runtimeCtxEntity2.RuntimeID, runtimeCtxEntity2.Key, runtimeCtxEntity2.Value),
   527  					}
   528  				},
   529  				InvalidRowsProvider: func() []*sqlmock.Rows {
   530  					return []*sqlmock.Rows{sqlmock.NewRows(fixColumns)}
   531  				},
   532  			},
   533  		},
   534  		ExpectedModelEntities: []interface{}{runtimeCtxModel1, runtimeCtxModel2},
   535  		ExpectedDBEntities:    []interface{}{runtimeCtxEntity1, runtimeCtxEntity2},
   536  		ConverterMockProvider: func() testdb.Mock {
   537  			return &automock.EntityConverter{}
   538  		},
   539  		RepoConstructorFunc:       runtimectx.NewRepository,
   540  		MethodArgs:                []interface{}{tenantID, []string{runtimeCtx1ID, runtimeCtx2ID}},
   541  		MethodName:                "ListByIDs",
   542  		DisableConverterErrorTest: true,
   543  	}
   544  
   545  	suite.Run(t)
   546  
   547  	// Additional test - empty slice because test suite returns empty result given valid query
   548  	t.Run("returns empty slice given no scenarios", func(t *testing.T) {
   549  		// GIVEN
   550  		ctx := context.TODO()
   551  		repository := runtimectx.NewRepository(nil)
   552  
   553  		// WHEN
   554  		actual, err := repository.ListByIDs(ctx, tenantID, []string{})
   555  
   556  		// THEN
   557  		assert.NoError(t, err)
   558  		assert.Nil(t, actual)
   559  	})
   560  }
   561  func TestPgRepository_ListAll(t *testing.T) {
   562  	runtimeCtx1ID := "aec0e9c5-06da-4625-9f8a-bda17ab8c3b9"
   563  	runtimeCtx2ID := "ccdbef8f-b97a-490c-86e2-2bab2862a6e4"
   564  	runtimeCtxEntity1 := fixEntityRuntimeCtxWithID(runtimeCtx1ID)
   565  	runtimeCtxEntity2 := fixEntityRuntimeCtxWithID(runtimeCtx2ID)
   566  
   567  	runtimeCtxModel1 := fixModelRuntimeCtxWithID(runtimeCtx1ID)
   568  	runtimeCtxModel2 := fixModelRuntimeCtxWithID(runtimeCtx2ID)
   569  
   570  	suite := testdb.RepoListTestSuite{
   571  		Name: "List Runtime Contexts",
   572  		SQLQueryDetails: []testdb.SQLQueryDetails{
   573  			{
   574  				Query:    regexp.QuoteMeta(`SELECT id, runtime_id, key, value FROM public.runtime_contexts WHERE (id IN (SELECT id FROM tenant_runtime_contexts WHERE tenant_id = $1))`),
   575  				Args:     []driver.Value{tenantID},
   576  				IsSelect: true,
   577  				ValidRowsProvider: func() []*sqlmock.Rows {
   578  					return []*sqlmock.Rows{sqlmock.NewRows(fixColumns).
   579  						AddRow(runtimeCtxEntity1.ID, runtimeCtxEntity1.RuntimeID, runtimeCtxEntity1.Key, runtimeCtxEntity1.Value).
   580  						AddRow(runtimeCtxEntity2.ID, runtimeCtxEntity2.RuntimeID, runtimeCtxEntity2.Key, runtimeCtxEntity2.Value),
   581  					}
   582  				},
   583  				InvalidRowsProvider: func() []*sqlmock.Rows {
   584  					return []*sqlmock.Rows{sqlmock.NewRows(fixColumns)}
   585  				},
   586  			},
   587  		},
   588  		ConverterMockProvider: func() testdb.Mock {
   589  			return &automock.EntityConverter{}
   590  		},
   591  		ExpectedDBEntities:        []interface{}{runtimeCtxEntity1, runtimeCtxEntity2},
   592  		ExpectedModelEntities:     []interface{}{runtimeCtxModel1, runtimeCtxModel2},
   593  		RepoConstructorFunc:       runtimectx.NewRepository,
   594  		MethodArgs:                []interface{}{tenantID},
   595  		MethodName:                "ListAll",
   596  		DisableConverterErrorTest: true,
   597  	}
   598  
   599  	suite.Run(t)
   600  }
   601  
   602  func TestPgRepository_ListAllForRuntime(t *testing.T) {
   603  	runtimeCtx1ID := "aec0e9c5-06da-4625-9f8a-bda17ab8c3b9"
   604  	runtimeCtx2ID := "ccdbef8f-b97a-490c-86e2-2bab2862a6e4"
   605  	runtimeCtxEntity1 := fixEntityRuntimeCtxWithID(runtimeCtx1ID)
   606  	runtimeCtxEntity2 := fixEntityRuntimeCtxWithID(runtimeCtx2ID)
   607  
   608  	runtimeCtxModel1 := fixModelRuntimeCtxWithID(runtimeCtx1ID)
   609  	runtimeCtxModel2 := fixModelRuntimeCtxWithID(runtimeCtx2ID)
   610  
   611  	suite := testdb.RepoListTestSuite{
   612  		Name: "List Runtime Contexts",
   613  		SQLQueryDetails: []testdb.SQLQueryDetails{
   614  			{
   615  				Query: regexp.QuoteMeta(`SELECT id, runtime_id, key, value FROM public.runtime_contexts WHERE runtime_id = $1 AND
   616  												(id IN (SELECT id FROM tenant_runtime_contexts WHERE tenant_id = $2))`),
   617  				Args:     []driver.Value{runtimeID, tenantID},
   618  				IsSelect: true,
   619  				ValidRowsProvider: func() []*sqlmock.Rows {
   620  					return []*sqlmock.Rows{sqlmock.NewRows(fixColumns).
   621  						AddRow(runtimeCtxEntity1.ID, runtimeCtxEntity1.RuntimeID, runtimeCtxEntity1.Key, runtimeCtxEntity1.Value).
   622  						AddRow(runtimeCtxEntity2.ID, runtimeCtxEntity2.RuntimeID, runtimeCtxEntity2.Key, runtimeCtxEntity2.Value),
   623  					}
   624  				},
   625  				InvalidRowsProvider: func() []*sqlmock.Rows {
   626  					return []*sqlmock.Rows{sqlmock.NewRows(fixColumns)}
   627  				},
   628  			},
   629  		},
   630  		ConverterMockProvider: func() testdb.Mock {
   631  			return &automock.EntityConverter{}
   632  		},
   633  		ExpectedDBEntities:        []interface{}{runtimeCtxEntity1, runtimeCtxEntity2},
   634  		ExpectedModelEntities:     []interface{}{runtimeCtxModel1, runtimeCtxModel2},
   635  		RepoConstructorFunc:       runtimectx.NewRepository,
   636  		MethodArgs:                []interface{}{tenantID, runtimeID},
   637  		MethodName:                "ListAllForRuntime",
   638  		DisableConverterErrorTest: true,
   639  	}
   640  
   641  	suite.Run(t)
   642  }
   643  
   644  func TestPgRepository_Create(t *testing.T) {
   645  	var nilRuntimeCtxModel *model.RuntimeContext
   646  	runtimeCtxModel := fixModelRuntimeCtx()
   647  	runtimeCtxEntity := fixEntityRuntimeCtx()
   648  
   649  	suite := testdb.RepoCreateTestSuite{
   650  		Name: "Create Runtime Context",
   651  		SQLQueryDetails: []testdb.SQLQueryDetails{
   652  			{
   653  				Query:       `^INSERT INTO public.runtime_contexts \(.+\) VALUES \(.+\)$`,
   654  				Args:        []driver.Value{runtimeCtxModel.ID, runtimeCtxModel.RuntimeID, runtimeCtxModel.Key, runtimeCtxModel.Value},
   655  				ValidResult: sqlmock.NewResult(-1, 1),
   656  			},
   657  			{
   658  				Query:       regexp.QuoteMeta(`WITH RECURSIVE parents AS (SELECT t1.id, t1.parent FROM business_tenant_mappings t1 WHERE id = ? UNION ALL SELECT t2.id, t2.parent FROM business_tenant_mappings t2 INNER JOIN parents t on t2.id = t.parent) INSERT INTO tenant_runtime_contexts ( tenant_id, id, owner ) (SELECT parents.id AS tenant_id, ? as id, ? AS owner FROM parents)`),
   659  				Args:        []driver.Value{tenantID, runtimeCtxModel.ID, true},
   660  				ValidResult: sqlmock.NewResult(-1, 1),
   661  			},
   662  		},
   663  		ConverterMockProvider: func() testdb.Mock {
   664  			return &automock.EntityConverter{}
   665  		},
   666  		RepoConstructorFunc:       runtimectx.NewRepository,
   667  		ModelEntity:               runtimeCtxModel,
   668  		DBEntity:                  runtimeCtxEntity,
   669  		NilModelEntity:            nilRuntimeCtxModel,
   670  		TenantID:                  tenantID,
   671  		DisableConverterErrorTest: true,
   672  		IsTopLevelEntity:          true,
   673  	}
   674  
   675  	suite.Run(t)
   676  }
   677  
   678  func TestPgRepository_Update(t *testing.T) {
   679  	var nilRuntimeCtxModel *model.RuntimeContext
   680  	runtimeCtxModel := fixModelRuntimeCtx()
   681  	runtimeCtxEntity := fixEntityRuntimeCtx()
   682  
   683  	suite := testdb.RepoUpdateTestSuite{
   684  		Name: "Update Runtime Context",
   685  		SQLQueryDetails: []testdb.SQLQueryDetails{
   686  			{
   687  				Query:         regexp.QuoteMeta(`UPDATE public.runtime_contexts SET key = ?, value = ? WHERE id = ? AND (id IN (SELECT id FROM tenant_runtime_contexts WHERE tenant_id = ? AND owner = true))`),
   688  				Args:          []driver.Value{runtimeCtxModel.Key, runtimeCtxModel.Value, runtimeCtxModel.ID, tenantID},
   689  				ValidResult:   sqlmock.NewResult(-1, 1),
   690  				InvalidResult: sqlmock.NewResult(-1, 0),
   691  			},
   692  		},
   693  		ConverterMockProvider: func() testdb.Mock {
   694  			return &automock.EntityConverter{}
   695  		},
   696  		RepoConstructorFunc:       runtimectx.NewRepository,
   697  		ModelEntity:               runtimeCtxModel,
   698  		DBEntity:                  runtimeCtxEntity,
   699  		NilModelEntity:            nilRuntimeCtxModel,
   700  		TenantID:                  tenantID,
   701  		DisableConverterErrorTest: true,
   702  	}
   703  
   704  	suite.Run(t)
   705  }
   706  
   707  func TestPgRepository_Delete(t *testing.T) {
   708  	suite := testdb.RepoDeleteTestSuite{
   709  		Name: "Runtime Context Delete",
   710  		SQLQueryDetails: []testdb.SQLQueryDetails{
   711  			{
   712  				Query:         regexp.QuoteMeta(`DELETE FROM public.runtime_contexts WHERE id = $1 AND (id IN (SELECT id FROM tenant_runtime_contexts WHERE tenant_id = $2 AND owner = true))`),
   713  				Args:          []driver.Value{runtimeCtxID, tenantID},
   714  				ValidResult:   sqlmock.NewResult(-1, 1),
   715  				InvalidResult: sqlmock.NewResult(-1, 2),
   716  			},
   717  		},
   718  		ConverterMockProvider: func() testdb.Mock {
   719  			return &automock.EntityConverter{}
   720  		},
   721  		RepoConstructorFunc: runtimectx.NewRepository,
   722  		MethodArgs:          []interface{}{tenantID, runtimeCtxID},
   723  	}
   724  
   725  	suite.Run(t)
   726  }
   727  
   728  func TestPgRepository_Exist(t *testing.T) {
   729  	suite := testdb.RepoExistTestSuite{
   730  		Name: "Runtime Context Exists",
   731  		SQLQueryDetails: []testdb.SQLQueryDetails{
   732  			{
   733  				Query:    regexp.QuoteMeta(`SELECT 1 FROM public.runtime_contexts WHERE id = $1 AND (id IN (SELECT id FROM tenant_runtime_contexts WHERE tenant_id = $2))`),
   734  				Args:     []driver.Value{runtimeCtxID, tenantID},
   735  				IsSelect: true,
   736  				ValidRowsProvider: func() []*sqlmock.Rows {
   737  					return []*sqlmock.Rows{testdb.RowWhenObjectExist()}
   738  				},
   739  				InvalidRowsProvider: func() []*sqlmock.Rows {
   740  					return []*sqlmock.Rows{testdb.RowWhenObjectDoesNotExist()}
   741  				},
   742  			},
   743  		},
   744  		ConverterMockProvider: func() testdb.Mock {
   745  			return &automock.EntityConverter{}
   746  		},
   747  		RepoConstructorFunc: runtimectx.NewRepository,
   748  		TargetID:            runtimeCtxID,
   749  		TenantID:            tenantID,
   750  		MethodName:          "Exists",
   751  		MethodArgs:          []interface{}{tenantID, runtimeCtxID},
   752  	}
   753  
   754  	suite.Run(t)
   755  }
   756  
   757  func TestPgRepository_ExistsByRuntimeID(t *testing.T) {
   758  	suite := testdb.RepoExistTestSuite{
   759  		Name: "Runtime Context Exists By Runtime ID",
   760  		SQLQueryDetails: []testdb.SQLQueryDetails{
   761  			{
   762  				Query:    regexp.QuoteMeta(`SELECT 1 FROM public.runtime_contexts WHERE runtime_id = $1 AND (id IN (SELECT id FROM tenant_runtime_contexts WHERE tenant_id = $2))`),
   763  				Args:     []driver.Value{runtimeID, tenantID},
   764  				IsSelect: true,
   765  				ValidRowsProvider: func() []*sqlmock.Rows {
   766  					return []*sqlmock.Rows{testdb.RowWhenObjectExist()}
   767  				},
   768  				InvalidRowsProvider: func() []*sqlmock.Rows {
   769  					return []*sqlmock.Rows{testdb.RowWhenObjectDoesNotExist()}
   770  				},
   771  			},
   772  		},
   773  		ConverterMockProvider: func() testdb.Mock {
   774  			return &automock.EntityConverter{}
   775  		},
   776  		RepoConstructorFunc: runtimectx.NewRepository,
   777  		TargetID:            runtimeCtxID,
   778  		TenantID:            tenantID,
   779  		MethodName:          "ExistsByRuntimeID",
   780  		MethodArgs:          []interface{}{tenantID, runtimeID},
   781  	}
   782  
   783  	suite.Run(t)
   784  }