Skip to content

Commit 2833c00

Browse files
feat: TT-123 exclude test users from report data (#252)
* fix: TT-123 Functions of test users * feat: TT-123 Add new feature to filter by role data * feat: TT-123 make single methods in azure class. * feat: TT-123 remove whitespace Co-authored-by: roberto <[email protected]>
1 parent f2b35a2 commit 2833c00

File tree

4 files changed

+175
-3
lines changed

4 files changed

+175
-3
lines changed

tests/time_tracker_api/time_entries/time_entries_namespace_test.py

Lines changed: 94 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from datetime import timedelta
2-
from unittest.mock import ANY, Mock
2+
from unittest.mock import ANY, Mock, patch
33

44
from faker import Faker
55
from flask import json
@@ -16,6 +16,9 @@
1616
datetime_str,
1717
)
1818
from utils import worked_time
19+
from time_tracker_api.time_entries.time_entries_model import (
20+
TimeEntryCosmosDBModel,
21+
)
1922

2023
from werkzeug.exceptions import NotFound, UnprocessableEntity, HTTPException
2124

@@ -189,6 +192,81 @@ def test_get_time_entry_should_succeed_with_valid_id(
189192
dao_get_all_mock.assert_called_once()
190193

191194

195+
@patch(
196+
'time_tracker_api.time_entries.time_entries_dao.TimeEntriesCosmosDBDao.create_event_context',
197+
Mock(),
198+
)
199+
@patch(
200+
'time_tracker_api.time_entries.time_entries_dao.TimeEntriesCosmosDBDao.build_custom_query',
201+
Mock(),
202+
)
203+
@patch(
204+
'time_tracker_api.time_entries.time_entries_dao.TimeEntriesCosmosDBDao.handle_date_filter_args',
205+
Mock(),
206+
)
207+
@patch('msal.ConfidentialClientApplication', Mock())
208+
@patch('utils.azure_users.AzureConnection.get_token', Mock())
209+
@patch('utils.azure_users.AzureConnection.is_test_user')
210+
@patch('utils.azure_users.AzureConnection.get_test_user_ids')
211+
@pytest.mark.parametrize(
212+
'current_user_is_tester, expected_user_ids',
213+
[
214+
(True, ['id1', 'id2']),
215+
(False, ['id2']),
216+
],
217+
)
218+
def test_get_time_entries_by_type_of_user(
219+
get_test_user_ids_mock,
220+
is_test_user_mock,
221+
client: FlaskClient,
222+
valid_header: dict,
223+
time_entries_dao,
224+
current_user_is_tester,
225+
expected_user_ids,
226+
):
227+
test_user_id = "id1"
228+
non_test_user_id = "id2"
229+
te1 = TimeEntryCosmosDBModel(
230+
{
231+
"id": '1',
232+
"project_id": "1",
233+
"owner_id": test_user_id,
234+
"tenant_id": '1',
235+
"start_date": "",
236+
}
237+
)
238+
te2 = TimeEntryCosmosDBModel(
239+
{
240+
"id": '2',
241+
"project_id": "2",
242+
"owner_id": non_test_user_id,
243+
"tenant_id": '2',
244+
"start_date": "",
245+
}
246+
)
247+
248+
find_all_mock = Mock()
249+
find_all_mock.return_value = [te1, te2]
250+
251+
time_entries_dao.repository.find_all = find_all_mock
252+
253+
is_test_user_mock.return_value = current_user_is_tester
254+
get_test_user_ids_mock.return_value = [test_user_id]
255+
256+
response = client.get(
257+
"/time-entries?user_id=*", headers=valid_header, follow_redirects=True
258+
)
259+
260+
is_test_user_mock.assert_called()
261+
find_all_mock.assert_called()
262+
263+
expected_user_ids_in_time_entries = expected_user_ids
264+
actual_user_ids_in_time_entries = [
265+
time_entry["owner_id"] for time_entry in json.loads(response.data)
266+
]
267+
assert expected_user_ids_in_time_entries == actual_user_ids_in_time_entries
268+
269+
192270
def test_get_time_entry_should_succeed_with_valid_id(
193271
client: FlaskClient,
194272
mocker: MockFixture,
@@ -595,6 +673,11 @@ def test_create_with_valid_uuid_format_should_return_created(
595673
repository_container_create_item_mock.assert_called()
596674

597675

676+
@patch('msal.ConfidentialClientApplication', Mock())
677+
@patch('utils.azure_users.AzureConnection.get_token', Mock())
678+
@patch(
679+
'utils.azure_users.AzureConnection.is_test_user', Mock(return_value=True)
680+
)
598681
@pytest.mark.parametrize(
599682
'url',
600683
[
@@ -624,6 +707,11 @@ def test_get_all_passes_date_range_built_from_params_to_find_all(
624707
assert 'end_date' in kwargs['date_range']
625708

626709

710+
@patch('msal.ConfidentialClientApplication', Mock())
711+
@patch('utils.azure_users.AzureConnection.get_token', Mock())
712+
@patch(
713+
'utils.azure_users.AzureConnection.is_test_user', Mock(return_value=True)
714+
)
627715
@pytest.mark.parametrize(
628716
'url,start_date,end_date',
629717
[
@@ -660,6 +748,11 @@ def test_get_all_passes_date_range_to_find_all_with_default_tz_offset(
660748
assert kwargs['date_range']['end_date'] == end_date
661749

662750

751+
@patch('msal.ConfidentialClientApplication', Mock())
752+
@patch('utils.azure_users.AzureConnection.get_token', Mock())
753+
@patch(
754+
'utils.azure_users.AzureConnection.is_test_user', Mock(return_value=True)
755+
)
663756
@pytest.mark.parametrize(
664757
'url,start_date,end_date',
665758
[

tests/utils/azure_users_test.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
from unittest.mock import Mock, patch
2+
from utils.azure_users import AzureConnection, ROLE_FIELD_VALUES
3+
from pytest import mark
4+
5+
6+
@patch('msal.ConfidentialClientApplication', Mock())
7+
@patch('utils.azure_users.AzureConnection.get_token', Mock())
8+
@patch('requests.get')
9+
@mark.parametrize(
10+
'field_name,field_value,is_test_user_expected_value',
11+
[
12+
(ROLE_FIELD_VALUES['test'][0], ROLE_FIELD_VALUES['test'][1], True),
13+
(ROLE_FIELD_VALUES['test'][0], None, False),
14+
],
15+
)
16+
def test_azure_connection_is_test_user(
17+
get_mock, field_name, field_value, is_test_user_expected_value,
18+
):
19+
response_mock = Mock()
20+
response_mock.status_code = 200
21+
response_mock.json = Mock(return_value={field_name: field_value})
22+
get_mock.return_value = response_mock
23+
24+
test_user_id = 'test-user-id'
25+
az_conn = AzureConnection()
26+
assert az_conn.is_test_user(test_user_id) == is_test_user_expected_value
27+
28+
29+
@patch('msal.ConfidentialClientApplication', Mock())
30+
@patch('utils.azure_users.AzureConnection.get_token', Mock())
31+
@patch('requests.get')
32+
def test_azure_connection_get_test_user_ids(get_mock):
33+
response_mock = Mock()
34+
response_mock.status_code = 200
35+
response_mock.json = Mock(
36+
return_value={'value': [{'objectId': 'ID1'}, {'objectId': 'ID2'},]}
37+
)
38+
get_mock.return_value = response_mock
39+
40+
ids = ['ID1', 'ID2']
41+
az_conn = AzureConnection()
42+
assert az_conn.get_test_user_ids() == ids

time_tracker_api/time_entries/time_entries_dao.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
)
2929
from time_tracker_api.database import CRUDDao, APICosmosDBDao
3030
from time_tracker_api.security import current_user_id
31+
from utils.azure_users import AzureConnection
3132

3233

3334
class TimeEntriesDao(CRUDDao):
@@ -93,21 +94,35 @@ def build_custom_query(self, is_admin: bool, conditions: dict = None):
9394
def get_all(self, conditions: dict = None, **kwargs) -> list:
9495
event_ctx = self.create_event_context("read-many")
9596
conditions.update({"owner_id": event_ctx.user_id})
96-
97+
is_complete_query = conditions.get("user_id") == '*'
9798
custom_query = self.build_custom_query(
9899
is_admin=event_ctx.is_admin,
99100
conditions=conditions,
100101
)
101102
date_range = self.handle_date_filter_args(args=conditions)
102103
limit = conditions.get("limit", None)
103104
conditions.pop("limit", None)
104-
return self.repository.find_all(
105+
azure_connection = AzureConnection()
106+
current_user_is_tester = azure_connection.is_test_user(
107+
event_ctx.user_id
108+
)
109+
time_entries_list = self.repository.find_all(
105110
event_ctx,
106111
conditions=conditions,
107112
custom_sql_conditions=custom_query,
108113
date_range=date_range,
109114
max_count=limit,
110115
)
116+
if not current_user_is_tester and is_complete_query:
117+
test_user_ids = azure_connection.get_test_user_ids()
118+
time_entries_list = [
119+
time_entry
120+
for time_entry in time_entries_list
121+
if time_entry.owner_id not in test_user_ids
122+
]
123+
return time_entries_list
124+
else:
125+
return time_entries_list
111126

112127
def get_lastest_entries_by_project(
113128
self, conditions: dict = None, **kwargs

utils/azure_users.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,3 +185,25 @@ def get_role_data(self, role_id, is_grant=True):
185185
return {field_name: field_value}
186186
else:
187187
return {field_name: None}
188+
189+
def is_test_user(self, user_id):
190+
endpoint = "{endpoint}/users/{user_id}?api-version=1.6".format(
191+
endpoint=self.config.ENDPOINT, user_id=user_id
192+
)
193+
response = requests.get(endpoint, auth=BearerAuth(self.access_token))
194+
assert 200 == response.status_code
195+
item = response.json()
196+
field_name, field_value = ROLE_FIELD_VALUES['test']
197+
return field_name in item and field_value == item[field_name]
198+
199+
def get_test_user_ids(self):
200+
field_name, field_value = ROLE_FIELD_VALUES['test']
201+
endpoint = "{endpoint}/users?api-version=1.6&$select=objectId,{field_name}&$filter={field_name} eq '{field_value}'".format(
202+
endpoint=self.config.ENDPOINT,
203+
field_name=field_name,
204+
field_value=field_value,
205+
)
206+
response = requests.get(endpoint, auth=BearerAuth(self.access_token))
207+
assert 200 == response.status_code
208+
assert 'value' in response.json()
209+
return [item['objectId'] for item in response.json()['value']]

0 commit comments

Comments
 (0)