Files
2026-08-21 16:00:13 +08:00

105 lines
3.4 KiB
Python

"""时间深度与离线档位测试。"""
from datetime import UTC, datetime, timedelta
import pytest
from pydantic import ValidationError
from app.modules.time_sense.duration import (
OfflineThresholds,
classify_offline_band,
compute_acquaintance_duration,
format_duration_cn,
)
def test_acquaintance_duration_zero_days():
anchor = datetime(2026, 7, 3, tzinfo=UTC)
now = anchor
d = compute_acquaintance_duration(anchor, now)
assert d.total_days == 0
assert "今天" in d.natural_language or "刚" in d.natural_language
def test_acquaintance_duration_287_days():
anchor = datetime(2026, 7, 3, tzinfo=UTC) - timedelta(days=287)
now = datetime(2026, 7, 3, tzinfo=UTC)
d = compute_acquaintance_duration(anchor, now)
assert d.total_days == 287
assert d.years == 0
assert d.months == 9
assert d.days == 17 # 287 % 30 = 17(按 30 天/月近似)
assert "287" in d.natural_language
def test_acquaintance_duration_one_year():
anchor = datetime(2025, 7, 3, tzinfo=UTC)
now = datetime(2026, 7, 3, tzinfo=UTC)
d = compute_acquaintance_duration(anchor, now)
assert d.total_days == 365
assert d.years == 1
def test_format_duration_cn_100_days():
# 100 天 = 3 个月 10 天(按 30 天/月近似),实现走月日分支
assert format_duration_cn(100) == "3 个月 10 天(100 天)"
def test_format_duration_cn_365_days():
text = format_duration_cn(365)
assert "1 年" in text
def test_classify_offline_band_active():
now = datetime(2026, 7, 3, 12, 0, tzinfo=UTC)
last = now - timedelta(minutes=30)
t = OfflineThresholds()
result = classify_offline_band(last, now, t)
assert result.band == "active"
assert result.minutes == 30
def test_classify_offline_band_short_absence():
now = datetime(2026, 7, 3, 12, 0, tzinfo=UTC)
last = now - timedelta(hours=5)
result = classify_offline_band(last, now, OfflineThresholds())
assert result.band == "short_absence"
def test_classify_offline_band_reunion_impact():
now = datetime(2026, 7, 3, 12, 0, tzinfo=UTC)
last = now - timedelta(days=5)
result = classify_offline_band(last, now, OfflineThresholds())
assert result.band == "reunion_impact"
assert result.reunion_intensity > 0.5
def test_classify_offline_boundary_59min_is_active():
now = datetime(2026, 7, 3, 12, 0, tzinfo=UTC)
last = now - timedelta(minutes=59)
assert classify_offline_band(last, now, OfflineThresholds()).band == "active"
def test_classify_offline_boundary_60min_is_short_absence():
now = datetime(2026, 7, 3, 12, 0, tzinfo=UTC)
last = now - timedelta(minutes=60)
assert classify_offline_band(last, now, OfflineThresholds()).band == "short_absence"
def test_offline_thresholds_violates_monotonicity():
with pytest.raises(ValidationError):
OfflineThresholds(active_minutes=100, short_absence_minutes=50)
def test_reunion_intensity_clamped_05_to_1():
now = datetime(2026, 7, 3, 12, 0, tzinfo=UTC)
# 刚过 reunion_impact 阈值(4320 分钟 + 1 分钟)
last = now - timedelta(minutes=4321)
intensity = classify_offline_band(last, now, OfflineThresholds()).reunion_intensity
assert 0.5 <= intensity <= 1.0
# 超大间隔:30 天 = 43200 分钟,应趋近 1.0
last_far = now - timedelta(minutes=43200)
intensity_far = classify_offline_band(last_far, now, OfflineThresholds()).reunion_intensity
assert intensity_far > 0.9