Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 1df3344

Browse files
authoredSep 18, 2024··
#65: Add integration tests for essential logic (#67)
* Change date params type for search ReportTimeEntry method * Add check for too old date params for listing TimeEntries
1 parent 58637f1 commit 1df3344

11 files changed

+334
-51
lines changed
 

‎tests/factories/time_entry.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ def time_entry_response_factory(
9696
"duronly": fake.boolean(),
9797
"id": fake.random_number(digits=11, fix_len=True),
9898
"permissions": None,
99-
"project_id": project_id or fake.random_int() if fake.boolean() else None,
99+
"project_id": project_id or fake.random_int(),
100100
"server_deleted_at": (
101101
fake.date_time_this_month(tzinfo=tz).isoformat(timespec="seconds")
102102
if fake.boolean()
@@ -106,7 +106,7 @@ def time_entry_response_factory(
106106
"stop": stop or _datetime_repr_factory(tz),
107107
"tag_ids": tag_ids or [],
108108
"tags": tags or [],
109-
"task_id": task_id or fake.random_int() if fake.boolean() else None,
109+
"task_id": task_id or fake.random_int(),
110110
"user_id": user_id or fake.random_int(),
111111
"workspace_id": workspace_id,
112112
}

‎tests/integration/conftest.py

+9
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import pytest
88
from toggl_python.auth import TokenAuth
9+
from toggl_python.entities.report_time_entry import ReportTimeEntry
910
from toggl_python.entities.user import CurrentUser
1011
from toggl_python.entities.workspace import Workspace
1112

@@ -30,6 +31,14 @@ def i_authed_workspace() -> Workspace:
3031
return Workspace(auth=auth)
3132

3233

34+
@pytest.fixture(scope="session")
35+
def i_authed_report_time_entry() -> ReportTimeEntry:
36+
token = os.environ["TOGGL_TOKEN"]
37+
auth = TokenAuth(token=token)
38+
39+
return ReportTimeEntry(auth=auth)
40+
41+
3342
@pytest.fixture()
3443
def me_response(i_authed_user: CurrentUser) -> MeResponse:
3544
return i_authed_user.me()

‎tests/integration/test_project.py

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from __future__ import annotations
2+
3+
import os
4+
from typing import TYPE_CHECKING
5+
6+
from toggl_python.schemas.project import ProjectResponse
7+
8+
# Necessary to mark all tests in module as integration
9+
from tests.integration import pytestmark # noqa: F401 - imported but unused
10+
11+
12+
if TYPE_CHECKING:
13+
from toggl_python.entities.workspace import Workspace
14+
15+
16+
def test_get_projects__without_query_params(i_authed_workspace: Workspace) -> None:
17+
# Later Create project and init and delete it at the end
18+
# Now this actions are not implemented
19+
workspace_id = int(os.environ["WORKSPACE_ID"])
20+
expected_result = set(ProjectResponse.model_fields.keys())
21+
22+
result = i_authed_workspace.get_projects(workspace_id)
23+
24+
assert result[0].model_fields_set == expected_result
25+
26+
27+
def test_get_project_by_id(i_authed_workspace: Workspace) -> None:
28+
# Later Create project and init and delete it at the end
29+
# Now this actions are not implemented
30+
workspace_id = int(os.environ["WORKSPACE_ID"])
31+
project_id = int(os.environ["PROJECT_ID"])
32+
expected_result = set(ProjectResponse.model_fields.keys())
33+
34+
result = i_authed_workspace.get_project(workspace_id, project_id)
35+
36+
assert result.model_fields_set == expected_result
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
from __future__ import annotations
2+
3+
import os
4+
from datetime import timedelta
5+
from typing import TYPE_CHECKING
6+
7+
import pytest
8+
from toggl_python.schemas.report_time_entry import (
9+
SearchReportTimeEntriesResponse,
10+
)
11+
12+
from tests.conftest import fake
13+
14+
# Necessary to mark all tests in module as integration
15+
from tests.integration import pytestmark # noqa: F401 - imported but unused
16+
17+
18+
if TYPE_CHECKING:
19+
from toggl_python.entities.report_time_entry import ReportTimeEntry
20+
from toggl_python.entities.workspace import Workspace
21+
22+
23+
try:
24+
import zoneinfo
25+
except ImportError:
26+
from backports import zoneinfo
27+
28+
29+
@pytest.mark.parametrize(
30+
argnames="use_dates_repr",
31+
argvalues=(True, False),
32+
ids=("str date arguments", "date date arguments"),
33+
)
34+
def test_search_report_time_entries__with_start_and_end_dates(
35+
use_dates_repr: bool,
36+
i_authed_report_time_entry: ReportTimeEntry,
37+
i_authed_workspace: Workspace,
38+
) -> None:
39+
workspace_id = int(os.environ["WORKSPACE_ID"])
40+
timezone_name = fake.timezone()
41+
tz = zoneinfo.ZoneInfo(timezone_name)
42+
start_date = fake.date_this_decade()
43+
delta = fake.random_int(min=1, max=364)
44+
end_date = start_date + timedelta(days=delta)
45+
time_entry = i_authed_workspace.create_time_entry(
46+
workspace_id,
47+
start_datetime=fake.date_time_between_dates(start_date, end_date, tzinfo=tz),
48+
created_with=fake.word(),
49+
)
50+
51+
expected_result = set(SearchReportTimeEntriesResponse.model_fields.keys())
52+
53+
result = i_authed_report_time_entry.search(
54+
workspace_id,
55+
start_date=start_date.isoformat() if use_dates_repr else start_date,
56+
end_date=end_date.isoformat() if use_dates_repr else end_date,
57+
)
58+
59+
assert result[0].model_fields_set == expected_result
60+
61+
_ = i_authed_workspace.delete_time_entry(workspace_id, time_entry.id)
62+
63+
64+
def test_search_report_time_entries__not_found(
65+
i_authed_report_time_entry: ReportTimeEntry,
66+
) -> None:
67+
workspace_id = int(os.environ["WORKSPACE_ID"])
68+
# Set explicit date range to avoid finding unexpected existing test TimeEntries
69+
time_entry_start_date = fake.date_between(start_date="-15y", end_date="-2y")
70+
delta = fake.random_int(min=1, max=364)
71+
end_date = time_entry_start_date + timedelta(days=delta)
72+
start_date = fake.date_between_dates(time_entry_start_date, end_date)
73+
74+
result = i_authed_report_time_entry.search(
75+
workspace_id,
76+
start_date=start_date,
77+
end_date=end_date,
78+
)
79+
80+
assert result == []

‎tests/integration/test_time_entry.py

+69
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
from __future__ import annotations
22

33
import os
4+
from datetime import timedelta
45
from typing import TYPE_CHECKING
56

67
from toggl_python.schemas.time_entry import MeTimeEntryResponse
78

9+
from tests.conftest import fake
810
from tests.factories.time_entry import (
911
time_entry_extended_request_factory,
1012
time_entry_request_factory,
@@ -14,7 +16,14 @@
1416
from tests.integration import pytestmark # noqa: F401 - imported but unused
1517

1618

19+
try:
20+
import zoneinfo
21+
except ImportError:
22+
from backports import zoneinfo
23+
24+
1725
if TYPE_CHECKING:
26+
from toggl_python.entities.user import CurrentUser
1827
from toggl_python.entities.workspace import Workspace
1928

2029

@@ -60,3 +69,63 @@ def test_create_time_entry__all_fields(i_authed_workspace: Workspace) -> None:
6069
assert result.model_fields_set == expected_result
6170

6271
_ = i_authed_workspace.delete_time_entry(workspace_id=workspace_id, time_entry_id=result.id)
72+
73+
74+
def test_list_time_entries__with_start_and_end_date__datetime(
75+
i_authed_user: CurrentUser, i_authed_workspace: Workspace
76+
) -> None:
77+
workspace_id = int(os.environ["WORKSPACE_ID"])
78+
timezone_name = fake.timezone()
79+
tz = zoneinfo.ZoneInfo(timezone_name)
80+
start_date = fake.date_time_this_month(tzinfo=tz, before_now=True)
81+
delta = fake.random_int(min=1, max=999999)
82+
end_date = start_date + timedelta(seconds=delta)
83+
time_entry = i_authed_workspace.create_time_entry(
84+
workspace_id,
85+
start_datetime=fake.date_time_between_dates(start_date, end_date, tzinfo=tz),
86+
created_with=fake.word(),
87+
)
88+
89+
expected_result = set(MeTimeEntryResponse.model_fields.keys())
90+
91+
result = i_authed_user.get_time_entries(start_date=start_date, end_date=end_date)
92+
93+
assert result[0].model_fields_set == expected_result
94+
95+
_ = i_authed_workspace.delete_time_entry(workspace_id, time_entry.id)
96+
97+
98+
def test_list_time_entries__with_start_and_end_date__str(
99+
i_authed_user: CurrentUser, i_authed_workspace: Workspace
100+
) -> None:
101+
workspace_id = int(os.environ["WORKSPACE_ID"])
102+
start_date = fake.date_this_month(before_today=True)
103+
delta = fake.random_int(min=1, max=999)
104+
end_date = start_date + timedelta(days=delta)
105+
timezone_name = fake.timezone()
106+
tz = zoneinfo.ZoneInfo(timezone_name)
107+
time_entry = i_authed_workspace.create_time_entry(
108+
workspace_id,
109+
start_datetime=fake.date_time_between_dates(start_date, end_date, tzinfo=tz),
110+
created_with=fake.word(),
111+
)
112+
113+
expected_result = set(MeTimeEntryResponse.model_fields.keys())
114+
115+
result = i_authed_user.get_time_entries(
116+
start_date=start_date.isoformat(), end_date=end_date.isoformat()
117+
)
118+
119+
assert result[0].model_fields_set == expected_result
120+
121+
_ = i_authed_workspace.delete_time_entry(workspace_id, time_entry.id)
122+
123+
124+
def test_list_time_entries__no_results(i_authed_user: CurrentUser) -> None:
125+
start_date = fake.date_time_between(start_date="-6m", end_date="-3m")
126+
delta = fake.random_int(min=0, max=999)
127+
end_date = start_date + timedelta(days=delta)
128+
129+
result = i_authed_user.get_time_entries(start_date=start_date, end_date=end_date)
130+
131+
assert result == []

‎tests/integration/test_workspace.py

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
from __future__ import annotations
2+
3+
import os
4+
from typing import TYPE_CHECKING
5+
6+
from toggl_python.schemas.workspace import WorkspaceResponse
7+
8+
# Necessary to mark all tests in module as integration
9+
from tests.integration import pytestmark # noqa: F401 - imported but unused
10+
11+
12+
if TYPE_CHECKING:
13+
from toggl_python.entities.workspace import Workspace
14+
15+
16+
def test_get_workspace_by_id(i_authed_workspace: Workspace) -> None:
17+
workspace_id = int(os.environ["WORKSPACE_ID"])
18+
expected_result = set(WorkspaceResponse.model_fields.keys())
19+
20+
result = i_authed_workspace.get(workspace_id)
21+
22+
assert result.model_fields_set == expected_result
23+
24+
25+
def test_get_workspaces__without_query_params(i_authed_workspace: Workspace)-> None:
26+
expected_result = set(WorkspaceResponse.model_fields.keys())
27+
28+
result = i_authed_workspace.list()
29+
30+
assert result[0].model_fields_set == expected_result

‎tests/test_report_time_entry.py

+26-20
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
from __future__ import annotations
22

3-
from datetime import datetime, timezone
4-
from typing import TYPE_CHECKING, Dict, Union
3+
from typing import TYPE_CHECKING, Union
54

65
import pytest
76
from httpx import Response
87
from pydantic import ValidationError
98
from toggl_python.schemas.report_time_entry import SearchReportTimeEntriesResponse
109

10+
from tests.conftest import fake
1111
from tests.responses.report_time_entry_post import SEARCH_REPORT_TIME_ENTRY_RESPONSE
1212

1313

1414
if TYPE_CHECKING:
15+
from datetime import date
16+
1517
from respx import MockRouter
1618
from toggl_python.entities.report_time_entry import ReportTimeEntry
1719

@@ -26,29 +28,30 @@ def test_search_report_time_entries__without_params(
2628

2729

2830
@pytest.mark.parametrize(
29-
argnames="request_body, start_date, end_date",
31+
argnames="start_date, end_date",
3032
argvalues=(
3133
(
32-
{"start_date": "2020-06-10T00:00:00+00:00", "end_date": "2020-10-01T00:00:00+00:00"},
33-
datetime(2020, 6, 10, tzinfo=timezone.utc),
34-
datetime(2020, 10, 1, tzinfo=timezone.utc),
34+
fake.date_this_decade(before_today=True).isoformat(),
35+
fake.date_this_decade(before_today=True).isoformat(),
3536
),
3637
(
37-
{"start_date": "2023-09-12T00:00:00-03:00", "end_date": "2023-10-12T00:00:00-01:00"},
38-
"2023-09-12T00:00:00-03:00",
39-
"2023-10-12T00:00:00-01:00",
38+
fake.date_this_decade(before_today=True),
39+
fake.date_this_decade(before_today=True),
4040
),
4141
),
4242
)
4343
def test_search_report_time_entries__with_start_and_end_date(
44-
request_body: Dict[str, str],
45-
start_date: Union[datetime, str],
46-
end_date: Union[datetime, str],
44+
start_date: Union[date, str],
45+
end_date: Union[date, str],
4746
response_report_mock: MockRouter,
4847
authed_report_time_entry: ReportTimeEntry,
4948
) -> None:
5049
fake_workspace_id = 123
5150
uri = f"/{fake_workspace_id}/search/time_entries"
51+
request_body = {
52+
"start_date": start_date if isinstance(start_date, str) else start_date.isoformat(),
53+
"end_date": end_date if isinstance(end_date, str) else end_date.isoformat(),
54+
}
5255
mocked_route = response_report_mock.post(uri, json=request_body).mock(
5356
return_value=Response(status_code=200, json=[SEARCH_REPORT_TIME_ENTRY_RESPONSE]),
5457
)
@@ -57,7 +60,9 @@ def test_search_report_time_entries__with_start_and_end_date(
5760
]
5861

5962
result = authed_report_time_entry.search(
60-
workspace_id=fake_workspace_id, start_date=start_date, end_date=end_date
63+
workspace_id=fake_workspace_id,
64+
start_date=start_date,
65+
end_date=end_date,
6166
)
6267

6368
assert mocked_route.called is True
@@ -68,14 +73,15 @@ def test_search_report_time_entries__with_all_params(
6873
response_report_mock: MockRouter,
6974
authed_report_time_entry: ReportTimeEntry,
7075
) -> None:
71-
fake_workspace_id = 123
76+
fake_workspace_id = fake.random_int(min=1)
77+
page_size = fake.random_int(min=1, max=100)
7278
request_body = {
73-
"start_date": "2021-12-20T00:00:00+00:00",
74-
"end_date": "2021-12-30T00:00:00+00:00",
75-
"user_ids": [30809356],
76-
"project_ids": [202793182],
77-
"page_size": 10,
78-
"first_row_number": 11,
79+
"start_date": fake.date(),
80+
"end_date": fake.date(),
81+
"user_ids": [fake.random_int()],
82+
"project_ids": [fake.random_int()],
83+
"page_size": page_size,
84+
"first_row_number": page_size + 1,
7985
}
8086
uri = f"/{fake_workspace_id}/search/time_entries"
8187
mocked_route = response_report_mock.post(uri, json=request_body).mock(

0 commit comments

Comments
 (0)
Please sign in to comment.