"""OA 发布校验单元测试""" import unittest from unittest.mock import MagicMock, patch from app.services.oa_publish import ( count_fillable_fields, validate_control_types, validate_draft_for_publish, validate_flow_tree, ) def _linear_ok_flow(): return { "id": "s1", "type": "start", "name": "发起人", "child": { "id": "a1", "type": "approval", "name": "部门负责人", "approverConfig": {"assigneeType": "dept_leader"}, "child": {"id": "e1", "type": "end", "name": "结束"}, }, } def _condition_flow(rules=None, when=None, is_default=False): return { "type": "start", "name": "发起人", "child": { "type": "condition", "name": "条件", "conditionGroups": [ { "name": "分支1", "rules": rules if rules is not None else [], "when": when, "isDefault": False, "child": { "type": "approval", "name": "审批A", "approverConfig": {"assigneeType": "dept_leader"}, "child": {"type": "end", "name": "结束"}, }, }, { "name": "默认", "rules": [], "isDefault": True, "child": { "type": "approval", "name": "审批B", "approverConfig": {"assigneeType": "dept_leader"}, "child": {"type": "end", "name": "结束"}, }, }, ], }, } class TestOaPublishValidate(unittest.TestCase): def test_count_fillable_fields(self): schema = { "rule": [ {"type": "html", "props": {"innerHTML": "说明"}}, {"type": "input", "field": "title", "title": "标题"}, { "type": "fcRow", "children": [ {"type": "inputNumber", "field": "days", "title": "天数"}, ], }, ] } self.assertEqual(count_fillable_fields(schema), 2) self.assertEqual(count_fillable_fields({"rule": []}), 0) def test_flow_ok(self): self.assertEqual(validate_flow_tree(_linear_ok_flow()), []) def test_flow_missing_end(self): tree = { "type": "start", "name": "发起人", "child": { "type": "approval", "name": "审批", "approverConfig": {"assigneeType": "users", "userIds": ["u1"]}, }, } errs = validate_flow_tree(tree) self.assertTrue(any("结束" in e or "后续" in e for e in errs)) def test_approval_users_empty_rejected(self): tree = { "type": "start", "child": { "type": "approval", "name": "审批", "approverConfig": {"assigneeType": "users", "userIds": []}, "child": {"type": "end"}, }, } errs = validate_flow_tree(tree) self.assertTrue(any("指定成员" in e for e in errs)) def test_condition_branch_incomplete(self): tree = { "type": "start", "child": { "type": "condition", "name": "条件", "conditionGroups": [{"name": "大于3天", "rules": [], "child": None}], }, } errs = validate_flow_tree(tree) self.assertTrue(any("下游" in e or "结束" in e for e in errs)) def test_draft_requires_fillable_and_assignee(self): db = MagicMock() with patch( "app.services.oa_publish.validate_oa_roles_referenced", return_value=[], ): errs = validate_draft_for_publish( db, tenant_no="zkxw", form_schema={"rule": [{"type": "html"}]}, flow_tree={ "type": "start", "child": { "type": "approval", "approverConfig": {"assigneeType": "users", "userIds": []}, "child": {"type": "end"}, }, }, ) self.assertTrue(any("可填字段" in e for e in errs)) self.assertTrue(any("指定成员" in e for e in errs)) def test_valid_draft_passes_form_and_flow(self): db = MagicMock() with patch( "app.services.oa_publish.validate_oa_roles_referenced", return_value=[], ): errs = validate_draft_for_publish( db, tenant_no="zkxw", form_schema={"rule": [{"type": "input", "field": "reason"}]}, flow_tree=_linear_ok_flow(), ) self.assertEqual(errs, []) def test_unknown_control_type_rejected(self): errs = validate_control_types( {"rule": [{"type": "aiMagicBox", "field": "x"}, {"type": "input", "field": "y"}]} ) self.assertTrue(any("aiMagicBox" in e for e in errs)) def test_upload_is_allowed(self): self.assertEqual( validate_control_types( {"rule": [{"type": "upload", "field": "files", "title": "上传"}]} ), [], ) self.assertEqual( count_fillable_fields( {"rule": [{"type": "upload", "field": "files", "title": "上传"}]} ), 1, ) class TestConditionPublish(unittest.TestCase): def test_when_only_rejected(self): errs = validate_flow_tree(_condition_flow(rules=[], when="{days} > 3")) self.assertTrue(any("可视化" in e or "重新配置" in e or "rules" in e for e in errs)) def test_rules_empty_non_default_rejected(self): errs = validate_flow_tree(_condition_flow(rules=[])) self.assertTrue(any("条件" in e for e in errs)) def test_rules_ok(self): errs = validate_flow_tree( _condition_flow(rules=[{"field": "days", "op": ">", "value": 3}]) ) self.assertEqual(errs, []) def test_invalid_field_reference(self): db = MagicMock() with patch( "app.services.oa_publish.validate_oa_roles_referenced", return_value=[], ): errs = validate_draft_for_publish( db, tenant_no="zkxw", form_schema={ "rule": [ { "type": "inputNumber", "field": "days", "title": "天数", "useInCondition": True, "$required": True, } ] }, flow_tree=_condition_flow( rules=[{"field": "other", "op": ">", "value": 1}] ), ) self.assertTrue(any("无效字段" in e or "other" in e for e in errs)) def test_field_not_flagged_rejected(self): db = MagicMock() with patch( "app.services.oa_publish.validate_oa_roles_referenced", return_value=[], ): errs = validate_draft_for_publish( db, tenant_no="zkxw", form_schema={ "rule": [ { "type": "inputNumber", "field": "days", "title": "天数", "useInCondition": False, } ] }, flow_tree=_condition_flow( rules=[{"field": "days", "op": ">", "value": 3}] ), ) self.assertTrue(any("无效字段" in e or "参与" in e for e in errs)) if __name__ == "__main__": unittest.main()