"""WHMCS 面板适配器测试:页面解析与状态映射 使用真实客户区结构的 HTML 片段,不发起网络请求。 """ from datetime import date import pytest from app.adapters.whmcs import WHMCSAdapter, WHMCSError SERVICES_HTML = """
產品 / 服務價格下次到期日狀態
TOT K11
remote-dev
226,950VND 季繳 2027-01-07星期四, 一月 7第, 2027 可用
TOT K11
vpn.host
$3.03 月繳 2026-11-25星期三, 十一月 25第, 2026 可用
TOT D12
123.31.29.104
275,000VND 月繳 2026-06-11星期四, 六月 11第, 2026 已刪除
TOT K11
paused.host
$3.03 月繳 2026-12-01星期二, 十二月 1第, 2026 已暫停
""" def _adapter(): return WHMCSAdapter({ "base_url": "https://billing.example.com/clientarea.php", "username": "user@example.com", "password": "secret", }) def test_parse_services_extracts_all_fields(): services = WHMCSAdapter.parse_services(SERVICES_HTML) assert [s["id"] for s in services] == ["36979", "28911", "36980", "36981"] first = services[0] assert first["product"] == "TOT K11" assert first["domain"] == "remote-dev" assert first["label"] == "TOT K11 remote-dev" assert first["expiry_date"] == date(2027, 1, 7) assert first["status"] == "active" assert first["status_text"] == "可用" assert first["amount"] == 226950.0 assert first["cycle"] == "季繳" def test_parse_services_maps_whmcs_status_classes(): services = {s["id"]: s for s in WHMCSAdapter.parse_services(SERVICES_HTML)} assert services["36979"]["status"] == "active" assert services["36980"]["status"] == "cancelled" assert services["36981"]["status"] == "stopped" def test_parse_services_requires_service_table(): with pytest.raises(WHMCSError): WHMCSAdapter.parse_services("no table") def test_list_vps_filters_terminated_and_keeps_ip(monkeypatch): adapter = _adapter() services = WHMCSAdapter.parse_services(SERVICES_HTML) monkeypatch.setattr(adapter, "fetch_services", lambda include_terminated=True: [ s for s in services if include_terminated or s["status"] != "cancelled" ]) vps = adapter.list_vps() assert [v.external_id for v in vps] == ["36979", "28911", "36981"] by_id = {v.external_id: v for v in vps} assert by_id["36979"].name == "TOT K11 remote-dev" assert by_id["36979"].ip_address == "" # 主机名不是 IP assert by_id["36979"].expiry_date == date(2027, 1, 7) assert "36980" not in by_id # 已删除项不出现在 list_vps def test_extract_token_supports_attribute_orders(): assert WHMCSAdapter._extract_token('') == "abc" assert WHMCSAdapter._extract_token('') == "xyz" def test_extract_token_prefers_login_form(): """页面上有多个带 token 的表单时,必须取登录表单里的那个""" page = """
""" assert WHMCSAdapter._extract_token(page) == "right" @pytest.mark.parametrize("body", [ 'Logout', '退出登录', '登出', 'ログアウト', ]) def test_logged_in_markers_cover_localized_themes(body): """主题本地化后不再输出英文 Logout,误判会让核对永远失败""" from app.adapters.whmcs import _LOGGED_IN_RE assert _LOGGED_IN_RE.search(body) def test_logged_in_marker_ignores_plain_text(): from app.adapters.whmcs import _LOGGED_IN_RE assert not _LOGGED_IN_RE.search("

请勿退出页面

") def test_adapter_requires_base_url(): with pytest.raises(WHMCSError): WHMCSAdapter({"username": "u", "password": "p"})