from datetime import datetime, timedelta from django.utils import timezone from tracker.services.validation import validate_event, sanitize_url NOW = timezone.now() def _ev(**kw): base = dict(event_id="e1", event_name="page_view", occurred_at=NOW, path="/p", title="t", properties={}) base.update(kw) return base def test_ok_event(): assert validate_event(_ev()) is None # None = 通过 def test_unknown_event_name(): assert validate_event(_ev(event_name="xxx")) == "event_name_not_allowed" def test_sensitive_property_rejected(): r = validate_event(_ev(properties={"user_password": "1"})) assert "sensitive" in r def test_shape_type_and_whitelist(): assert validate_event(["x"]) == "event_invalid_shape" assert validate_event(_ev(event_id=123)) == "invalid_event_id" assert validate_event(_ev(properties={"foo": "bar"})) == "property_not_allowed" def test_too_many_props_and_long_string(): r = validate_event(_ev(properties={f"k{i}": "v" for i in range(40)})) assert "properties" in r r = validate_event(_ev(properties={"referrer": "x" * 600})) assert "properties" in r def test_future_and_stale_time(): r = validate_event(_ev(occurred_at=NOW + timedelta(days=2))) assert "occurred_at" in r # 超窗拒绝 assert validate_event(_ev(occurred_at=NOW - timedelta(hours=2))) is None # 窗内迟到可收 def test_sanitize_url_drops_sensitive_params(): u = sanitize_url("/p?email=a@b.com&token=x&id=3&utm_source=google&monkey=1") assert "email=" not in u and "token=" not in u and "utm_source=google" in u assert "monkey=1" in u # fullmatch:含 key 子串的正常参数不误伤 def test_nested_sensitive_key_rejected(): r = validate_event(_ev(properties={"utm": {"utm_a": 1, "user_password": "x"}})) assert "sensitive" in r # utm 在 page_view 白名单内,嵌套敏感键仍须命中 def test_naive_datetime_rejected(): assert validate_event(_ev(occurred_at=NOW.replace(tzinfo=None))) == "invalid_occurred_at"