import sys import types import pathlib import pytest # Ensure the backend directory is on sys.path so we can import the geoip module directly BACKEND_DIR = pathlib.Path(__file__).resolve().parents[1] # backend/ folder if str(BACKEND_DIR) not in sys.path: sys.path.insert(0, str(BACKEND_DIR)) import geoip as geoip @pytest.fixture(autouse=True) def reset_geoip_reader(): # Ensure each test starts with a clean cache geoip._geoip_reader = None yield geoip._geoip_reader = None def test_get_reader_import_error(monkeypatch): import builtins real_import = getattr(builtins, "__import__") def fake_import(name, globals=None, locals=None, fromlist=(), level=0): if name == "geoip2.database": raise ImportError("simulate missing geoip2") return real_import(name, globals, locals, fromlist, level) monkeypatch.setattr(builtins, "__import__", fake_import) geoip._geoip_reader = None assert geoip._get_reader() is None def test_get_reader_db_missing(monkeypatch): # Provide a fake geoip2 module, but force the database file to be considered missing fake_db_module = types.ModuleType("geoip2.database") class FakeReader: def __init__(self, path): self.path = path fake_db_module.Reader = FakeReader fake_geoip2 = types.ModuleType("geoip2") fake_geoip2.database = fake_db_module sys.modules["geoip2"] = fake_geoip2 sys.modules["geoip2.database"] = fake_db_module # Ensure path existence check returns False monkeypatch.setattr(geoip.os.path, "exists", lambda p: False) geoip._geoip_reader = None assert geoip._get_reader() is None # Clean up injected modules del sys.modules["geoip2"] del sys.modules["geoip2.database"] def test_get_reader_loads_and_caches(monkeypatch): fake_db_module = types.ModuleType("geoip2.database") class FakeReader: def __init__(self, path): self.path = path fake_db_module.Reader = FakeReader fake_geoip2 = types.ModuleType("geoip2") fake_geoip2.database = fake_db_module sys.modules["geoip2"] = fake_geoip2 sys.modules["geoip2.database"] = fake_db_module # Simulate that the database file exists monkeypatch.setattr(geoip.os.path, "exists", lambda p: True) geoip._geoip_reader = None r1 = geoip._get_reader() assert isinstance(r1, FakeReader) # Second call should return the same cached instance r2 = geoip._get_reader() assert r1 is r2 # Clean up injected modules del sys.modules["geoip2"] del sys.modules["geoip2.database"] @pytest.mark.parametrize("ip", [None, "", "127.0.0.1", "localhost", "::1"]) def test_get_ip_location_none_inputs(ip): assert geoip.get_ip_location(ip) is None def test_get_ip_location_reader_none(monkeypatch): # When there is no reader (no database), return None monkeypatch.setattr(geoip, "_get_reader", lambda: None) assert geoip.get_ip_location("1.2.3.4") is None def test_get_ip_location_successful_lookup(monkeypatch): from types import SimpleNamespace country = SimpleNamespace(name="United States") region = SimpleNamespace(name="California") resp = SimpleNamespace( country=country, subdivisions=SimpleNamespace(most_specific=region), city=SimpleNamespace(name="Mountain View"), ) class FakeReader: def city(self, ip): return resp monkeypatch.setattr(geoip, "_get_reader", lambda: FakeReader()) loc = geoip.get_ip_location("1.2.3.4") assert loc == { "country": "United States", "region": "California", "city": "Mountain View", "display": "United States California Mountain View", } def test_get_ip_location_reader_exception(monkeypatch): class FakeReader: def city(self, ip): raise Exception("boom") monkeypatch.setattr(geoip, "_get_reader", lambda: FakeReader()) assert geoip.get_ip_location("1.2.3.4") is None def test_get_ip_location_no_location_parts(monkeypatch): from types import SimpleNamespace resp = SimpleNamespace(country=SimpleNamespace(name=None), subdivisions=None, city=None) class FakeReader: def city(self, ip): return resp monkeypatch.setattr(geoip, "_get_reader", lambda: FakeReader()) assert geoip.get_ip_location("1.2.3.4") is None def test_get_ip_location_text_valid(monkeypatch): from types import SimpleNamespace country = SimpleNamespace(name="United States") region = SimpleNamespace(name="California") resp = SimpleNamespace( country=country, subdivisions=SimpleNamespace(most_specific=region), city=SimpleNamespace(name="Mountain View"), ) class FakeReader: def city(self, ip): return resp monkeypatch.setattr(geoip, "_get_reader", lambda: FakeReader()) assert geoip.get_ip_location_text("1.2.3.4") == "United States California Mountain View" def test_get_ip_location_text_none_when_no_location(monkeypatch): # Force get_ip_location to return None monkeypatch.setattr(geoip, "get_ip_location", lambda ip: None) assert geoip.get_ip_location_text("1.2.3.4") == ""