github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/internal/domain/document/repository_test.go (about) 1 package document_test 2 3 import ( 4 "context" 5 "database/sql/driver" 6 "errors" 7 "regexp" 8 "testing" 9 10 "github.com/kyma-incubator/compass/components/director/internal/repo" 11 12 "github.com/kyma-incubator/compass/components/director/pkg/persistence" 13 "github.com/stretchr/testify/require" 14 15 "github.com/kyma-incubator/compass/components/director/pkg/pagination" 16 17 "github.com/DATA-DOG/go-sqlmock" 18 "github.com/kyma-incubator/compass/components/director/internal/domain/document" 19 "github.com/kyma-incubator/compass/components/director/internal/domain/document/automock" 20 "github.com/kyma-incubator/compass/components/director/internal/model" 21 "github.com/kyma-incubator/compass/components/director/internal/repo/testdb" 22 ) 23 24 var columns = []string{"id", "bundle_id", "app_id", "app_template_version_id", "title", "display_name", "description", "format", "kind", "data", "ready", "created_at", "updated_at", "deleted_at", "error"} 25 26 func TestRepository_Create(t *testing.T) { 27 refID := bndlID() 28 var nilDocModel *model.Document 29 docModel := fixModelDocumentForApp(givenID(), refID) 30 docEntity := fixEntityDocumentForApp(givenID(), refID) 31 32 suite := testdb.RepoCreateTestSuite{ 33 Name: "Create Document", 34 SQLQueryDetails: []testdb.SQLQueryDetails{ 35 { 36 Query: regexp.QuoteMeta("SELECT 1 FROM bundles_tenants WHERE tenant_id = $1 AND id = $2 AND owner = $3"), 37 Args: []driver.Value{givenTenant(), refID, true}, 38 IsSelect: true, 39 ValidRowsProvider: func() []*sqlmock.Rows { 40 return []*sqlmock.Rows{testdb.RowWhenObjectExist()} 41 }, 42 InvalidRowsProvider: func() []*sqlmock.Rows { 43 return []*sqlmock.Rows{testdb.RowWhenObjectDoesNotExist()} 44 }, 45 }, 46 { 47 Query: regexp.QuoteMeta("INSERT INTO public.documents ( id, bundle_id, app_id, app_template_version_id, title, display_name, description, format, kind, data, ready, created_at, updated_at, deleted_at, error ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )"), 48 Args: []driver.Value{givenID(), refID, appID, repo.NewValidNullableString(""), docEntity.Title, docEntity.DisplayName, docEntity.Description, docEntity.Format, docEntity.Kind, docEntity.Data, 49 docEntity.Ready, docEntity.CreatedAt, docEntity.UpdatedAt, docEntity.DeletedAt, docEntity.Error}, 50 ValidResult: sqlmock.NewResult(-1, 1), 51 }, 52 }, 53 ConverterMockProvider: func() testdb.Mock { 54 return &automock.Converter{} 55 }, 56 RepoConstructorFunc: document.NewRepository, 57 ModelEntity: docModel, 58 DBEntity: docEntity, 59 NilModelEntity: nilDocModel, 60 TenantID: givenTenant(), 61 } 62 63 suite.Run(t) 64 } 65 66 func TestRepository_CreateGlobal(t *testing.T) { 67 refID := bndlID() 68 var nilDocModel *model.Document 69 docModel := fixModelDocumentForAppTemplateVersion(givenID(), refID) 70 docEntity := fixEntityDocumentForAppTemplateVersion(givenID(), refID) 71 72 suite := testdb.RepoCreateTestSuite{ 73 Name: "Create Document Global", 74 SQLQueryDetails: []testdb.SQLQueryDetails{ 75 { 76 Query: regexp.QuoteMeta("INSERT INTO public.documents ( id, bundle_id, app_id, app_template_version_id, title, display_name, description, format, kind, data, ready, created_at, updated_at, deleted_at, error ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )"), 77 Args: []driver.Value{givenID(), refID, repo.NewValidNullableString(""), appTemplateVersionID, docEntity.Title, docEntity.DisplayName, docEntity.Description, docEntity.Format, docEntity.Kind, docEntity.Data, 78 docEntity.Ready, docEntity.CreatedAt, docEntity.UpdatedAt, docEntity.DeletedAt, docEntity.Error}, 79 ValidResult: sqlmock.NewResult(-1, 1), 80 }, 81 }, 82 ConverterMockProvider: func() testdb.Mock { 83 return &automock.Converter{} 84 }, 85 RepoConstructorFunc: document.NewRepository, 86 ModelEntity: docModel, 87 DBEntity: docEntity, 88 NilModelEntity: nilDocModel, 89 IsGlobal: true, 90 MethodName: "CreateGlobal", 91 } 92 93 suite.Run(t) 94 } 95 96 func TestRepository_CreateMany(t *testing.T) { 97 parentAccessQuery := regexp.QuoteMeta("SELECT 1 FROM bundles_tenants WHERE tenant_id = $1 AND id = $2 AND owner = $3") 98 insertQuery := regexp.QuoteMeta("INSERT INTO public.documents ( id, bundle_id, app_id, app_template_version_id, title, display_name, description, format, kind, data, ready, created_at, updated_at, deleted_at, error ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )") 99 100 t.Run("Success", func(t *testing.T) { 101 // GIVEN 102 conv := &automock.Converter{} 103 defer conv.AssertExpectations(t) 104 105 given := []*model.Document{ 106 fixModelDocumentForApp("1", bndlID()), 107 fixModelDocumentForApp("2", bndlID()), 108 fixModelDocumentForApp("3", bndlID()), 109 } 110 expected := []*document.Entity{ 111 fixEntityDocumentForApp("1", bndlID()), 112 fixEntityDocumentForApp("2", bndlID()), 113 fixEntityDocumentForApp("3", bndlID()), 114 } 115 116 db, dbMock := testdb.MockDatabase(t) 117 defer dbMock.AssertExpectations(t) 118 119 for i, givenModel := range given { 120 expectedEntity := expected[i] 121 conv.On("ToEntity", givenModel).Return(expectedEntity, nil).Once() 122 dbMock.ExpectQuery(parentAccessQuery).WithArgs(givenTenant(), bndlID(), true).WillReturnRows(testdb.RowWhenObjectExist()) 123 dbMock.ExpectExec(insertQuery). 124 WithArgs(givenModel.ID, bndlID(), appID, repo.NewValidNullableString(""), givenModel.Title, givenModel.DisplayName, givenModel.Description, givenModel.Format, givenModel.Kind, givenModel.Data, 125 givenModel.Ready, givenModel.CreatedAt, givenModel.UpdatedAt, givenModel.DeletedAt, givenModel.Error).WillReturnResult(sqlmock.NewResult(-1, 1)) 126 } 127 128 ctx := persistence.SaveToContext(context.TODO(), db) 129 repo := document.NewRepository(conv) 130 // WHEN 131 err := repo.CreateMany(ctx, givenTenant(), given) 132 // THEN 133 require.NoError(t, err) 134 }) 135 136 t.Run("DB Error", func(t *testing.T) { 137 // GIVEN 138 conv := &automock.Converter{} 139 defer conv.AssertExpectations(t) 140 141 given := []*model.Document{ 142 fixModelDocumentForApp("1", bndlID()), 143 fixModelDocumentForApp("2", bndlID()), 144 fixModelDocumentForApp("3", bndlID()), 145 } 146 expected := []*document.Entity{ 147 fixEntityDocumentForApp("1", bndlID()), 148 fixEntityDocumentForApp("2", bndlID()), 149 } 150 151 db, dbMock := testdb.MockDatabase(t) 152 defer dbMock.AssertExpectations(t) 153 154 conv.On("ToEntity", given[0]).Return(expected[0], nil).Once() 155 conv.On("ToEntity", given[1]).Return(expected[1], nil).Once() 156 157 dbMock.ExpectQuery(parentAccessQuery).WithArgs(givenTenant(), bndlID(), true).WillReturnRows(testdb.RowWhenObjectExist()) 158 dbMock.ExpectExec(insertQuery). 159 WithArgs(expected[0].ID, bndlID(), appID, repo.NewValidNullableString(""), expected[0].Title, expected[0].DisplayName, expected[0].Description, expected[0].Format, expected[0].Kind, expected[0].Data, 160 expected[0].Ready, expected[0].CreatedAt, expected[0].UpdatedAt, expected[0].DeletedAt, expected[0].Error).WillReturnResult(sqlmock.NewResult(-1, 1)) 161 162 dbMock.ExpectQuery(parentAccessQuery).WithArgs(givenTenant(), bndlID(), true).WillReturnRows(testdb.RowWhenObjectExist()) 163 dbMock.ExpectExec(insertQuery). 164 WithArgs(expected[1].ID, bndlID(), appID, repo.NewValidNullableString(""), expected[1].Title, expected[1].DisplayName, expected[1].Description, expected[1].Format, expected[1].Kind, expected[1].Data, 165 expected[1].Ready, expected[1].CreatedAt, expected[1].UpdatedAt, expected[1].DeletedAt, expected[1].Error).WillReturnError(givenError()) 166 167 ctx := persistence.SaveToContext(context.TODO(), db) 168 repo := document.NewRepository(conv) 169 170 // WHEN 171 err := repo.CreateMany(ctx, givenTenant(), given) 172 // THEN 173 require.EqualError(t, err, "while creating Document with ID 2: Internal Server Error: Unexpected error while executing SQL query") 174 }) 175 176 t.Run("Converter Error", func(t *testing.T) { 177 // GIVEN 178 conv := &automock.Converter{} 179 defer conv.AssertExpectations(t) 180 181 given := []*model.Document{ 182 fixModelDocumentForApp("1", bndlID()), 183 fixModelDocumentForApp("2", bndlID()), 184 fixModelDocumentForApp("3", bndlID()), 185 } 186 expected := []*document.Entity{ 187 fixEntityDocumentForApp("1", bndlID()), 188 fixEntityDocumentForApp("2", bndlID()), 189 } 190 191 db, dbMock := testdb.MockDatabase(t) 192 //defer dbMock.AssertExpectations(t) 193 194 conv.On("ToEntity", given[0]).Return(expected[0], nil).Once() 195 conv.On("ToEntity", given[1]).Return(nil, givenError()).Once() 196 dbMock.ExpectQuery(parentAccessQuery).WithArgs(givenTenant(), bndlID(), true).WillReturnRows(testdb.RowWhenObjectExist()) 197 dbMock.ExpectExec(insertQuery). 198 WithArgs(expected[0].ID, bndlID(), appID, repo.NewValidNullableString(""), expected[0].Title, expected[0].DisplayName, expected[0].Description, expected[0].Format, expected[0].Kind, expected[0].Data, 199 expected[0].Ready, expected[0].CreatedAt, expected[0].UpdatedAt, expected[0].DeletedAt, expected[0].Error).WillReturnResult(sqlmock.NewResult(-1, 1)) 200 201 ctx := persistence.SaveToContext(context.TODO(), db) 202 repo := document.NewRepository(conv) 203 204 // WHEN 205 err := repo.CreateMany(ctx, givenTenant(), given) 206 // THEN 207 require.EqualError(t, err, "while creating Document with ID 2: while creating Document entity from model: some error") 208 }) 209 } 210 211 func TestRepository_ListAllForBundle(t *testing.T) { 212 pageSize := 1 213 cursor := "" 214 215 emptyPageBundleID := "emptyPageBundleID" 216 217 onePageBundleID := "onePageBundleID" 218 firstDocID := "111111111-1111-1111-1111-111111111111" 219 firstDocEntity := fixEntityDocumentForApp(firstDocID, onePageBundleID) 220 firstDocModel := fixModelDocumentForApp(firstDocID, onePageBundleID) 221 222 multiplePagesBundleID := "multiplePagesBundleID" 223 224 secondDocID := "222222222-2222-2222-2222-222222222222" 225 secondDocEntity := fixEntityDocumentForApp(secondDocID, multiplePagesBundleID) 226 secondDocModel := fixModelDocumentForApp(secondDocID, multiplePagesBundleID) 227 228 suite := testdb.RepoListPageableTestSuite{ 229 Name: "List Documents for multiple bundles with paging", 230 SQLQueryDetails: []testdb.SQLQueryDetails{ 231 { 232 Query: regexp.QuoteMeta(`(SELECT id, bundle_id, app_id, app_template_version_id, title, display_name, description, format, kind, data, ready, created_at, updated_at, deleted_at, error FROM public.documents WHERE (id IN (SELECT id FROM documents_tenants WHERE tenant_id = $1)) AND bundle_id = $2 ORDER BY bundle_id ASC, id ASC LIMIT $3 OFFSET $4) 233 UNION 234 (SELECT id, bundle_id, app_id, app_template_version_id, title, display_name, description, format, kind, data, ready, created_at, updated_at, deleted_at, error FROM public.documents WHERE (id IN (SELECT id FROM documents_tenants WHERE tenant_id = $5)) AND bundle_id = $6 ORDER BY bundle_id ASC, id ASC LIMIT $7 OFFSET $8) 235 UNION 236 (SELECT id, bundle_id, app_id, app_template_version_id, title, display_name, description, format, kind, data, ready, created_at, updated_at, deleted_at, error FROM public.documents WHERE (id IN (SELECT id FROM documents_tenants WHERE tenant_id = $9)) AND bundle_id = $10 ORDER BY bundle_id ASC, id ASC LIMIT $11 OFFSET $12)`), 237 238 Args: []driver.Value{givenTenant(), emptyPageBundleID, pageSize, 0, givenTenant(), onePageBundleID, pageSize, 0, givenTenant(), multiplePagesBundleID, pageSize, 0}, 239 IsSelect: true, 240 ValidRowsProvider: func() []*sqlmock.Rows { 241 return []*sqlmock.Rows{sqlmock.NewRows(columns). 242 AddRow(firstDocID, firstDocEntity.BndlID, firstDocEntity.AppID, repo.NewValidNullableString(""), firstDocEntity.Title, firstDocEntity.DisplayName, firstDocEntity.Description, firstDocEntity.Format, firstDocEntity.Kind, firstDocEntity.Data, firstDocEntity.Ready, firstDocEntity.CreatedAt, firstDocEntity.UpdatedAt, firstDocEntity.DeletedAt, firstDocEntity.Error). 243 AddRow(secondDocID, secondDocEntity.BndlID, secondDocEntity.AppID, repo.NewValidNullableString(""), secondDocEntity.Title, secondDocEntity.DisplayName, secondDocEntity.Description, secondDocEntity.Format, secondDocEntity.Kind, secondDocEntity.Data, secondDocEntity.Ready, secondDocEntity.CreatedAt, secondDocEntity.UpdatedAt, secondDocEntity.DeletedAt, secondDocEntity.Error), 244 } 245 }, 246 }, 247 { 248 Query: regexp.QuoteMeta(`SELECT bundle_id AS id, COUNT(*) AS total_count FROM public.documents WHERE (id IN (SELECT id FROM documents_tenants WHERE tenant_id = $1)) GROUP BY bundle_id ORDER BY bundle_id ASC`), 249 Args: []driver.Value{givenTenant()}, 250 IsSelect: true, 251 ValidRowsProvider: func() []*sqlmock.Rows { 252 return []*sqlmock.Rows{sqlmock.NewRows([]string{"id", "total_count"}).AddRow(emptyPageBundleID, 0).AddRow(onePageBundleID, 1).AddRow(multiplePagesBundleID, 2)} 253 }, 254 }, 255 }, 256 Pages: []testdb.PageDetails{ 257 { 258 ExpectedModelEntities: nil, 259 ExpectedDBEntities: nil, 260 ExpectedPage: &model.DocumentPage{ 261 Data: nil, 262 PageInfo: &pagination.Page{ 263 StartCursor: "", 264 EndCursor: "", 265 HasNextPage: false, 266 }, 267 TotalCount: 0, 268 }, 269 }, 270 { 271 ExpectedModelEntities: []interface{}{firstDocModel}, 272 ExpectedDBEntities: []interface{}{firstDocEntity}, 273 ExpectedPage: &model.DocumentPage{ 274 Data: []*model.Document{firstDocModel}, 275 PageInfo: &pagination.Page{ 276 StartCursor: "", 277 EndCursor: "", 278 HasNextPage: false, 279 }, 280 TotalCount: 1, 281 }, 282 }, 283 { 284 ExpectedModelEntities: []interface{}{secondDocModel}, 285 ExpectedDBEntities: []interface{}{secondDocEntity}, 286 ExpectedPage: &model.DocumentPage{ 287 Data: []*model.Document{secondDocModel}, 288 PageInfo: &pagination.Page{ 289 StartCursor: "", 290 EndCursor: pagination.EncodeNextOffsetCursor(0, pageSize), 291 HasNextPage: true, 292 }, 293 TotalCount: 2, 294 }, 295 }, 296 }, 297 ConverterMockProvider: func() testdb.Mock { 298 return &automock.Converter{} 299 }, 300 RepoConstructorFunc: document.NewRepository, 301 MethodName: "ListByBundleIDs", 302 MethodArgs: []interface{}{givenTenant(), []string{emptyPageBundleID, onePageBundleID, multiplePagesBundleID}, pageSize, cursor}, 303 } 304 305 suite.Run(t) 306 } 307 308 func TestRepository_Exists(t *testing.T) { 309 suite := testdb.RepoExistTestSuite{ 310 Name: "Document Exists", 311 SQLQueryDetails: []testdb.SQLQueryDetails{ 312 { 313 Query: regexp.QuoteMeta(`SELECT 1 FROM public.documents WHERE id = $1 AND (id IN (SELECT id FROM documents_tenants WHERE tenant_id = $2))`), 314 Args: []driver.Value{givenID(), givenTenant()}, 315 IsSelect: true, 316 ValidRowsProvider: func() []*sqlmock.Rows { 317 return []*sqlmock.Rows{testdb.RowWhenObjectExist()} 318 }, 319 InvalidRowsProvider: func() []*sqlmock.Rows { 320 return []*sqlmock.Rows{testdb.RowWhenObjectDoesNotExist()} 321 }, 322 }, 323 }, 324 ConverterMockProvider: func() testdb.Mock { 325 return &automock.Converter{} 326 }, 327 RepoConstructorFunc: document.NewRepository, 328 TargetID: givenID(), 329 TenantID: givenTenant(), 330 MethodName: "Exists", 331 MethodArgs: []interface{}{givenTenant(), givenID()}, 332 } 333 334 suite.Run(t) 335 } 336 337 func TestRepository_GetByID(t *testing.T) { 338 docModel := fixModelDocumentForApp(givenID(), bndlID()) 339 docEntity := fixEntityDocumentForApp(givenID(), bndlID()) 340 341 suite := testdb.RepoGetTestSuite{ 342 Name: "Get Document", 343 SQLQueryDetails: []testdb.SQLQueryDetails{ 344 { 345 Query: regexp.QuoteMeta(`SELECT id, bundle_id, app_id, app_template_version_id, title, display_name, description, format, kind, data, ready, created_at, updated_at, deleted_at, error FROM public.documents WHERE id = $1 AND (id IN (SELECT id FROM documents_tenants WHERE tenant_id = $2))`), 346 Args: []driver.Value{givenID(), givenTenant()}, 347 IsSelect: true, 348 ValidRowsProvider: func() []*sqlmock.Rows { 349 return []*sqlmock.Rows{ 350 sqlmock.NewRows(columns). 351 AddRow(givenID(), docEntity.BndlID, docEntity.AppID, repo.NewValidNullableString(""), docEntity.Title, docEntity.DisplayName, docEntity.Description, docEntity.Format, docEntity.Kind, docEntity.Data, docEntity.Ready, docEntity.CreatedAt, docEntity.UpdatedAt, docEntity.DeletedAt, docEntity.Error), 352 } 353 }, 354 InvalidRowsProvider: func() []*sqlmock.Rows { 355 return []*sqlmock.Rows{ 356 sqlmock.NewRows(columns), 357 } 358 }, 359 }, 360 }, 361 ConverterMockProvider: func() testdb.Mock { 362 return &automock.Converter{} 363 }, 364 RepoConstructorFunc: document.NewRepository, 365 ExpectedModelEntity: docModel, 366 ExpectedDBEntity: docEntity, 367 MethodArgs: []interface{}{givenTenant(), givenID()}, 368 } 369 370 suite.Run(t) 371 } 372 373 func TestRepository_GetForBundle(t *testing.T) { 374 docModel := fixModelDocumentForApp(givenID(), bndlID()) 375 docEntity := fixEntityDocumentForApp(givenID(), bndlID()) 376 377 suite := testdb.RepoGetTestSuite{ 378 Name: "Get Document For Bundle", 379 SQLQueryDetails: []testdb.SQLQueryDetails{ 380 { 381 Query: regexp.QuoteMeta(`SELECT id, bundle_id, app_id, app_template_version_id, title, display_name, description, format, kind, data, ready, created_at, updated_at, deleted_at, error FROM public.documents WHERE id = $1 AND bundle_id = $2 AND (id IN (SELECT id FROM documents_tenants WHERE tenant_id = $3))`), 382 Args: []driver.Value{givenID(), bndlID(), givenTenant()}, 383 IsSelect: true, 384 ValidRowsProvider: func() []*sqlmock.Rows { 385 return []*sqlmock.Rows{ 386 sqlmock.NewRows(columns). 387 AddRow(givenID(), docEntity.BndlID, docEntity.AppID, repo.NewValidNullableString(""), docEntity.Title, docEntity.DisplayName, docEntity.Description, docEntity.Format, docEntity.Kind, docEntity.Data, docEntity.Ready, docEntity.CreatedAt, docEntity.UpdatedAt, docEntity.DeletedAt, docEntity.Error), 388 } 389 }, 390 InvalidRowsProvider: func() []*sqlmock.Rows { 391 return []*sqlmock.Rows{ 392 sqlmock.NewRows(columns), 393 } 394 }, 395 }, 396 }, 397 ConverterMockProvider: func() testdb.Mock { 398 return &automock.Converter{} 399 }, 400 RepoConstructorFunc: document.NewRepository, 401 ExpectedModelEntity: docModel, 402 ExpectedDBEntity: docEntity, 403 MethodArgs: []interface{}{givenTenant(), givenID(), bndlID()}, 404 MethodName: "GetForBundle", 405 } 406 407 suite.Run(t) 408 } 409 410 func TestRepository_Delete(t *testing.T) { 411 suite := testdb.RepoDeleteTestSuite{ 412 Name: "Document Delete", 413 SQLQueryDetails: []testdb.SQLQueryDetails{ 414 { 415 Query: regexp.QuoteMeta(`DELETE FROM public.documents WHERE id = $1 AND (id IN (SELECT id FROM documents_tenants WHERE tenant_id = $2 AND owner = true))`), 416 Args: []driver.Value{givenID(), givenTenant()}, 417 ValidResult: sqlmock.NewResult(-1, 1), 418 InvalidResult: sqlmock.NewResult(-1, 2), 419 }, 420 }, 421 ConverterMockProvider: func() testdb.Mock { 422 return &automock.Converter{} 423 }, 424 RepoConstructorFunc: document.NewRepository, 425 MethodArgs: []interface{}{givenTenant(), givenID()}, 426 } 427 428 suite.Run(t) 429 } 430 431 func givenID() string { 432 return "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" 433 } 434 435 func bndlID() string { 436 return "ppppppppp-pppp-pppp-pppp-pppppppppppp" 437 } 438 439 func givenTenant() string { 440 return "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb" 441 } 442 443 func givenError() error { 444 return errors.New("some error") 445 }