Skip to content

Commit

Permalink
#65: Add integration tests for essential logic (#67)
Browse files Browse the repository at this point in the history
* Change date params type for search ReportTimeEntry method
* Add check for too old date params for listing TimeEntries
  • Loading branch information
nifadyev authored Sep 18, 2024
1 parent 58637f1 commit 1df3344
Show file tree
Hide file tree
Showing 11 changed files with 334 additions and 51 deletions.
4 changes: 2 additions & 2 deletions tests/factories/time_entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ def time_entry_response_factory(
"duronly": fake.boolean(),
"id": fake.random_number(digits=11, fix_len=True),
"permissions": None,
"project_id": project_id or fake.random_int() if fake.boolean() else None,
"project_id": project_id or fake.random_int(),
"server_deleted_at": (
fake.date_time_this_month(tzinfo=tz).isoformat(timespec="seconds")
if fake.boolean()
Expand All @@ -106,7 +106,7 @@ def time_entry_response_factory(
"stop": stop or _datetime_repr_factory(tz),
"tag_ids": tag_ids or [],
"tags": tags or [],
"task_id": task_id or fake.random_int() if fake.boolean() else None,
"task_id": task_id or fake.random_int(),
"user_id": user_id or fake.random_int(),
"workspace_id": workspace_id,
}
9 changes: 9 additions & 0 deletions tests/integration/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import pytest
from toggl_python.auth import TokenAuth
from toggl_python.entities.report_time_entry import ReportTimeEntry
from toggl_python.entities.user import CurrentUser
from toggl_python.entities.workspace import Workspace

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


@pytest.fixture(scope="session")
def i_authed_report_time_entry() -> ReportTimeEntry:
token = os.environ["TOGGL_TOKEN"]
auth = TokenAuth(token=token)

return ReportTimeEntry(auth=auth)


@pytest.fixture()
def me_response(i_authed_user: CurrentUser) -> MeResponse:
return i_authed_user.me()
Expand Down
36 changes: 36 additions & 0 deletions tests/integration/test_project.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from __future__ import annotations

import os
from typing import TYPE_CHECKING

from toggl_python.schemas.project import ProjectResponse

# Necessary to mark all tests in module as integration
from tests.integration import pytestmark # noqa: F401 - imported but unused


if TYPE_CHECKING:
from toggl_python.entities.workspace import Workspace


def test_get_projects__without_query_params(i_authed_workspace: Workspace) -> None:
# Later Create project and init and delete it at the end
# Now this actions are not implemented
workspace_id = int(os.environ["WORKSPACE_ID"])
expected_result = set(ProjectResponse.model_fields.keys())

result = i_authed_workspace.get_projects(workspace_id)

assert result[0].model_fields_set == expected_result


def test_get_project_by_id(i_authed_workspace: Workspace) -> None:
# Later Create project and init and delete it at the end
# Now this actions are not implemented
workspace_id = int(os.environ["WORKSPACE_ID"])
project_id = int(os.environ["PROJECT_ID"])
expected_result = set(ProjectResponse.model_fields.keys())

result = i_authed_workspace.get_project(workspace_id, project_id)

assert result.model_fields_set == expected_result
80 changes: 80 additions & 0 deletions tests/integration/test_report_time_entry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
from __future__ import annotations

import os
from datetime import timedelta
from typing import TYPE_CHECKING

import pytest
from toggl_python.schemas.report_time_entry import (
SearchReportTimeEntriesResponse,
)

from tests.conftest import fake

# Necessary to mark all tests in module as integration
from tests.integration import pytestmark # noqa: F401 - imported but unused


if TYPE_CHECKING:
from toggl_python.entities.report_time_entry import ReportTimeEntry
from toggl_python.entities.workspace import Workspace


try:
import zoneinfo
except ImportError:
from backports import zoneinfo


@pytest.mark.parametrize(
argnames="use_dates_repr",
argvalues=(True, False),
ids=("str date arguments", "date date arguments"),
)
def test_search_report_time_entries__with_start_and_end_dates(
use_dates_repr: bool,
i_authed_report_time_entry: ReportTimeEntry,
i_authed_workspace: Workspace,
) -> None:
workspace_id = int(os.environ["WORKSPACE_ID"])
timezone_name = fake.timezone()
tz = zoneinfo.ZoneInfo(timezone_name)
start_date = fake.date_this_decade()
delta = fake.random_int(min=1, max=364)
end_date = start_date + timedelta(days=delta)
time_entry = i_authed_workspace.create_time_entry(
workspace_id,
start_datetime=fake.date_time_between_dates(start_date, end_date, tzinfo=tz),
created_with=fake.word(),
)

expected_result = set(SearchReportTimeEntriesResponse.model_fields.keys())

result = i_authed_report_time_entry.search(
workspace_id,
start_date=start_date.isoformat() if use_dates_repr else start_date,
end_date=end_date.isoformat() if use_dates_repr else end_date,
)

assert result[0].model_fields_set == expected_result

_ = i_authed_workspace.delete_time_entry(workspace_id, time_entry.id)


def test_search_report_time_entries__not_found(
i_authed_report_time_entry: ReportTimeEntry,
) -> None:
workspace_id = int(os.environ["WORKSPACE_ID"])
# Set explicit date range to avoid finding unexpected existing test TimeEntries
time_entry_start_date = fake.date_between(start_date="-15y", end_date="-2y")
delta = fake.random_int(min=1, max=364)
end_date = time_entry_start_date + timedelta(days=delta)
start_date = fake.date_between_dates(time_entry_start_date, end_date)

result = i_authed_report_time_entry.search(
workspace_id,
start_date=start_date,
end_date=end_date,
)

assert result == []
69 changes: 69 additions & 0 deletions tests/integration/test_time_entry.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
from __future__ import annotations

import os
from datetime import timedelta
from typing import TYPE_CHECKING

from toggl_python.schemas.time_entry import MeTimeEntryResponse

from tests.conftest import fake
from tests.factories.time_entry import (
time_entry_extended_request_factory,
time_entry_request_factory,
Expand All @@ -14,7 +16,14 @@
from tests.integration import pytestmark # noqa: F401 - imported but unused


try:
import zoneinfo
except ImportError:
from backports import zoneinfo


if TYPE_CHECKING:
from toggl_python.entities.user import CurrentUser
from toggl_python.entities.workspace import Workspace


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

_ = i_authed_workspace.delete_time_entry(workspace_id=workspace_id, time_entry_id=result.id)


def test_list_time_entries__with_start_and_end_date__datetime(
i_authed_user: CurrentUser, i_authed_workspace: Workspace
) -> None:
workspace_id = int(os.environ["WORKSPACE_ID"])
timezone_name = fake.timezone()
tz = zoneinfo.ZoneInfo(timezone_name)
start_date = fake.date_time_this_month(tzinfo=tz, before_now=True)
delta = fake.random_int(min=1, max=999999)
end_date = start_date + timedelta(seconds=delta)
time_entry = i_authed_workspace.create_time_entry(
workspace_id,
start_datetime=fake.date_time_between_dates(start_date, end_date, tzinfo=tz),
created_with=fake.word(),
)

expected_result = set(MeTimeEntryResponse.model_fields.keys())

result = i_authed_user.get_time_entries(start_date=start_date, end_date=end_date)

assert result[0].model_fields_set == expected_result

_ = i_authed_workspace.delete_time_entry(workspace_id, time_entry.id)


def test_list_time_entries__with_start_and_end_date__str(
i_authed_user: CurrentUser, i_authed_workspace: Workspace
) -> None:
workspace_id = int(os.environ["WORKSPACE_ID"])
start_date = fake.date_this_month(before_today=True)
delta = fake.random_int(min=1, max=999)
end_date = start_date + timedelta(days=delta)
timezone_name = fake.timezone()
tz = zoneinfo.ZoneInfo(timezone_name)
time_entry = i_authed_workspace.create_time_entry(
workspace_id,
start_datetime=fake.date_time_between_dates(start_date, end_date, tzinfo=tz),
created_with=fake.word(),
)

expected_result = set(MeTimeEntryResponse.model_fields.keys())

result = i_authed_user.get_time_entries(
start_date=start_date.isoformat(), end_date=end_date.isoformat()
)

assert result[0].model_fields_set == expected_result

_ = i_authed_workspace.delete_time_entry(workspace_id, time_entry.id)


def test_list_time_entries__no_results(i_authed_user: CurrentUser) -> None:
start_date = fake.date_time_between(start_date="-6m", end_date="-3m")
delta = fake.random_int(min=0, max=999)
end_date = start_date + timedelta(days=delta)

result = i_authed_user.get_time_entries(start_date=start_date, end_date=end_date)

assert result == []
30 changes: 30 additions & 0 deletions tests/integration/test_workspace.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from __future__ import annotations

import os
from typing import TYPE_CHECKING

from toggl_python.schemas.workspace import WorkspaceResponse

# Necessary to mark all tests in module as integration
from tests.integration import pytestmark # noqa: F401 - imported but unused


if TYPE_CHECKING:
from toggl_python.entities.workspace import Workspace


def test_get_workspace_by_id(i_authed_workspace: Workspace) -> None:
workspace_id = int(os.environ["WORKSPACE_ID"])
expected_result = set(WorkspaceResponse.model_fields.keys())

result = i_authed_workspace.get(workspace_id)

assert result.model_fields_set == expected_result


def test_get_workspaces__without_query_params(i_authed_workspace: Workspace)-> None:
expected_result = set(WorkspaceResponse.model_fields.keys())

result = i_authed_workspace.list()

assert result[0].model_fields_set == expected_result
46 changes: 26 additions & 20 deletions tests/test_report_time_entry.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
from __future__ import annotations

from datetime import datetime, timezone
from typing import TYPE_CHECKING, Dict, Union
from typing import TYPE_CHECKING, Union

import pytest
from httpx import Response
from pydantic import ValidationError
from toggl_python.schemas.report_time_entry import SearchReportTimeEntriesResponse

from tests.conftest import fake
from tests.responses.report_time_entry_post import SEARCH_REPORT_TIME_ENTRY_RESPONSE


if TYPE_CHECKING:
from datetime import date

from respx import MockRouter
from toggl_python.entities.report_time_entry import ReportTimeEntry

Expand All @@ -26,29 +28,30 @@ def test_search_report_time_entries__without_params(


@pytest.mark.parametrize(
argnames="request_body, start_date, end_date",
argnames="start_date, end_date",
argvalues=(
(
{"start_date": "2020-06-10T00:00:00+00:00", "end_date": "2020-10-01T00:00:00+00:00"},
datetime(2020, 6, 10, tzinfo=timezone.utc),
datetime(2020, 10, 1, tzinfo=timezone.utc),
fake.date_this_decade(before_today=True).isoformat(),
fake.date_this_decade(before_today=True).isoformat(),
),
(
{"start_date": "2023-09-12T00:00:00-03:00", "end_date": "2023-10-12T00:00:00-01:00"},
"2023-09-12T00:00:00-03:00",
"2023-10-12T00:00:00-01:00",
fake.date_this_decade(before_today=True),
fake.date_this_decade(before_today=True),
),
),
)
def test_search_report_time_entries__with_start_and_end_date(
request_body: Dict[str, str],
start_date: Union[datetime, str],
end_date: Union[datetime, str],
start_date: Union[date, str],
end_date: Union[date, str],
response_report_mock: MockRouter,
authed_report_time_entry: ReportTimeEntry,
) -> None:
fake_workspace_id = 123
uri = f"/{fake_workspace_id}/search/time_entries"
request_body = {
"start_date": start_date if isinstance(start_date, str) else start_date.isoformat(),
"end_date": end_date if isinstance(end_date, str) else end_date.isoformat(),
}
mocked_route = response_report_mock.post(uri, json=request_body).mock(
return_value=Response(status_code=200, json=[SEARCH_REPORT_TIME_ENTRY_RESPONSE]),
)
Expand All @@ -57,7 +60,9 @@ def test_search_report_time_entries__with_start_and_end_date(
]

result = authed_report_time_entry.search(
workspace_id=fake_workspace_id, start_date=start_date, end_date=end_date
workspace_id=fake_workspace_id,
start_date=start_date,
end_date=end_date,
)

assert mocked_route.called is True
Expand All @@ -68,14 +73,15 @@ def test_search_report_time_entries__with_all_params(
response_report_mock: MockRouter,
authed_report_time_entry: ReportTimeEntry,
) -> None:
fake_workspace_id = 123
fake_workspace_id = fake.random_int(min=1)
page_size = fake.random_int(min=1, max=100)
request_body = {
"start_date": "2021-12-20T00:00:00+00:00",
"end_date": "2021-12-30T00:00:00+00:00",
"user_ids": [30809356],
"project_ids": [202793182],
"page_size": 10,
"first_row_number": 11,
"start_date": fake.date(),
"end_date": fake.date(),
"user_ids": [fake.random_int()],
"project_ids": [fake.random_int()],
"page_size": page_size,
"first_row_number": page_size + 1,
}
uri = f"/{fake_workspace_id}/search/time_entries"
mocked_route = response_report_mock.post(uri, json=request_body).mock(
Expand Down
Loading

0 comments on commit 1df3344

Please sign in to comment.