|
@@ -20,9 +20,10 @@ import static org.mockito.Mockito.verifyNoInteractions;
|
|
|
/**
|
|
/**
|
|
|
* IP 归属地解析器。
|
|
* IP 归属地解析器。
|
|
|
*
|
|
*
|
|
|
- * <p>下面 5 段 JSON 是 2026-10-08 用 curl 从 {@code https://ipwho.is/{ip}?lang=zh-CN}
|
|
|
|
|
- * 抄回来的**真实回包**(末尾的 flag/connection/timezone/readme 四个嵌套对象原样略去,
|
|
|
|
|
- * 其余逐字未改);末一条是构造输入,已标注。用真实回包是因为这个解析器的全部工作
|
|
|
|
|
|
|
+ * <p>下面 6 段 JSON 是 2026-10-08 用 curl 从 {@code https://ipwho.is/{ip}?lang=zh-CN}
|
|
|
|
|
+ * 抄回来的**真实回包**(末尾的 flag/connection/timezone/readme 四个嵌套对象与 {@code borders}
|
|
|
|
|
+ * 字段略去 —— 2026-10-08 逐字段比对过线上回包,此处据实写成「也略去」;其余逐字未改);
|
|
|
|
|
+ * 末一条是构造输入,已标注。用真实回包是因为这个解析器的全部工作
|
|
|
* 就是「上游到底怎么回」,自造的理想 JSON 会把真实坑(繁体「區」、区级 city 名)滤掉。
|
|
* 就是「上游到底怎么回」,自造的理想 JSON 会把真实坑(繁体「區」、区级 city 名)滤掉。
|
|
|
*/
|
|
*/
|
|
|
class HarIpRegionResolverTest {
|
|
class HarIpRegionResolverTest {
|
|
@@ -34,6 +35,13 @@ class HarIpRegionResolverTest {
|
|
|
"latitude":32.0616657,"longitude":118.7777759,"is_eu":false,"postal":"210000","calling_code":"86",
|
|
"latitude":32.0616657,"longitude":118.7777759,"is_eu":false,"postal":"210000","calling_code":"86",
|
|
|
"capital":"北京"}""";
|
|
"capital":"北京"}""";
|
|
|
|
|
|
|
|
|
|
+ /** 实测:118.248.202.107 → 湖南省 / **长沙**(用户 2026-10-08 从 ipwho.is 取回的同一个 IP) */
|
|
|
|
|
+ private static final String P_HN_CHANGSHA = """
|
|
|
|
|
+ {"ip":"118.248.202.107","success":true,"type":"IPv4","continent":"亚洲","continent_code":"AS",
|
|
|
|
|
+ "country":"中国","country_code":"CN","region":"湖南省","region_code":"HN","city":"长沙",
|
|
|
|
|
+ "latitude":28.1987397,"longitude":112.9708739,"is_eu":false,"postal":"410000","calling_code":"86",
|
|
|
|
|
+ "capital":"北京"}""";
|
|
|
|
|
+
|
|
|
/** 实测:218.201.4.2 → 重庆市 / **渝中區**(区级名 + 繁体「區」) */
|
|
/** 实测:218.201.4.2 → 重庆市 / **渝中區**(区级名 + 繁体「區」) */
|
|
|
private static final String P_CQ_YUZHONG = """
|
|
private static final String P_CQ_YUZHONG = """
|
|
|
{"ip":"218.201.4.2","success":true,"type":"IPv4","continent":"亚洲","continent_code":"AS",
|
|
{"ip":"218.201.4.2","success":true,"type":"IPv4","continent":"亚洲","continent_code":"AS",
|
|
@@ -98,6 +106,23 @@ class HarIpRegionResolverTest {
|
|
|
assertEquals("南京", geo.cityName());
|
|
assertEquals("南京", geo.cityName());
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 湖南省这条回包的 {@code city} 是「长沙」——**不带「市」后缀**(与「南京」「杭州」同形)。
|
|
|
|
|
+ *
|
|
|
|
|
+ * <p>钉它的理由:省名带后缀、市名不带,是上游两种不同的习惯;解析器这一层**不做任何规整**,
|
|
|
|
|
+ * 归一化在 {@code HarRegionCatalog}(那边有「长沙 → 4301」的用例)。这条一旦被人顺手 trim 或
|
|
|
|
|
+ * 补后缀,两边就有一处会红。
|
|
|
|
|
+ */
|
|
|
|
|
+ @Test
|
|
|
|
|
+ void lookup_parsesRealChangshaPayload() {
|
|
|
|
|
+ IpGeo geo = resolverReturning(P_HN_CHANGSHA).lookup("118.248.202.107").orElse(null);
|
|
|
|
|
+
|
|
|
|
|
+ assertEquals("CN", geo.countryCode());
|
|
|
|
|
+ assertEquals("湖南省", geo.regionName());
|
|
|
|
|
+ assertEquals("HN", geo.regionCode());
|
|
|
|
|
+ assertEquals("长沙", geo.cityName());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
/** ★ {@code ?lang=zh-CN} 是这套设计的前提:不带它上游回拼音/英文,按中文名匹配全线失效。 */
|
|
/** ★ {@code ?lang=zh-CN} 是这套设计的前提:不带它上游回拼音/英文,按中文名匹配全线失效。 */
|
|
|
@Test
|
|
@Test
|
|
|
void lookup_asksForChineseNamesExplicitly() {
|
|
void lookup_asksForChineseNamesExplicitly() {
|