168 lines
5.1 KiB
Python
168 lines
5.1 KiB
Python
|
|
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") == ""
|