|
1 | 1 | from __future__ import annotations |
2 | 2 |
|
3 | 3 | from datetime import date, datetime, timezone |
4 | | -from random import randint |
5 | 4 | from typing import TYPE_CHECKING, Dict, List, Union |
6 | 5 | from unittest.mock import Mock, patch |
7 | 6 |
|
8 | 7 | import pytest |
9 | 8 | from httpx import Response |
10 | 9 | from pydantic import ValidationError |
11 | 10 | from toggl_python.exceptions import BadRequest |
| 11 | +from toggl_python.schemas.base import BulkEditOperation, BulkEditOperations, BulkEditResponse |
12 | 12 | from toggl_python.schemas.time_entry import ( |
13 | 13 | BulkEditTimeEntriesFieldNames, |
14 | | - BulkEditTimeEntriesOperation, |
15 | | - BulkEditTimeEntriesOperations, |
16 | | - BulkEditTimeEntriesResponse, |
17 | 14 | MeTimeEntryResponse, |
18 | 15 | MeTimeEntryWithMetaResponse, |
19 | 16 | MeWebTimerResponse, |
20 | 17 | ) |
21 | 18 |
|
22 | 19 | from tests.conftest import fake |
| 20 | +from tests.factories.base import bulk_edit_response_factory, datetime_repr_factory |
23 | 21 | from tests.factories.time_entry import ( |
24 | 22 | time_entry_extended_request_factory, |
25 | 23 | time_entry_request_factory, |
26 | 24 | time_entry_response_factory, |
27 | 25 | ) |
28 | 26 | from tests.responses.me_get import ME_WEB_TIMER_RESPONSE |
29 | 27 | from tests.responses.time_entry_get import ME_TIME_ENTRY_RESPONSE, ME_TIME_ENTRY_WITH_META_RESPONSE |
30 | | -from tests.responses.time_entry_put_and_patch import BULK_EDIT_TIME_ENTRIES_RESPONSE |
31 | 28 |
|
32 | 29 |
|
33 | 30 | if TYPE_CHECKING: |
@@ -119,6 +116,7 @@ def test_create_time_entry__invalid_start_stop_and_duration(authed_workspace: Wo |
119 | 116 | stop=request_body["stop"], |
120 | 117 | ) |
121 | 118 |
|
| 119 | + |
122 | 120 | def test_get_time_entry__without_query_params( |
123 | 121 | response_mock: MockRouter, authed_current_user: CurrentUser |
124 | 122 | ) -> None: |
@@ -419,68 +417,79 @@ def test_delete_time_entry__ok(response_mock: MockRouter, authed_workspace: Work |
419 | 417 |
|
420 | 418 |
|
421 | 419 | def test_bulk_edit_time_entries__too_much_ids(authed_workspace: Workspace) -> None: |
422 | | - workspace_id = 123 |
423 | | - time_entry_ids = [randint(100000, 999999) for _ in range(101)] # noqa: S311 |
424 | | - error_message = "Limit to max TimeEntry IDs exceeded. " |
| 420 | + workspace_id = fake.random_int() |
| 421 | + time_entry_ids = [fake.random_int() for _ in range(101)] |
| 422 | + error_message = "List should have at most 100 items after validation" |
425 | 423 |
|
426 | 424 | with pytest.raises(ValueError, match=error_message): |
427 | 425 | _ = authed_workspace.bulk_edit_time_entries(workspace_id, time_entry_ids, operations=[]) |
428 | 426 |
|
429 | 427 |
|
430 | 428 | def test_bulk_edit_time_entries__empty_time_entry_ids(authed_workspace: Workspace) -> None: |
431 | | - workspace_id = 123 |
432 | | - error_message = "Specify at least one TimeEntry ID" |
| 429 | + workspace_id = fake.random_int() |
| 430 | + error_message = "List should have at least 1 item after validation" |
433 | 431 |
|
434 | 432 | with pytest.raises(ValueError, match=error_message): |
435 | 433 | _ = authed_workspace.bulk_edit_time_entries(workspace_id, time_entry_ids=[], operations=[]) |
436 | 434 |
|
437 | 435 |
|
438 | 436 | def test_bulk_edit_time_entries__empty_operations(authed_workspace: Workspace) -> None: |
439 | | - workspace_id = 123 |
440 | | - time_entry_ids = [12345677] |
441 | | - error_message = "Specify at least one edit operation" |
| 437 | + workspace_id = fake.random_int() |
| 438 | + time_entry_ids = [fake.random_int()] |
| 439 | + error_message = "List should have at least 1 item after validation" |
442 | 440 |
|
443 | 441 | with pytest.raises(ValueError, match=error_message): |
444 | 442 | _ = authed_workspace.bulk_edit_time_entries(workspace_id, time_entry_ids, operations=[]) |
445 | 443 |
|
446 | 444 |
|
447 | 445 | @pytest.mark.parametrize( |
448 | | - argnames=("operation"), argvalues=[item.value for item in BulkEditTimeEntriesOperations] |
| 446 | + argnames=("operation"), argvalues=[item.value for item in BulkEditOperations] |
449 | 447 | ) |
450 | 448 | @pytest.mark.parametrize( |
451 | 449 | argnames=("field_name", "field_value"), |
452 | 450 | argvalues=[ |
453 | | - (BulkEditTimeEntriesFieldNames.billable.value, True), |
454 | | - (BulkEditTimeEntriesFieldNames.description.value, "updated description"), |
455 | | - (BulkEditTimeEntriesFieldNames.duration.value, -1), |
456 | | - (BulkEditTimeEntriesFieldNames.project_id.value, 757542305), |
457 | | - (BulkEditTimeEntriesFieldNames.shared_with_user_ids.value, [1243543643, 676586868]), |
458 | | - (BulkEditTimeEntriesFieldNames.start.value, datetime(2024, 5, 10, tzinfo=timezone.utc)), |
459 | | - (BulkEditTimeEntriesFieldNames.stop.value, datetime(2022, 4, 15, tzinfo=timezone.utc)), |
460 | | - (BulkEditTimeEntriesFieldNames.tag_ids.value, [24032, 354742502]), |
461 | | - (BulkEditTimeEntriesFieldNames.tags.value, ["new tag"]), |
462 | | - (BulkEditTimeEntriesFieldNames.task_id.value, 1593268409), |
463 | | - (BulkEditTimeEntriesFieldNames.user_id.value, 573250897), |
| 451 | + (BulkEditTimeEntriesFieldNames.billable.value, fake.boolean()), |
| 452 | + (BulkEditTimeEntriesFieldNames.description.value, fake.text(max_nb_chars=32)), |
| 453 | + (BulkEditTimeEntriesFieldNames.duration.value, fake.random_int(min=-1, max=128)), |
| 454 | + (BulkEditTimeEntriesFieldNames.project_id.value, fake.random_int()), |
| 455 | + ( |
| 456 | + BulkEditTimeEntriesFieldNames.shared_with_user_ids.value, |
| 457 | + [fake.random_int() for _ in range(fake.random_int(max=10))], |
| 458 | + ), |
| 459 | + (BulkEditTimeEntriesFieldNames.start.value, datetime_repr_factory()), |
| 460 | + (BulkEditTimeEntriesFieldNames.stop.value, datetime_repr_factory()), |
| 461 | + ( |
| 462 | + BulkEditTimeEntriesFieldNames.tag_ids.value, |
| 463 | + [fake.random_int() for _ in range(fake.random_int(max=10))], |
| 464 | + ), |
| 465 | + ( |
| 466 | + BulkEditTimeEntriesFieldNames.tags.value, |
| 467 | + [fake.word() for _ in range(fake.random_int(max=10))], |
| 468 | + ), |
| 469 | + (BulkEditTimeEntriesFieldNames.task_id.value, fake.random_int()), |
| 470 | + (BulkEditTimeEntriesFieldNames.user_id.value, fake.random_int()), |
464 | 471 | ], |
465 | 472 | ) |
466 | 473 | def test_bulk_edit_time_entries__ok( |
467 | 474 | field_name: BulkEditTimeEntriesFieldNames, |
468 | 475 | field_value: Union[str, int], |
469 | | - operation: BulkEditTimeEntriesOperations, |
| 476 | + operation: BulkEditOperations, |
470 | 477 | response_mock: MockRouter, |
471 | 478 | authed_workspace: Workspace, |
472 | 479 | ) -> None: |
473 | | - workspace_id = 123 |
474 | | - time_entry_ids = [98765, 43210] |
475 | | - edit_operation = BulkEditTimeEntriesOperation( |
| 480 | + workspace_id = fake.random_int() |
| 481 | + time_entry_ids = [fake.random_int() for _ in range(fake.random_int(min=1, max=8))] |
| 482 | + time_entry_ids_repr = ",".join(str(item) for item in time_entry_ids) |
| 483 | + edit_operation = BulkEditOperation( |
476 | 484 | operation=operation, field_name=field_name, field_value=field_value |
477 | 485 | ) |
| 486 | + response = bulk_edit_response_factory() |
478 | 487 | mocked_route = response_mock.patch( |
479 | | - f"/workspaces/{workspace_id}/time_entries/{time_entry_ids}" |
| 488 | + f"/workspaces/{workspace_id}/time_entries/{time_entry_ids_repr}" |
480 | 489 | ).mock( |
481 | | - return_value=Response(status_code=200, json=BULK_EDIT_TIME_ENTRIES_RESPONSE), |
| 490 | + return_value=Response(status_code=200, json=response), |
482 | 491 | ) |
483 | | - expected_result = BulkEditTimeEntriesResponse.model_validate(BULK_EDIT_TIME_ENTRIES_RESPONSE) |
| 492 | + expected_result = BulkEditResponse.model_validate(response) |
484 | 493 |
|
485 | 494 | result = authed_workspace.bulk_edit_time_entries( |
486 | 495 | workspace_id, time_entry_ids, operations=[edit_operation] |
|
0 commit comments