""" Tests for get_client_ip() from security/rate_limiter.py Tests cover: - X-Forwarded-For header parsing (single IP, multiple IPs, whitespace stripping) - X-Real-IP header parsing (presence, whitespace stripping) - Header priority (X-Forwarded-For over X-Real-IP) - Fallback to get_remote_address() when no proxy headers present """ from unittest.mock import patch from flask import Flask from local_deep_research.security.rate_limiter import get_client_ip def _make_app(): """Create a minimal Flask test app.""" app = Flask(__name__) app.config["TESTING"] = True return app class TestGetClientIp: """Tests for get_client_ip function.""" def test_returns_single_ip_from_x_forwarded_for(self): """Should return the IP when X-Forwarded-For contains a single IP.""" app = _make_app() with app.test_request_context( environ_base={"HTTP_X_FORWARDED_FOR": "1.2.3.4"} ): result = get_client_ip() assert result == "1.2.3.4" def test_returns_first_ip_from_x_forwarded_for_with_multiple(self): """Should return the first IP when X-Forwarded-For has comma-separated IPs.""" app = _make_app() with app.test_request_context( environ_base={ "HTTP_X_FORWARDED_FOR": "1.2.3.4, 5.6.7.8, 9.10.11.12" } ): result = get_client_ip() assert result == "1.2.3.4" def test_strips_whitespace_from_x_forwarded_for(self): """Should strip leading/trailing whitespace from X-Forwarded-For IP.""" app = _make_app() with app.test_request_context( environ_base={"HTTP_X_FORWARDED_FOR": " 1.2.3.4 , 5.6.7.8"} ): result = get_client_ip() assert result == "1.2.3.4" def test_returns_x_real_ip_when_no_x_forwarded_for(self): """Should return X-Real-IP when X-Forwarded-For is absent.""" app = _make_app() with app.test_request_context( environ_base={"HTTP_X_REAL_IP": "10.20.30.40"} ): result = get_client_ip() assert result == "10.20.30.40" def test_strips_whitespace_from_x_real_ip(self): """Should strip leading/trailing whitespace from X-Real-IP.""" app = _make_app() with app.test_request_context( environ_base={"HTTP_X_REAL_IP": " 10.20.30.40 "} ): result = get_client_ip() assert result == "10.20.30.40" def test_x_forwarded_for_takes_priority_over_x_real_ip(self): """X-Forwarded-For should take priority when both headers are present.""" app = _make_app() with app.test_request_context( environ_base={ "HTTP_X_FORWARDED_FOR": "192.168.1.1", "HTTP_X_REAL_IP": "10.0.0.1", } ): result = get_client_ip() assert result == "192.168.1.1" def test_falls_back_to_get_remote_address(self): """Should fall back to get_remote_address() when no proxy headers present.""" app = _make_app() with app.test_request_context(): with patch( "local_deep_research.security.rate_limiter.get_remote_address", return_value="127.0.0.1", ): result = get_client_ip() assert result == "127.0.0.1"