feat(stats): buffer visits in memory and expose POST /api/visit + /api/stats
This commit is contained in:
1 parent
cdd11823b9
commit
3c16caac1a
8 files changed
+905
-5
No files matched your search
+70
-1
@@ -172,13 +172,82 @@ def test_analytics_defaults_and_validation():
|
||||
raise AssertionError(f"非法 analytics 必须拒绝: {bad}")
|
||||
|
||||
|
||||
def test_web_track_defaults_and_validation():
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
path = Path(tmp) / "config.json"
|
||||
write(path, base_config())
|
||||
cfg = load_config(path, env={})
|
||||
track = cfg.web.track
|
||||
assert track.enabled is True
|
||||
assert track.header_order == ("X-Real-IP", "X-Forwarded-For")
|
||||
assert "172.16.0.0/12" in track.trusted_proxies
|
||||
assert track.count_bots is False
|
||||
assert track.flush_seconds == 60
|
||||
assert track.keep_days == 180
|
||||
assert track.max_hits_per_ip_path_per_day == 0
|
||||
|
||||
data = base_config()
|
||||
data["web"] = {
|
||||
"host": "0.0.0.0", "port": 8080, "auth_token": "t",
|
||||
"public_base_url": "",
|
||||
"track": {
|
||||
"enabled": False,
|
||||
"header_order": ["X-Forwarded-For", "X-Real-IP", "X-Real-IP"],
|
||||
"trusted_proxies": ["10.0.0.0/8", "192.168.1.5"],
|
||||
"count_bots": True,
|
||||
"flush_seconds": 0,
|
||||
"keep_days": 30,
|
||||
"max_hits_per_ip_path_per_day": 500,
|
||||
},
|
||||
}
|
||||
write(path, data)
|
||||
cfg = load_config(path, env={})
|
||||
assert cfg.web.track.enabled is False
|
||||
# 去重且保持顺序
|
||||
assert cfg.web.track.header_order == ("X-Forwarded-For", "X-Real-IP")
|
||||
assert cfg.web.track.trusted_proxies == ("10.0.0.0/8", "192.168.1.5/32")
|
||||
assert cfg.web.track.count_bots is True
|
||||
assert cfg.web.track.flush_seconds == 0
|
||||
assert cfg.web.track.max_hits_per_ip_path_per_day == 500
|
||||
|
||||
for bad, hint in (
|
||||
({"header_order": []}, "header_order"),
|
||||
({"header_order": ["X-Real-IP", 5]}, "header_order"),
|
||||
({"trusted_proxies": ["not-a-cidr"]}, "trusted_proxies"),
|
||||
({"flush_seconds": -1}, "flush_seconds"),
|
||||
({"keep_days": 0}, "keep_days"),
|
||||
({"max_hits_per_ip_path_per_day": -1}, "max_hits_per_ip_path_per_day"),
|
||||
):
|
||||
data = base_config()
|
||||
data["web"] = {"track": bad}
|
||||
write(path, data)
|
||||
try:
|
||||
load_config(path, env={})
|
||||
except ConfigError as exc:
|
||||
assert hint in str(exc), (bad, str(exc))
|
||||
else:
|
||||
raise AssertionError(f"非法 web.track 必须拒绝: {bad}")
|
||||
|
||||
# web.track 不是对象
|
||||
data = base_config()
|
||||
data["web"] = {"track": "nope"}
|
||||
write(path, data)
|
||||
try:
|
||||
load_config(path, env={})
|
||||
except ConfigError as exc:
|
||||
assert "web.track" in str(exc)
|
||||
else:
|
||||
raise AssertionError("web.track 必须是对象")
|
||||
|
||||
|
||||
def main():
|
||||
test_dual_accounts_and_env_override()
|
||||
test_flat_and_missing_account_rejected()
|
||||
test_validation_require_read_auth_and_hot_reload()
|
||||
test_invalid_values()
|
||||
test_analytics_defaults_and_validation()
|
||||
print("OK: 双账号配置加载、拒绝 flat 格式、ENV 覆盖、热加载与 analytics 测试全部通过")
|
||||
test_web_track_defaults_and_validation()
|
||||
print("OK: 双账号配置加载、拒绝 flat 格式、ENV 覆盖、热加载与 analytics/web.track 测试全部通过")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in new issue
Block a user