wx-rust-common 0.1.1

WxRust 公共基础设施:错误模型、token/执行引擎、HTTP 抽象、会话、重复检查(对应 weixin-java-common)
Documentation
//! 覆盖率提升测试:cp / ma / mp / channel 四份错误码翻译表全分支覆盖。
//!
//! 对应 Java `me.chanjar.weixin.common.error` 包中的
//! `WxCpErrorMsgEnum` / `WxMaErrorMsgEnum` / `WxMpErrorMsgEnum` / `WxChannelErrorMsgEnum`。
//! 被测函数 [`find_msg_by_code`] 均由 `scripts/gen_error_msg_enums.py`
//! 从 Java 源码自动生成的巨型 Match(错误码分支 + 兜底 `None`),
//! 本测试用脚本从源文件提取的全量错误码清单逐码调用,覆盖每一个 match 分支,
//! 并用若干已知正确值做黄金断言(与 Java 枚举逐一比对)。

use wx_rust_common::error::wx_channel_error_msg_enum::find_msg_by_code as channel_find_msg_by_code;
use wx_rust_common::error::wx_cp_error_msg_enum::find_msg_by_code as cp_find_msg_by_code;
use wx_rust_common::error::wx_ma_error_msg_enum::find_msg_by_code as ma_find_msg_by_code;
use wx_rust_common::error::wx_mp_error_msg_enum::find_msg_by_code as mp_find_msg_by_code;

// ═══ CP(企业微信,对应 Java WxCpErrorMsgEnum)全量错误码清单 ═══
/// 与源文件 match 分支一一对应的全量错误码(脚本提取,勿手改)。
const CP_ALL_CODES: &[i32] = &[
    -1, 0, 40001, 40003, 40004, 40005, 40006, 40007, 40008, 40009, 40011, 40013, 40014, 40016,
    40017, 40018, 40019, 40020, 40022, 40023, 40024, 40025, 40026, 40027, 40029, 40031, 40032,
    40033, 40035, 40050, 40054, 40055, 40056, 40057, 40058, 40059, 40063, 40066, 40068, 40070,
    40071, 40072, 40073, 40074, 40077, 40078, 40080, 40082, 40083, 40084, 40085, 40086, 40088,
    40089, 40091, 40092, 40093, 40094, 40096, 41001, 41002, 41004, 41006, 41008, 41009, 41010,
    41011, 41016, 41017, 41019, 41021, 41022, 41023, 41024, 41025, 41033, 42001, 42007, 42009,
    43004, 44001, 44004, 45001, 45002, 45004, 45007, 45008, 45009, 45022, 45024, 45026, 45032,
    45033, 46003, 46004, 48002, 48003, 48004, 48005, 50001, 50002, 50003, 60001, 60003, 60004,
    60005, 60006, 60007, 60008, 60009, 60010, 60011, 60012, 60020, 60028, 60102, 60103, 60104,
    60105, 60106, 60107, 60110, 60111, 60112, 60123, 60124, 60125, 60127, 60129, 72023, 72024,
    72037, 80001, 81001, 81002, 81011, 81013, 81014, 82001, 82002, 82003, 84014, 84015, 84019,
    84020, 84021, 84022, 84023, 84024, 84025, 84052, 84053, 84054, 84055, 84056, 84057, 84058,
    84059, 84060, 84061, 84062, 84063, 84064, 84065, 84066, 84067, 84069, 84070, 84071, 84072,
    84073, 84074, 84075, 84076, 84077, 84078, 84079, 84080, 84081, 84082, 84083, 84084, 84085,
    84086, 84087, 84088, 84089, 84090, 84091, 84092, 84093, 84094, 84095, 84096, 84097, 84098,
    84099, 84100, 84101, 84102, 84103, 84104, 84105, 84106, 84107, 84108, 84109, 84110, 84111,
    84112, 84113, 84114, 84115, 84116, 84117, 84118, 84119, 84120, 84121, 84122, 84123, 84124,
    84125, 84126, 84127, 84128, 84129, 84130, 84200, 85002, 85004, 85005, 86001, 86003, 86004,
    86005, 86006, 86007, 86008, 86216, 86217, 86220, 90001, 90002, 90003, 90100, 90101, 90102,
    90103, 90104, 90105, 90106, 91040, 301002, 301005, 301012, 301013, 301014, 301015, 301016,
    301017, 301021, 301022, 301023, 301024, 301025, 301036, 301055, 301056, 301057, 301069, 301070,
    301071, 301072, 301075, 302003, 302004, 302005, 302006, 302007, 2000002,
];

