You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
166 lines
6.4 KiB
166 lines
6.4 KiB
package memory
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"yunyan/comm"
|
|
"yunyan/pb"
|
|
)
|
|
|
|
func mkItem(cat, date, clock, rule string, weekday int32, ahead int32) *pb.DBMemoryItem {
|
|
return &pb.DBMemoryItem{
|
|
Id: 1, Category: cat, HappenDate: date, HappenTime: clock,
|
|
RepeatRule: rule, Weekday: weekday, RemindAhead: ahead,
|
|
State: pb.MemoryState_MemoryState_Pending, Tz: "Asia/Shanghai",
|
|
}
|
|
}
|
|
|
|
// 一次性项:只在那一天产生一个时刻,且要扣掉提前量
|
|
func TestExpandItem_Once(t *testing.T) {
|
|
loc, _ := time.LoadLocation("Asia/Shanghai")
|
|
from := time.Date(2026, 9, 5, 8, 0, 0, 0, loc)
|
|
it := mkItem(comm.MemoryCatAlarm, "2026-09-06", "09:00", comm.MemoryRepeatOnce, 0, 5)
|
|
|
|
slots := expandItem(it, from, 7, loc)
|
|
if len(slots) != 1 {
|
|
t.Fatalf("一次性项应产生 1 个时刻,得到 %d", len(slots))
|
|
}
|
|
want := time.Date(2026, 9, 6, 8, 55, 0, 0, loc).Unix()
|
|
if slots[0].RemindAt != want {
|
|
t.Errorf("提醒时刻应为 08:55(09:00 提前5分钟),得到 %s",
|
|
time.Unix(slots[0].RemindAt, 0).In(loc).Format("2006-01-02 15:04"))
|
|
}
|
|
}
|
|
|
|
// 已经过去的一次性项不该再排。排了就是 App 一打开立刻弹一堆过期提醒。
|
|
func TestExpandItem_SkipsPast(t *testing.T) {
|
|
loc, _ := time.LoadLocation("Asia/Shanghai")
|
|
from := time.Date(2026, 9, 10, 8, 0, 0, 0, loc)
|
|
it := mkItem(comm.MemoryCatAlarm, "2026-09-06", "09:00", comm.MemoryRepeatOnce, 0, 5)
|
|
if slots := expandItem(it, from, 7, loc); len(slots) != 0 {
|
|
t.Errorf("过期项不该产生时刻,得到 %d 个", len(slots))
|
|
}
|
|
}
|
|
|
|
// 重复项是 v0.1 设计的坑:一个 remind_at 字段表达不了「每天」。
|
|
// 展开必须给出窗口内的每一天。
|
|
func TestExpandItem_Daily(t *testing.T) {
|
|
loc, _ := time.LoadLocation("Asia/Shanghai")
|
|
from := time.Date(2026, 9, 5, 8, 0, 0, 0, loc)
|
|
it := mkItem(comm.MemoryCatAlarm, "2026-09-01", "09:00", comm.MemoryRepeatDaily, 0, 5)
|
|
|
|
slots := expandItem(it, from, 7, loc)
|
|
if len(slots) != 7 {
|
|
t.Fatalf("每天重复在 7 天窗口内应有 7 个时刻,得到 %d", len(slots))
|
|
}
|
|
// 今天 09:00 还没到(from 是 08:00),所以第一个应该是今天
|
|
first := time.Unix(slots[0].RemindAt, 0).In(loc)
|
|
if first.Format("2006-01-02") != "2026-09-05" {
|
|
t.Errorf("第一个时刻应是今天 2026-09-05,得到 %s", first.Format("2006-01-02"))
|
|
}
|
|
}
|
|
|
|
// 每周:锚点日远在过去时也必须能算出未来的时刻,
|
|
// 否则去年设的闹钟今年就不响了。
|
|
func TestExpandItem_WeeklyWithOldAnchor(t *testing.T) {
|
|
loc, _ := time.LoadLocation("Asia/Shanghai")
|
|
from := time.Date(2026, 9, 5, 8, 0, 0, 0, loc) // 周六
|
|
// 锚点在一年前,规则是每周一
|
|
it := mkItem(comm.MemoryCatAlarm, "2025-09-01", "09:00", comm.MemoryRepeatWeekly, 1, 5)
|
|
|
|
slots := expandItem(it, from, 14, loc)
|
|
if len(slots) != 2 {
|
|
t.Fatalf("14 天窗口内每周一应有 2 个时刻,得到 %d", len(slots))
|
|
}
|
|
for _, s := range slots {
|
|
d := time.Unix(s.HappenAt, 0).In(loc)
|
|
if d.Weekday() != time.Monday {
|
|
t.Errorf("每周一的时刻落在了 %v (%s)", d.Weekday(), d.Format("2006-01-02"))
|
|
}
|
|
}
|
|
}
|
|
|
|
// 灵感与花销是「记下来」不是「提醒我」,不该排提醒
|
|
func TestExpandItem_IdeaAndExpenseNeverRemind(t *testing.T) {
|
|
loc, _ := time.LoadLocation("Asia/Shanghai")
|
|
from := time.Date(2026, 9, 5, 8, 0, 0, 0, loc)
|
|
for _, cat := range []string{comm.MemoryCatIdea, comm.MemoryCatExpense} {
|
|
it := mkItem(cat, "2026-09-06", "09:00", comm.MemoryRepeatOnce, 0, 5)
|
|
if slots := expandItem(it, from, 7, loc); len(slots) != 0 {
|
|
t.Errorf("%s 不该产生提醒,得到 %d 个", cat, len(slots))
|
|
}
|
|
}
|
|
}
|
|
|
|
// remind_ahead=0 表示不提醒
|
|
func TestExpandItem_NoRemindWhenAheadZero(t *testing.T) {
|
|
loc, _ := time.LoadLocation("Asia/Shanghai")
|
|
from := time.Date(2026, 9, 5, 8, 0, 0, 0, loc)
|
|
it := mkItem(comm.MemoryCatTodo, "2026-09-06", "09:00", comm.MemoryRepeatOnce, 0, 0)
|
|
if slots := expandItem(it, from, 7, loc); len(slots) != 0 {
|
|
t.Errorf("remind_ahead=0 不该产生提醒,得到 %d 个", len(slots))
|
|
}
|
|
}
|
|
|
|
// 已完成的项不再提醒
|
|
func TestExpandItem_DoneNeverRemind(t *testing.T) {
|
|
loc, _ := time.LoadLocation("Asia/Shanghai")
|
|
from := time.Date(2026, 9, 5, 8, 0, 0, 0, loc)
|
|
it := mkItem(comm.MemoryCatTodo, "2026-09-06", "09:00", comm.MemoryRepeatOnce, 0, 5)
|
|
it.State = pb.MemoryState_MemoryState_Done
|
|
if slots := expandItem(it, from, 7, loc); len(slots) != 0 {
|
|
t.Errorf("已完成项不该产生提醒,得到 %d 个", len(slots))
|
|
}
|
|
}
|
|
|
|
// 没有 happen_time 的全天项按 9:00 计,不能落在 0:00 半夜吵人
|
|
func TestExpandItem_AllDayUsesNineAM(t *testing.T) {
|
|
loc, _ := time.LoadLocation("Asia/Shanghai")
|
|
from := time.Date(2026, 9, 5, 0, 30, 0, 0, loc)
|
|
it := mkItem(comm.MemoryCatTodo, "2026-09-05", "", comm.MemoryRepeatOnce, 0, 5)
|
|
slots := expandItem(it, from, 7, loc)
|
|
if len(slots) != 1 {
|
|
t.Fatalf("应有 1 个时刻,得到 %d", len(slots))
|
|
}
|
|
h := time.Unix(slots[0].HappenAt, 0).In(loc)
|
|
if h.Hour() != 9 || h.Minute() != 0 {
|
|
t.Errorf("全天项应按 09:00 计,得到 %02d:%02d", h.Hour(), h.Minute())
|
|
}
|
|
}
|
|
|
|
// expandAll 必须按时间升序:客户端要按 iOS 的 64 条上限截断,
|
|
// 顺序错了会把明天的排掉、留下一周后的
|
|
func TestExpandAll_SortedAscending(t *testing.T) {
|
|
items := []*pb.DBMemoryItem{
|
|
mkItem(comm.MemoryCatAlarm, "2026-09-30", "09:00", comm.MemoryRepeatOnce, 0, 5),
|
|
mkItem(comm.MemoryCatAlarm, "2026-09-06", "09:00", comm.MemoryRepeatDaily, 0, 5),
|
|
}
|
|
items[0].Id, items[1].Id = 10, 20
|
|
slots := expandAll(items, 30, "Asia/Shanghai")
|
|
for i := 1; i < len(slots); i++ {
|
|
if slots[i-1].RemindAt > slots[i].RemindAt {
|
|
t.Fatalf("展开结果未按时间升序:第 %d 个 %d > 第 %d 个 %d",
|
|
i-1, slots[i-1].RemindAt, i, slots[i].RemindAt)
|
|
}
|
|
}
|
|
}
|
|
|
|
// 单条项按它自己的 tz 算,不跟着请求方的时区走:
|
|
// 「明早 8 点」不该因为用户落地伦敦就变成伦敦时间 8 点
|
|
func TestExpandItem_UsesItemOwnTimezone(t *testing.T) {
|
|
it := mkItem(comm.MemoryCatAlarm, "2026-09-06", "09:00", comm.MemoryRepeatOnce, 0, 0)
|
|
it.RemindAhead = 5
|
|
it.Tz = "Asia/Shanghai"
|
|
|
|
// 请求方声称在伦敦,但项自己带了上海时区
|
|
slots := expandAll([]*pb.DBMemoryItem{it}, 30, "Europe/London")
|
|
if len(slots) != 1 {
|
|
t.Fatalf("应有 1 个时刻,得到 %d", len(slots))
|
|
}
|
|
sh, _ := time.LoadLocation("Asia/Shanghai")
|
|
got := time.Unix(slots[0].HappenAt, 0).In(sh)
|
|
if got.Hour() != 9 {
|
|
t.Errorf("应按项自己的上海时区算出 09:00,得到 %02d:%02d", got.Hour(), got.Minute())
|
|
}
|
|
}
|
|
|