Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Time Tacker CI/ CD

on:
push:
branches:
- TT-394-Git-Hub-Action-to-Time-tracker-Backend-V2

pull_request:
branches:
- TT-394-Git-Hub-Action-to-Time-tracker-Backend-V2

jobs:
build-ci-time-tracker:
runs-on: ubuntu-latest
strategy:
max-parallel: 5
steps:
- uses: actions/checkout@v2
- name: Set up Python 3.10.0
uses: actions/setup-python@v2
with:
python-version: 3.10.0

- name: Add conda to system path
run: |
echo $CONDA/bin >> $GITHUB_PATH

- name: Install dependencies
run: |
pip install -r V2/requirements.txt

- name: Lint with flake8
run: |
cd V2
flake8 . --show-source --statistics
- name: Test with pytest
run: |
cd V2
python -m pytest -v
4 changes: 4 additions & 0 deletions V2/.flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[flake8]
exclude = .git,__pycache__,./node_modules,
max-complexity = 10
max_line_length = 120
1 change: 1 addition & 0 deletions V2/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ azure-functions-worker

# Tests
pytest
flake8==4.0.1

# Mocking
pytest-mock
Expand Down
61 changes: 34 additions & 27 deletions V2/tests/api/azure/activity_azure_endpoints_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,17 @@

import azure.functions as func
import json
import typing


ACTIVITY_URL = '/api/activities/'
ACTIVITY_URL = "/api/activities/"


def test__activity_azure_endpoint__returns_all_activities(
create_temp_activities,
):
activities_json, tmp_directory = create_temp_activities
activities._get_activities.JSON_PATH = tmp_directory
req = func.HttpRequest(method='GET', body=None, url=ACTIVITY_URL)
req = func.HttpRequest(method="GET", body=None, url=ACTIVITY_URL)

response = activities.get_activities(req)
activities_json_data = response.get_body().decode("utf-8")
Expand All @@ -29,10 +28,10 @@ def test__activity_azure_endpoint__returns_an_activity__when_activity_matches_it
activities_json, tmp_directory = create_temp_activities
activities._get_activities.JSON_PATH = tmp_directory
req = func.HttpRequest(
method='GET',
method="GET",
body=None,
url=ACTIVITY_URL,
route_params={"id": activities_json[0]['id']},
route_params={"id": activities_json[0]["id"]},
)

response = activities.get_activities(req)
Expand All @@ -48,17 +47,17 @@ def test__activity_azure_endpoint__returns_an_activity_with_inactive_status__whe
activities_json, tmp_directory = create_temp_activities
activities._delete_activity.JSON_PATH = tmp_directory
req = func.HttpRequest(
method='DELETE',
method="DELETE",
body=None,
url=ACTIVITY_URL,
route_params={"id": activities_json[0]['id']},
route_params={"id": activities_json[0]["id"]},
)

response = activities.delete_activity(req)
activity_json_data = json.loads(response.get_body().decode("utf-8"))

assert response.status_code == 200
assert activity_json_data['status'] == 'inactive'
assert activity_json_data["status"] == "inactive"


def test__update_activity_azure_endpoint__returns_an_activity__when_found_an_activity_to_update(
Expand All @@ -68,10 +67,10 @@ def test__update_activity_azure_endpoint__returns_an_activity__when_found_an_act
activities._update_activity.JSON_PATH = tmp_directory
activity_data = {"description": Faker().sentence()}
req = func.HttpRequest(
method='PUT',
method="PUT",
body=json.dumps(activity_data).encode("utf-8"),
url=ACTIVITY_URL,
route_params={"id": activities_json[0]['id']},
route_params={"id": activities_json[0]["id"]},
)

response = activities.update_activity(req)
Expand All @@ -81,21 +80,29 @@ def test__update_activity_azure_endpoint__returns_an_activity__when_found_an_act
assert response.status_code == 200
assert activitiy_json_data == json.dumps(new_activity)


def test__activity_azure_endpoint__creates_an_activity__when_activity_has_all_attributes(
create_temp_activities,
):
activities_json, tmp_directory = create_temp_activities
activities._create_activity._JSON_PATH = tmp_directory

activity_body = {'id': None, 'name': Faker().user_name(), 'description': Faker().sentence(),'deleted': Faker().uuid4() ,'status': 'active', 'tenant_id': Faker().uuid4()}
body = json.dumps(activity_body).encode("utf-8")
req = func.HttpRequest(
method='POST',
body= body,
url=ACTIVITY_URL,
)

response = activities.create_activity(req)
activitiy_json_data = response.get_body()
assert response.status_code == 201
assert activitiy_json_data == body
create_temp_activities,
):
activities_json, tmp_directory = create_temp_activities
activities._create_activity._JSON_PATH = tmp_directory

