|
7 | 7 | from uipath.platform import UiPathApiConfig, UiPathExecutionContext |
8 | 8 | from uipath.platform.action_center import Task |
9 | 9 | from uipath.platform.action_center._tasks_service import TasksService |
| 10 | +from uipath.platform.action_center.tasks import TaskRecipient, TaskRecipientType |
10 | 11 | from uipath.platform.common.constants import HEADER_USER_AGENT |
11 | 12 |
|
12 | 13 |
|
@@ -186,6 +187,167 @@ def test_create_with_assignee( |
186 | 187 | assert action.title == "Test Action" |
187 | 188 |
|
188 | 189 |
|
| 190 | +def _mock_app_lookup_and_create( |
| 191 | + httpx_mock: HTTPXMock, |
| 192 | + base_url: str, |
| 193 | + org: str, |
| 194 | + tenant: str, |
| 195 | + monkeypatch: pytest.MonkeyPatch, |
| 196 | +) -> None: |
| 197 | + """Common httpx mock setup for app lookup + task creation + assign.""" |
| 198 | + monkeypatch.setenv("UIPATH_TENANT_ID", "test-tenant-id") |
| 199 | + httpx_mock.add_response( |
| 200 | + url=f"{base_url}{org}/apps_/default/api/v1/default/deployed-action-apps-schemas?search=test-app&filterByDeploymentTitle=true", |
| 201 | + status_code=200, |
| 202 | + json={ |
| 203 | + "deployed": [ |
| 204 | + { |
| 205 | + "systemName": "test-app", |
| 206 | + "deploymentTitle": "test-app", |
| 207 | + "actionSchema": { |
| 208 | + "key": "test-key", |
| 209 | + "inputs": [], |
| 210 | + "outputs": [], |
| 211 | + "inOuts": [], |
| 212 | + "outcomes": [], |
| 213 | + }, |
| 214 | + "deploymentFolder": { |
| 215 | + "fullyQualifiedName": "test-folder-path", |
| 216 | + "key": "test-folder-key", |
| 217 | + }, |
| 218 | + } |
| 219 | + ] |
| 220 | + }, |
| 221 | + ) |
| 222 | + httpx_mock.add_response( |
| 223 | + url=f"{base_url}{org}{tenant}/orchestrator_/tasks/AppTasks/CreateAppTask", |
| 224 | + status_code=200, |
| 225 | + json={"id": 1, "title": "Test Action"}, |
| 226 | + ) |
| 227 | + httpx_mock.add_response( |
| 228 | + url=f"{base_url}{org}{tenant}/orchestrator_/odata/Tasks/UiPath.Server.Configuration.OData.AssignTasks", |
| 229 | + status_code=200, |
| 230 | + json={}, |
| 231 | + ) |
| 232 | + |
| 233 | + |
| 234 | +def _assign_request_payload(httpx_mock: HTTPXMock) -> dict[str, Any]: |
| 235 | + """Return the parsed JSON body of the last AssignTasks request captured by the mock.""" |
| 236 | + assign_request = next( |
| 237 | + req |
| 238 | + for req in reversed(httpx_mock.get_requests()) |
| 239 | + if "AssignTasks" in str(req.url) |
| 240 | + ) |
| 241 | + return json.loads(assign_request.content) |
| 242 | + |
| 243 | + |
| 244 | +class TestAssignTaskSpec: |
| 245 | + """Tests for the task-assignment payload built by `_assign_task_spec`.""" |
| 246 | + |
| 247 | + def test_assign_workload_recipient_uses_workload_criteria_with_group( |
| 248 | + self, |
| 249 | + httpx_mock: HTTPXMock, |
| 250 | + service: TasksService, |
| 251 | + base_url: str, |
| 252 | + org: str, |
| 253 | + tenant: str, |
| 254 | + monkeypatch: pytest.MonkeyPatch, |
| 255 | + ) -> None: |
| 256 | + _mock_app_lookup_and_create(httpx_mock, base_url, org, tenant, monkeypatch) |
| 257 | + |
| 258 | + service.create( |
| 259 | + title="Test Action", |
| 260 | + app_name="test-app", |
| 261 | + data={"x": 1}, |
| 262 | + recipient=TaskRecipient( |
| 263 | + type=TaskRecipientType.WORKLOAD, |
| 264 | + value="Support Team", |
| 265 | + displayName="Support Team", |
| 266 | + ), |
| 267 | + ) |
| 268 | + |
| 269 | + payload = _assign_request_payload(httpx_mock) |
| 270 | + assert payload == { |
| 271 | + "taskAssignments": [ |
| 272 | + { |
| 273 | + "taskId": 1, |
| 274 | + "assignmentCriteria": "Workload", |
| 275 | + "assigneeNamesOrEmails": ["Support Team"], |
| 276 | + } |
| 277 | + ] |
| 278 | + } |
| 279 | + |
| 280 | + def test_assign_round_robin_recipient_uses_round_robin_criteria( |
| 281 | + self, |
| 282 | + httpx_mock: HTTPXMock, |
| 283 | + service: TasksService, |
| 284 | + base_url: str, |
| 285 | + org: str, |
| 286 | + tenant: str, |
| 287 | + monkeypatch: pytest.MonkeyPatch, |
| 288 | + ) -> None: |
| 289 | + _mock_app_lookup_and_create(httpx_mock, base_url, org, tenant, monkeypatch) |
| 290 | + |
| 291 | + service.create( |
| 292 | + title="Test Action", |
| 293 | + app_name="test-app", |
| 294 | + data={"x": 1}, |
| 295 | + recipient=TaskRecipient( |
| 296 | + type=TaskRecipientType.ROUND_ROBIN, |
| 297 | + value="Support Team", |
| 298 | + displayName="Support Team", |
| 299 | + ), |
| 300 | + ) |
| 301 | + |
| 302 | + payload = _assign_request_payload(httpx_mock) |
| 303 | + assert payload == { |
| 304 | + "taskAssignments": [ |
| 305 | + { |
| 306 | + "taskId": 1, |
| 307 | + "assignmentCriteria": "RoundRobin", |
| 308 | + "assigneeNamesOrEmails": ["Support Team"], |
| 309 | + } |
| 310 | + ] |
| 311 | + } |
| 312 | + |
| 313 | + def test_assign_workload_with_multiple_emails_uses_values_list( |
| 314 | + self, |
| 315 | + httpx_mock: HTTPXMock, |
| 316 | + service: TasksService, |
| 317 | + base_url: str, |
| 318 | + org: str, |
| 319 | + tenant: str, |
| 320 | + monkeypatch: pytest.MonkeyPatch, |
| 321 | + ) -> None: |
| 322 | + """Custom-assignees path: Workload criteria with a list of emails.""" |
| 323 | + _mock_app_lookup_and_create(httpx_mock, base_url, org, tenant, monkeypatch) |
| 324 | + |
| 325 | + service.create( |
| 326 | + title="Test Action", |
| 327 | + app_name="test-app", |
| 328 | + data={"x": 1}, |
| 329 | + recipient=TaskRecipient( |
| 330 | + type=TaskRecipientType.WORKLOAD, |
| 331 | + value="alice@example.com", |
| 332 | + values=["alice@example.com", "bob@example.com"], |
| 333 | + ), |
| 334 | + ) |
| 335 | + |
| 336 | + payload = _assign_request_payload(httpx_mock) |
| 337 | + assert payload == { |
| 338 | + "taskAssignments": [ |
| 339 | + { |
| 340 | + "taskId": 1, |
| 341 | + "assignmentCriteria": "Workload", |
| 342 | + "assigneeNamesOrEmails": [ |
| 343 | + "alice@example.com", |
| 344 | + "bob@example.com", |
| 345 | + ], |
| 346 | + } |
| 347 | + ] |
| 348 | + } |
| 349 | + |
| 350 | + |
189 | 351 | def _make_deployed_app( |
190 | 352 | name: str, |
191 | 353 | folder_path: str, |
|
0 commit comments