weather-query 自检
========================================================================
  PASS  A1 settings.json 可解析
  PASS  A2 settings 必需字段齐全
  PASS  A3 metric/imperial 都在
  PASS  A4 wmo.json 可解析
  PASS  A5 风向表 16 个方位
  PASS  A6 unknown/no_data 文案存在
  PASS  A7 默认 days=1
  PASS  A8 上限 7 天
  PASS  A9 默认超时 10s
  PASS  A10 时区固定 auto
  PASS  A11 配置缺文件 -> InputError
  PASS  A12 配置非 JSON -> InputError
  PASS  B1 code 0 -> 晴朗
  PASS  B2 code 3 -> 阴天
  PASS  B3 code 45 -> 雾
  PASS  B4 code 61 -> 小雨
  PASS  B5 code 80 -> 小阵雨
  PASS  B6 code 95 -> 雷暴
  PASS  B7 code 999 -> 未知天气(code=999)
  PASS  B8 code None -> 无数据文案
  PASS  B9 code 字符串 '61' 也认
  PASS  B10 风向 0 -> 北
  PASS  B11 风向 90 -> 东
  PASS  B12 风向 180 -> 南
  PASS  B13 风向 270 -> 西
  PASS  B14 风向 45 -> 东北
  PASS  B15 风向 204 -> 西南偏南
  PASS  B16 风向 360 回绕 -> 北
  PASS  B17 风向 None -> 空串
  PASS  B18 风向 非法串 -> 空串
  PASS  C1 天数 3 原样通过
  PASS  C2 天数 3 不产生提示
  PASS  C3 天数 0 夹到下限 1
  PASS  C4 下限有提示
  PASS  C5 天数 9 夹到上限 7
  PASS  C6 上限有提示
  PASS  C7 负数夹到 1
  PASS  C8 天数 7 边界通过
  PASS  D1 温度 25.8
  PASS  D2 体感 22.7
  PASS  D3 湿度 16
  PASS  D4 当前天气文本 晴朗
  PASS  D5 当前风向文本 西南偏南
  PASS  D6 观测时间原文
  PASS  D7 daily 3 天
  PASS  D8 第1天 阴天
  PASS  D9 第2天 大毛毛雨
  PASS  D10 第3天 中阵雨
  PASS  D11 第1天最低 11.6
  PASS  D12 第1天最高 25.9
  PASS  D13 第3天降水 53.8
  PASS  D14 地点串 北京（北京市, 中国）
  PASS  D15 温度单位取响应  °C
  PASS  D16 风速单位取响应 km/h
  PASS  D17 降水单位取响应 mm
  PASS  D18 时区 Asia/Shanghai
  PASS  D19 days_effective = 3
  PASS  D20 imperial 温度单位 °F
  PASS  D21 imperial 风速单位 mph
  PASS  D22 imperial 降水单位 in
  PASS  D23 imperial 有 1 天预报
  PASS  D24 响应原生单位原样存档
  PASS  D25 展示风速标签用配置 mph
  PASS  D26 展示降水标签用配置 in
  PASS  D27 公制响应存档 °C
  PASS  D28 单位一致时不告警
  PASS  D29 单位不符时告警
  PASS  D30 响应缺单位时不误报
  PASS  D31 正常查询无告警 notes
  PASS  E1 标题含地点
  PASS  E2 温度行
  PASS  E3 天气行
  PASS  E4 湿度行
  PASS  E5 风速+风向行
  PASS  E6 预报表头
  PASS  E7 日期带星期
  PASS  E8 第3天降水行
  PASS  E9 第2天日期
  PASS  E10 第3天日期
  PASS  E11 预报行数 3
  PASS  E12 观测时间格式化
  PASS  E13 emoji 可 utf-8 编码
  PASS  E14 缺数据用破折号占位
  PASS  E15 缺数据天气文案
  PASS  E16 无预报时不打印预报段
  PASS  E17 提示行可见
  PASS  E18 imperial 渲染
  PASS  F1 正常解析 JSON
  PASS  F2 timeout 真的传下去
  PASS  F3 自定义 UA 生效
  PASS  F4 内部参数 _ua 不进 query
  PASS  F5 普通参数在 query 里
  PASS  F6 非 ASCII 被 percent-encode
  PASS  F7 HTTP 500 -> ServiceError
  PASS  F8 网络异常 -> ServiceError
  PASS  F9 超时 -> ServiceError
  PASS  F10 非 JSON 响应 -> ServiceError
  PASS  F11 非 200 状态码 -> ServiceError
  PASS  F12 未指定 _ua 时用默认 UA
  PASS  G1 默认查询退出码 0
  PASS  G2 stderr 为空
  PASS  G3 输出了报告
  PASS  G4 默认 forecast_days=1
  PASS  G5 timezone=auto
  PASS  G6 默认公制 celsius
  PASS  G7 公制风速 kmh
  PASS  G8 默认请求超时 10s
  PASS  G9 current 变量齐全
  PASS  G10 daily 变量齐全
  PASS  G11 地理编码走了 geocoding API
  PASS  G12 地理编码 language=zh
  PASS  G13 经纬度取自地理编码结果
  PASS  G14 --days 3 退出码 0
  PASS  G15 --days 3 传给 API
  PASS  G16 输出 3 天预报
  PASS  G17 输入城市名原样上送
  PASS  G18 --units imperial 退出码 0
  PASS  G19 传 fahrenheit
  PASS  G20 传 mph
  PASS  G21 输出 °F
  PASS  G22 越界天数仍退出码 0
  PASS  G23 越界提示可见
  PASS  G24 stderr 仍为空
  PASS  G25 --timeout 3 退出码 0
  PASS  G26 --timeout 传下去
  PASS  G27 超时 0 退出码 2
  PASS  G28 超时非正数提示
  PASS  G29 城市未找到退出码 2
  PASS  G30 提示检查拼写
  PASS  G31 错误也走 stdout（stderr 空）
  PASS  G32 缺城市退出码 2
  PASS  G33 提示缺城市名
  PASS  G34 服务不可用退出码 3
  PASS  G35 友好提示
  PASS  G36 stderr 空
  PASS  G37 --json 退出码 0
  PASS  G38 --json 输出是合法 JSON
  PASS  G39 JSON 含 place/current/daily/labels
  PASS  G40 JSON daily 3 条
  PASS  G41 JSON 中文未被转义
  PASS  G42 非法单位被拦（exit 2）
  PASS  I1 config 候选数 5
  PASS  I2 返回候选列表
  PASS  I3 候选含名称
  PASS  I4 候选含 admin1
  PASS  I5 候选含国家
  PASS  I6 候选含经纬度
  PASS  I7 北京 名实相符不告警
  PASS  I8 NewYork 返回 5 条候选
  PASS  I9 NewYork 首条其实是约克
  PASS  I10 NewYork 判定名实不符
  PASS  I10b NewYork 小城 -> 需要提醒
  PASS  I10c NYC 也是跨文字匹配
  PASS  I10d NYC 是大城市(8.8M) -> 不提醒
  PASS  I10e NYC 无误报提示
  PASS  I11 上海 取首条
  PASS  I12 上海 名实相符
  PASS  I13 朝阳 5 条同名候选
  PASS  I14 朝阳 首条是重庆那个
  PASS  I15 纽约(中文)零结果 -> InputError
  PASS  I16 空城市 -> InputError
  PASS  I16b 城市名前后空白被裁掉
  PASS  I17 默认选第 1 条
  PASS  I18 --pick 3 取第 3 条
  PASS  I19 --pick 3 取到英格兰那个
  PASS  I20 --pick 0 越界 -> InputError
  PASS  I21 --pick 99 越界 -> InputError
  PASS  I22 --pick 非整数 -> InputError
  PASS  I23 名实不符产生 2 条提示
  PASS  I24 提示点明实际匹配到哪
  PASS  I25 提示给出其它候选
  PASS  I26 提示告知 --candidates/--pick
  PASS  I27 名实相符无提示
  PASS  I28 名实相符不污染 notes
  PASS  I29 清单表头
  PASS  I30 清单含序号与地点
  PASS  I31 清单含人口
  PASS  I32 清单含用法提示
  PASS  J1 名实不符仍退出码 0
  PASS  J2 输出点明实际城市
  PASS  J3 输出带提示行
  PASS  J4 stderr 空
  PASS  J5 --candidates 退出码 0
  PASS  J6 列出候选
  PASS  J7 列出 5 条
  PASS  J8 --candidates 不查天气
  PASS  J9 只调了一次地理编码
  PASS  J10 --pick 3 退出码 0
  PASS  J11 --pick 3 用了第 3 条纬度
  PASS  J12 --pick 3 纬度不是第 1 条的 40.868
  PASS  J13 --pick 越界退出码 2
  PASS  J14 越界提示可见
  PASS  J15 零结果退出码 2
  PASS  J16 提示换当地语言名称
  PASS  H1 零依赖：不引用 requests
  PASS  H1 零依赖：不引用 httpx
  PASS  H1 零依赖：不引用 aiohttp
  PASS  H1 零依赖：不引用 bs4
  PASS  H1 零依赖：不引用 pandas
  PASS  H2 无删除类调用
  PASS  H3 只读：没有写文件模式
  PASS  H4 无凭证/密钥字样
  PASS  H5 网络调用强制带 timeout
  PASS  H5b 默认 opener 就是 urlopen 且带 timeout
  PASS  H6 外壳不 import 网络库以外的东西
  PASS  H7 import 阶段做了 UTF-8 reconfigure
  PASS  H8 提示一律走 stdout（不写 stderr）
  PASS  H9 未找到 fixture 确实没有 results
  PASS  H10 真实 fixture 有 3 天数据
========================================================================
合计 209 项：209 通过 / 0 失败
全绿 ✅
