package unit import ( "context" "errors" "testing" "time" "autostore/internal/application/commands" "autostore/internal/domain/entities" "autostore/internal/domain/specifications" ) func createTestCommand() (*commands.AddItemCommand, *mockItemRepository, *mockOrderService, *mockTimeProvider, *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.NewAddItemCommand(itemRepo, orderService, timeProvider, expirationSpec, logger) return cmd, itemRepo, orderService, timeProvider, logger } func TestWhenItemNotExpiredThenItemSaved(t *testing.T) { // Given cmd, itemRepo, _, _, _ := createTestCommand() expirationTime, _ := time.Parse(dateFormat, notExpiredDate) itemSaved := false itemRepo.saveFunc = func(ctx context.Context, item *entities.ItemEntity) error { itemSaved = true if item.GetName() != itemName { t.Errorf("Expected item name %s, got %s", itemName, item.GetName()) } if item.GetOrderURL() != orderURL { t.Errorf("Expected order URL %s, got %s", orderURL, item.GetOrderURL()) } if item.GetUserID().String() != userID { t.Errorf("Expected user ID %s, got %s", userID, item.GetUserID().String()) } return nil } // When resultID, err := cmd.Execute(context.Background(), itemName, expirationTime, orderURL, userID) // Then if err != nil { t.Errorf("Expected no error, got %v", err) } if resultID == "" { t.Error("Expected non-empty result ID") } if !itemSaved { t.Error("Expected item to be saved") } } func TestWhenItemNotExpiredThenOrderIsNotPlaced(t *testing.T) { // Given cmd, _, orderService, _, _ := createTestCommand() expirationTime, _ := time.Parse(dateFormat, notExpiredDate) orderPlaced := false orderService.orderItemFunc = func(ctx context.Context, item *entities.ItemEntity) error { orderPlaced = true return nil } // When _, err := cmd.Execute(context.Background(), itemName, expirationTime, orderURL, userID) // Then if err != nil { t.Errorf("Expected no error, got %v", err) } if orderPlaced { t.Error("Expected order not to be placed for non-expired item") } } func TestWhenItemNotExpiredThenNewItemIdIsReturned(t *testing.T) { // Given cmd, _, _, _, _ := createTestCommand() expirationTime, _ := time.Parse(dateFormat, notExpiredDate) // When resultID, err := cmd.Execute(context.Background(), itemName, expirationTime, orderURL, userID) // Then if err != nil { t.Errorf("Expected no error, got %v", err) } if resultID == "" { t.Error("Expected non-empty result ID") } } func TestWhenItemIsExpiredThenOrderPlaced(t *testing.T) { // Given cmd, itemRepo, orderService, timeProvider, _ := createTestCommand() // Set expiration time to 1 hour before current time to ensure it's expired currentTime := timeProvider.Now() expirationTime := currentTime.Add(-1 * time.Hour) orderPlaced := false var orderedItem *entities.ItemEntity itemRepo.saveFunc = func(ctx context.Context, item *entities.ItemEntity) error { return nil } orderService.orderItemFunc = func(ctx context.Context, item *entities.ItemEntity) error { orderPlaced = true orderedItem = item return nil } // When resultID, err := cmd.Execute(context.Background(), itemName, expirationTime, orderURL, userID) // Then if err != nil { t.Errorf("Expected no error, got %v", err) } if resultID == "" { t.Error("Expected non-empty result ID") } if !orderPlaced { t.Error("Expected order to be placed for expired item") } if orderedItem == nil { t.Error("Expected ordered item to be captured") } if orderedItem != nil && orderedItem.GetName() != itemName { t.Errorf("Expected ordered item name %s, got %s", itemName, orderedItem.GetName()) } } func TestWhenItemNameIsEmptyThenErrorReturned(t *testing.T) { // Given cmd, _, _, _, _ := createTestCommand() expirationTime, _ := time.Parse(dateFormat, notExpiredDate) // When _, err := cmd.Execute(context.Background(), "", expirationTime, orderURL, userID) // Then if err == nil { t.Error("Expected error for empty item name") } } func TestWhenOrderUrlIsEmptyThenErrorReturned(t *testing.T) { // Given cmd, _, _, _, _ := createTestCommand() expirationTime, _ := time.Parse(dateFormat, notExpiredDate) // When _, err := cmd.Execute(context.Background(), itemName, expirationTime, "", userID) // Then if err == nil { t.Error("Expected error for empty order URL") } } func TestWhenUserIdIsEmptyThenErrorReturned(t *testing.T) { // Given cmd, _, _, _, _ := createTestCommand() expirationTime, _ := time.Parse(dateFormat, notExpiredDate) // When _, err := cmd.Execute(context.Background(), itemName, expirationTime, orderURL, "") // Then if err == nil { t.Error("Expected error for empty user ID") } } func TestWhenOrderServiceFailsThenErrorLogged(t *testing.T) { // Given cmd, itemRepo, orderService, _, _ := createTestCommand() expirationTime, _ := time.Parse(dateFormat, expiredDate) itemRepo.saveFunc = func(ctx context.Context, item *entities.ItemEntity) error { return nil } orderService.orderItemFunc = func(ctx context.Context, item *entities.ItemEntity) error { return errors.New("order service failed") } // When - the handler should not throw an exception when the order service fails // It should log the error and continue resultID, err := cmd.Execute(context.Background(), itemName, expirationTime, orderURL, userID) // Then if err != nil { t.Errorf("Expected no error when order service fails, got %v", err) } if resultID == "" { t.Error("Expected non-empty result ID even when order service fails") } } func TestWhenRepositorySaveThrowsExceptionThenErrorReturned(t *testing.T) { // Given cmd, itemRepo, _, _, _ := createTestCommand() expirationTime, _ := time.Parse(dateFormat, notExpiredDate) expectedError := errors.New("repository error") itemRepo.saveFunc = func(ctx context.Context, item *entities.ItemEntity) error { return expectedError } // When _, err := cmd.Execute(context.Background(), itemName, expirationTime, orderURL, userID) // Then if err == nil { t.Error("Expected error when repository save fails") } } func TestWhenRepositorySaveThrowsExceptionThenOrderIsNotPlaced(t *testing.T) { // Given cmd, itemRepo, orderService, _, _ := createTestCommand() expirationTime, _ := time.Parse(dateFormat, expiredDate) expectedError := errors.New("repository error") itemRepo.saveFunc = func(ctx context.Context, item *entities.ItemEntity) error { return expectedError } orderPlaced := false orderService.orderItemFunc = func(ctx context.Context, item *entities.ItemEntity) error { orderPlaced = true return nil } // When _, err := cmd.Execute(context.Background(), itemName, expirationTime, orderURL, userID) // Then if err == nil { t.Error("Expected error when repository save fails") } if orderPlaced { t.Error("Expected order not to be placed when repository save fails") } } func TestWhenTimeProviderThrowsExceptionThenErrorReturned(t *testing.T) { // Given cmd, _, _, timeProvider, _ := createTestCommand() expirationTime, _ := time.Parse(dateFormat, notExpiredDate) expectedError := errors.New("time provider error") timeProvider.nowFunc = func() time.Time { panic(expectedError) } // When defer func() { if r := recover(); r != nil { // Expected panic } else { t.Error("Expected panic when time provider fails") } }() cmd.Execute(context.Background(), itemName, expirationTime, orderURL, userID) } func TestWhenItemExpirationDateIsExactlyCurrentTimeThenOrderIsPlaced(t *testing.T) { // Given cmd, itemRepo, orderService, timeProvider, _ := createTestCommand() // Use the exact current time from the time provider currentTime := timeProvider.Now() orderPlaced := false itemRepo.saveFunc = func(ctx context.Context, item *entities.ItemEntity) error { return nil } orderService.orderItemFunc = func(ctx context.Context, item *entities.ItemEntity) error { orderPlaced = true return nil } // When resultID, err := cmd.Execute(context.Background(), itemName, currentTime, orderURL, userID) // Then if err != nil { t.Errorf("Expected no error, got %v", err) } if resultID == "" { t.Error("Expected non-empty result ID") } if !orderPlaced { t.Error("Expected order to be placed when expiration date equals current time") } } func TestWhenItemExpirationDateIsInFutureThenItemSaved(t *testing.T) { // Given cmd, itemRepo, _, _, _ := createTestCommand() expirationTime, _ := time.Parse(dateFormat, notExpiredDate) itemSaved := false itemRepo.saveFunc = func(ctx context.Context, item *entities.ItemEntity) error { itemSaved = true return nil } // When resultID, err := cmd.Execute(context.Background(), itemName, expirationTime, orderURL, userID) // Then if err != nil { t.Errorf("Expected no error, got %v", err) } if resultID == "" { t.Error("Expected non-empty result ID") } if !itemSaved { t.Error("Expected item to be saved when expiration date is in future") } }