Files
re-xxm/tests/test_timeutil.py
T
bot-ci 9969e65202 fix(scheduler): drop cross-task mutex, quiet window + retry
Incident: evening full snapshot could be recorded as ok with
already_completed/elapsed=0 after mutual skip with hourly sync, then
next_run jumped to the next day.

- Full-site snapshot never skips because another task is running
- 23:00-01:00 Asia/Shanghai: defer discovery / snapshot_recent /
  candidate_cleanup to after 01:00; fans never defers
- Skip records ok=false + skipped=true (not success / not completed)
- Skip/fail reschedule soon (~5m); daily success still next snapshot_at
- TaskControl return value drives next_run; system UI shows skip fields
2026-11-03 00:07:19 +08:00

117 lines
3.7 KiB
Python

# -*- coding: utf-8 -*-
"""上海时区与每日时间计算测试: uv run python tests/test_timeutil.py"""
import os
import sys
from datetime import datetime
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
sys.stdout.reconfigure(encoding="utf-8")
from reaction_board.timeutil import ( # noqa: E402
SHANGHAI_TZ,
day_bounds,
in_snapshot_quiet_window,
interval_next_run,
is_date,
next_after_snapshot_quiet_window,
next_daily_run,
parse_hhmm,
shanghai_date,
shanghai_datetime,
)
def ts(text):
return datetime.strptime(text, "%Y-%m-%d %H:%M:%S").replace(tzinfo=SHANGHAI_TZ).timestamp()
def test_shanghai_date_boundary():
# UTC 2026-10-31 15:59:59 = 上海 2026-10-31 23:59:59
assert shanghai_date(ts("2026-10-31 23:59:59")) == "2026-10-31"
# UTC 2026-10-31 16:00:00 = 上海 2026-11-01 00:00:00
assert shanghai_date(ts("2026-11-01 00:00:00")) == "2026-11-01"
assert shanghai_datetime().tzinfo is not None
def test_parse_hhmm():
assert parse_hhmm("00:01") == (0, 1)
assert parse_hhmm("23:59") == (23, 59)
for bad in ("24:00", "1:01", "00:60", "", "abc"):
try:
parse_hhmm(bad)
except ValueError:
pass
else:
raise AssertionError(f"{bad!r} 应失败")
def test_next_daily_run():
# 当天 00:01 之前 -> 当天 00:01
now = ts("2026-11-01 00:00:10")
nxt = next_daily_run(now, "00:01")
assert nxt == ts("2026-11-01 00:01:00")
# 正好 00:01:00 -> 下一天(严格大于)
assert next_daily_run(ts("2026-11-01 00:01:00"), "00:01") == ts("2026-11-02 00:01:00")
# 当天 00:01 之后 -> 次日
assert next_daily_run(ts("2026-11-01 23:59:59"), "00:01") == ts("2026-11-02 00:01:00")
# 跨月
assert next_daily_run(ts("2026-10-31 12:00:00"), "00:01") == ts("2026-11-01 00:01:00")
def test_day_bounds_and_date_validation():
start, end = day_bounds("2026-11-01")
assert start == ts("2026-11-01 00:00:00")
assert end == ts("2026-11-02 00:00:00")
assert end - start == 86400
assert is_date("2026-11-01")
for bad in ("2026-13-01", "2026-02-30", "2026/11/01", "20261101", ""):
assert not is_date(bad), bad
try:
day_bounds("bad")
except ValueError:
pass
else:
raise AssertionError("非法日期应失败")
def test_snapshot_quiet_window():
assert in_snapshot_quiet_window(ts("2026-11-02 23:00:00"))
assert in_snapshot_quiet_window(ts("2026-11-02 23:59:59"))
assert in_snapshot_quiet_window(ts("2026-11-03 00:00:00"))
assert in_snapshot_quiet_window(ts("2026-11-03 00:59:59"))
assert not in_snapshot_quiet_window(ts("2026-11-03 01:00:00"))
assert not in_snapshot_quiet_window(ts("2026-11-02 22:59:59"))
assert not in_snapshot_quiet_window(ts("2026-11-02 12:00:00"))
assert next_after_snapshot_quiet_window(ts("2026-11-02 22:00:00")) == ts(
"2026-11-02 22:00:00"
)
assert next_after_snapshot_quiet_window(ts("2026-11-02 23:15:00")) == ts(
"2026-11-03 01:00:00"
)
assert next_after_snapshot_quiet_window(ts("2026-11-03 00:30:00")) == ts(
"2026-11-03 01:00:00"
)
# 22:50 + 60min = 23:50 → 推到次日 01:00
assert interval_next_run(ts("2026-11-02 22:50:00"), 3600) == ts(
"2026-11-03 01:00:00"
)
assert interval_next_run(ts("2026-11-02 12:00:00"), 3600) == ts(
"2026-11-02 13:00:00"
)
def main():
test_shanghai_date_boundary()
test_parse_hhmm()
test_next_daily_run()
test_day_bounds_and_date_validation()
test_snapshot_quiet_window()
print("OK: 上海时区与每日时间计算测试全部通过")
if __name__ == "__main__":
main()