activity_body = {
"id": None,
"name": Faker().user_name(),
"description": Faker().sentence(),
"deleted": Faker().uuid4(),
"status": "active",
"tenant_id": Faker().uuid4(),
}
body = json.dumps(activity_body).encode("utf-8")
req = func.HttpRequest(
method="POST",
body=body,
url=ACTIVITY_URL,
)

response = activities.create_activity(req)
activitiy_json_data = response.get_body()
assert response.status_code == 201
assert activitiy_json_data == body
1 change: 1 addition & 0 deletions V2/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
# flake8: noqa
from tests.api.api_fixtures import create_temp_activities
51 changes: 27 additions & 24 deletions V2/tests/integration/daos/activities_json_dao_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,21 @@

fake_activities = [
{
'id': Faker().uuid4(),
'name': Faker().user_name(),
'description': Faker().sentence(),
'deleted': Faker().uuid4(),
'status': 'active',
'tenant_id': Faker().uuid4(),
"id": Faker().uuid4(),
"name": Faker().user_name(),
"description": Faker().sentence(),
"deleted": Faker().uuid4(),
"status": "active",
"tenant_id": Faker().uuid4(),
}
]


@pytest.fixture(name='create_fake_activities')
@pytest.fixture(name="create_fake_activities")
def _create_fake_activities(mocker) -> typing.List[Activity]:
def _creator(activities):
read_data = json.dumps(activities)
mocker.patch('builtins.open', mocker.mock_open(read_data=read_data))
mocker.patch("builtins.open", mocker.mock_open(read_data=read_data))
return [Activity(**activity) for activity in activities]

return _creator
Expand Down Expand Up @@ -94,7 +94,7 @@ def test_delete__returns_an_activity_with_inactive_status__when_an_activity_matc
activity_dto = activities.pop()
result = activities_json_dao.delete(activity_dto.id)

assert result.status == 'inactive'
assert result.status == "inactive"


