package expiry import ( "testing" ) func TestService_HelperBranches(t *testing.T) { t.Run("normalize filters", func(t *testing.T) { got := normalizeFilters(ItemFilters{}) if got.Page != 1 || got.PageSize != 20 || got.Status != "all" || got.Category != "all" || got.Sort != "expiry_date" { t.Fatalf("unexpected normalized filters: %+v", got) } capped := normalizeFilters(ItemFilters{Page: 1, PageSize: 1000, Status: "all", Category: "all", Sort: "expiry_date"}) if capped.PageSize != 100 { t.Fatalf("expected page size capped at 100, got %d", capped.PageSize) } }) t.Run("validate filters", func(t *testing.T) { if err := validateFilters(ItemFilters{Status: "all", Category: "all", Sort: "expiry_date"}); err != nil { t.Fatalf("unexpected valid filters error: %v", err) } if err := validateFilters(ItemFilters{Status: "x", Category: "all", Sort: "expiry_date"}); err != ErrExpiryFilterStatusInvalid { t.Fatalf("expected status invalid, got %v", err) } if err := validateFilters(ItemFilters{Status: "all", Category: "x", Sort: "expiry_date"}); err != ErrExpiryFilterCategoryInvalid { t.Fatalf("expected category invalid, got %v", err) } if err := validateFilters(ItemFilters{Status: "all", Category: "all", Sort: "x"}); err != ErrExpiryFilterSortInvalid { t.Fatalf("expected sort invalid, got %v", err) } }) t.Run("validate base fields", func(t *testing.T) { if err := validateBaseFields("名称", CategoryFood, 1, "位置", "备注"); err != nil { t.Fatalf("expected valid fields, got %v", err) } if err := validateBaseFields("", CategoryFood, 1, "", ""); err != ErrExpiryNameInvalid { t.Fatalf("expected name invalid, got %v", err) } if err := validateBaseFields("名称", "bad", 1, "", ""); err != ErrExpiryCategoryInvalid { t.Fatalf("expected category invalid, got %v", err) } if err := validateBaseFields("名称", CategoryFood, -1, "", ""); err != ErrExpiryQuantityInvalid { t.Fatalf("expected quantity invalid, got %v", err) } }) t.Run("apply expiry date", func(t *testing.T) { prod := mustDate(t, "2031-01-01") shelf := 5 expiry := mustDate(t, "2031-01-20") item := &ExpiryItem{ProductionDate: &prod} if err := applyExpiryDate(item, &expiry, &shelf); err != nil { t.Fatalf("expected no error, got %v", err) } if item.ExpiryDate.Format("2006-01-02") != "2031-01-06" { t.Fatalf("expected computed expiry, got %s", item.ExpiryDate.Format("2006-01-02")) } item2 := &ExpiryItem{} if err := applyExpiryDate(item2, &expiry, nil); err != nil { t.Fatalf("expected no error on expiry only, got %v", err) } item3 := &ExpiryItem{} if err := applyExpiryDate(item3, nil, nil); err != ErrExpiryDateRequired { t.Fatalf("expected ErrExpiryDateRequired, got %v", err) } }) t.Run("category and remind days", func(t *testing.T) { if !isValidCategory(CategoryCosmetic) || isValidCategory("invalid") { t.Fatalf("isValidCategory result unexpected") } remindDays, err := validateRemindDays([]int{10, 10, 3, 1}) if err != nil { t.Fatalf("validate remind days error: %v", err) } if len(remindDays) != 3 { t.Fatalf("expected deduped len=3, got %d", len(remindDays)) } if _, err := validateRemindDays([]int{}); err != ErrExpiryRemindDaysInvalid { t.Fatalf("expected remind days invalid for empty, got %v", err) } }) } func TestService_UpdateItemPreserveStatus(t *testing.T) { db := newTestDB(t) repo := NewRepository(db) service := NewService(repo) expiryDate := mustDate(t, "2031-02-01") created, err := service.CreateItem(88, CreateItemRequest{ MiniProgramID: 1, Name: "牙膏", Category: CategoryOther, ExpiryDate: &expiryDate, Quantity: 1, }) if err != nil { t.Fatalf("create item: %v", err) } if err := service.UpdateItemStatus(created.ID, 88, StatusDiscarded); err != nil { t.Fatalf("update status discarded: %v", err) } newExpiry := mustDate(t, "2031-03-01") updated, err := service.UpdateItem(created.ID, 88, UpdateItemRequest{ MiniProgramID: 1, Name: "牙膏(更新)", Category: CategoryOther, ExpiryDate: &newExpiry, Quantity: 1, }) if err != nil { t.Fatalf("update discarded item: %v", err) } if updated.Status != StatusDiscarded { t.Fatalf("expected discarded status preserved, got %s", updated.Status) } }