|
|
@@ -2,7 +2,12 @@
|
|
|
import unittest
|
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
|
|
-from app.services.oa_assignee import AssigneeResolveError, resolve_assignees
|
|
|
+from app.services.oa_assignee import (
|
|
|
+ AssigneeResolveError,
|
|
|
+ normalize_cc_assignee_types,
|
|
|
+ resolve_assignees,
|
|
|
+ resolve_cc_assignees,
|
|
|
+)
|
|
|
|
|
|
|
|
|
class TestOaAssignee(unittest.TestCase):
|
|
|
@@ -131,6 +136,101 @@ class TestOaAssignee(unittest.TestCase):
|
|
|
initiator_select_user_ids=[],
|
|
|
)
|
|
|
|
|
|
+ def test_normalize_prefers_assignee_types(self):
|
|
|
+ self.assertEqual(
|
|
|
+ normalize_cc_assignee_types(
|
|
|
+ {"assigneeTypes": ["users", "dept_leader", "users", "bad"], "assigneeType": "oa_roles"}
|
|
|
+ ),
|
|
|
+ ["users", "dept_leader"],
|
|
|
+ )
|
|
|
+
|
|
|
+ def test_normalize_fallback_single(self):
|
|
|
+ self.assertEqual(
|
|
|
+ normalize_cc_assignee_types({"assigneeType": "initiator_select"}),
|
|
|
+ ["initiator_select"],
|
|
|
+ )
|
|
|
+ self.assertEqual(normalize_cc_assignee_types({}), [])
|
|
|
+
|
|
|
+ def test_resolve_cc_union_dedupe(self):
|
|
|
+ db = MagicMock()
|
|
|
+ with patch(
|
|
|
+ "app.services.oa_assignee.resolve_assignees",
|
|
|
+ side_effect=[
|
|
|
+ (["u1", "u2"], {"resolve_source": "users"}),
|
|
|
+ (["u2", "u3"], {"resolve_source": "dept_leader"}),
|
|
|
+ ],
|
|
|
+ ) as mock_resolve:
|
|
|
+ users, meta = resolve_cc_assignees(
|
|
|
+ db,
|
|
|
+ tenant_no="zkxw",
|
|
|
+ assignee_config={
|
|
|
+ "assigneeTypes": ["users", "dept_leader"],
|
|
|
+ "userIds": ["u1", "u2"],
|
|
|
+ },
|
|
|
+ initiator_user_id="zhang",
|
|
|
+ initiator_dept_id="d1",
|
|
|
+ )
|
|
|
+ self.assertEqual(users, ["u1", "u2", "u3"])
|
|
|
+ self.assertEqual(meta.get("resolve_source"), "multi")
|
|
|
+ self.assertEqual(meta.get("sources"), ["users", "dept_leader"])
|
|
|
+ self.assertEqual(mock_resolve.call_count, 2)
|
|
|
+
|
|
|
+ def test_resolve_cc_skips_failed_strategy(self):
|
|
|
+ db = MagicMock()
|
|
|
+ with patch(
|
|
|
+ "app.services.oa_assignee.resolve_assignees",
|
|
|
+ side_effect=[
|
|
|
+ AssigneeResolveError("部门负责人未配置"),
|
|
|
+ (["u1"], {"resolve_source": "users"}),
|
|
|
+ ],
|
|
|
+ ):
|
|
|
+ users, meta = resolve_cc_assignees(
|
|
|
+ db,
|
|
|
+ tenant_no="zkxw",
|
|
|
+ assignee_config={
|
|
|
+ "assigneeTypes": ["dept_leader", "users"],
|
|
|
+ "userIds": ["u1"],
|
|
|
+ },
|
|
|
+ initiator_user_id="zhang",
|
|
|
+ initiator_dept_id="d1",
|
|
|
+ )
|
|
|
+ self.assertEqual(users, ["u1"])
|
|
|
+ self.assertEqual(meta.get("resolve_source"), "users")
|
|
|
+ self.assertEqual(len(meta.get("skipped") or []), 1)
|
|
|
+
|
|
|
+ def test_resolve_cc_all_fail_returns_empty(self):
|
|
|
+ db = MagicMock()
|
|
|
+ with patch(
|
|
|
+ "app.services.oa_assignee.resolve_assignees",
|
|
|
+ side_effect=AssigneeResolveError("fail"),
|
|
|
+ ):
|
|
|
+ users, meta = resolve_cc_assignees(
|
|
|
+ db,
|
|
|
+ tenant_no="zkxw",
|
|
|
+ assignee_config={"assigneeTypes": ["dept_leader"]},
|
|
|
+ initiator_user_id="zhang",
|
|
|
+ initiator_dept_id="d1",
|
|
|
+ )
|
|
|
+ self.assertEqual(users, [])
|
|
|
+ self.assertTrue(meta.get("skipped"))
|
|
|
+
|
|
|
+ def test_resolve_cc_legacy_single_type(self):
|
|
|
+ db = MagicMock()
|
|
|
+ with patch(
|
|
|
+ "app.services.oa_assignee.resolve_assignees",
|
|
|
+ return_value=(["u1"], {"resolve_source": "users"}),
|
|
|
+ ) as mock_resolve:
|
|
|
+ users, meta = resolve_cc_assignees(
|
|
|
+ db,
|
|
|
+ tenant_no="zkxw",
|
|
|
+ assignee_config={"assigneeType": "users", "userIds": ["u1"]},
|
|
|
+ initiator_user_id="zhang",
|
|
|
+ )
|
|
|
+ self.assertEqual(users, ["u1"])
|
|
|
+ self.assertEqual(meta.get("resolve_source"), "users")
|
|
|
+ single_cfg = mock_resolve.call_args.kwargs["assignee_config"]
|
|
|
+ self.assertEqual(single_cfg.get("assigneeType"), "users")
|
|
|
+
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
unittest.main()
|