"""Security hardening: refuse the default signing key in prod; throttle /chat.""" from __future__ import annotations from types import SimpleNamespace import pytest from app.main import _check_security from app.routes import chat def _settings(**over): base = dict(testing=False, admin_token="tok", secret_key="a-real-strong-key") base.update(over) return SimpleNamespace(**base) def test_default_secret_key_refuses_to_boot_in_prod(): with pytest.raises(RuntimeError, match="SECRET_KEY"): _check_security(_settings(secret_key="dev-insecure-change-me")) def test_strong_secret_key_boots(): _check_security(_settings()) # no raise def test_testing_mode_skips_the_guard(): # tests run with the default key; testing=True must never crash them _check_security(_settings(testing=True, secret_key="dev-insecure-change-me")) # ---- /chat rate limit ---- def _req(ip="1.2.3.4"): return SimpleNamespace(headers={"x-forwarded-for": ip}, client=SimpleNamespace(host=ip)) def test_chat_rate_limit_blocks_after_burst(monkeypatch): # fresh limiter so other tests don't pollute the window from app.ratelimit import RateLimiter monkeypatch.setattr(chat, "_chat_limiter", RateLimiter(chat._CHAT_PER_IP_PER_MIN, 60)) req = _req("9.9.9.9") for _ in range(chat._CHAT_PER_IP_PER_MIN): chat._enforce_chat_rate(req) # allowed with pytest.raises(Exception) as ei: chat._enforce_chat_rate(req) # one over -> 429 assert getattr(ei.value, "status_code", None) == 429 def test_chat_rate_limit_is_per_ip(monkeypatch): from app.ratelimit import RateLimiter monkeypatch.setattr(chat, "_chat_limiter", RateLimiter(2, 60)) chat._enforce_chat_rate(_req("1.1.1.1")) chat._enforce_chat_rate(_req("1.1.1.1")) # a different IP still has its own fresh budget chat._enforce_chat_rate(_req("2.2.2.2"))