def test_delete__returns_none__when_no_activity_matching_its_id_is_found(
Expand Down Expand Up @@ -129,21 +129,24 @@ def test_update__returns_none__when_doesnt_found_one_activity_to_update(
create_fake_activities([])
activity_data = {"description": Faker().sentence()}

result = activities_json_dao.update('', activity_data)
result = activities_json_dao.update("", activity_data)

assert result == None
assert result is None

def test_create_activity__returns_an_activity_dto__when_create_an_activity_that_matches_attributes(create_fake_activities):
create_fake_activities([])

activities_json_dao = ActivitiesJsonDao(Faker().file_path())
activity_data = {
"name": "test_name",
"description": "test_description",
"tenant_id": "test_tenant_id",
"id": "test_id",
"deleted": "test_deleted",
"status": "test_status",
}
result = activities_json_dao.create_activity(activity_data)
assert result == Activity(**activity_data)
def test_create_activity__returns_an_activity_dto__when_create_an_activity_that_matches_attributes(
create_fake_activities,
):
create_fake_activities([])

activities_json_dao = ActivitiesJsonDao(Faker().file_path())
activity_data = {
"name": "test_name",
"description": "test_description",
"tenant_id": "test_tenant_id",
"id": "test_id",
"deleted": "test_deleted",
"status": "test_status",
}
result = activities_json_dao.create_activity(activity_data)
assert result == Activity(**activity_data)
1 change: 1 addition & 0 deletions V2/tests/unit/services/activity_service_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ def test__update_activity__uses_the_activity_dao__to_update_one_activity(
assert activity_dao.update.called
assert expected_activity == updated_activity


def test__create_activity__uses_the_activity_dao__to_create_an_activity(mocker):
expected_activity = mocker.Mock()
activity_dao = mocker.Mock(
Expand Down
35 changes: 15 additions & 20 deletions V2/tests/unit/use_cases/activities_use_case_test.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from time_entries._domain import ActivityService
from time_entries._domain import _use_cases
from pytest_mock import MockFixture
from faker import Faker
Expand Down Expand Up @@ -37,45 +36,41 @@ def test__get_activity_by_id_function__uses_the_activity_service__to_retrieve_ac


def test__create_activity_function__uses_the_activities_service__to_create_activity(
mocker: MockFixture,
):
expected_activity = mocker.Mock()
activity_service = mocker.Mock(
create_activity=mocker.Mock(return_value=expected_activity)
)
mocker: MockFixture,
):
expected_activity = mocker.Mock()
activity_service = mocker.Mock(
create_activity=mocker.Mock(return_value=expected_activity)
)

activity_use_case = _use_cases.CreateActivityUseCase(activity_service)
actual_activity = activity_use_case.create_activity(fake.pydict())

activity_use_case = _use_cases.CreateActivityUseCase(activity_service)
actual_activity = activity_use_case.create_activity(fake.pydict())
assert activity_service.create_activity.called
assert expected_activity == actual_activity

assert activity_service.create_activity.called
assert expected_activity == actual_activity

def test__delete_activity_function__uses_the_activity_service__to_change_activity_status(
mocker: MockFixture,
):
expected_activity = mocker.Mock()
activity_service = mocker.Mock(
delete=mocker.Mock(return_value=expected_activity)
)
activity_service = mocker.Mock(delete=mocker.Mock(return_value=expected_activity))

activity_use_case = _use_cases.DeleteActivityUseCase(activity_service)
deleted_activity = activity_use_case.delete_activity(fake.uuid4())

assert activity_service.delete.called
assert expected_activity == deleted_activity


def test__update_activity_function__uses_the_activities_service__to_update_an_activity(
mocker: MockFixture,
):
expected_activity = mocker.Mock()
activity_service = mocker.Mock(
update=mocker.Mock(return_value=expected_activity)
)
activity_service = mocker.Mock(update=mocker.Mock(return_value=expected_activity))

activity_use_case = _use_cases.UpdateActivityUseCase(activity_service)
updated_activity = activity_use_case.update_activity(
fake.uuid4(), fake.pydict()
)
updated_activity = activity_use_case.update_activity(fake.uuid4(), fake.pydict())

assert activity_service.update.called
assert expected_activity == updated_activity
1 change: 1 addition & 0 deletions V2/time_entries/_application/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# flake8: noqa
from ._activities import get_activities
from ._activities import delete_activity
from ._activities import update_activity
Expand Down
1 change: 1 addition & 0 deletions V2/time_entries/_application/_activities/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# flake8: noqa
from ._get_activities import get_activities
from ._delete_activity import delete_activity
from ._update_activity import update_activity
Expand Down
7 changes: 1 addition & 6 deletions V2/time_entries/_application/_activities/_create_activity.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import json
import logging
import dataclasses
import typing

Expand All @@ -18,27 +17,23 @@ def create_activity(req: func.HttpRequest) -> func.HttpResponse:
activity_service = _domain.ActivityService(activity_dao)
use_case = _domain._use_cases.CreateActivityUseCase(activity_service)


activity_data = req.get_json()


validation_errors = _validate_activity(activity_data)
if validation_errors:
return func.HttpResponse(
body=json.dumps(validation_errors), status_code=400, mimetype="application/json"
)


activity_to_create = _domain.Activity(
id= None,
id=None,
name=activity_data['name'],
description=activity_data['description'],
status=activity_data['status'],
deleted=activity_data['deleted'],
tenant_id=activity_data['tenant_id']
)


created_activity = use_case.create_activity(activity_to_create.__dict__)
if not create_activity:
return func.HttpResponse(
Expand Down
1 change: 1 addition & 0 deletions V2/time_entries/_domain/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# flake8: noqa
from ._entities import Activity
from ._persistence_contracts import ActivitiesDao
from ._services import ActivityService
Expand Down
1 change: 1 addition & 0 deletions V2/time_entries/_domain/_entities/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
# flake8: noqa
from ._activity import Activity
1 change: 1 addition & 0 deletions V2/time_entries/_domain/_persistence_contracts/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
# flake8: noqa
from ._activities_dao import ActivitiesDao
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,3 @@ def update(self, id: str, new_activity: dict) -> Activity:
@abc.abstractmethod
def create_activity(self, activity_data: dict) -> Activity:
pass

@abc.abstractmethod
def delete(self, id: str) -> Activity:
pass
1 change: 1 addition & 0 deletions V2/time_entries/_domain/_services/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
# flake8: noqa
from ._activity import ActivityService
1 change: 1 addition & 0 deletions V2/time_entries/_domain/_use_cases/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# flake8: noqa
from ._get_activities_use_case import GetActivitiesUseCase
from ._get_activity_by_id_use_case import GetActivityUseCase
from ._delete_activity_use_case import DeleteActivityUseCase
Expand Down
Loading