112 lines
3.7 KiB
Go
112 lines
3.7 KiB
Go
package scheduler
|
||
|
||
import (
|
||
"strings"
|
||
"testing"
|
||
"time"
|
||
|
||
"github.com/agentmail/gateway/internal/models"
|
||
)
|
||
|
||
func TestRenderReminder(t *testing.T) {
|
||
at := time.Date(2026, 9, 4, 9, 30, 0, 0, time.Local)
|
||
e := models.CalendarEvent{
|
||
Title: "每日站会",
|
||
Description: "同步昨天进展与今天计划",
|
||
EventTime: at,
|
||
}
|
||
|
||
t.Run("三个变量都替换", func(t *testing.T) {
|
||
got := RenderReminder("日程提醒:{title}\n时间:{time}\n{description}", e)
|
||
for _, want := range []string{"每日站会", "2026-09-04 09:30", "同步昨天进展与今天计划"} {
|
||
if !strings.Contains(got, want) {
|
||
t.Errorf("渲染结果缺 %q:\n%s", want, got)
|
||
}
|
||
}
|
||
if strings.Contains(got, "{") {
|
||
t.Errorf("仍有未替换的占位符:\n%s", got)
|
||
}
|
||
})
|
||
|
||
t.Run("同一变量出现多次全部替换", func(t *testing.T) {
|
||
// ReplaceAll 而非 Replace:模板里写两遍 {title} 时
|
||
// 只替换第一处会让 Agent 收到一封半成品邮件。
|
||
got := RenderReminder("{title} —— 请开始 {title}", e)
|
||
if strings.Contains(got, "{title}") {
|
||
t.Errorf("第二处 {title} 未替换:%s", got)
|
||
}
|
||
})
|
||
|
||
t.Run("空模板不产生占位符残留", func(t *testing.T) {
|
||
if got := RenderReminder("", e); got != "" {
|
||
t.Errorf("空模板应渲染成空串,得到 %q", got)
|
||
}
|
||
})
|
||
|
||
t.Run("没有变量的模板原样返回", func(t *testing.T) {
|
||
const plain = "该跑测试了"
|
||
if got := RenderReminder(plain, e); got != plain {
|
||
t.Errorf("纯文本模板应原样返回,得到 %q", got)
|
||
}
|
||
})
|
||
|
||
t.Run("描述为空时不留下空行以外的痕迹", func(t *testing.T) {
|
||
e2 := e
|
||
e2.Description = ""
|
||
got := RenderReminder("{title}|{description}|", e2)
|
||
if got != "每日站会||" {
|
||
t.Errorf("空描述应替换成空串,得到 %q", got)
|
||
}
|
||
})
|
||
|
||
// {time} 必须是本地时间。DSN 带 _timezone=UTC,从库里读回的 EventTime
|
||
// 是 UTC;不转本地就会把人在 +0800 输入的 14:30 写成 06:30,
|
||
// 而前端预览用的是本地时间 —— 两边差 8 小时且都不报错。
|
||
t.Run("time 用本地时区而非 UTC", func(t *testing.T) {
|
||
// 刻意构造一个 UTC 时刻(模拟从库里 Scan 出来的样子)
|
||
utcEvent := models.CalendarEvent{
|
||
Title: "跨时区检查",
|
||
EventTime: time.Date(2026, 9, 3, 6, 30, 0, 0, time.UTC),
|
||
}
|
||
got := RenderReminder("{time}", utcEvent)
|
||
want := time.Date(2026, 9, 3, 6, 30, 0, 0, time.UTC).Local().Format("2006-01-02 15:04")
|
||
if got != want {
|
||
t.Errorf("{time} = %q,期望本地时间 %q", got, want)
|
||
}
|
||
})
|
||
|
||
// 同一时刻无论以哪个时区的 Location 传进来,渲染结果必须一致 ——
|
||
// 它代表的是「墙上时钟的那一刻」,与 Location 的表示方式无关。
|
||
t.Run("同一时刻不同 Location 渲染一致", func(t *testing.T) {
|
||
base := time.Date(2026, 9, 3, 6, 30, 0, 0, time.UTC)
|
||
a := RenderReminder("{time}", models.CalendarEvent{EventTime: base})
|
||
b := RenderReminder("{time}", models.CalendarEvent{EventTime: base.Local()})
|
||
if a != b {
|
||
t.Errorf("UTC 与 Local 表示同一时刻却渲染出不同结果:%q vs %q", a, b)
|
||
}
|
||
})
|
||
}
|
||
|
||
func TestShort(t *testing.T) {
|
||
// 日志里截前 8 位;短 id(测试里可能出现)不能 panic
|
||
if got := short("0123456789abcdef"); got != "01234567" {
|
||
t.Errorf("长 id 应截断成 8 位,得到 %q", got)
|
||
}
|
||
if got := short("abc"); got != "abc" {
|
||
t.Errorf("短 id 应原样返回,得到 %q", got)
|
||
}
|
||
if got := short(""); got != "" {
|
||
t.Errorf("空串应原样返回,得到 %q", got)
|
||
}
|
||
}
|
||
|
||
func TestStartStopIdempotent(t *testing.T) {
|
||
// Stop 在没启动时被调(defer 里必然发生)不该 panic;
|
||
// Start 两次也不该泄漏 goroutine(第二次先停旧的)。
|
||
Stop()
|
||
Start()
|
||
Start()
|
||
Stop()
|
||
Stop()
|
||
}
|