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

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