Skip to content

Commit 377214b

Browse files
authored
Merge pull request #44 from ioet/feature/implement-activity-model#14
Implement activities model. Close #14
2 parents 8843766 + b683c62 commit 377214b

File tree

4 files changed

+242
-21
lines changed

4 files changed

+242
-21
lines changed

tests/activities/__init__.py

Whitespace-only changes.
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
from faker import Faker
2+
from flask import json
3+
from flask.testing import FlaskClient
4+
from pytest_mock import MockFixture
5+
from flask_restplus._http import HTTPStatus
6+
7+
fake = Faker()
8+
9+
valid_activity_data = {
10+
"name": fake.company(),
11+
"description": fake.paragraph()
12+
}
13+
14+
fake_activity = ({
15+
"id": fake.random_int(1, 9999)
16+
}).update(valid_activity_data)
17+
18+
19+
def test_create_activity_should_succeed_with_valid_request(client: FlaskClient, mocker: MockFixture):
20+
from time_tracker_api.activities.activities_namespace import activity_dao
21+
repository_create_mock = mocker.patch.object(activity_dao.repository,
22+
'create',
23+
return_value=fake_activity)
24+
25+
response = client.post("/activities", json=valid_activity_data, follow_redirects=True)
26+
27+
assert HTTPStatus.CREATED == response.status_code
28+
repository_create_mock.assert_called_once_with(valid_activity_data)
29+
30+
31+
def test_create_activity_should_reject_bad_request(client: FlaskClient, mocker: MockFixture):
32+
from time_tracker_api.activities.activities_namespace import activity_dao
33+
invalid_activity_data = valid_activity_data.copy().update({
34+
"invalid_field": 123,
35+
})
36+
repository_create_mock = mocker.patch.object(activity_dao.repository,
37+
'create',
38+
return_value=fake_activity)
39+
40+
response = client.post("/activities", json=invalid_activity_data, follow_redirects=True)
41+
42+
assert HTTPStatus.BAD_REQUEST == response.status_code
43+
repository_create_mock.assert_not_called()
44+
45+
46+
def test_list_all_activities(client: FlaskClient, mocker: MockFixture):
47+
from time_tracker_api.activities.activities_namespace import activity_dao
48+
repository_find_all_mock = mocker.patch.object(activity_dao.repository,
49+
'find_all',
50+
return_value=[])
51+
52+
response = client.get("/activities", follow_redirects=True)
53+
54+
assert HTTPStatus.OK == response.status_code
55+
json_data = json.loads(response.data)
56+
assert [] == json_data
57+
repository_find_all_mock.assert_called_once()
58+
59+
#HEY
60+
def test_get_activity_should_succeed_with_valid_id(client: FlaskClient, mocker: MockFixture):
61+
from time_tracker_api.activities.activities_namespace import activity_dao
62+
63+
valid_id = fake.random_int(1, 9999)
64+
65+
repository_find_mock = mocker.patch.object(activity_dao.repository,
66+
'find',
67+
return_value=fake_activity)
68+
69+
response = client.get("/activities/%s" % valid_id, follow_redirects=True)
70+
71+
assert HTTPStatus.OK == response.status_code
72+
fake_activity == json.loads(response.data)
73+
repository_find_mock.assert_called_once_with(str(valid_id))
74+
75+
76+
def test_get_activity_should_return_not_found_with_invalid_id(client: FlaskClient, mocker: MockFixture):
77+
from time_tracker_api.activities.activities_namespace import activity_dao
78+
from werkzeug.exceptions import NotFound
79+
80+
invalid_id = fake.random_int(1, 9999)
81+
82+
repository_find_mock = mocker.patch.object(activity_dao.repository,
83+
'find',
84+
side_effect=NotFound)
85+
86+
response = client.get("/activities/%s" % invalid_id, follow_redirects=True)
87+
88+
assert HTTPStatus.NOT_FOUND == response.status_code
89+
repository_find_mock.assert_called_once_with(str(invalid_id))
90+
91+
92+
def test_get_activity_should_return_422_for_invalid_id_format(client: FlaskClient, mocker: MockFixture):
93+
from time_tracker_api.activities.activities_namespace import activity_dao
94+
from werkzeug.exceptions import UnprocessableEntity
95+
96+
invalid_id = fake.company()
97+
98+
repository_find_mock = mocker.patch.object(activity_dao.repository,
99+
'find',
100+
side_effect=UnprocessableEntity)
101+
102+
response = client.get("/activities/%s" % invalid_id, follow_redirects=True)
103+
104+
assert HTTPStatus.UNPROCESSABLE_ENTITY == response.status_code
105+
repository_find_mock.assert_called_once_with(str(invalid_id))
106+
107+
108+
def test_update_activity_should_succeed_with_valid_data(client: FlaskClient, mocker: MockFixture):
109+
from time_tracker_api.activities.activities_namespace import activity_dao
110+
111+
repository_update_mock = mocker.patch.object(activity_dao.repository,
112+
'update',
113+
return_value=fake_activity)
114+
115+
valid_id = fake.random_int(1, 9999)
116+
response = client.put("/activities/%s" % valid_id, json=valid_activity_data, follow_redirects=True)
117+
118+
assert HTTPStatus.OK == response.status_code
119+
fake_activity == json.loads(response.data)
120+
repository_update_mock.assert_called_once_with(str(valid_id), valid_activity_data)
121+
122+
123+
def test_update_activity_should_reject_bad_request(client: FlaskClient, mocker: MockFixture):
124+
from time_tracker_api.activities.activities_namespace import activity_dao
125+
invalid_activity_data = valid_activity_data.copy().update({
126+
"invalid_field": 123,
127+
})
128+
repository_update_mock = mocker.patch.object(activity_dao.repository,
129+
'update',
130+
return_value=fake_activity)
131+
132+
valid_id = fake.random_int(1, 9999)
133+
response = client.put("/activities/%s" % valid_id, json=invalid_activity_data, follow_redirects=True)
134+
135+
assert HTTPStatus.BAD_REQUEST == response.status_code
136+
repository_update_mock.assert_not_called()
137+
138+
139+
def test_update_activity_should_return_not_found_with_invalid_id(client: FlaskClient, mocker: MockFixture):
140+
from time_tracker_api.activities.activities_namespace import activity_dao
141+
from werkzeug.exceptions import NotFound
142+
143+
invalid_id = fake.random_int(1, 9999)
144+
145+
repository_update_mock = mocker.patch.object(activity_dao.repository,
146+
'update',
147+
side_effect=NotFound)
148+
149+
response = client.put("/activities/%s" % invalid_id,
150+
json=valid_activity_data,
151+
follow_redirects=True)
152+
153+
assert HTTPStatus.NOT_FOUND == response.status_code
154+
repository_update_mock.assert_called_once_with(str(invalid_id), valid_activity_data)
155+
156+
157+
def test_delete_activity_should_succeed_with_valid_id(client: FlaskClient, mocker: MockFixture):
158+
from time_tracker_api.activities.activities_namespace import activity_dao
159+
160+
valid_id = fake.random_int(1, 9999)
161+
162+
repository_remove_mock = mocker.patch.object(activity_dao.repository,
163+
'remove',
164+
return_value=None)
165+
166+
response = client.delete("/activities/%s" % valid_id, follow_redirects=True)
167+
168+
assert HTTPStatus.NO_CONTENT == response.status_code
169+
assert b'' == response.data
170+
repository_remove_mock.assert_called_once_with(str(valid_id))
171+
172+
173+
def test_delete_activity_should_return_not_found_with_invalid_id(client: FlaskClient, mocker: MockFixture):
174+
from time_tracker_api.activities.activities_namespace import activity_dao
175+
from werkzeug.exceptions import NotFound
176+
177+
invalid_id = fake.random_int(1, 9999)
178+
179+
repository_remove_mock = mocker.patch.object(activity_dao.repository,
180+
'remove',
181+
side_effect=NotFound)
182+
183+
response = client.delete("/activities/%s" % invalid_id, follow_redirects=True)
184+
185+
assert HTTPStatus.NOT_FOUND == response.status_code
186+
repository_remove_mock.assert_called_once_with(str(invalid_id))
187+
188+
189+
def test_delete_activity_should_return_422_for_invalid_id_format(client: FlaskClient, mocker: MockFixture):
190+
from time_tracker_api.activities.activities_namespace import activity_dao
191+
from werkzeug.exceptions import UnprocessableEntity
192+
193+
invalid_id = fake.company()
194+
195+
repository_remove_mock = mocker.patch.object(activity_dao.repository,
196+
'remove',
197+
side_effect=UnprocessableEntity)
198+
199+
response = client.delete("/activities/%s" % invalid_id, follow_redirects=True)
200+
201+
assert HTTPStatus.UNPROCESSABLE_ENTITY == response.status_code
202+
repository_remove_mock.assert_called_once_with(str(invalid_id))
Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,28 @@
11
from time_tracker_api.database import CRUDDao
22

