package unit import ( "context" "errors" "testing" "time" "autostore/internal/application/commands" "autostore/internal/domain/entities" "autostore/internal/domain/specifications" "autostore/internal/domain/value_objects" ) func createExpiredItem1() *entities.ItemEntity { expirationTime, _ := time.Parse(dateFormat, expiredDate) itemID, _ := value_objects.NewItemIDFromString("550e8400-e29b-41d4-a716-446655440001") userID, _ := value_objects.NewUserIDFromString("550e8400-e29b-41d4-a716-446655440003") expirationDate, _ := value_objects.NewExpirationDate(expirationTime) item, _ := entities.NewItem(itemID, "Expired Item 1", expirationDate, "http://example.com/order1", userID) return item } func createExpiredItem2() *entities.ItemEntity { expirationTime, _ := time.Parse(dateFormat, expiredDate) itemID, _ := value_objects.NewItemIDFromString("550e8400-e29b-41d4-a716-446655440002") userID, _ := value_objects.NewUserIDFromString("550e8400-e29b-41d4-a716-446655440004") expirationDate, _ := value_objects.NewExpirationDate(expirationTime) item, _ := entities.NewItem(itemID, "Expired Item 2", expirationDate, "http://example.com/order2", userID) return item } func createTestHandleExpiredItemsCommand() (*commands.HandleExpiredItemsCommand, *mockItemRepository, *mockOrderService, *mockTimeProvider, *specifications.ItemExpirationSpec, *mockLogger) { itemRepo := &mockItemRepository{} orderService := &mockOrderService{} timeProvider := &mockTimeProvider{ nowFunc: func() time.Time { t, _ := time.Parse(dateFormat, mockedNow) return t }, } expirationSpec := specifications.NewItemExpirationSpec() logger := &mockLogger{} cmd := commands.NewHandleExpiredItemsCommand(itemRepo, orderService, timeProvider, expirationSpec, logger) return cmd, itemRepo, orderService, timeProvider, expirationSpec, logger } func TestWhenNoExpiredItemsExistThenNoOrdersPlaced(t *testing.T) { // Given cmd, itemRepo, orderService, _, _, logger := createTestHandleExpiredItemsCommand() itemRepo.findWhereFunc = func(ctx context.Context, spec specifications.Specification[*entities.ItemEntity]) ([]*entities.ItemEntity, error) { return []*entities.ItemEntity{}, nil } orderCalled := false orderService.orderItemFunc = func(ctx context.Context, item *entities.ItemEntity) error { orderCalled = true return nil } deleteCalled := false itemRepo.deleteFunc = func(ctx context.Context, id value_objects.ItemID) error { deleteCalled = true return nil } // When err := cmd.Execute(context.Background()) // Then if err != nil { t.Errorf("Expected no error, got %v", err) } if orderCalled { t.Error("Expected order service not to be called") } if deleteCalled { t.Error("Expected delete not to be called") } if len(logger.errorLogs) > 0 { t.Error("Expected no error logs") } } func TestWhenExpiredItemsExistThenOrdersPlacedAndItemsDeleted(t *testing.T) { // Given cmd, itemRepo, orderService, _, _, logger := createTestHandleExpiredItemsCommand() expiredItem1 := createExpiredItem1() expiredItem2 := createExpiredItem2() expiredItems := []*entities.ItemEntity{expiredItem1, expiredItem2} itemRepo.findWhereFunc = func(ctx context.Context, spec specifications.Specification[*entities.ItemEntity]) ([]*entities.ItemEntity, error) { return expiredItems, nil } orderCallCount := 0 orderService.orderItemFunc = func(ctx context.Context, item *entities.ItemEntity) error { orderCallCount++ return nil } deleteCallCount := 0 itemRepo.deleteFunc = func(ctx context.Context, id value_objects.ItemID) error { deleteCallCount++ return nil } // When err := cmd.Execute(context.Background()) // Then if err != nil { t.Errorf("Expected no error, got %v", err) } if orderCallCount != 2 { t.Errorf("Expected order service to be called 2 times, got %d", orderCallCount) } if deleteCallCount != 2 { t.Errorf("Expected delete to be called 2 times, got %d", deleteCallCount) } if len(logger.errorLogs) > 0 { t.Error("Expected no error logs") } } func TestWhenOrderServiceFailsForOneItemThenErrorLoggedAndOtherItemProcessed(t *testing.T) { // Given cmd, itemRepo, orderService, _, _, logger := createTestHandleExpiredItemsCommand() expiredItem1 := createExpiredItem1() expiredItem2 := createExpiredItem2() expiredItems := []*entities.ItemEntity{expiredItem1, expiredItem2} itemRepo.findWhereFunc = func(ctx context.Context, spec specifications.Specification[*entities.ItemEntity]) ([]*entities.ItemEntity, error) { return expiredItems, nil } orderCallCount := 0 orderService.orderItemFunc = func(ctx context.Context, item *entities.ItemEntity) error { orderCallCount++ if orderCallCount == 1 { return errors.New("order service failed") } return nil } deleteCallCount := 0 itemRepo.deleteFunc = func(ctx context.Context, id value_objects.ItemID) error { deleteCallCount++ return nil } // When err := cmd.Execute(context.Background()) // Then if err != nil { t.Errorf("Expected no error, got %v", err) } if orderCallCount != 2 { t.Errorf("Expected order service to be called 2 times, got %d", orderCallCount) } if deleteCallCount != 1 { t.Errorf("Expected delete to be called 1 time (only for successful order), got %d", deleteCallCount) } if len(logger.errorLogs) != 1 { t.Errorf("Expected 1 error log, got %d", len(logger.errorLogs)) } } func TestWhenRepositoryFindThrowsErrorThenErrorReturned(t *testing.T) { // Given cmd, itemRepo, orderService, _, _, logger := createTestHandleExpiredItemsCommand() expectedError := errors.New("repository find error") itemRepo.findWhereFunc = func(ctx context.Context, spec specifications.Specification[*entities.ItemEntity]) ([]*entities.ItemEntity, error) { return nil, expectedError } orderCalled := false orderService.orderItemFunc = func(ctx context.Context, item *entities.ItemEntity) error { orderCalled = true return nil } // When err := cmd.Execute(context.Background()) // Then if err == nil { t.Error("Expected error, got nil") } if orderCalled { t.Error("Expected order service not to be called") } if len(logger.errorLogs) != 1 { t.Errorf("Expected 1 error log, got %d", len(logger.errorLogs)) } } func TestWhenRepositoryDeleteThrowsExceptionThenErrorLogged(t *testing.T) { // Given cmd, itemRepo, orderService, _, _, logger := createTestHandleExpiredItemsCommand() expiredItem1 := createExpiredItem1() expiredItems := []*entities.ItemEntity{expiredItem1} itemRepo.findWhereFunc = func(ctx context.Context, spec specifications.Specification[*entities.ItemEntity]) ([]*entities.ItemEntity, error) { return expiredItems, nil } orderService.orderItemFunc = func(ctx context.Context, item *entities.ItemEntity) error { return nil } expectedError := errors.New("delete failed") itemRepo.deleteFunc = func(ctx context.Context, id value_objects.ItemID) error { return expectedError } // When err := cmd.Execute(context.Background()) // Then if err == nil { t.Error("Expected error, got nil") } if len(logger.errorLogs) != 1 { t.Errorf("Expected 1 error log, got %d", len(logger.errorLogs)) } } func TestWhenTimeProviderThrowsErrorThenErrorReturned(t *testing.T) { // Given cmd, _, _, timeProvider, _, _ := createTestHandleExpiredItemsCommand() timeProvider.nowFunc = func() time.Time { panic("time provider error") } // When & Then defer func() { if r := recover(); r != nil { // Expected panic } else { t.Error("Expected panic when time provider fails") } }() cmd.Execute(context.Background()) } func TestWhenAllOrderServicesFailThenAllErrorsLogged(t *testing.T) { // Given cmd, itemRepo, orderService, _, _, logger := createTestHandleExpiredItemsCommand() expiredItem1 := createExpiredItem1() expiredItem2 := createExpiredItem2() expiredItems := []*entities.ItemEntity{expiredItem1, expiredItem2} itemRepo.findWhereFunc = func(ctx context.Context, spec specifications.Specification[*entities.ItemEntity]) ([]*entities.ItemEntity, error) { return expiredItems, nil } orderService.orderItemFunc = func(ctx context.Context, item *entities.ItemEntity) error { return errors.New("order service failed") } deleteCalled := false itemRepo.deleteFunc = func(ctx context.Context, id value_objects.ItemID) error { deleteCalled = true return nil } // When err := cmd.Execute(context.Background()) // Then if err != nil { t.Errorf("Expected no error, got %v", err) } if deleteCalled { t.Error("Expected delete not to be called when all orders fail") } if len(logger.errorLogs) != 2 { t.Errorf("Expected 2 error logs, got %d", len(logger.errorLogs)) } }