def test_with_invalid_params(self, mocked_db): batch_size = -1 start_from = 1 invalid_args = [ { 'start_from_property_value': 'boom', 'start_from_id': sample_expenses[0]['id'], 'start_from_property': 'timestamp_utc', 'batch_size': 1, 'ordering_direction': 'asc' }, { 'start_from_id': 1, 'start_from_property': 'timestamp_utc', 'start_from_property_value': utc_now_str(), }, { 'start_from_property_value': utc_now_str().replace('Z', '+00:00'), # invalid for a ts to end in +00:00 'start_from_property': 'timestamp_utc', 'start_from_id': 'some str', } ] for args in invalid_args: mocked_db.get_list.return_value = [] raw_resp = self.get(url=endpoint, url_args={'start_id': start_from, 'batch_size': batch_size}) self.assertEqual(raw_resp.status_code, 400, "args should have been rejected" + str(args)) json_resp = loads(raw_resp.get_data(as_text=True)) self.assertIn("error", json_resp, "error key missing from non-200 response") self.assertIn(ApiError.INVALID_QUERY_PARAMS, json_resp['error']) self.assertFalse(mocked_db.get_list.called, "Shouldn't have been called") mocked_db.get_list.reset_mock()
def test_fails_on_invalid_payload(self, mocked_db): type(mocked_db).max_sync_request_size = PropertyMock(return_value=non_mocked_facade.max_sync_request_size) invalid_payloads = [ [{"timestamp_utc_updated": 'invalid ts', 'id': 'valid id'}], [{"timestamp_utc_updated": utc_now_str(), 'id': None}], # valid ts invalid id [{"timestamp_utc_updated": utc_now_str()}], # valid ts but missing id [{'id': 'valid id but missing ts'}] ] for payload in invalid_payloads: raw_resp = self.post(url=endpoint, data=payload) self.assertEqual(raw_resp.status_code, 400)
def test_(self): self.mocked_time = self.patch('app.helpers.time.utc_now_str') self.mocked_time.return_value = '1969' from app.helpers.time import utc_now_str self.assertEqual(utc_now_str(), '1969')
def _touch_expense_in_db(self, item, others=[]): """ :param item: :param others: list of dicts. each dict has "attr" and "value" keys :return: """ other_updates = {} for other in others: other_updates[other['attr']] = { "Value": other['value'], 'Action': "PUT" } self.expenses_table.update_item(Key={ 'timestamp_utc': item['timestamp_utc'], 'user_uid': self.firebase_uid }, AttributeUpdates={ 'timestamp_utc_updated': { 'Value': utc_now_str(), 'Action': 'PUT' }, **other_updates })
def test_call_on_good_request(self, mocked_db: type(db_facade)): mocked_db.get_list.return_value = [] property_value = utc_now_str() property_name = 'timestamp_utc' id = 'asd' batch_size = 10 request_args = { 'start_from_id': id, 'start_from_property_value': property_value, 'start_from_property': property_name, 'batch_size': batch_size, } resp = self.get(url=endpoint, url_args=request_args) self.assertEqual(1, mocked_db.get_list.call_count, 'The get list should have been called once') call_args, actual_kwargs = mocked_db.get_list.call_args self.assertEqual(0, len(call_args)) expected_kwargs = { 'property_value': property_value, 'property_name': property_name, "ordering_direction": OrderingDirection.desc, 'batch_size': batch_size, 'user_uid': self.firebase_uid } self.assertEqual(len(expected_kwargs), len(actual_kwargs)) self.assertDictEqual(expected_kwargs, actual_kwargs, "get_list wasn't called with the expected kwargs") self.assertEqual(200, resp.status_code) self.assertEqual('[]', resp.get_data(as_text=True))
def _make_expense(self): new_expense = sample_expenses[-1].copy() new_expense['id'] = str(uuid.uuid4()) now = utc_now_str() new_expense['timestamp_utc'] = now new_expense['timestamp_utc_created'] = now new_expense['timestamp_utc_updated'] = now new_expense['user_uid'] = self.firebase_uid self.expenses_table.put_item( Item=self.facade.converter.convertToDbFormat(new_expense)) return new_expense
def test_timestamp(self): exp = sample_expenses[0].copy() exp['id'] = None persisted = self.facade.persist(exp, user_uid=self.firebase_uid) now = dt_from_utc_iso_str(utc_now_str()) persisted_at_dt = dt_from_utc_iso_str( persisted['timestamp_utc_created']) updated_at_dt = dt_from_utc_iso_str(persisted['timestamp_utc_created']) self.assertEqual(persisted_at_dt, updated_at_dt) for ts in [persisted_at_dt, updated_at_dt]: diff = int((now - ts).total_seconds()) self.assertTrue( 1 >= diff, "the expense's ts must be less than a second ago from now")
def test_errors_on_no_such_expense(self): """ remove() should raise an exception in either cases: * an expense with the same SORT key attribute doesn't exist * an expense with the same `id` attribute doesn't exist """ assert self.expenses_table.item_count exp = SINGLE_EXPENSE.copy() non_persisted_expenses = [{ **exp, 'id': 'boom' }, { **exp, 'timestamp_utc': utc_now_str() }] for e in non_persisted_expenses: self.assertRaises(NoExpenseWithThisId, self.facade.remove, e, self.firebase_uid)
def test_on_range_attr_updated(self): old_exp = sample_expenses[0].copy() to_update = old_exp.copy() assert 'timestamp_utc' == self.facade.RANGE_KEY to_update['timestamp_utc'] = utc_now_str() assert to_update['timestamp_utc'] != old_exp['timestamp_utc'] updated = self.facade.update(to_update, old_exp, user_uid=self.firebase_uid) self.assertTrue(updated['id'] == to_update['id'] == old_exp['id']) # check the item in the database now exp_from_db = self.expenses_table.get_item(Key={ 'user_uid': self.firebase_uid, 'timestamp_utc': updated['timestamp_utc'] }, ConsistentRead=True)['Item'] self.assertTrue(exp_from_db['id'] == old_exp['id'])
import json from dateutil.relativedelta import relativedelta from app.helpers.time import utc_now_str, ensure_ts_str_ends_with_z from tests.base_test import BaseTest, BaseTestWithHTTPMethodsMixin, NoAuthenticationMarkerMixin from tests.test_expenses_api import db_facade_path from datetime import datetime, timezone, timedelta from unittest.mock import patch from app.expenses_api.views import db_facade from app.expenses_api.api_error_msgs import ApiError endpoint = 'expenses_api.statistics' valid_url_args = { "from_dt": (datetime.now(timezone.utc) - timedelta(minutes=60)).isoformat(), "to_dt": utc_now_str() } class TestStatisticsAuth(BaseTest, BaseTestWithHTTPMethodsMixin): def test_auth(self): resp = self.get(url=endpoint, url_for_args=valid_url_args) self.assertEqual(403, resp.status_code) @patch(db_facade_path, autospec=True) class TestStatistics(BaseTest, BaseTestWithHTTPMethodsMixin, NoAuthenticationMarkerMixin): def test_normal_usage(self, mocked_db): mocked_result = {"BGN": 100} mocked_db.statistics.return_value = mocked_result
exp1 = sample_expenses[0].copy() exp2 = sample_expenses[1].copy() exp3 = sample_expenses[2].copy() exp4 = sample_expenses[3].copy() now = dt.now(tz.utc) exp1_dt = ensure_ts_str_ends_with_z((now - td(hours=2)).isoformat()) exp1['timestamp_utc'] = exp1_dt exp1['currency'] = "EUR" exp2_dt = ensure_ts_str_ends_with_z((now - td(minutes=5)).isoformat()) exp2['timestamp_utc'] = exp2_dt exp2['currency'] = "EUR" exp3_dt = utc_now_str() exp3['timestamp_utc'] = ensure_ts_str_ends_with_z( (now - td(seconds=30)).isoformat()) exp3['currency'] = 'USD' exp4_dt = ensure_ts_str_ends_with_z((now - td(seconds=5)).isoformat()) exp4['timestamp_utc'] = exp4_dt exp4['currency'] = 'USD' class TestStatistics(DbTestBase): def test_normal_usage(self): self.seedData(firebase_uid=self.firebase_uid, items=[exp1, exp2, exp3, exp4]) test = [{ 'from':
def setUp(self): super(TestGETExpensesList, self).setUp() self.start_from_property = 'timestamp_utc' self.start_from_property_value = utc_now_str() self.valid_request_args = valid_request_args
from app.helpers.time import utc_now_str from app.models.expense_validation import Validator from tests.common_methods import sample_expenses from tests.test_expenses_api import db_facade_path from app.expenses_api.views import MAX_BATCH_SIZE, db_facade from app.expenses_api.api_error_msgs import ApiError endpoint = 'expenses_api.get_expenses_list' reversed_expenses = list(reversed(sample_expenses)) valid_request_args = { "start_from_id": 'some id', "start_from_property": 'timestamp_utc', "start_from_property_value": utc_now_str(), "batch_size": 10, "ordering_direction": 'desc' } class TestGetListAuth(BaseTest, BaseTestWithHTTPMethodsMixin): def test_auth(self): resp = self.get(url=endpoint, url_args=valid_request_args.copy()) self.assertEqual(403, resp.status_code) @patch(db_facade_path, autospec=True) class TestGETExpensesList(BaseTest, BaseTestWithHTTPMethodsMixin, NoAuthenticationMarkerMixin): def setUp(self): super(TestGETExpensesList, self).setUp()
def touch_timestamp(expense, ts_property): assert ts_property in expense expense[ts_property] = utc_now_str()