// ═══ MA(微信小程序,对应 Java WxMaErrorMsgEnum)全量错误码清单 ═══
/// 与源文件 match 分支一一对应的全量错误码(脚本提取,勿手改)。
const MA_ALL_CODES: &[i32] = &[
    -1, 40001, 40002, 40003, 40004, 40007, 40009, 40013, 40029, 40037, 40097, 41002, 41004, 41006,
    41028, 41029, 41030, 42001, 43101, 44002, 45009, 45011, 45015, 45029, 45047, 45072, 45080,
    45081, 46001, 47001, 47003, 47501, 47502, 47503, 47504, 48001, 48006, 53010, 53011, 53012,
    53013, 53014, 53015, 53016, 53017, 53018, 53019, 53200, 53201, 53202, 61070, 76001, 76002,
    76003, 76004, 76021, 76022, 80066, 80067, 80082, 85001, 85002, 85003, 85004, 85006, 85007,
    85008, 85009, 85010, 85011, 85012, 85013, 85014, 85015, 85016, 85017, 85018, 85019, 85020,
    85021, 85022, 85023, 85026, 85027, 85043, 85044, 85045, 85046, 85047, 85048, 85060, 85061,
    85062, 85063, 85064, 85065, 85066, 85068, 85069, 85070, 85071, 85072, 85073, 85074, 85075,
    85077, 85079, 85080, 85081, 85082, 85083, 85084, 85085, 85086, 85087, 86000, 86001, 86002,
    86004, 87011, 87012, 87013, 87014, 89002, 89019, 89020, 89021, 89029, 89231, 89247, 89248,
    89249, 89250, 89251, 89252, 89253, 89254, 89255, 89300, 89401, 89402, 89403, 89404, 89405,
    89408, 89409, 89410, 89411, 89412, 89413, 89414, 89415, 89416, 89417, 89418, 89419, 89420,
    89421, 89422, 89423, 89424, 89425, 89426, 89427, 89428, 89429, 89430, 89431, 89432, 91001,
    91002, 91003, 91004, 91005, 91006, 91007, 91008, 91009, 91010, 91011, 91012, 91013, 91014,
    91015, 91016, 91017, 268490001, 268490002, 268490003, 268490004, 268490005, 268490006,
    268490007, 268490008, 268490009, 268490011, 268490012, 268490013, 268490014, 268490015,
    268490016, 268490018, 268490019, 268490020, 268490021, 268490022, 268490023, 268490024,
];

// ═══ MP(微信公众号,对应 Java WxMpErrorMsgEnum)全量错误码清单 ═══
/// 与源文件 match 分支一一对应的全量错误码(脚本提取,勿手改)。
const MP_ALL_CODES: &[i32] = &[
    -1, 0, 40001, 40002, 40003, 40004, 40005, 40006, 40007, 40008, 40009, 40010, 40011, 40012,
    40013, 40014, 40015, 40016, 40017, 40018, 40019, 40020, 40021, 40022, 40023, 40024, 40025,
    40026, 40027, 40028, 40029, 40030, 40031, 40032, 40033, 40035, 40038, 40039, 40050, 40051,
    40060, 40117, 40118, 40119, 40120, 40121, 40132, 40137, 40155, 40163, 41001, 41002, 41003,
    41004, 41005, 41006, 41007, 41008, 41009, 42001, 42002, 42003, 42007, 43001, 43002, 43003,
    43004, 43005, 43019, 44001, 44002, 44003, 44004, 45001, 45002, 45003, 45004, 45005, 45006,
    45007, 45008, 45009, 45010, 45011, 45015, 45016, 45017, 45018, 45047, 45065, 45066, 45067,
    45083, 45084, 45159, 46001, 46002, 46003, 46004, 47001, 48001, 48002, 48004, 48005, 48006,
    48008, 50001, 50002, 50005, 61450, 61451, 61452, 61453, 61454, 61455, 61456, 61457, 61500,
    65301, 65302, 65303, 65304, 65305, 65306, 65307, 65308, 65309, 65310, 65311, 65312, 65313,
    65314, 65316, 65317, 9001001, 9001002, 9001003, 9001004, 9001005, 9001006, 9001007, 9001008,
    9001009, 9001010, 9001020, 9001021, 9001022, 9001023, 9001024, 9001025, 9001026, 9001027,
    9001028, 9001029, 9001030, 9001031, 9001032, 9001033, 9001034, 9001035, 9001036,
];

