Files

76 lines
4.9 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from datetime import date, timedelta
from django.utils import timezone
from tracker.models import Site, Visitor, VisitSession, Event
from tracker.services.stats import refresh_daily
# 用上海本地时刻做基准:refresh_daily 的日界是上海自然日(make_aware),
# 若直接用 UTC 的 timezone.now(),在本地 00:00-08:00 之间跑测试时 NOW.date() 会错一天
NOW = timezone.localtime(timezone.now())
YESTERDAY = NOW - timedelta(days=1)
def _mk(site, vid, sid, day, n_events, stay=0):
v, _ = Visitor.objects.get_or_create(site=site, visitor_id=vid)
sess = VisitSession.objects.create(site=site, visitor=v, session_id=sid,
started_at=day, ended_at=day + timedelta(minutes=5),
stay_ms=stay, event_count=n_events)
for i in range(n_events):
Event.objects.create(site=site, visitor=v, session=sess, event_id=f"{vid}-{sid}-{day:%Y%m%d}-{i}",
event_name="page_view", occurred_at=day + timedelta(seconds=i), path="/")
def test_refresh_daily_two_days(db):
site = Site.objects.create(name="s", domains=["a.com"])
_mk(site, "v1", "s1", YESTERDAY, 3) # v1 昨天 3 事件
_mk(site, "v1", "s2", NOW, 2, stay=8000) # v1 今天 2 事件
_mk(site, "v2", "s3", NOW, 4) # v2 今天 4 事件
y = refresh_daily(site, YESTERDAY.date())
t = refresh_daily(site, NOW.date())
# new_visitors 口径 = first_seen_at 落入当日。测试里 v1 虽有昨日事件,但访客记录是测试运行时(今天)建档的
# (auto_now_add),故昨天 new=0、今天 new=2(v1+v2)——按 first_seen_at 真实语义断言
assert (y.pv, y.uv, y.sessions, y.new_visitors) == (3, 1, 1, 0)
assert (t.pv, t.uv, t.sessions, t.new_visitors) == (6, 2, 2, 2) # 今天:pv=6 uv=2(v1+v2) sessions=2 new=2(v1+v2)
def test_refresh_daily_shanghai_boundary(db):
# 固定时间戳钉死日界(不依赖运行时刻):UTC 2026-09-15T16:30 = 上海 09-16 00:30 → 落 9-16 不落 9-15
site = Site.objects.create(name="s", domains=["a.com"])
v = Visitor.objects.create(site=site, visitor_id="v")
s = VisitSession.objects.create(site=site, visitor=v, session_id="s",
started_at="2026-09-15T16:30:00Z", ended_at="2026-09-15T16:30:00Z")
Event.objects.create(site=site, visitor=v, session=s, event_id="e",
event_name="page_view", occurred_at="2026-09-15T16:30:00Z", path="/")
d15 = refresh_daily(site, date(2026, 9, 15))
d16 = refresh_daily(site, date(2026, 9, 16))
assert (d15.pv, d16.pv) == (0, 1)
def test_refresh_daily_excludes_bots(db):
# 爬虫口径:事件/会话/访客三处 is_bot 均不计入四指标(与 pv/uv/sessions 统一)
site = Site.objects.create(name="s", domains=["a.com"])
bot = Visitor.objects.create(site=site, visitor_id="bot", is_bot=True)
human = Visitor.objects.create(site=site, visitor_id="human")
bs = VisitSession.objects.create(site=site, visitor=bot, session_id="bs",
started_at="2026-09-15T10:00:00Z", ended_at="2026-09-15T10:01:00Z")
hs = VisitSession.objects.create(site=site, visitor=human, session_id="hs",
started_at="2026-09-15T10:00:00Z", ended_at="2026-09-15T10:01:00Z")
Event.objects.create(site=site, visitor=bot, session=bs, event_id="b1",
event_name="page_view", occurred_at="2026-09-15T10:00:00Z", path="/", is_bot=True)
Event.objects.create(site=site, visitor=human, session=hs, event_id="h1",
event_name="page_view", occurred_at="2026-09-15T10:00:30Z", path="/")
d = refresh_daily(site, date(2026, 9, 15))
assert (d.pv, d.uv, d.sessions) == (1, 1, 1) # 只计 human 的事件/会话
t = refresh_daily(site, timezone.localdate()) # 两位访客 first_seen_at 均为运行时,刷今天窗口才能非空验证 new_visitors
assert t.new_visitors == 1 # 爬虫访客不计入新增
def test_refresh_daily_pv_only_counts_page_view(db):
# PV 口径回归(阶段A T11):SDK 0.2.0 每 15s 一条 page_stay 增量 + 启动 sdk_loaded,
# pv 只计 page_view——增量停留事件不得灌倍 PV(5 分钟活跃访问 ≈ 22 条事件只有 1 条是浏览)。
# 固定时间戳钉死日界不依赖运行时刻(同 boundary 用例做法)
site = Site.objects.create(name="s", domains=["a.com"])
v = Visitor.objects.create(site=site, visitor_id="v1")
sess = VisitSession.objects.create(site=site, visitor=v, session_id="s1",
started_at="2026-09-15T10:00:00Z", ended_at="2026-09-15T10:05:00Z")
names = ["page_view", "sdk_loaded", "page_view", "page_stay", "page_stay", "page_stay"]
for i, name in enumerate(names):
Event.objects.create(site=site, visitor=v, session=sess, event_id=f"x{i}",
event_name=name, occurred_at=f"2026-09-15T10:00:0{i}Z", path="/")
d = refresh_daily(site, date(2026, 9, 15))
assert (d.pv, d.uv, d.sessions) == (2, 1, 1) # 6 条事件仅 2 条 page_view 计 PV;uv 仍按任意事件 distinct