Skip to content

Commit 261e42c

Browse files
committed
feat: TT-357 sql dao integration test implementation
1 parent 1892ffe commit 261e42c

File tree

5 files changed

+171
-62
lines changed

5 files changed

+171
-62
lines changed
Lines changed: 125 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,158 @@
1-
from time_entries._domain import Activity, ActivitiesDao
2-
from time_entries._infrastructure import DataAccessLayerSQL,db, ActivitiesSQLDao
3-
41
import pytest
5-
import typing
2+
import uuid
3+
4+
import time_entries._domain as domain
5+
import time_entries._infrastructure as infrastructure
6+
67

7-
@pytest.fixture
8-
def _create_fake_database() -> ActivitiesDao:
9-
db.db_init('sqlite:///:memory:')
10-
query = db.activity.insert()
8+
@pytest.fixture(name='create_fake_database')
9+
def _create_fake_database(with_data: bool) -> domain.ActivitiesDao:
10+
infrastructure.db.db_init('sqlite:///:memory:')
1111
demo_data = [
1212
{
13-
'id': 'c61a4a49-3364-49a3-a666-0c5f2d15072b',
13+
'id': uuid.UUID('b4327ba6-9f96-49ee-a9ac-3c1edf525172'),
1414
'name': 'Activity Demo create',
1515
'tenant_id': 'cc925a5d-9644-4a4f-8d99-0bee49aadd05',
1616
'description': 'test demo create an new activity',
1717
'status': 'active',
1818
'deleted': 'b4327ba6-9f96-49ee-a9ac-3c1edf525172',
1919
},
2020
{
21-
'id': 'c61a4a49-3364-49a3-a666-0c5f2d15072b',
21+
'id': uuid.UUID('c61a4a49-3364-49a3-a7f7-0c5f2d15072b'),
2222
'name': 'Activity Demo create',
2323
'tenant_id': 'cc925a5d-9644-4a4f-8d99-0bee49aadd05',
2424
'description': 'test demo create an new activity',
2525
'status': 'active',
2626
'deleted': 'b4327ba6-9f96-49ee-a9ac-3c1edf525172',
2727
},
2828
]
29-
db.connection.execute(query, demo_data)
30-
return ActivitiesSQLDao(db)
29+
if with_data:
30+
query = infrastructure.db.activity.insert()
31+
infrastructure.db.connection.execute(query, demo_data)
32+
return infrastructure.db
33+
34+
35+
@pytest.mark.parametrize('with_data',[False])
36+
def test__create_activity__returns_a_activity_dto__when_saves_correctly_with_sql_database(create_fake_database):
37+
activity = {
38+
'name': 'Activity Demo create',
39+
'tenant_id': 'cc925a5d-9644-4a4f-8d99-0bee49aadd05',
40+
'description': 'test demo create an new activity',
41+
'status': 'active',
42+
'deleted': 'b4327ba6-9f96-49ee-a9ac-3c1edf525172',
43+
}
44+
dao = infrastructure.ActivitiesSQLDao(create_fake_database)
45+
results = dao.create_activity(activity)
46+
assert results.id != None
47+
3148

