Files
fangke/server/tracker/tests/test_geo.py
T
浅念 e880dbe458 fix: geo 收口(模块单例/IPv6测试/降级日志)
- 导出模块级 resolver 单例:Task 6 起 import resolver 使用,
  防多实例各持 ~11MB xdb 副本、lru_cache 按实例分片
- 补公网 IPv6→None 测试,锁定 M1 v4-only 决策
- __init__ 降级路径(文件过短/结构无法识别/except)均记
  warning 日志,运维可区分「没部署 xdb」与「库 API 崩了」
- 删掉测试未使用的 monkeypatch 参数

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-15 12:39:08 +08:00

80 lines
3.1 KiB
Python

import os
import pytest
from django.conf import settings
from tracker.services.geo import GeoResolver, parse_region
XDB_EXISTS = os.path.exists(settings.IP2REGION_XDB)
def test_private_ip_returns_local():
r = GeoResolver()
assert r.resolve("192.168.1.5") is None # 内网不解析(后台显示"本地")
assert r.resolve("127.0.0.1") is None
def test_public_ip_parsed_or_none():
r = GeoResolver()
# xdb 未下载/解析失败时优雅降级返回 None,不抛异常
out = r.resolve("8.8.8.8")
assert out is None or set(out) == {"country", "province", "city"}
def test_public_ipv6_returns_none():
# 锁定 M1 的 v4-only 决策:公网 IPv6 查 v4 库因字节长度不符抛 ValueError,
# 降级为 None(防库/数据未来变更悄悄改变行为)
assert GeoResolver().resolve("2001:4860:4860::8888") is None
def test_parse_region_two_layouts():
# 旧版 2.0 结构:"国家|区域|省|市|ISP"
assert parse_region("中国|0|江苏省|南京市|电信", new_layout=False) == {
"country": "中国", "province": "江苏省", "city": "南京市"}
# 新版 3.0 结构(v4/v6 库):"国家|省/区域|城市|ISP|国家代码"
assert parse_region("中国|江苏省|南京市|0|CN", new_layout=True) == {
"country": "中国", "province": "江苏省", "city": "南京市"}
# ISP 段不得串进 city
assert parse_region("United States|California|0|Google LLC|US", new_layout=True) == {
"country": "United States", "province": "California", "city": "0"}
assert parse_region("", new_layout=True) is None
assert parse_region("中国", new_layout=True) is None # 不足 4 段
assert parse_region(None, new_layout=False) is None
class _FakeSearcher:
def __init__(self, raw):
self._raw = raw
def search(self, ip):
return self._raw
def test_resolve_uses_layout_dispatch():
r = GeoResolver()
r._searcher = _FakeSearcher("中国|北京市|北京市|百度|CN")
r._new_layout = True
assert r.resolve("1.2.3.4") == {
"country": "中国", "province": "北京市", "city": "北京市"}
r2 = GeoResolver()
r2._searcher = _FakeSearcher("中国|华北|北京市|北京市|百度")
r2._new_layout = False
assert r2.resolve("1.2.3.4") == {
"country": "中国", "province": "北京市", "city": "北京市"}
@pytest.mark.skipif(not XDB_EXISTS, reason="ip2region.xdb 未下载,跳过真实解析探针")
def test_real_probe_public_ip():
r = GeoResolver()
assert r._searcher is not None, "xdb 存在时必须成功加载"
raw = r._searcher.search("8.8.8.8")
assert len(raw.split("|")) == 5, f"应为五段格式,实际: {raw!r}"
out = r.resolve("8.8.8.8")
assert set(out) == {"country", "province", "city"}
assert out["country"] in ("美国", "United States")
assert out["city"] != "Google LLC", "ISP 不得串进 city(布局映射错误)"
cn = r.resolve("114.114.114.114")
assert cn["country"] == "中国"
assert cn["province"] not in ("", "0"), "国内公网 IP 应解析出省份"