138 lines
4.3 KiB
Go
138 lines
4.3 KiB
Go
// Copyright 2026 tamakyi. All rights reserved.
|
||
// Use of this source code is governed by a MIT-style
|
||
// license that can be found in the LICENSE file.
|
||
|
||
package conf
|
||
|
||
import (
|
||
"testing"
|
||
"time"
|
||
)
|
||
|
||
// setAnnouncement 在测试期间覆盖全局公告配置,结束后恢复。
|
||
func setAnnouncement(t *testing.T, content, start, end string) {
|
||
t.Helper()
|
||
old := Announcement
|
||
t.Cleanup(func() { Announcement = old })
|
||
Announcement = announcementConfig{Content: content, Start: start, End: end}
|
||
sanitizeAnnouncement()
|
||
}
|
||
|
||
func mustParse(t *testing.T, layout, value string) time.Time {
|
||
t.Helper()
|
||
parsed, err := time.ParseInLocation(layout, value, time.Local)
|
||
if err != nil {
|
||
t.Fatalf("parse %q: %v", value, err)
|
||
}
|
||
return parsed
|
||
}
|
||
|
||
func TestAnnouncementDisabledWhenContentEmpty(t *testing.T) {
|
||
setAnnouncement(t, " ", "2026-10-24 00:00", "2026-10-26 00:00")
|
||
|
||
if _, ok := Announcement.Active(time.Now()); ok {
|
||
t.Fatal("content 为空时公告应关闭")
|
||
}
|
||
}
|
||
|
||
func TestAnnouncementWindowIsHalfOpen(t *testing.T) {
|
||
setAnnouncement(t, "维护通知", "2026-10-24 00:00", "2026-10-26 00:00")
|
||
|
||
start := mustParse(t, "2006-01-02 15:04", "2026-10-24 00:00")
|
||
end := mustParse(t, "2006-01-02 15:04", "2026-10-26 00:00")
|
||
|
||
cases := []struct {
|
||
name string
|
||
now time.Time
|
||
want bool
|
||
}{
|
||
{"开始前一秒", start.Add(-time.Second), false},
|
||
{"开始时刻(闭区间)", start, true},
|
||
{"窗口内", start.Add(12 * time.Hour), true},
|
||
{"结束时刻(开区间)", end, false},
|
||
{"结束后", end.Add(time.Hour), false},
|
||
}
|
||
for _, c := range cases {
|
||
t.Run(c.name, func(t *testing.T) {
|
||
content, ok := Announcement.Active(c.now)
|
||
if ok != c.want {
|
||
t.Fatalf("Active(%s) = %v, want %v", c.now, ok, c.want)
|
||
}
|
||
if ok && content != "维护通知" {
|
||
t.Fatalf("content = %q, want 维护通知", content)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// 只写一侧的边界必须保持「该侧不限」,不能因为另一侧为空就整体失效。
|
||
func TestAnnouncementOneSidedWindow(t *testing.T) {
|
||
now := time.Date(2026, 10, 22, 12, 0, 0, 0, time.Local)
|
||
|
||
setAnnouncement(t, "只有开始", "2026-10-23 00:00", "")
|
||
if _, ok := Announcement.Active(now); ok {
|
||
t.Fatal("只写 start 且尚未到点时应不展示")
|
||
}
|
||
if _, ok := Announcement.Active(now.Add(48 * time.Hour)); !ok {
|
||
t.Fatal("只写 start 时到点后应一直展示")
|
||
}
|
||
|
||
setAnnouncement(t, "只有结束", "", "2026-10-23 00:00")
|
||
if _, ok := Announcement.Active(now); !ok {
|
||
t.Fatal("只写 end 且未到点时应立即展示")
|
||
}
|
||
if _, ok := Announcement.Active(now.Add(48 * time.Hour)); ok {
|
||
t.Fatal("只写 end 时过点后应下线")
|
||
}
|
||
|
||
setAnnouncement(t, "一直展示", "", "")
|
||
if _, ok := Announcement.Active(now.AddDate(5, 0, 0)); !ok {
|
||
t.Fatal("起止都留空时应一直展示")
|
||
}
|
||
}
|
||
|
||
func TestAnnouncementTimeFormats(t *testing.T) {
|
||
// 带偏移的 RFC3339 必须按字面时刻解析,而不是再套一层本地时区。
|
||
at, ok := parseAnnouncementTime("2026-10-25T02:00:00+08:00")
|
||
if !ok {
|
||
t.Fatal("RFC3339 应可解析")
|
||
}
|
||
want := time.Date(2026, 10, 24, 18, 0, 0, 0, time.UTC)
|
||
if !at.Equal(want) {
|
||
t.Fatalf("解析结果 = %s, want %s", at.UTC(), want)
|
||
}
|
||
|
||
if at, ok := parseAnnouncementTime("2026-10-25"); !ok || at.Hour() != 0 {
|
||
t.Fatalf("纯日期应解析为当天 00:00,got %s ok=%v", at, ok)
|
||
}
|
||
if at, ok := parseAnnouncementTime("2026-10-25 02:00:59"); !ok || at.Second() != 59 {
|
||
t.Fatalf("秒级格式应可解析,got %s ok=%v", at, ok)
|
||
}
|
||
if _, ok := parseAnnouncementTime("2026/10/25 02:00"); ok {
|
||
t.Fatal("错误格式应解析失败")
|
||
}
|
||
if _, ok := parseAnnouncementTime("2026-13-01"); ok {
|
||
t.Fatal("非法月份应解析失败")
|
||
}
|
||
}
|
||
|
||
// 时间写错只关公告,不能让站点起不来;同时必须清空解析结果,避免残留时间窗。
|
||
func TestAnnouncementInvalidTimeDisables(t *testing.T) {
|
||
for _, c := range []struct{ name, start, end string }{
|
||
{"start 格式错", "2026/10/25 02:00", ""},
|
||
{"end 格式错", "", "10月26日"},
|
||
{"end 早于 start", "2026-10-26 00:00", "2026-10-25 00:00"},
|
||
{"end 等于 start", "2026-10-25 00:00", "2026-10-25 00:00"},
|
||
} {
|
||
t.Run(c.name, func(t *testing.T) {
|
||
setAnnouncement(t, "内容", c.start, c.end)
|
||
if Announcement.Content != "" {
|
||
t.Fatal("配置无效时公告应被关闭")
|
||
}
|
||
if !Announcement.StartAt.IsZero() || !Announcement.EndAt.IsZero() {
|
||
t.Fatal("配置无效时应清空已解析的时间点")
|
||
}
|
||
})
|
||
}
|
||
}
|