package comm import ( "testing" "time" ) // 周日在 Go 里 Weekday()==0、在 Dart 里 weekday==7。这套换算错了不会报错, // 只会让「每周日」的闹钟在周一响——所以单独守住。 func TestMemoryRepeatHits_WeekdayMapping(t *testing.T) { loc := time.UTC // 2026-09-06 是周日,2026-09-07 是周一 sunday := time.Date(2026, 9, 6, 0, 0, 0, 0, loc) monday := time.Date(2026, 9, 7, 0, 0, 0, 0, loc) if sunday.Weekday() != time.Sunday || monday.Weekday() != time.Monday { t.Fatalf("测试基准日期不对: %v %v", sunday.Weekday(), monday.Weekday()) } // weekly + weekday=7(Dart 口径的周日)应命中周日、不命中周一 if !MemoryRepeatHits(MemoryRepeatWeekly, 7, sunday, sunday) { t.Error("weekday=7 应命中周日") } if MemoryRepeatHits(MemoryRepeatWeekly, 7, monday, sunday) { t.Error("weekday=7 不应命中周一") } // weekday=1 是周一 if !MemoryRepeatHits(MemoryRepeatWeekly, 1, monday, sunday) { t.Error("weekday=1 应命中周一") } } func TestMemoryRepeatHits_WeekdaysAndWeekend(t *testing.T) { loc := time.UTC days := map[string]time.Time{ "周五": time.Date(2026, 9, 4, 0, 0, 0, 0, loc), "周六": time.Date(2026, 9, 5, 0, 0, 0, 0, loc), "周日": time.Date(2026, 9, 6, 0, 0, 0, 0, loc), "周一": time.Date(2026, 9, 7, 0, 0, 0, 0, loc), } anchor := days["周五"] cases := []struct { day string weekday bool weekend bool }{ {"周五", true, false}, {"周六", false, true}, {"周日", false, true}, // 周日必须算周末,不能因为 Go 的 0 被当成周一 {"周一", true, false}, } for _, c := range cases { if got := MemoryRepeatHits(MemoryRepeatWeekdays, 0, days[c.day], anchor); got != c.weekday { t.Errorf("%s weekdays 期望 %v 得到 %v", c.day, c.weekday, got) } if got := MemoryRepeatHits(MemoryRepeatWeekend, 0, days[c.day], anchor); got != c.weekend { t.Errorf("%s weekend 期望 %v 得到 %v", c.day, c.weekend, got) } } } // ISO 周口径:跨年那一周最容易出错 func TestMemoryWeekKeyAndRange(t *testing.T) { loc := time.UTC // 2026-01-01 是周四,按 ISO 属于 2026-W01,那一周的周一是 2025-12-29 d := time.Date(2026, 1, 1, 12, 0, 0, 0, loc) if k := MemoryWeekKey(d); k != "2026-W01" { t.Errorf("周键期望 2026-W01 得到 %s", k) } start, end := MemoryWeekRange(d) if FormatMemoryDate(start) != "2025-12-29" { t.Errorf("周起期望 2025-12-29 得到 %s", FormatMemoryDate(start)) } if FormatMemoryDate(end) != "2026-01-04" { t.Errorf("周止期望 2026-01-04 得到 %s", FormatMemoryDate(end)) } // 周日当天取到的必须还是本周(周一~周日),不能滑到下一周 sunday := time.Date(2026, 1, 4, 23, 0, 0, 0, loc) s2, e2 := MemoryWeekRange(sunday) if FormatMemoryDate(s2) != "2025-12-29" || FormatMemoryDate(e2) != "2026-01-04" { t.Errorf("周日取周范围错: %s ~ %s", FormatMemoryDate(s2), FormatMemoryDate(e2)) } } func TestMemoryMonthRange(t *testing.T) { loc := time.UTC // 闰年 2 月 d := time.Date(2028, 2, 10, 0, 0, 0, 0, loc) s, e := MemoryMonthRange(d) if FormatMemoryDate(s) != "2028-02-01" || FormatMemoryDate(e) != "2028-02-29" { t.Errorf("2028-02 范围错: %s ~ %s", FormatMemoryDate(s), FormatMemoryDate(e)) } } // 认不出的时区必须回退到本地时区,绝不能回退成 UTC—— // 那会让所有时刻静默偏移 8 小时。 func TestLoadMemoryLocation_FallbackIsLocalNotUTC(t *testing.T) { if got := LoadMemoryLocation("Nowhere/Nothing"); got != time.Local { t.Errorf("非法时区应回退 time.Local,得到 %v", got) } if got := LoadMemoryLocation(""); got != time.Local { t.Errorf("空时区应回退 time.Local,得到 %v", got) } if got := LoadMemoryLocation("Asia/Tokyo"); got.String() != "Asia/Tokyo" { t.Errorf("合法时区解析失败,得到 %v", got) } } func TestNewMemoryClientKey_NotEmptyAndUnique(t *testing.T) { seen := map[string]bool{} for i := 0; i < 1000; i++ { k := NewMemoryClientKey() if k == "" { t.Fatal("client_key 不能为空:它参与唯一索引,空串会让第二条记录插不进去") } if seen[k] { t.Fatalf("client_key 重复: %s", k) } seen[k] = true } } func TestParseMemoryClock(t *testing.T) { if h, m, ok := ParseMemoryClock("15:30"); !ok || h != 15 || m != 30 { t.Errorf("15:30 解析错: %d %d %v", h, m, ok) } if _, _, ok := ParseMemoryClock(""); ok { t.Error("空串不该解析成功") } if _, _, ok := ParseMemoryClock("25:00"); ok { t.Error("25:00 不该解析成功") } }