32-
def test__get_all__returns_a_list_of_activity_dto_objects__when_one_or_more_activities_are_found_in_sql_database(activitiesSqlDao: ActivitiesSQLDao):
49+
@pytest.mark.parametrize('with_data',[True])
50+
def test_update__returns_an_activity_updated__when_an_activity_matching_its_id_is_found_with_sql_database(create_fake_database):
51+
activity = {
52+
'name': 'Activity Demo create 3',
53+
'tenant_id': 'cc925a5d-9644-4a4f-8d99-0bee49aadd05',
54+
'description': 'test demo create an new activity',
55+
'status': 'active',
56+
'deleted': 'b4327ba6-9f96-49ee-a9ac-3c1edf525172',
57+
}
58+
expected_result=domain.Activity(**{
59+
'id': 'b4327ba69f9649eea9ac3c1edf525172',
60+
'name': 'Activity Demo create 3',
61+
'tenant_id': 'cc925a5d-9644-4a4f-8d99-0bee49aadd05',
62+
'description': 'test demo create an new activity',
63+
'status': 'active',
64+
'deleted': 'b4327ba6-9f96-49ee-a9ac-3c1edf525172',
65+
})
66+
dao = infrastructure.ActivitiesSQLDao(create_fake_database)
67+
results = dao.update('b4327ba69f9649eea9ac3c1edf525172',activity)
68+
assert results == expected_result
69+
70+
71+
@pytest.mark.parametrize('with_data',[False])
72+
def test_update__returns_none__when_no_activity_matching_its_id_is_found_with_sql_database(create_fake_database):
73+
activity = {
74+
'name': 'Activity Demo create 3',
75+
'tenant_id': 'cc925a5d-9644-4a4f-8d99-0bee49aadd05',
76+
'description': 'test demo create an new activity',
77+
'status': 'active',
78+
'deleted': 'b4327ba6-9f96-49ee-a9ac-3c1edf525172',
79+
}
80+
dao = infrastructure.ActivitiesSQLDao(create_fake_database)
81+
results = dao.update('b4327ba69f9649eea9ac3c1edf525172',activity)
82+
assert results == None
83+
84+
85+
@pytest.mark.parametrize('with_data',[True])
86+
def test__get_all__returns_a_list_of_activity_dto_objects__when_one_or_more_activities_are_found_with_sql_database(create_fake_database):
3387
expected_result=[
34-
Activity(**{
35-
'id': 'c61a4a49-3364-49a3-a666-0c5f2d15072b',
88+
domain.Activity(**{
89+
'id': 'b4327ba69f9649eea9ac3c1edf525172',
3690
'name': 'Activity Demo create',
3791
'tenant_id': 'cc925a5d-9644-4a4f-8d99-0bee49aadd05',
3892
'description': 'test demo create an new activity',
3993
'status': 'active',
4094
'deleted': 'b4327ba6-9f96-49ee-a9ac-3c1edf525172',
4195
}),
42-
Activity(**{
43-
'id': 'c61a4a49-3364-49a3-a666-0c5f2d15072b',
96+
domain.Activity(**{
97+
'id': 'c61a4a49336449a3a7f70c5f2d15072b',
4498
'name': 'Activity Demo create',
4599
'tenant_id': 'cc925a5d-9644-4a4f-8d99-0bee49aadd05',
46100
'description': 'test demo create an new activity',
47101
'status': 'active',
48102
'deleted': 'b4327ba6-9f96-49ee-a9ac-3c1edf525172',
49103
}),
50104
]
51-
results = activitiesSqlDao.get_all()
52-
assert expected_result == results
105+
dao = infrastructure.ActivitiesSQLDao(create_fake_database)
106+
results = dao.get_all()
107+
assert results == expected_result
108+
109+
110+
@pytest.mark.parametrize('with_data',[True])
111+
def test_get_by_id__returns_an_activity_dto__when_found_one_activity_that_matches_its_id_with_sql_database(create_fake_database):
112+
expected_result=domain.Activity(**{
113+
'id': 'b4327ba69f9649eea9ac3c1edf525172',
114+
'name': 'Activity Demo create',
115+
'tenant_id': 'cc925a5d-9644-4a4f-8d99-0bee49aadd05',
116+
'description': 'test demo create an new activity',
117+
'status': 'active',
118+
'deleted': 'b4327ba6-9f96-49ee-a9ac-3c1edf525172',
119+
})
120+
dao = infrastructure.ActivitiesSQLDao(create_fake_database)
121+
results = dao.get_by_id('b4327ba69f9649eea9ac3c1edf525172')
122+
assert results == expected_result
123+
124+
125+
@pytest.mark.parametrize('with_data',[False])
126+
def test__get_by_id__returns_none__when_no_activity_matches_its_id_with_sql_database(create_fake_database):
127+
dao = infrastructure.ActivitiesSQLDao(create_fake_database)
128+
results = dao.get_by_id('b4327ba69f9649eea9ac3c1edf525172')
129+
assert results == None
130+
131+
132+
@pytest.mark.parametrize('with_data',[False])
133+
def test_get_all__returns_an_empty_list__when_doesnt_found_any_activities_with_sql_database(create_fake_database):
134+
dao = infrastructure.ActivitiesSQLDao(create_fake_database)
135+
results = dao.get_all()
136+
assert results == []
137+
138+
139+
@pytest.mark.parametrize('with_data',[True])
140+
def test_delete__returns_an_activity_with_inactive_status__when_an_activity_matching_its_id_is_found_with_sql_database(create_fake_database):
141+
expected_result=domain.Activity(**{
142+
'id': 'b4327ba69f9649eea9ac3c1edf525172',
143+
'name': 'Activity Demo create',
144+
'tenant_id': 'cc925a5d-9644-4a4f-8d99-0bee49aadd05',
145+
'description': 'test demo create an new activity',
146+
'status': 'inactive',
147+
'deleted': 'b4327ba6-9f96-49ee-a9ac-3c1edf525172',
148+
})
149+
dao = infrastructure.ActivitiesSQLDao(create_fake_database)
150+
results = dao.delete('b4327ba69f9649eea9ac3c1edf525172')
151+
assert results == expected_result
152+
153+
154+
@pytest.mark.parametrize('with_data',[False])
155+
def test_delete__returns_none__when_no_activity_matching_its_id_is_found_with_sql_database(create_fake_database):
156+
dao = infrastructure.ActivitiesSQLDao(create_fake_database)
157+
results = dao.delete('b4327ba69f9649eea9ac3c1edf525172')
158+
assert results == None

V2/time_entries/_application/_activities/_delete_activity.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,4 @@ def _delete(activity_id: str) -> str:
3535
def _create_activity_service(path: str):
3636
activity_json = ActivitiesJsonDao(path)
3737
activity_sql = ActivitiesSQLDao(DATABASE)
38-
return ActivityService(activity_sql)
38+
return ActivityService(activity_json)

V2/time_entries/_application/_activities/_get_activities.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,4 @@ def _get_all() -> str:
5454
def _create_activity_service(path: str):
5555
activity_json = ActivitiesJsonDao(path)
5656
activity_sql = ActivitiesSQLDao(DATABASE)
57-
return ActivityService(activity_sql)
57+
return ActivityService(activity_json)
Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,47 @@
1-
from time_entries._domain import ActivitiesDao, Activity
2-
from sqlalchemy.sql import select
31
import dataclasses
42
import typing
53

6-
class ActivitiesSQLDao(ActivitiesDao):
4+
import sqlalchemy.sql as sql
5+
6+
import time_entries._domain as domain
7+
class ActivitiesSQLDao(domain.ActivitiesDao):
8+
79
def __init__(self,database):
810
self.activity_keys = [
9-
field.name for field in dataclasses.fields(Activity)
11+
field.name for field in dataclasses.fields(domain.Activity)
1012
]
1113
self.db = database
12-
self.db.db_init()
13-
1414

15-
#GET Activity by id
16-
def get_by_id(self, activity_id: str) -> Activity:
17-
"""search for the activity by the given id
18-
parameters: str (the id string of the searched activity)
19-
returns: Activity (The activity model with the response if found or a void if not)."""
20-
query = select(self.db.activity).where(self.db.activity.c.id == activity_id)
21-
activity = self.db.connection.execute(query).one()
15+
def get_by_id(self, activity_id: str) -> domain.Activity:
16+
query = sql.select(self.db.activity).where(self.db.activity.c.id == activity_id)
17+
activity = self.db.get_connection().execute(query).one_or_none()
2218
return self.__create_activity_dto(dict(activity)) if activity else None
2319

24-
def get_all(self) -> typing.List[Activity]:
25-
query = select(self.db.activity)
26-
result = self.db.connection.execute(query)
20+
def get_all(self) -> typing.List[domain.Activity]:
21+
query = sql.select(self.db.activity)
22+
result = self.db.get_connection().execute(query)
2723
return [
2824
self.__create_activity_dto(dict(activity))
2925
for activity in result
3026
]
3127

32-
def create_activity(self, activity_data: dict) -> Activity:
28+
def create_activity(self, activity_data: dict) -> domain.Activity:
29+
activity_data.pop('id', None)
3330
query = self.db.activity.insert().values(activity_data)
34-
activity = self.db.connection.execute(query)
31+
activity = self.db.get_connection().execute(query)
3532
return self.__create_activity_dto(activity.last_inserted_params())
3633

37-
def delete(self, activity_id: str) -> Activity:
34+
def delete(self, activity_id: str) -> domain.Activity:
3835
query = self.db.activity.update().where(self.db.activity.c.id == activity_id).values(status = 'inactive')
39-
self.db.connection.execute(query)
36+
self.db.get_connection().execute(query)
4037
return self.get_by_id(activity_id);
4138

42-
def update(self, activity_id: str, new_activity: dict) -> Activity:
39+
def update(self, activity_id: str, new_activity: dict) -> domain.Activity:
40+
new_activity.pop('id', None)
4341
query = self.db.activity.update().where(self.db.activity.c.id==activity_id).values(new_activity)
44-
self.db.connection.execute(query)
42+
self.db.get_connection().execute(query)
4543
return self.get_by_id(activity_id)
4644

47-
def __create_activity_dto(self, activity: dict) -> Activity:
45+
def __create_activity_dto(self, activity: dict) -> domain.Activity:
4846
activity = {key: activity.get(key).hex if key == "id" else activity.get(key) for key in self.activity_keys}
49-
return Activity(**activity)
47+
return domain.Activity(**activity)

V2/time_entries/_infrastructure/_data_persistence/_data_access_layer_sql.py

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
1-
from sqlalchemy.types import TypeDecorator, CHAR
2-
from sqlalchemy.dialects.postgresql import UUID
3-
from sqlalchemy import create_engine,Table, Column, String, MetaData
4-
51
import uuid
6-
class GUID(TypeDecorator):
2+
3+
import sqlalchemy.types as types
4+
import sqlalchemy.dialects.postgresql as postgresql
5+
import sqlalchemy
6+
class GUID(types.TypeDecorator):
77
"""Platform-independent GUID type.
88
Uses PostgreSQL's UUID type, otherwise uses
99
CHAR(32), storing as stringified hex values.
1010
"""
11-
impl = CHAR
11+
impl = types.CHAR
1212

1313
def load_dialect_impl(self, dialect):
1414
if dialect.name == 'postgresql':
15-
return dialect.type_descriptor(UUID())
15+
return dialect.type_descriptor(postgresql.UUID())
1616
else:
17-
return dialect.type_descriptor(CHAR(32))
17+
return dialect.type_descriptor(types.CHAR(32))
1818

1919
def process_bind_param(self, value, dialect):
2020
if value is None:
@@ -40,19 +40,24 @@ class DataAccessLayerSQL:
4040
connection = None
4141
engine = None
4242
conn_string = 'postgresql://postgres:root@localhost/time-tracker'
43-
metadata = MetaData()
44-
activity = Table('activity', metadata,
45-
Column('id',GUID() , primary_key=True,default= uuid.uuid4),
46-
Column('name', String),
47-
Column('description', String),
48-
Column('deleted', String),
49-
Column('status', String),
50-
Column('tenant_id', String),
43+
metadata = sqlalchemy.MetaData()
44+
activity = sqlalchemy.Table('activity', metadata,
45+
sqlalchemy.Column('id',GUID() , primary_key=True,default= uuid.uuid4),
46+
sqlalchemy.Column('name', sqlalchemy.String),
47+
sqlalchemy.Column('description', sqlalchemy.String),
48+
sqlalchemy.Column('deleted', sqlalchemy.String),
49+
sqlalchemy.Column('status', sqlalchemy.String),
50+
sqlalchemy.Column('tenant_id', sqlalchemy.String),
5151
)
5252

5353
def db_init(self, conn_string: str = conn_string):
54-
self.engine = create_engine(conn_string)
54+
self.engine = sqlalchemy.create_engine(conn_string)
5555
self.metadata.create_all(self.engine)
5656
self.connection = self.engine.connect()
5757

58+
def get_connection(self):
59+
if self.connection == None:
60+
self.db_init()
61+
return self.connection
62+
5863
db = DataAccessLayerSQL()

0 commit comments

Comments
 (0)