// ═══ CHANNEL(微信视频号,对应 Java WxChannelErrorMsgEnum)全量错误码清单 ═══
/// 与源文件 match 分支一一对应的全量错误码(脚本提取,勿手改)。
const CHANNEL_ALL_CODES: &[i32] = &[
    -1, 0, 40001, 40003, 40013, 40066, 41001, 41002, 41018, 42001, 43002, 43003, 44002, 45002,
    45009, 45011, 45035, 47001, 48001, 48004, 50001, 50002, 61004, 61007, 10080000, 10080001,
    10080002,
];

// ═══ 全分支遍历 ═══

/// CP:每一个已收录的错误码都必须翻译出非空信息(覆盖全部 Some 分支)。
#[test]
fn test_cp_all_codes_have_non_empty_msg() {
    assert_eq!(CP_ALL_CODES.len(), 281);
    for &code in CP_ALL_CODES {
        let msg = cp_find_msg_by_code(code)
            .unwrap_or_else(|| panic!("CP 错误码 {code} 应被收录,实际返回 None"));
        assert!(!msg.is_empty(), "CP 错误码 {code} 的翻译信息不应为空字符串");
    }
}

/// MA:每一个已收录的错误码都必须翻译出非空信息(覆盖全部 Some 分支)。
#[test]
fn test_ma_all_codes_have_non_empty_msg() {
    assert_eq!(MA_ALL_CODES.len(), 208);
    for &code in MA_ALL_CODES {
        let msg = ma_find_msg_by_code(code)
            .unwrap_or_else(|| panic!("MA 错误码 {code} 应被收录,实际返回 None"));
        assert!(!msg.is_empty(), "MA 错误码 {code} 的翻译信息不应为空字符串");
    }
}

/// MP:每一个已收录的错误码都必须翻译出非空信息(覆盖全部 Some 分支)。
#[test]
fn test_mp_all_codes_have_non_empty_msg() {
    assert_eq!(MP_ALL_CODES.len(), 161);
    for &code in MP_ALL_CODES {
        let msg = mp_find_msg_by_code(code)
            .unwrap_or_else(|| panic!("MP 错误码 {code} 应被收录,实际返回 None"));
        assert!(!msg.is_empty(), "MP 错误码 {code} 的翻译信息不应为空字符串");
    }
}

/// CHANNEL:每一个已收录的错误码都必须翻译出非空信息(覆盖全部 Some 分支)。
#[test]
fn test_channel_all_codes_have_non_empty_msg() {
    assert_eq!(CHANNEL_ALL_CODES.len(), 27);
    for &code in CHANNEL_ALL_CODES {
        let msg = channel_find_msg_by_code(code)
            .unwrap_or_else(|| panic!("CHANNEL 错误码 {code} 应被收录,实际返回 None"));
        assert!(
            !msg.is_empty(),
            "CHANNEL 错误码 {code} 的翻译信息不应为空字符串"
        );
    }
}

/// 未收录的错误码统一走兜底分支返回 `None`(四份枚举合并验证)。
#[test]
fn test_unknown_code_returns_none() {
    for code in [999_999, 0x7FFF_FFFF, -2, i32::MIN, i32::MAX] {
        assert_eq!(
            cp_find_msg_by_code(code),
            None,
            "cp 错误码 {code} 不应被收录"
        );
        assert_eq!(
            ma_find_msg_by_code(code),
            None,
            "ma 错误码 {code} 不应被收录"
        );
        assert_eq!(
            mp_find_msg_by_code(code),
            None,
            "mp 错误码 {code} 不应被收录"
        );
        assert_eq!(
            channel_find_msg_by_code(code),
            None,
            "channel 错误码 {code} 不应被收录"
        );
    }
}

// ═══ 黄金断言(与 Java 源码逐字比对) ═══

