from pathlib import Path from contract_flow_steps import ( ContractFlowContext, begin_date_for, check_customer_diligence, read_contract_number, sign_date_for, _as_index_list, _classify_dialog_text, _click_toolbar_button, _lui_btn_xpath, _next_unused_goods_row, _pick_contract_row, _read_contract_number_on_page, _resolve_contract_body_path, _submit_error_from_body, _submit_succeeded, ) from errors import DiligenceError import pytest def _ctx(**overrides) -> ContractFlowContext: data = dict( contract_url="http://example", username="u", password="p", supplier_name="示例", order_date="2026-09-28", source_type="", ) data.update(overrides) return ContractFlowContext(**data) class _FakeBrowser: def __init__(self, text: str, url: str = "", script_result: str = ""): self._text = text self._url = url self._script_result = script_result self.opened: list[str] = [] def read_body_text(self) -> str: return self._text def switch_to_default_content(self) -> None: return None def open_url(self, url: str) -> None: self.opened.append(url) self._url = url def execute_script(self, script: str, *args): if "_xform_docNumber" in script: return self._script_result if "location.href" in script: return self._url return "" def test_sign_and_begin_fall_back_to_order_date(): ctx = _ctx() assert sign_date_for(ctx) == "2026-09-28" assert begin_date_for(ctx) == "2026-09-28" def test_sign_and_begin_use_contract_dates_when_set(): ctx = _ctx(contract_sign_date="2026-10-01", contract_start_date="2026-10-02") assert sign_date_for(ctx) == "2026-10-01" assert begin_date_for(ctx) == "2026-10-02" def test_diligence_raises_when_page_says_not_done(): with pytest.raises(DiligenceError, match="客户未尽调"): check_customer_diligence(_FakeBrowser("客户未尽调,不能起草"), _ctx()) def test_diligence_passes_when_text_absent(): check_customer_diligence(_FakeBrowser("客户已尽调"), _ctx()) def test_same_product_keeps_own_row(): assert _next_unused_goods_row([0, 1, 2], []) == 0 assert _next_unused_goods_row([0, 1, 2], [0]) == 1 assert _next_unused_goods_row([0, 1, 2], [0, 1]) == 2 assert _next_unused_goods_row([0], [0]) == -1 assert _next_unused_goods_row([], [0]) == -1 def test_goods_row_indexes_from_script_string(): assert _as_index_list("0,1,2") == [0, 1, 2] assert _as_index_list("") == [] assert _as_index_list(None) == [] assert _as_index_list([0, 1]) == [0, 1] def test_pick_contract_row_fd_id_beats_latest(): rows = [ {"no": "HT-NEW", "title": "别人的合同", "customer": "X", "href": "fdId=aaa"}, { "no": "HT-OURS", "title": "重庆亿昊药业有限公司采购协议20261002", "customer": "DW0100046361", "href": "kmAgreementApply.do?method=view&fdId=ours", }, ] assert ( _pick_contract_row( rows, fd_id="ours", title="重庆亿昊药业有限公司采购协议20261002", customer_code="DW0100046361", ) == "HT-OURS" ) def test_pick_contract_row_title_and_customer(): rows = [ {"no": "HT-NEW", "title": "别人的合同", "customer": "X", "href": "fdId=aaa"}, { "no": "HT-OURS", "title": "重庆亿昊药业有限公司采购协议20261002", "customer": "DW0100046361", "href": "fdId=ours", }, ] assert ( _pick_contract_row( rows, title="重庆亿昊药业有限公司采购协议20261002", customer_code="DW0100046361", ) == "HT-OURS" ) def test_pick_contract_row_does_not_take_first_unmatched(): rows = [{"no": "HT-NEW", "title": "别人的合同", "customer": "X", "href": "fdId=aaa"}] assert _pick_contract_row(rows, title="重庆亿昊", customer_code="DW0100046361") == "" def test_submit_error_from_doc_missing_page(): body = "您的请求已提交,系统返回如下信息:\n操作失败!\n该文档不存在" assert _submit_error_from_body(body) == "该文档不存在" assert _submit_error_from_body("客户编码 提交") == "" def test_submit_succeeded_on_success_toast(): browser = _FakeBrowser("您的操作已成功", url="kmAgreementApply.do?method=edit&fdId=1") assert _submit_succeeded(browser) is True def test_submit_succeeded_on_view_page(): browser = _FakeBrowser("合同详情", url="kmAgreementApply.do?method=view&fdId=1") assert _submit_succeeded(browser) is True def test_submit_not_succeeded_on_edit_or_add_page(): edit = _FakeBrowser("客户编码 暂存 提交", url="kmAgreementApply.do?method=edit&fdId=1") add = _FakeBrowser("客户编码 暂存 提交", url="kmAgreementApply.do?method=add") assert _submit_succeeded(edit) is False assert _submit_succeeded(add) is False def test_classify_dialog_text_kinds(): assert _classify_dialog_text("操作失败!该文档不存在") == "error" assert _classify_dialog_text("您确定要提交此文档吗?") == "confirm" assert _classify_dialog_text("您的操作已成功") == "success" assert _classify_dialog_text("客户编码") == "none" def test_lui_btn_xpath_is_exact_txt(): xpath = _lui_btn_xpath("提交") assert "lui_widget_btn_txt" in xpath assert "normalize-space()='提交'" in xpath class _ClickBrowser(_FakeBrowser): def __init__(self, script_result): super().__init__("") self.script_result = script_result self.scripts: list[tuple] = [] self.native: list[list[str]] = [] self.first: list[list[str]] = [] def execute_script(self, script, *args): self.scripts.append((script, args)) return self.script_result def click_native(self, selectors): self.native.append(list(selectors)) def click_first(self, selectors): self.first.append(list(selectors)) def test_click_toolbar_button_targets_lui_btn_txt(): browser = _ClickBrowser({"ok": True, "via": "btn_txt", "cid": "lui-id-36"}) assert _click_toolbar_button(browser, "提交") is True script, args = browser.scripts[0] assert "lui_widget_btn_txt" in script assert args[0] == "提交" assert browser.native == [] assert browser.first == [] def test_click_toolbar_button_fallback_uses_exact_txt(): browser = _ClickBrowser({"ok": False, "texts": ["暂存"]}) assert _click_toolbar_button(browser, "提交") is True joined = " ".join(browser.native[0]) assert "lui_widget_btn_txt" in joined assert "normalize-space()='提交'" in joined assert "text=提交" not in joined assert "contains(text()" not in joined def test_read_contract_number_from_xform_doc_number(): browser = _FakeBrowser( "手机号 13559935238 合同编号", script_result="035012026100835435", ) assert _read_contract_number_on_page(browser) == "035012026100835435" no = read_contract_number(browser, _ctx(), fd_id="abc") assert no == "035012026100835435" assert any("method=view" in url and "fdId=abc" in url for url in browser.opened) assert all("我的合同" not in url for url in browser.opened) def test_read_contract_number_requires_fd_id(): browser = _FakeBrowser("合同编号") assert read_contract_number(browser, _ctx(), fd_id="") == "" assert browser.opened == [] def test_read_contract_number_skips_placeholder_and_phone(): browser = _FakeBrowser( "手机号\n13559935238\n提交时自动生成", script_result="提交时自动生成", ) assert _read_contract_number_on_page(browser) == "" def test_pick_contract_row_skips_empty_number(): rows = [ { "no": "", "title": "重庆亿昊药业有限公司采购协议20261009", "customer": "DW0100046361", "href": "kmAgreementApply.do?method=view&fdId=ours", } ] assert ( _pick_contract_row( rows, fd_id="ours", title="重庆亿昊药业有限公司采购协议20261009", customer_code="DW0100046361", ) == "" ) def test_submit_without_file_uses_placeholder_body(): ctx = _ctx(submit_mode="submit", source_type="") path = _resolve_contract_body_path(ctx) assert Path(path).is_file() assert Path(path).stat().st_size > 64 def test_dry_run_without_file_skips_body(): ctx = _ctx(submit_mode="dry_run", source_type="") assert _resolve_contract_body_path(ctx) == ""