diff --git a/internal/app/application/services/asset.go b/internal/app/application/services/asset.go index e21949e..e590f3f 100644 --- a/internal/app/application/services/asset.go +++ b/internal/app/application/services/asset.go @@ -14,6 +14,7 @@ type AssetServiceInterface interface { GetByEntity(entity entities.Entity, pageFilter *PageFilter) ([]*entities.Asset, error) Delete(asset *entities.Asset) error GetByEntities(entities []entities.Entity) ([]*entities.Asset, error) + UpdateByEntity(entity entities.Entity, file *os.File) (*entities.Asset, error) } type AssetService struct { @@ -128,6 +129,27 @@ func (s *AssetService) GetByEntities(theEntities []entities.Entity) ([]*entities return assets, nil } +func (s *AssetService) UpdateByEntity(theEntity entities.Entity, file *os.File) (*entities.Asset, error) { + oldAssets, err := s.assetRepository.FindByEntity(theEntity, nil) + if err != nil { + return nil, err + } + + for _, oldAsset := range oldAssets { + err = s.Delete(oldAsset) + if err != nil { + return nil, err + } + } + + asset, err := s.CreateFromFile(file, theEntity) + if err != nil { + return nil, err + } + + return asset, nil +} + type AssetServiceMock struct { mock.Mock } @@ -173,3 +195,16 @@ func (s *AssetServiceMock) GetByEntities( return args.Get(0).([]*entities.Asset), args.Error(1) } + +func (s *AssetServiceMock) UpdateByEntity( + theEntity entities.Entity, + file *os.File, +) (*entities.Asset, error) { + args := s.Called(theEntity, file) + + if args.Get(0) == nil { + return nil, args.Error(1) + } + + return args.Get(0).(*entities.Asset), args.Error(1) +} diff --git a/internal/app/application/services/asset_test.go b/internal/app/application/services/asset_test.go index 2e9b161..1e59daf 100644 --- a/internal/app/application/services/asset_test.go +++ b/internal/app/application/services/asset_test.go @@ -296,3 +296,42 @@ func TestAssetServiceGetByEntitiesErrorFromAssetRepository(t *testing.T) { assert.Nil(t, assets) assetRepository.AssertExpectations(t) } + +func TestAssetServiceUpdateByEntity(t *testing.T) { + assetRepository := &stub.AssetRepositoryMock{} + fileManager := &serviceStubs.FileManagerMock{} + service := NewAssetService(fileManager, assetRepository) + + entity := entities.NewIdentifiableEntity(uuid.NewString()) + oldAssetId := uuid.NewString() + oldFileID := uuid.NewString() + oldExtension := ".png" + var filter *repositories.PageFilter + + file, err := os.CreateTemp("", "*_"+uuid.NewString()) + defer file.Close() + + assetRepository.On("FindByEntity", entity, filter). + Return([]*entities.Asset{ + { + ID: oldAssetId, + FileID: oldFileID, + Extension: oldExtension, + }, + }, nil) + assetRepository.On("Delete", oldAssetId). + Return(nil) + assetRepository.On("Create", mock.AnythingOfType("*entities.Asset")). + Return(nil) + fileManager.On("Upload", file). + Return(uuid.NewString(), nil) + fileManager.On("Delete", oldFileID, oldExtension). + Return(nil) + + asset, err := service.UpdateByEntity(entity, file) + + assert.NoError(t, err) + assert.NotNil(t, asset) + assetRepository.AssertExpectations(t) + fileManager.AssertExpectations(t) +} diff --git a/internal/app/application/services/item.go b/internal/app/application/services/item.go index 7966e21..be69078 100644 --- a/internal/app/application/services/item.go +++ b/internal/app/application/services/item.go @@ -190,3 +190,61 @@ func (s *ItemService) makeGetAllQueryFilter( return queryFilter } + +func (s *ItemService) Update( + id string, + name string, + sku string, + description *string, + unit string, + keywords []string, + imageFile *os.File, +) (*entities.Item, error) { + item, err := s.itemRepository.GetByID(id) + if err != nil { + return nil, err + } + + err = item.Update(sku, name, description, unit) + if err != nil { + return nil, err + } + + err = s.itemRepository.Update(item) + if err != nil { + return nil, err + } + + err = s.itemKeywordRepository.DeleteByItemID(item.ID) + if err != nil { + return nil, err + } + + if len(keywords) > 0 { + var itemKeywords []*entities.ItemKeyword + for _, keyword := range keywords { + itemKeyword, err := entities.NewItemKeyword(item.ID, keyword) + if err != nil { + return nil, err + } + + itemKeywords = append(itemKeywords, itemKeyword) + } + + err = s.itemKeywordRepository.CreateMany(itemKeywords) + if err != nil { + return nil, err + } + + item.Keywords = itemKeywords + } + + if imageFile != nil { + _, err = s.assetService.UpdateByEntity(item, imageFile) + if err != nil { + return nil, err + } + } + + return item, nil +} diff --git a/internal/app/application/services/item_test.go b/internal/app/application/services/item_test.go index 6e4ae5b..6f5fe25 100644 --- a/internal/app/application/services/item_test.go +++ b/internal/app/application/services/item_test.go @@ -416,3 +416,244 @@ func TestItemServiceCountAllErrorOnItemRepository(t *testing.T) { itemKeywordRepository.AssertExpectations(t) assetService.AssertExpectations(t) } + +func TestItemServiceUpdate(t *testing.T) { + itemRepository := &stub.ItemRepositoryMock{} + itemKeywordRepository := &stub.ItemKeywordRepositoryMock{} + assetService := &AssetServiceMock{} + + itemService := NewItemService( + itemRepository, + itemKeywordRepository, + assetService, + ) + + id := uuid.NewString() + name := random.String(10, random.Alphanumeric) + sku := random.String(10, random.Alphanumeric) + description := random.String(100, random.Alphanumeric) + unit := "unit" + keywords := []string{ + random.String(10, random.Alphanumeric), + random.String(10, random.Alphanumeric), + } + file := &os.File{} + + itemRepository.On("GetByID", id). + Return(&entities.Item{ + ID: id, + Sku: random.String(10, random.Alphanumeric), + Name: random.String(10, random.Alphanumeric), + Description: nil, + Unit: "unit", + UserID: uuid.NewString(), + CreatedAt: time.Now(), + UpdatedAt: time.Now(), + }, nil) + itemRepository.On("Update", mock.AnythingOfType("*entities.Item")). + Return(nil) + itemKeywordRepository.On("DeleteByItemID", id). + Return(nil) + itemKeywordRepository.On("CreateMany", mock.AnythingOfType("[]*entities.ItemKeyword")). + Return(nil) + assetService.On("UpdateByEntity", mock.AnythingOfType("*entities.Item"), file). + Return(&entities.Asset{ + ID: uuid.NewString(), + Name: random.String(10, random.Alphanumeric), + Extension: ".png", + Size: 12314, + FileID: uuid.NewString(), + EntityID: id, + EntityName: "item", + CreatedAt: time.Now(), + UpdatedAt: time.Now(), + }, nil) + + item, err := itemService.Update( + id, + name, + sku, + &description, + unit, + keywords, + file, + ) + + assert.NoError(t, err) + assert.NotNil(t, item) + assert.Equal(t, id, item.ID) + assert.Equal(t, sku, item.Sku) + assert.Equal(t, name, item.Name) + assert.Equal(t, description, *item.Description) + assert.Equal(t, unit, item.Unit) + itemRepository.AssertExpectations(t) + itemKeywordRepository.AssertExpectations(t) + assetService.AssertExpectations(t) +} + +func TestItemServiceUpdateErrorOnItemRepository(t *testing.T) { + itemRepository := &stub.ItemRepositoryMock{} + itemKeywordRepository := &stub.ItemKeywordRepositoryMock{} + assetService := &AssetServiceMock{} + + itemService := NewItemService( + itemRepository, + itemKeywordRepository, + assetService, + ) + + id := uuid.NewString() + name := random.String(10, random.Alphanumeric) + sku := random.String(10, random.Alphanumeric) + description := random.String(100, random.Alphanumeric) + unit := "unit" + keywords := []string{ + random.String(10, random.Alphanumeric), + random.String(10, random.Alphanumeric), + } + file := &os.File{} + + itemRepository.On("GetByID", id). + Return(&entities.Item{ + ID: id, + Sku: random.String(10, random.Alphanumeric), + Name: random.String(10, random.Alphanumeric), + Description: nil, + Unit: "unit", + UserID: uuid.NewString(), + CreatedAt: time.Now(), + UpdatedAt: time.Now(), + }, nil) + itemRepository.On("Update", mock.AnythingOfType("*entities.Item")). + Return(errors.New("item repository error")) + + item, err := itemService.Update( + id, + name, + sku, + &description, + unit, + keywords, + file, + ) + + assert.Error(t, err) + assert.Nil(t, item) + itemRepository.AssertExpectations(t) + itemKeywordRepository.AssertExpectations(t) + assetService.AssertExpectations(t) +} + +func TestItemServiceUpdateErrorOnItemKeywordRepository(t *testing.T) { + itemRepository := &stub.ItemRepositoryMock{} + itemKeywordRepository := &stub.ItemKeywordRepositoryMock{} + assetService := &AssetServiceMock{} + + itemService := NewItemService( + itemRepository, + itemKeywordRepository, + assetService, + ) + + id := uuid.NewString() + name := random.String(10, random.Alphanumeric) + sku := random.String(10, random.Alphanumeric) + description := random.String(100, random.Alphanumeric) + unit := "unit" + keywords := []string{ + random.String(10, random.Alphanumeric), + random.String(10, random.Alphanumeric), + } + file := &os.File{} + + itemRepository.On("GetByID", id). + Return(&entities.Item{ + ID: id, + Sku: random.String(10, random.Alphanumeric), + Name: random.String(10, random.Alphanumeric), + Description: nil, + Unit: "unit", + UserID: uuid.NewString(), + CreatedAt: time.Now(), + UpdatedAt: time.Now(), + }, nil) + itemRepository.On("Update", mock.AnythingOfType("*entities.Item")). + Return(nil) + itemKeywordRepository.On("DeleteByItemID", id). + Return(errors.New("item keyword repository error")) + + item, err := itemService.Update( + id, + name, + sku, + &description, + unit, + keywords, + file, + ) + + assert.Error(t, err) + assert.Nil(t, item) + itemRepository.AssertExpectations(t) + itemKeywordRepository.AssertExpectations(t) + assetService.AssertExpectations(t) +} + +func TestItemServiceUpdateErrorOnAssetService(t *testing.T) { + itemRepository := &stub.ItemRepositoryMock{} + itemKeywordRepository := &stub.ItemKeywordRepositoryMock{} + assetService := &AssetServiceMock{} + + itemService := NewItemService( + itemRepository, + itemKeywordRepository, + assetService, + ) + + id := uuid.NewString() + name := random.String(10, random.Alphanumeric) + sku := random.String(10, random.Alphanumeric) + description := random.String(100, random.Alphanumeric) + unit := "unit" + keywords := []string{ + random.String(10, random.Alphanumeric), + random.String(10, random.Alphanumeric), + } + file := &os.File{} + + itemRepository.On("GetByID", id). + Return(&entities.Item{ + ID: id, + Sku: random.String(10, random.Alphanumeric), + Name: random.String(10, random.Alphanumeric), + Description: nil, + Unit: "unit", + UserID: uuid.NewString(), + CreatedAt: time.Now(), + UpdatedAt: time.Now(), + }, nil) + itemRepository.On("Update", mock.AnythingOfType("*entities.Item")). + Return(nil) + itemKeywordRepository.On("DeleteByItemID", id). + Return(nil) + itemKeywordRepository.On("CreateMany", mock.AnythingOfType("[]*entities.ItemKeyword")). + Return(nil) + assetService.On("UpdateByEntity", mock.AnythingOfType("*entities.Item"), file). + Return(nil, errors.New("asset service error")) + + item, err := itemService.Update( + id, + name, + sku, + &description, + unit, + keywords, + file, + ) + + assert.Error(t, err) + assert.Nil(t, item) + itemRepository.AssertExpectations(t) + itemKeywordRepository.AssertExpectations(t) + assetService.AssertExpectations(t) +} diff --git a/internal/app/domain/entities/item.go b/internal/app/domain/entities/item.go index 877b69a..f46d956 100644 --- a/internal/app/domain/entities/item.go +++ b/internal/app/domain/entities/item.go @@ -51,56 +51,113 @@ type Item struct { } func NewItem(sku, name string, description *string, unit string, userID string) (*Item, error) { + item := &Item{ + ID: uuid.NewString(), + CreatedAt: time.Now(), + UpdatedAt: time.Now(), + } + + err := item.Update(sku, name, description, unit) + if err != nil { + return nil, err + } + + err = item.ChangeUserID(userID) + if err != nil { + return nil, err + } + + return item, nil +} + +func (i *Item) Update(sku, name string, description *string, unit string) error { + err := i.ChangeSku(sku) + if err != nil { + return err + } + + err = i.ChangeName(name) + if err != nil { + return err + } + + err = i.ChangeDescription(description) + if err != nil { + return err + } + + err = i.ChangeUnit(unit) + if err != nil { + return err + } + + i.UpdatedAt = time.Now() + return nil +} + +func (i *Item) EntityID() string { + return i.ID +} + +func (i *Item) EntityName() string { + return "item" +} + +func (i *Item) ChangeSku(sku string) error { if strings.TrimSpace(sku) == "" { - return nil, ErrItemSkuShouldNotBeEmpty + return ErrItemSkuShouldNotBeEmpty } if len(sku) > 100 { - return nil, ErrItemSkuShouldHaveLessThan100Characters + return ErrItemSkuShouldHaveLessThan100Characters } + i.Sku = sku + return nil +} + +func (i *Item) ChangeName(name string) error { if strings.TrimSpace(name) == "" { - return nil, ErrItemNameShouldNotBeEmpty + return ErrItemNameShouldNotBeEmpty } if len(name) > 255 { - return nil, ErrItemNameShouldHaveLessThan255Characters + return ErrItemNameShouldHaveLessThan255Characters } + i.Name = name + return nil +} + +func (i *Item) ChangeDescription(description *string) error { if description != nil { if strings.TrimSpace(*description) == "" { - return nil, ErrItemDescriptionShouldNotBeEmpty + return ErrItemDescriptionShouldNotBeEmpty } if len(*description) > 65535 { - return nil, ErrItemDescriptionShouldHaveLessThan65535Characters + return ErrItemDescriptionShouldHaveLessThan65535Characters } } - if strings.TrimSpace(userID) == "" { - return nil, ErrItemUserIDShouldNotBeEmpty - } + i.Description = description + return nil +} +func (i *Item) ChangeUnit(unit string) error { if _, ok := ValidItemUnits[unit]; !ok { - return nil, ErrItemUnitShouldBeValid + return ErrItemUnitShouldBeValid } - return &Item{ - ID: uuid.NewString(), - Sku: sku, - Name: name, - Description: description, - Unit: unit, - UserID: userID, - CreatedAt: time.Now(), - UpdatedAt: time.Now(), - }, nil + i.Unit = unit + return nil } -func (i *Item) EntityID() string { - return i.ID -} +func (i *Item) ChangeUserID(userID string) error { + if strings.TrimSpace(userID) == "" { + return ErrItemUserIDShouldNotBeEmpty + } -func (i *Item) EntityName() string { - return "item" + i.UserID = userID + return nil } diff --git a/internal/app/domain/repositories/item.go b/internal/app/domain/repositories/item.go index fa43dd8..3af9d7b 100644 --- a/internal/app/domain/repositories/item.go +++ b/internal/app/domain/repositories/item.go @@ -6,10 +6,11 @@ import ( ) var ( + ErrItemRepositoryCanNotCountByQueryFilters = errors.New("can not count by query filters") ErrItemRepositoryCanNotCreateItem = errors.New("can not create item") - ErrItemRepositoryItemNotFound = errors.New("item not found") ErrItemRepositoryCanNotGetByQueryFilters = errors.New("can not get by query filters") - ErrItemRepositoryCanNotCountByQueryFilters = errors.New("can not count by query filters") + ErrItemRepositoryCanNotUpdateItem = errors.New("can not update item") + ErrItemRepositoryItemNotFound = errors.New("item not found") ) type ItemRepository interface { @@ -17,4 +18,5 @@ type ItemRepository interface { GetByID(id string) (*entities.Item, error) GetByQueryFilters(queryFilter QueryFilter, pageFilter *PageFilter) ([]*entities.Item, error) CountByQueryFilters(queryFilter QueryFilter) (int64, error) + Update(item *entities.Item) error } diff --git a/internal/app/domain/repositories/item_keyword.go b/internal/app/domain/repositories/item_keyword.go index 2425e7f..950bce9 100644 --- a/internal/app/domain/repositories/item_keyword.go +++ b/internal/app/domain/repositories/item_keyword.go @@ -7,8 +7,10 @@ import ( var ( ErrItemKeywordRepositoryCanNotCreateItemKeywords = errors.New("can not create item keywords") + ErrItemKeywordRepositoryCanNotDeleteByItemID = errors.New("can not delete by item id") ) type ItemKeywordRepository interface { CreateMany(itemKeyword []*entities.ItemKeyword) error + DeleteByItemID(itemID string) error } diff --git a/internal/app/infrastructure/controllers/update_item.go b/internal/app/infrastructure/controllers/update_item.go new file mode 100644 index 0000000..04955ea --- /dev/null +++ b/internal/app/infrastructure/controllers/update_item.go @@ -0,0 +1,96 @@ +package controllers + +import ( + "errors" + "github.com/jibaru/home-inventory-api/m/internal/app/application/services" + "github.com/jibaru/home-inventory-api/m/internal/app/infrastructure/responses" + "github.com/jibaru/home-inventory-api/m/logger" + "github.com/labstack/echo/v4" + "net/http" + "os" +) + +type UpdateItemController struct { + itemService *services.ItemService +} + +type UpdateItemRequest struct { + ItemID string `param:"itemID"` + Sku string `form:"sku"` + Name string `form:"name"` + Description *string `form:"description"` + Unit string `form:"unit"` + Keywords []string `form:"keywords[]"` +} + +type UpdateItemResponse struct { + ID string `json:"id"` + Sku string `json:"sku"` + Name string `json:"name"` + Description *string `json:"description"` + Unit string `json:"unit"` + Keywords []string `json:"keywords"` +} + +func NewUpdateItemController(itemService *services.ItemService) *UpdateItemController { + return &UpdateItemController{ + itemService: itemService, + } +} + +func (c *UpdateItemController) Handle(ctx echo.Context) error { + request := UpdateItemRequest{} + + err := (&echo.DefaultBinder{}).BindBody(ctx, &request) + if err != nil { + return ctx.JSON(http.StatusBadRequest, responses.NewMessageResponse(err.Error())) + } + err = (&echo.DefaultBinder{}).BindPathParams(ctx, &request) + if err != nil { + return ctx.JSON(http.StatusBadRequest, responses.NewMessageResponse(err.Error())) + } + + fileHeader, err := ctx.FormFile("file") + if err != nil && !errors.Is(err, http.ErrMissingFile) { + logger.LogError(err) + return ctx.JSON(http.StatusInternalServerError, responses.NewMessageResponse(err.Error())) + } + + var tempFile *os.File + if fileHeader != nil { + var tempDir string + tempDir, tempFile, err = mapFileHeaderToTempFolderAndFile(fileHeader) + if err != nil { + return ctx.JSON(http.StatusInternalServerError, responses.NewMessageResponse(err.Error())) + } + defer os.RemoveAll(tempDir) + defer tempFile.Close() + } + + item, err := c.itemService.Update( + request.ItemID, + request.Sku, + request.Name, + request.Description, + request.Unit, + request.Keywords, + tempFile, + ) + if err != nil { + return ctx.JSON(http.StatusBadRequest, responses.NewMessageResponse(err.Error())) + } + + keywords := make([]string, len(item.Keywords)) + for i, keyword := range item.Keywords { + keywords[i] = keyword.Value + } + + return ctx.JSON(http.StatusOK, responses.NewDataResponse(&UpdateItemResponse{ + ID: item.ID, + Sku: item.Sku, + Name: item.Name, + Description: item.Description, + Unit: item.Unit, + Keywords: keywords, + })) +} diff --git a/internal/app/infrastructure/http/server.go b/internal/app/infrastructure/http/server.go index 76ba701..34a31f0 100644 --- a/internal/app/infrastructure/http/server.go +++ b/internal/app/infrastructure/http/server.go @@ -60,6 +60,7 @@ func RunServer( deleteRoomController := controllers.NewDeleteRoomController(roomService) updateRoomController := controllers.NewUpdateRoomController(roomService) updateBoxController := controllers.NewUpdateBoxController(boxService) + updateItemController := controllers.NewUpdateItemController(itemService) loggerMiddleware := middlewares.NewLoggerMiddleware() needsAuthMiddleware := middlewares.NewNeedsAuthMiddleware(authService) @@ -87,6 +88,7 @@ func RunServer( authApi.DELETE("/rooms/:roomID", deleteRoomController.Handle) authApi.PATCH("/rooms/:roomID", updateRoomController.Handle) authApi.PATCH("/boxes/:boxID", updateBoxController.Handle) + authApi.PATCH("/items/:itemID", updateItemController.Handle) logger.LogError(e.Start(host + ":" + port)) } diff --git a/internal/app/infrastructure/repositories/gorm/item.go b/internal/app/infrastructure/repositories/gorm/item.go index 1f6ae74..a868424 100644 --- a/internal/app/infrastructure/repositories/gorm/item.go +++ b/internal/app/infrastructure/repositories/gorm/item.go @@ -72,3 +72,13 @@ func (r *ItemRepository) CountByQueryFilters(queryFilter repositories.QueryFilte return count, nil } + +func (r *ItemRepository) Update(item *entities.Item) error { + if err := r.db.Save(item).Error; err != nil { + logger.LogError(err) + notifier.NotifyError(err) + return repositories.ErrItemRepositoryCanNotUpdateItem + } + + return nil +} diff --git a/internal/app/infrastructure/repositories/gorm/item_keyword.go b/internal/app/infrastructure/repositories/gorm/item_keyword.go index 74b1b0a..0f1c8cb 100644 --- a/internal/app/infrastructure/repositories/gorm/item_keyword.go +++ b/internal/app/infrastructure/repositories/gorm/item_keyword.go @@ -27,3 +27,13 @@ func (r *ItemKeywordRepository) CreateMany(itemKeywords []*entities.ItemKeyword) return nil } + +func (r *ItemKeywordRepository) DeleteByItemID(itemID string) error { + if err := r.db.Where("item_id = ?", itemID).Delete(&entities.ItemKeyword{}).Error; err != nil { + logger.LogError(err) + notifier.NotifyError(err) + return repositories.ErrItemKeywordRepositoryCanNotDeleteByItemID + } + + return nil +} diff --git a/internal/app/infrastructure/repositories/gorm/item_keyword_test.go b/internal/app/infrastructure/repositories/gorm/item_keyword_test.go index ca71ef1..aa79355 100644 --- a/internal/app/infrastructure/repositories/gorm/item_keyword_test.go +++ b/internal/app/infrastructure/repositories/gorm/item_keyword_test.go @@ -103,3 +103,42 @@ func TestItemKeywordRepositoryCreateManyErrorCanNotCreateItemKeywords(t *testing err = dbMock.ExpectationsWereMet() assert.NoError(t, err) } + +func TestItemKeywordRepositoryDeleteByItemID(t *testing.T) { + db, dbMock := makeDBMock() + itemKeywordRepository := NewItemKeywordRepository(db) + + itemID := uuid.NewString() + + dbMock.ExpectBegin() + dbMock.ExpectExec(regexp.QuoteMeta("DELETE FROM `item_keywords` WHERE item_id = ?")). + WithArgs(itemID). + WillReturnResult(sqlmock.NewResult(1, 1)) + dbMock.ExpectCommit() + + err := itemKeywordRepository.DeleteByItemID(itemID) + + assert.NoError(t, err) + err = dbMock.ExpectationsWereMet() + assert.NoError(t, err) +} + +func TestItemKeywordRepositoryDeleteByItemIDErrorCanNotDeleteByItemID(t *testing.T) { + db, dbMock := makeDBMock() + itemKeywordRepository := NewItemKeywordRepository(db) + + itemID := uuid.NewString() + + dbMock.ExpectBegin() + dbMock.ExpectExec(regexp.QuoteMeta("DELETE FROM `item_keywords` WHERE item_id = ?")). + WithArgs(itemID). + WillReturnError(errors.New("database error")) + dbMock.ExpectRollback() + + err := itemKeywordRepository.DeleteByItemID(itemID) + + assert.Error(t, err) + assert.ErrorIs(t, err, repositories.ErrItemKeywordRepositoryCanNotDeleteByItemID) + err = dbMock.ExpectationsWereMet() + assert.NoError(t, err) +} diff --git a/internal/app/infrastructure/repositories/gorm/item_test.go b/internal/app/infrastructure/repositories/gorm/item_test.go index a22adb2..9b6ddd7 100644 --- a/internal/app/infrastructure/repositories/gorm/item_test.go +++ b/internal/app/infrastructure/repositories/gorm/item_test.go @@ -380,3 +380,59 @@ func TestItemRepositoryCountByQueryFiltersErrorCanNotCountByQueryFilters(t *test err = dbMock.ExpectationsWereMet() assert.NoError(t, err) } + +func TestItemRepositoryUpdate(t *testing.T) { + db, dbMock := makeDBMock() + itemRepository := NewItemRepository(db) + + item := &entities.Item{ + ID: uuid.NewString(), + Sku: random.String(20, random.Alphanumeric), + Name: random.String(100, random.Alphanumeric), + Description: nil, + Unit: "unit", + UserID: uuid.NewString(), + CreatedAt: time.Now(), + UpdatedAt: time.Now(), + } + + dbMock.ExpectBegin() + dbMock.ExpectExec(regexp.QuoteMeta("UPDATE `items` SET `sku`=?,`name`=?,`description`=?,`unit`=?,`user_id`=?,`created_at`=?,`updated_at`=? WHERE `id` = ?")). + WithArgs(item.Sku, item.Name, item.Description, item.Unit, item.UserID, item.CreatedAt, sqlmock.AnyArg(), item.ID). + WillReturnResult(sqlmock.NewResult(1, 1)) + dbMock.ExpectCommit() + + err := itemRepository.Update(item) + + assert.NoError(t, err) + err = dbMock.ExpectationsWereMet() + assert.NoError(t, err) +} + +func TestItemRepositoryUpdateErrorCanNotUpdateItem(t *testing.T) { + db, dbMock := makeDBMock() + itemRepository := NewItemRepository(db) + + item := &entities.Item{ + ID: uuid.NewString(), + Sku: random.String(20, random.Alphanumeric), + Name: random.String(100, random.Alphanumeric), + Description: nil, + Unit: "unit", + UserID: uuid.NewString(), + CreatedAt: time.Now(), + UpdatedAt: time.Now(), + } + + dbMock.ExpectBegin() + dbMock.ExpectExec(regexp.QuoteMeta("UPDATE `items` SET `sku`=?,`name`=?,`description`=?,`unit`=?,`user_id`=?,`created_at`=?,`updated_at`=? WHERE `id` = ?")). + WithArgs(item.Sku, item.Name, item.Description, item.Unit, item.UserID, item.CreatedAt, sqlmock.AnyArg(), item.ID). + WillReturnError(errors.New("database error")) + dbMock.ExpectRollback() + + err := itemRepository.Update(item) + + assert.Error(t, err) + err = dbMock.ExpectationsWereMet() + assert.NoError(t, err) +} diff --git a/internal/app/infrastructure/repositories/stub/item.go b/internal/app/infrastructure/repositories/stub/item.go index 279aad6..d010fc7 100644 --- a/internal/app/infrastructure/repositories/stub/item.go +++ b/internal/app/infrastructure/repositories/stub/item.go @@ -44,3 +44,8 @@ func (r *ItemRepositoryMock) CountByQueryFilters( args := r.Called(queryFilter) return args.Get(0).(int64), args.Error(1) } + +func (r *ItemRepositoryMock) Update(item *entities.Item) error { + args := r.Called(item) + return args.Error(0) +} diff --git a/internal/app/infrastructure/repositories/stub/item_keyword.go b/internal/app/infrastructure/repositories/stub/item_keyword.go index ab26cb2..19a5c1f 100644 --- a/internal/app/infrastructure/repositories/stub/item_keyword.go +++ b/internal/app/infrastructure/repositories/stub/item_keyword.go @@ -13,3 +13,8 @@ func (r *ItemKeywordRepositoryMock) CreateMany(itemKeywords []*entities.ItemKeyw args := r.Called(itemKeywords) return args.Error(0) } + +func (r *ItemKeywordRepositoryMock) DeleteByItemID(itemID string) error { + args := r.Called(itemID) + return args.Error(0) +}