33

4-
class ActivitiesDao(CRUDDao):
4+
class ActivityDao(CRUDDao):
55
pass
66

77

8-
def create_dao() -> ActivitiesDao:
8+
def create_dao() -> ActivityDao:
99
from time_tracker_api.sql_repository import db
1010
from time_tracker_api.sql_repository import SQLCRUDDao, AuditedSQLModel
1111

1212
class ActivitySQLModel(db.Model, AuditedSQLModel):
1313
__tablename__ = 'activity'
1414
id = db.Column(db.Integer, primary_key=True)
15+
name = db.Column(db.String(50), unique=True, nullable=False)
16+
description = db.Column(db.String(250), unique=False, nullable=False)
1517

16-
class ActivitiesSQLDao(SQLCRUDDao):
18+
def __repr__(self):
19+
return '<Activity %r>' % self.name
20+
21+
def __str___(self):
22+
return "the activity \"%s\"" % self.name
23+
24+
class ActivitySQLDao(SQLCRUDDao):
1725
def __init__(self):
1826
SQLCRUDDao.__init__(self, ActivitySQLModel)
1927

20-
return ActivitiesSQLDao()
28+
return ActivitySQLDao()

time_tracker_api/activities/activities_namespace.py

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
from faker import Faker
22
from flask_restplus import fields, Resource, Namespace
3+
from flask_restplus._http import HTTPStatus
34