/// CP 已知正确值抽检:错误码 → 中文信息的原文一致性。
#[test]
fn test_cp_known_golden_values() {
    // 对应 Java: CODE_1(-1, ...)
    assert_eq!(
        cp_find_msg_by_code(-1),
        Some("系统繁忙;服务器暂不可用,建议稍候重试。建议重试次数不超过3次。")
    );
    // 对应 Java: CODE_0(0, ...)
    assert_eq!(cp_find_msg_by_code(0), Some("请求成功;接口调用成功"));
    // 对应 Java: CODE_40001(40001, ...)
    assert_eq!(
        cp_find_msg_by_code(40001),
        Some("不合法的secret参数;secret在应用详情/通讯录管理助手可查看")
    );
    // 对应 Java: CODE_60001(60001, ...)
    assert_eq!(
        cp_find_msg_by_code(60001),
        Some("部门长度不符合限制;部门名称不能为空且长度不能超过32个字")
    );
    // 对应 Java: CODE_2000002(2000002, ...)
    assert_eq!(
        cp_find_msg_by_code(2000002),
        Some("CorpId参数无效;指定的CorpId不存在")
    );
}

/// MA 已知正确值抽检:错误码 → 中文信息的原文一致性。
#[test]
fn test_ma_known_golden_values() {
    // 对应 Java: CODE_MINUS_1(-1, ...)
    assert_eq!(
        ma_find_msg_by_code(-1),
        Some("系统繁忙,此时请开发者稍候再试")
    );
    // 对应 Java: CODE_40001(40001, ...)
    assert_eq!(
        ma_find_msg_by_code(40001),
        Some("access_token 无效或 AppSecret 错误")
    );
    // 对应 Java: CODE_61070(61070, ...)
    assert_eq!(ma_find_msg_by_code(61070), Some("法人姓名与微信号不一致"));
    // 对应 Java: CODE_268490024(268490024, ...)
    assert_eq!(
        ma_find_msg_by_code(268490024),
        Some("广告金充值金额必须大于0")
    );
    // 对应 Java: CODE_85075(85075, ...)
    assert_eq!(
        ma_find_msg_by_code(85075),
        Some("个人类型小程序无法设置二维码规则")
    );
}

/// MP 已知正确值抽检:错误码 → 中文信息的原文一致性。
#[test]
fn test_mp_known_golden_values() {
    // 对应 Java: CODE_1(-1, ...)
    assert_eq!(
        mp_find_msg_by_code(-1),
        Some("系统繁忙,此时请开发者稍候再试")
    );
    // 对应 Java: CODE_0(0, ...)
    assert_eq!(mp_find_msg_by_code(0), Some("请求成功"));
    // 对应 Java: CODE_40001(40001, ...)
    assert_eq!(
        mp_find_msg_by_code(40001),
        Some(
            "获取 access_token 时 AppSecret 错误,或者 access_token 无效。请开发者认真比对 AppSecret 的正确性,或查看是否正在为恰当的公众号调用接口"
        )
    );
    // 对应 Java: CODE_61450(61450, ...)
    assert_eq!(mp_find_msg_by_code(61450), Some("系统错误 (system error)"));
    // 对应 Java: CODE_9001036(9001036, ...)
    assert_eq!(
        mp_find_msg_by_code(9001036),
        Some("查询起始值 begin 不合法")
    );
}

/// CHANNEL 已知正确值抽检:错误码 → 中文信息的原文一致性。
#[test]
fn test_channel_known_golden_values() {
    // 对应 Java: CODE_1(-1, ...)
    assert_eq!(
        channel_find_msg_by_code(-1),
        Some("系统繁忙,此时请开发者稍候再试")
    );
    // 对应 Java: CODE_0(0, ...)
    assert_eq!(channel_find_msg_by_code(0), Some("请求成功"));
    // 对应 Java: CODE_40001(40001, ...)
    assert_eq!(
        channel_find_msg_by_code(40001),
        Some(
            "获取 access_token 时 AppSecret 错误,或者 access_token 无效。请开发者认真检查 AppSecret 的正确性"
        )
    );
    // 对应 Java: CODE_61004(61004, ...)
    assert_eq!(
        channel_find_msg_by_code(61004),
        Some("需要登录 channels.weixin.qq.com/shop 配置IP白名单")
    );
    // 对应 Java: CODE_10080002(10080002, ...)
    assert_eq!(
        channel_find_msg_by_code(10080002),
        Some("小店的视频号带货身份为达人号,不允许使用该功能,如需使用,请将带货身份修改为商家")
    );
}