from fastapi.testclient import TestClient def test_failed_login_generic_and_no_cookie(): pass # covered in test_auth; kept for grouping def test_csrf_required_on_bike_create(client): client.login() # No csrf token at all -> must be rejected, bike NOT created. resp = client.post("/bikes/new", data={"name": "Sneaky"}) assert "CSRF check failed" in resp.text def test_wrong_csrf_rejected(client): client.login() resp = client.post("/bikes/new", data={"csrf": "not-the-right-token", "name": "Sneaky"}) assert "CSRF check failed" in resp.text def test_login_cookie_attributes(client): r = client.login() sc = r.headers.get("set-cookie", "") low = sc.lower() assert "httponly" in low assert "samesite=lax" in low # Dev (not production, http) => no Secure flag. assert "secure" not in low def test_production_sets_secure_cookie(monkeypatch): monkeypatch.setenv("BIKE_ADMIN_IS_PRODUCTION", "true") from app.config import get_settings get_settings.cache_clear() from app.main import app with TestClient(app, follow_redirects=False) as c: # lifespan seeds the user r = c.post("/login", data={"username": "admin", "password": "correct-horse-battery-staple"}) sc = r.headers.get("set-cookie", "").lower() assert r.status_code == 303 assert "secure" in sc def test_login_throttle(client, monkeypatch): monkeypatch.setenv("BIKE_ADMIN_LOGIN_MAX_ATTEMPTS", "3") from app.config import get_settings get_settings.cache_clear() for _ in range(3): client.post("/login", data={"username": "admin", "password": "wrong"}) # 4th attempt: even the correct password is blocked. pw = "correct-horse-battery-staple" r = client.post("/login", data={"username": "admin", "password": pw}) assert r.status_code == 200 assert "too many" in r.text.lower()