45
from time_tracker_api.activities.activities_model import create_dao
56
from time_tracker_api.api import audit_fields
7+
from time_tracker_api.activities.activities_model import create_dao
68

79
faker = Faker()
810

@@ -41,41 +43,50 @@
4143
activity_response_fields
4244
)
4345

44-
activities_dao = create_dao()
46+
activity_dao = create_dao()
4547

4648

4749
@ns.route('')
4850
class Activities(Resource):
4951
@ns.doc('list_activities')
50-
@ns.marshal_list_with(activity, code=200)
52+
@ns.marshal_list_with(activity)
5153
def get(self):
52-
return []
54+
"""List all activities"""
55+
return activity_dao.get_all()
5356

5457
@ns.doc('create_activity')
58+
@ns.response(HTTPStatus.CONFLICT, 'This activity already exists')
59+
@ns.response(HTTPStatus.BAD_REQUEST, 'Invalid format or structure of the attributes of the activity')
5560
@ns.expect(activity_input)
56-
@ns.marshal_with(activity, code=201)
57-
@ns.response(400, 'Invalid format of the attributes of the activity.')
61+
@ns.marshal_with(activity, code=HTTPStatus.CREATED)
5862
def post(self):
59-
return ns.payload, 201
63+
"""Create an activity"""
64+
return activity_dao.create(ns.payload), HTTPStatus.CREATED
6065

6166

6267
@ns.route('/<string:id>')
63-
@ns.response(404, 'Activity not found')
64-
@ns.param('id', 'The unique identifier of the activity')
68+
@ns.response(HTTPStatus.NOT_FOUND, 'Activity not found')
69+
@ns.response(HTTPStatus.UNPROCESSABLE_ENTITY, 'The id has an invalid format')
70+
@ns.param('id', 'The activity identifier')
6571
class Activity(Resource):
6672
@ns.doc('get_activity')
6773
@ns.marshal_with(activity)
6874
def get(self, id):
69-
return {}
70-
71-
@ns.doc('delete_activity')
72-
@ns.response(204, 'The activity was deleted successfully (No content is returned)')
73-
def delete(self, id):
74-
return None, 204
75+
"""Get an activity"""
76+
return activity_dao.get(id)
7577

76-
@ns.doc('put_activity')
77-
@ns.response(400, 'Invalid format of the attributes of the activity.')
78+
@ns.doc('update_activity')
7879
@ns.expect(activity_input)
80+
@ns.response(HTTPStatus.BAD_REQUEST, 'Invalid format or structure of the activity')
81+
@ns.response(HTTPStatus.CONFLICT, 'An activity already exists with this new data')
7982
@ns.marshal_with(activity)
8083
def put(self, id):
81-
return ns.payload
84+
"""Update an activity"""
85+
return activity_dao.update(id, ns.payload)
86+
87+
@ns.doc('delete_activity')
88+
@ns.response(HTTPStatus.NO_CONTENT, 'Activity deleted successfully')
89+
def delete(self, id):
90+
"""Delete an activity"""
91+
activity_dao.delete(id)
92+
return None, HTTPStatus.NO_CONTENT

0 commit comments

Comments
 (0)