package expiry import ( "testing" "time" ) func TestRepository_CreateFindUpdateDelete(t *testing.T) { db := newTestDB(t) repo := NewRepository(db) expiryDate := mustDate(t, "2030-01-10") shelf := 30 item := &ExpiryItem{ UserID: 100, MiniProgramID: 1, Name: "牛奶", Category: CategoryFood, ExpiryDate: expiryDate, ShelfLifeDays: &shelf, Quantity: 2, Status: StatusNormal, } if err := repo.Create(item); err != nil { t.Fatalf("create item: %v", err) } if item.ID == 0 { t.Fatalf("expected item id > 0") } found, err := repo.FindByID(item.ID, item.UserID) if err != nil { t.Fatalf("find by id: %v", err) } if found.Name != "牛奶" { t.Fatalf("unexpected name: %s", found.Name) } found.Name = "牛奶(更新)" if err := repo.Update(found); err != nil { t.Fatalf("update item: %v", err) } updated, err := repo.FindByID(item.ID, item.UserID) if err != nil { t.Fatalf("find updated item: %v", err) } if updated.Name != "牛奶(更新)" { t.Fatalf("unexpected updated name: %s", updated.Name) } if err := repo.UpdateStatus(item.ID, item.UserID, StatusUsed); err != nil { t.Fatalf("update status: %v", err) } afterStatus, err := repo.FindByID(item.ID, item.UserID) if err != nil { t.Fatalf("find after status: %v", err) } if afterStatus.Status != StatusUsed { t.Fatalf("expected status used, got %s", afterStatus.Status) } if err := repo.Delete(item.ID, item.UserID); err != nil { t.Fatalf("delete item: %v", err) } _, err = repo.FindByID(item.ID, item.UserID) if err != ErrExpiryItemNotFound { t.Fatalf("expected ErrExpiryItemNotFound after delete, got %v", err) } } func TestRepository_FindByUserAndSummary(t *testing.T) { db := newTestDB(t) repo := NewRepository(db) now := dateOnly(mustDate(t, "2030-01-01")) items := []ExpiryItem{ { UserID: 200, MiniProgramID: 1, Name: "过期食品", Category: CategoryFood, ExpiryDate: now.AddDate(0, 0, -1), Quantity: 1, Status: StatusNormal, }, { UserID: 200, MiniProgramID: 1, Name: "临期药品", Category: CategoryMedicine, ExpiryDate: now.AddDate(0, 0, 3), Quantity: 1, Status: StatusNormal, }, { UserID: 200, MiniProgramID: 1, Name: "正常化妆品", Category: CategoryCosmetic, ExpiryDate: now.AddDate(0, 0, 30), Quantity: 1, Status: StatusNormal, }, { UserID: 200, MiniProgramID: 1, Name: "已使用", Category: CategoryOther, ExpiryDate: now.AddDate(0, 0, 30), Quantity: 1, Status: StatusUsed, }, { UserID: 200, MiniProgramID: 1, Name: "已丢弃", Category: CategoryOther, ExpiryDate: now.AddDate(0, 0, 30), Quantity: 1, Status: StatusDiscarded, }, } for idx := range items { if err := repo.Create(&items[idx]); err != nil { t.Fatalf("create item %d: %v", idx, err) } } list, total, err := repo.FindByUser(200, map[string]interface{}{ "status": "all", "category": "all", "sort": "expiry_date", }, 1, 20) if err != nil { t.Fatalf("find by user all: %v", err) } if total != 5 || len(list) != 5 { t.Fatalf("expected 5 items total/list, got total=%d len=%d", total, len(list)) } usedList, usedTotal, err := repo.FindByUser(200, map[string]interface{}{ "status": StatusUsed, "category": "all", "sort": "expiry_date", }, 1, 20) if err != nil { t.Fatalf("find used list: %v", err) } if usedTotal != 1 || len(usedList) != 1 { t.Fatalf("expected used list 1, got total=%d len=%d", usedTotal, len(usedList)) } summary, err := repo.GetSummary(200) if err != nil { t.Fatalf("get summary: %v", err) } if summary["total_items"] != 3 { t.Fatalf("expected total_items=3, got %d", summary["total_items"]) } if summary["used"] != 1 || summary["discarded"] != 1 { t.Fatalf("unexpected used/discarded: %+v", summary) } } func TestRepository_SettingsCRUD(t *testing.T) { db := newTestDB(t) repo := NewRepository(db) settings, err := repo.GetSettings(300) if err != nil { t.Fatalf("get empty settings: %v", err) } if settings != nil { t.Fatalf("expected nil settings for new user") } saved, err := repo.UpdateSettings(300, []int{7, 3, 1}) if err != nil { t.Fatalf("create settings: %v", err) } if len(saved.RemindDays) != 3 { t.Fatalf("expected 3 remind days, got %d", len(saved.RemindDays)) } updated, err := repo.UpdateSettings(300, []int{10, 5, 2}) if err != nil { t.Fatalf("update settings: %v", err) } if len(updated.RemindDays) != 3 || updated.RemindDays[0] != 10 { t.Fatalf("unexpected remind days: %+v", updated.RemindDays) } loaded, err := repo.GetSettings(300) if err != nil { t.Fatalf("reload settings: %v", err) } if len(loaded.RemindDays) != 3 || loaded.RemindDays[0] != 10 { t.Fatalf("unexpected loaded remind days: %+v", loaded.RemindDays) } } func TestRepository_ErrorAndFilterBranches(t *testing.T) { db := newTestDB(t) repo := NewRepository(db) if err := repo.Delete(999, 1); err != ErrExpiryItemNotFound { t.Fatalf("expected delete not found, got %v", err) } if err := repo.UpdateStatus(999, 1, StatusUsed); err != ErrExpiryItemNotFound { t.Fatalf("expected update status not found, got %v", err) } _, err := repo.FindByID(999, 1) if err != ErrExpiryItemNotFound { t.Fatalf("expected find not found, got %v", err) } now := dateOnly(time.Now()) sevenDaysLater := now.AddDate(0, 0, 7) items := []ExpiryItem{ { UserID: 400, MiniProgramID: 1, Name: "过期", Category: CategoryFood, ExpiryDate: now.AddDate(0, 0, -1), Quantity: 1, }, { UserID: 400, MiniProgramID: 1, Name: "临期", Category: CategoryFood, ExpiryDate: now.AddDate(0, 0, 1), Quantity: 1, }, { UserID: 400, MiniProgramID: 1, Name: "正常", Category: CategoryMedicine, ExpiryDate: sevenDaysLater.AddDate(0, 0, 3), Quantity: 1, }, } for i := range items { if err := repo.Create(&items[i]); err != nil { t.Fatalf("seed item %d: %v", i, err) } } expiredList, _, err := repo.FindByUser(400, map[string]interface{}{ "status": StatusExpired, "sort": "created_at", }, 1, 10) if err != nil { t.Fatalf("find expired: %v", err) } if len(expiredList) != 1 { t.Fatalf("expected 1 expired, got %d", len(expiredList)) } expiringList, _, err := repo.FindByUser(400, map[string]interface{}{ "status": StatusExpiring, }, 1, 10) if err != nil { t.Fatalf("find expiring: %v", err) } if len(expiringList) != 1 { t.Fatalf("expected 1 expiring, got %d", len(expiringList)) } normalList, _, err := repo.FindByUser(400, map[string]interface{}{ "status": StatusNormal, "category": CategoryMedicine, }, 1, 10) if err != nil { t.Fatalf("find normal: %v", err) } if len(normalList) != 1 { t.Fatalf("expected 1 normal, got %d", len(normalList)) } summary, err := repo.GetSummary(400) if err != nil { t.Fatalf("get summary: %v", err) } if summary["expired"] != 1 || summary["expiring_soon"] != 1 || summary["normal"] != 1 { t.Fatalf("unexpected summary: %+v", summary) } }