#!/usr/bin/env python3 import sys import unittest from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "scripts")) from journey import classify_outcome, contract_errors, propagate_results def valid_case(**overrides): case = { "id": "TC-CLIENT-001", "client_suite": "client-smoke", "driver": "browser", "session": "browser", "platform": ["android", "ios"], "journey": { "goal": "完成客户端目标", "entry_state": "logged_out", "actions": ["启动", "登录", "完成目标"], "assertions": ["看到目标页面"], "final_state": "目标已完成", "failure_policy": "stop", }, "fixture": {"account": "test-role", "data": []}, "cleanup": {"steps": ["清理本轮数据"]}, } case.update(overrides) return case class JourneyContractTests(unittest.TestCase): def test_contract_accepts_shared_client_shape(self): self.assertEqual(contract_errors(valid_case(), {"browser": {}}), []) def test_missing_fixture_and_cleanup_are_contract_errors(self): errors = contract_errors( valid_case(fixture=None, cleanup=None), {"browser": {}}, ) self.assertTrue(any("fixture 缺失" in error for error in errors)) self.assertTrue(any("cleanup 缺失" in error for error in errors)) def test_login_failure_is_product_failure_but_unavailable_environment_is_not(self): self.assertEqual(classify_outcome(ok=False, phase="login"), "fail") self.assertEqual( classify_outcome(ok=False, phase="login", environment_ready=False), "environment-fail", ) def test_failed_case_blocks_remaining_cases_and_keeps_explicit_skip(self): rows = propagate_results( ["TC-1", "TC-2", "TC-3"], {"TC-1": "fail", "TC-2": "pass", "TC-3": "pass"}, skipped={"TC-3": "未授权"}, ) self.assertEqual([row["result"] for row in rows], ["fail", "not-run", "skipped"]) def test_continue_policy_allows_next_case(self): rows = propagate_results( ["TC-1", "TC-2"], {"TC-1": "fail", "TC-2": "pass"}, policies={"TC-1": "continue"}, ) self.assertEqual([row["result"] for row in rows], ["fail", "pass"]) if __name__ == "__main__": unittest.main()