test_oa_publish.py 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. """OA 发布校验单元测试"""
  2. import unittest
  3. from unittest.mock import MagicMock, patch
  4. from app.services.oa_publish import (
  5. count_fillable_fields,
  6. validate_control_types,
  7. validate_draft_for_publish,
  8. validate_flow_tree,
  9. )
  10. def _linear_ok_flow():
  11. return {
  12. "id": "s1",
  13. "type": "start",
  14. "name": "发起人",
  15. "child": {
  16. "id": "a1",
  17. "type": "approval",
  18. "name": "部门负责人",
  19. "approverConfig": {"assigneeType": "dept_leader"},
  20. "child": {"id": "e1", "type": "end", "name": "结束"},
  21. },
  22. }
  23. def _condition_flow(rules=None, when=None, is_default=False):
  24. return {
  25. "type": "start",
  26. "name": "发起人",
  27. "child": {
  28. "type": "condition",
  29. "name": "条件",
  30. "conditionGroups": [
  31. {
  32. "name": "分支1",
  33. "rules": rules if rules is not None else [],
  34. "when": when,
  35. "isDefault": False,
  36. "child": {
  37. "type": "approval",
  38. "name": "审批A",
  39. "approverConfig": {"assigneeType": "dept_leader"},
  40. "child": {"type": "end", "name": "结束"},
  41. },
  42. },
  43. {
  44. "name": "默认",
  45. "rules": [],
  46. "isDefault": True,
  47. "child": {
  48. "type": "approval",
  49. "name": "审批B",
  50. "approverConfig": {"assigneeType": "dept_leader"},
  51. "child": {"type": "end", "name": "结束"},
  52. },
  53. },
  54. ],
  55. },
  56. }
  57. class TestOaPublishValidate(unittest.TestCase):
  58. def test_count_fillable_fields(self):
  59. schema = {
  60. "rule": [
  61. {"type": "html", "props": {"innerHTML": "说明"}},
  62. {"type": "input", "field": "title", "title": "标题"},
  63. {
  64. "type": "fcRow",
  65. "children": [
  66. {"type": "inputNumber", "field": "days", "title": "天数"},
  67. ],
  68. },
  69. ]
  70. }
  71. self.assertEqual(count_fillable_fields(schema), 2)
  72. self.assertEqual(count_fillable_fields({"rule": []}), 0)
  73. def test_flow_ok(self):
  74. self.assertEqual(validate_flow_tree(_linear_ok_flow()), [])
  75. def test_flow_missing_end(self):
  76. tree = {
  77. "type": "start",
  78. "name": "发起人",
  79. "child": {
  80. "type": "approval",
  81. "name": "审批",
  82. "approverConfig": {"assigneeType": "users", "userIds": ["u1"]},
  83. },
  84. }
  85. errs = validate_flow_tree(tree)
  86. self.assertTrue(any("结束" in e or "后续" in e for e in errs))
  87. def test_approval_users_empty_rejected(self):
  88. tree = {
  89. "type": "start",
  90. "child": {
  91. "type": "approval",
  92. "name": "审批",
  93. "approverConfig": {"assigneeType": "users", "userIds": []},
  94. "child": {"type": "end"},
  95. },
  96. }
  97. errs = validate_flow_tree(tree)
  98. self.assertTrue(any("指定成员" in e for e in errs))
  99. def test_condition_branch_incomplete(self):
  100. tree = {
  101. "type": "start",
  102. "child": {
  103. "type": "condition",
  104. "name": "条件",
  105. "conditionGroups": [{"name": "大于3天", "rules": [], "child": None}],
  106. },
  107. }
  108. errs = validate_flow_tree(tree)
  109. self.assertTrue(any("下游" in e or "结束" in e for e in errs))
  110. def test_draft_requires_fillable_and_assignee(self):
  111. db = MagicMock()
  112. with patch(
  113. "app.services.oa_publish.validate_oa_roles_referenced",
  114. return_value=[],
  115. ):
  116. errs = validate_draft_for_publish(
  117. db,
  118. tenant_no="zkxw",
  119. form_schema={"rule": [{"type": "html"}]},
  120. flow_tree={
  121. "type": "start",
  122. "child": {
  123. "type": "approval",
  124. "approverConfig": {"assigneeType": "users", "userIds": []},
  125. "child": {"type": "end"},
  126. },
  127. },
  128. )
  129. self.assertTrue(any("可填字段" in e for e in errs))
  130. self.assertTrue(any("指定成员" in e for e in errs))
  131. def test_valid_draft_passes_form_and_flow(self):
  132. db = MagicMock()
  133. with patch(
  134. "app.services.oa_publish.validate_oa_roles_referenced",
  135. return_value=[],
  136. ):
  137. errs = validate_draft_for_publish(
  138. db,
  139. tenant_no="zkxw",
  140. form_schema={"rule": [{"type": "input", "field": "reason"}]},
  141. flow_tree=_linear_ok_flow(),
  142. )
  143. self.assertEqual(errs, [])
  144. def test_unknown_control_type_rejected(self):
  145. errs = validate_control_types(
  146. {"rule": [{"type": "aiMagicBox", "field": "x"}, {"type": "input", "field": "y"}]}
  147. )
  148. self.assertTrue(any("aiMagicBox" in e for e in errs))
  149. def test_upload_is_allowed(self):
  150. self.assertEqual(
  151. validate_control_types(
  152. {"rule": [{"type": "upload", "field": "files", "title": "上传"}]}
  153. ),
  154. [],
  155. )
  156. self.assertEqual(
  157. count_fillable_fields(
  158. {"rule": [{"type": "upload", "field": "files", "title": "上传"}]}
  159. ),
  160. 1,
  161. )
  162. class TestConditionPublish(unittest.TestCase):
  163. def test_when_only_rejected(self):
  164. errs = validate_flow_tree(_condition_flow(rules=[], when="{days} > 3"))
  165. self.assertTrue(any("可视化" in e or "重新配置" in e or "rules" in e for e in errs))
  166. def test_rules_empty_non_default_rejected(self):
  167. errs = validate_flow_tree(_condition_flow(rules=[]))
  168. self.assertTrue(any("条件" in e for e in errs))
  169. def test_rules_ok(self):
  170. errs = validate_flow_tree(
  171. _condition_flow(rules=[{"field": "days", "op": ">", "value": 3}])
  172. )
  173. self.assertEqual(errs, [])
  174. def test_invalid_field_reference(self):
  175. db = MagicMock()
  176. with patch(
  177. "app.services.oa_publish.validate_oa_roles_referenced",
  178. return_value=[],
  179. ):
  180. errs = validate_draft_for_publish(
  181. db,
  182. tenant_no="zkxw",
  183. form_schema={
  184. "rule": [
  185. {
  186. "type": "inputNumber",
  187. "field": "days",
  188. "title": "天数",
  189. "useInCondition": True,
  190. "$required": True,
  191. }
  192. ]
  193. },
  194. flow_tree=_condition_flow(
  195. rules=[{"field": "other", "op": ">", "value": 1}]
  196. ),
  197. )
  198. self.assertTrue(any("无效字段" in e or "other" in e for e in errs))
  199. def test_field_not_flagged_rejected(self):
  200. db = MagicMock()
  201. with patch(
  202. "app.services.oa_publish.validate_oa_roles_referenced",
  203. return_value=[],
  204. ):
  205. errs = validate_draft_for_publish(
  206. db,
  207. tenant_no="zkxw",
  208. form_schema={
  209. "rule": [
  210. {
  211. "type": "inputNumber",
  212. "field": "days",
  213. "title": "天数",
  214. "useInCondition": False,
  215. }
  216. ]
  217. },
  218. flow_tree=_condition_flow(
  219. rules=[{"field": "days", "op": ">", "value": 3}]
  220. ),
  221. )
  222. self.assertTrue(any("无效字段" in e or "参与" in e for e in errs))
  223. if __name__ == "__main__":
  224. unittest.main()