class TaskQueueTestCase(WebAppTestCase): application = MockupApplication('MockupApplication', None) def test_worker(self): # noinspection PyArgumentList awesome_task = AwesomeTask() self.session.add(awesome_task) another_task = AnotherTask() self.session.add(another_task) bad_task = BadTask() self.session.add(bad_task) self.session.commit() tasks = worker(tries=0, filters=Task.type == 'awesome_task') self.assertEqual(len(tasks), 1) self.assertTrue(awesome_task_done.is_set()) self.assertFalse(another_task_done.is_set()) self.session.refresh(awesome_task) self.assertEqual(awesome_task.status, 'success') tasks = worker(tries=0, filters=Task.type == 'bad_task') self.assertEqual(len(tasks), 1) bad_task_id = tasks[0][0] self.session.refresh(bad_task) self.assertEqual(bad_task.status, 'failed') tasks = worker(tries=0, filters=Task.type == 'bad_task') self.assertEqual(len(tasks), 0) # Reset the status of one task self.session.refresh(bad_task) bad_task.status = 'in-progress' self.session.commit() self.session.refresh(bad_task) Task.reset_status(bad_task_id, self.session) self.session.commit() tasks = worker(tries=0, filters=Task.type == 'bad_task') self.assertEqual(len(tasks), 1) tasks = worker(tries=0, filters=Task.type == 'bad_task') self.assertEqual(len(tasks), 0) # Cleanup all tasks Task.cleanup(self.session, statuses=('in-progress', 'failed')) self.session.commit() tasks = worker(tries=0, filters=Task.type == 'bad_task') self.assertEqual(len(tasks), 1) tasks = worker(tries=0, filters=Task.type == 'bad_task') self.assertEqual(len(tasks), 0) # Doing all remaining tasks tasks = worker(tries=0) self.assertEqual(len(tasks), 1)
class ApproveRequiredMixinTestCase(WebAppTestCase): application = MockupApplication('MockupApplication', None) __configuration__ = ''' db: url: sqlite:// # In memory DB echo: false ''' @classmethod def configure_app(cls): cls.application.configure(force=True) settings.merge(cls.__configuration__) def test_approve_required_mixin(self): # noinspection PyArgumentList object1 = ApproveRequiredObject(title='object 1', ) DBSession.add(object1) DBSession.commit() self.assertFalse(object1.is_approved) self.assertEqual( DBSession.query(ApproveRequiredObject).filter( ApproveRequiredObject.is_approved).count(), 0) object1.is_approved = True self.assertTrue(object1.is_approved) DBSession.commit() object1 = DBSession.query(ApproveRequiredObject).one() self.assertTrue(object1.is_approved) json = object1.to_dict() self.assertIn('isApproved', json) self.assertEqual( DBSession.query(ApproveRequiredObject).filter( ApproveRequiredObject.is_approved).count(), 1) self.assertEqual(ApproveRequiredObject.filter_approved().count(), 1) self.assertFalse( ApproveRequiredObject.import_value( ApproveRequiredObject.is_approved, 'false')) self.assertFalse( ApproveRequiredObject.import_value( ApproveRequiredObject.is_approved, 'FALSE')) self.assertFalse( ApproveRequiredObject.import_value( ApproveRequiredObject.is_approved, 'False')) self.assertTrue( ApproveRequiredObject.import_value( ApproveRequiredObject.is_approved, 'true')) self.assertTrue( ApproveRequiredObject.import_value( ApproveRequiredObject.is_approved, 'TRUE')) self.assertTrue( ApproveRequiredObject.import_value( ApproveRequiredObject.is_approved, 'True')) self.assertEqual( ApproveRequiredObject.import_value(ApproveRequiredObject.title, 'title'), 'title')
class ValidationPreventFormTestCase(WebAppTestCase): application = MockupApplication('MockupApplication', Root()) @classmethod def configure_app(cls): super().configure_app() settings.merge(""" logging: loggers: default: level: info """) def test_validation_prevent_form(self): # Good self.request('All', 'POST', '/validation', doc=False) # Bad self.request('All', 'POST', '/validation', doc=False, params={'param': 'param'}, expected_status=400) self.request('All', 'POST', '/validation', doc=False, query_string={'param': 'param'}, expected_status=400)
class OrderableCheckingModelTestCase(WebAppTestCase): application = MockupApplication('MockupApplication', None) __configuration__ = ''' db: uri: sqlite:// # In memory DB echo: false ''' @classmethod def configure_app(cls): cls.application.configure(force=True) settings.merge(cls.__configuration__) def test_orderable_mixin(self): for i in range(3): # noinspection PyArgumentList instance = OrderableCheckingModel( title='test title %s' % i, order=i ) DBSession.add(instance) DBSession.commit() instances = OrderableCheckingModel.apply_default_sort().all() self.assertEqual(instances[0].order, 0) self.assertEqual(instances[2].order, 2)
class CommitDecoratorTestCase(WebAppTestCase): application = MockupApplication('MockupApplication', Root()) __configuration__ = ''' db: uri: sqlite:// # In memory DB echo: false ''' @classmethod def configure_app(cls): cls.application.configure(force=True) settings.merge(cls.__configuration__) def test_commit_decorator(self): self.request('ALL', 'POST', '/', params=dict(title='first'), doc=False) resp, ___ = self.request('ALL', 'GET', '/first', doc=False) self.assertEqual(resp['title'], 'first') self.assertEqual(resp['id'], 1) def test_commit_decorator_and_json_patch(self): # The commit decorator should not to do anything if the request is a jsonpatch. self.request('ALL', 'PATCH', '/', doc=False, json=[ dict(op='post', path='', value=dict(title='second')), dict(op='post', path='', value=dict(title='third')) ]) resp, ___ = self.request('ALL', 'GET', '/third', doc=False) self.assertEqual(resp['title'], 'third') def test_rollback(self): self.request('ALL', 'ERROR', '/', doc=False, expected_status=500)
class PaginationMixinTestCase(WebAppTestCase): application = MockupApplication('MockupApplication', None) __configuration__ = ''' db: url: sqlite:// # In memory DB echo: false ''' @classmethod def configure_app(cls): cls.application.configure(force=True) settings.merge(cls.__configuration__) def test_pagination_mixin(self): for i in range(1, 6): # noinspection PyArgumentList obj = PagingObject( title='object %s' % i, ) DBSession.add(obj) DBSession.commit() with Context({'QUERY_STRING': 'take=2&skip=1'}, self.application) as context: self.assertEqual(PagingObject.paginate_by_request().count(), 2) self.assertEqual(context.response_headers['X-Pagination-Take'], '2') self.assertEqual(context.response_headers['X-Pagination-Skip'], '1') self.assertEqual(context.response_headers['X-Pagination-Count'], '5') with Context({'QUERY_STRING': 'take=two&skip=one'}, self.application): self.assertEqual(PagingObject.paginate_by_request().count(), 4) with Context({'QUERY_STRING': 'take=5'}, self.application): self.assertRaises(HttpBadRequest, PagingObject.paginate_by_request)
class FullTextSearchMixinTestCase(WebAppTestCase): application = MockupApplication('MockupApplication', None) __configuration__ = ''' db: uri: sqlite:// # In memory DB echo: false ''' @classmethod def configure_app(cls): cls.application.configure(force=True) settings.merge(cls.__configuration__) def test_fts_escape(self): result = fts_escape('&%!^$*[](){}\\') self.assertEqual(result, '\&\%\!\^\$\*\[\]\(\)\{\}\\\\') def test_to_tsvector(self): result = to_tsvector('a', 'b', 'c') self.assertEqual(str(result), 'to_tsvector(:to_tsvector_1, :to_tsvector_2)') def test_activation_mixin(self): # noinspection PyArgumentList query = FullTextSearchObject.search('a') self.assertEqual( str(query).replace('\n', ' '), 'SELECT fulltext_search_object.id AS fulltext_search_object_id, fulltext_search_object.title AS ' 'fulltext_search_object_title FROM fulltext_search_object WHERE to_tsvector(%(to_tsvector_1)s, ' 'fulltext_search_object.title) @@ to_tsquery(%(to_tsvector_2)s)')
def oauth_mockup_server(root_controller): app = MockupApplication('mockup-oauth', root_controller) with http_server(app) as (server, url): settings.merge(f''' tokenizer: url: {url} ''') yield app
class ApplicationTestCase(WebAppTestCase): application = MockupApplication('MockupApplication', Root()) def test_index(self): response, headers = self.request('ALL', 'GET', '/') self.assertEqual(response, b'Index') def test_options(self): response, headers = self.request('ALL', 'OPTIONS', '/') self.assertEqual(headers['Cache-Control'], 'no-cache,no-store')
class EtagCheckingModelTestCase(WebAppTestCase): application = MockupApplication('MockupApplication', Root()) __configuration__ = ''' db: url: sqlite:// # In memory DB echo: false ''' @classmethod def configure_app(cls): cls.application.configure(force=True) settings.merge(cls.__configuration__) def test_etag_match(self): resp, headers = self.request( 'ALL', 'POST', '/', params=[FormParameter('title', 'etag_test')]) self.assertIn('ETag', headers) initial_etag = headers['ETag'] # Getting the resource with known etag, expected 304 self.request('ALL', 'GET', '/etag_test', headers={'If-None-Match': initial_etag}, expected_status=304) # Putting without the etag header, expected error: Precondition Failed self.request('ALL', 'PUT', '/etag_test', params=[FormParameter('title', 'etag_test_edit1')], expected_status=412) # Putting with the etag header, expected: success resp, headers = self.request( 'ALL', 'PUT', '/etag_test', params=[FormParameter('title', 'etag_test_edit1')], headers={'If-Match': initial_etag}) self.assertIn('ETag', headers) etag_after_put = headers['ETag'] self.assertNotEqual(initial_etag, etag_after_put) # Getting the resource with known etag, expected 304 self.request('ALL', 'GET', '/etag_test_edit1', headers={'If-None-Match': initial_etag}, expected_status=200) self.assertIn('ETag', headers) new_etag = headers['ETag'] self.assertNotEqual(new_etag, initial_etag) self.assertEqual(new_etag, etag_after_put)
class OrderingMixinTestCase(WebAppTestCase): application = MockupApplication('MockupApplication', None) __configuration__ = ''' db: uri: sqlite:// # In memory DB echo: false ''' @classmethod def configure_app(cls): cls.application.configure(force=True) settings.merge(cls.__configuration__) def test_ordering_mixin(self): for i in range(1, 6): # noinspection PyArgumentList obj = OrderableOrderingObject(title='object %s' % i, age=i * 10) DBSession.add(obj) # noinspection PyArgumentList obj = OrderingObject(title='object %s' % i, ) DBSession.add(obj) DBSession.commit() # Default soring with Orderable objects with Context({'QUERY_STRING': ''}, self.application): result = OrderableOrderingObject.sort_by_request().all() self.assertEqual(result[0].id, 1) self.assertEqual(result[-1].id, 5) # Default soring without Orderable objects with Context({'QUERY_STRING': ''}, self.application): result = OrderingObject.sort_by_request().all() self.assertEqual(len(result), 5) # Ascending with Context({'QUERY_STRING': 'sort=id'}, self.application): result = OrderableOrderingObject.sort_by_request().all() self.assertEqual(result[0].id, 1) self.assertEqual(result[-1].id, 5) # Descending with Context({'QUERY_STRING': 'sort=-id'}, self.application): result = OrderableOrderingObject.sort_by_request().all() self.assertEqual(result[0].id, 5) self.assertEqual(result[-1].id, 1) # Sort by Synonym Property with Context({'QUERY_STRING': 'sort=age'}, self.application): result = OrderableOrderingObject.sort_by_request().all() self.assertEqual(result[0].id, 1) self.assertEqual(result[-1].id, 5)
class ModificationCheckingModelTestCase(WebAppTestCase): application = MockupApplication('MockupApplication', None) __configuration__ = ''' db: url: sqlite:// # In memory DB echo: false ''' @classmethod def configure_app(cls): cls.application.configure(force=True) settings.merge(cls.__configuration__) def test_modified_mixin(self): # noinspection PyArgumentList instance = ModificationCheckingModel( title='test title', ) DBSession.add(instance) DBSession.commit() self.assertIsNone(instance.modified_at) self.assertIsNotNone(instance.created_at) self.assertEqual(instance.last_modification_time, instance.created_at) instance = DBSession.query(ModificationCheckingModel).one() self.assertIsNone(instance.modified_at) self.assertIsNotNone(instance.created_at) self.assertEqual(instance.last_modification_time, instance.created_at) instance.title = 'Edited title' DBSession.commit() self.assertIsNotNone(instance.modified_at) self.assertIsNotNone(instance.created_at) self.assertEqual(instance.last_modification_time, instance.modified_at) instance = DBSession.query(ModificationCheckingModel).one() self.assertIsNotNone(instance.modified_at) self.assertIsNotNone(instance.created_at) self.assertEqual(instance.last_modification_time, instance.modified_at)
class ModelValidationDecoratorTestCase(WebAppTestCase): application = MockupApplication('MockupApplication', Root()) __configuration__ = ''' db: url: sqlite:// # In memory DB echo: false ''' @classmethod def configure_app(cls): cls.application.configure(force=True) settings.merge(cls.__configuration__) def test_model_validate_decorator(self): # Required self.request('ALL', 'POST', '/', params={}, expected_status=400) # Correct pattern resp, ___ = self.request('ALL', 'POST', '/', params=dict(title='Test'), doc=False) self.assertEqual(resp['title'], 'Test') # Invalid pattern self.request('ALL', 'POST', '/', params=dict(title='startWithSmallCase'), expected_status=400, doc=False) # Readonly self.request('ALL', 'POST', '/', params=dict(title='Test', modified=datetime.now().isoformat()), expected_status=400, doc=False)
class SoftDeleteCheckingModelTestCase(WebAppTestCase): application = MockupApplication('MockupApplication', None) __configuration__ = ''' db: uri: sqlite:// # In memory DB echo: false ''' @classmethod def configure_app(cls): cls.application.configure(force=True) settings.merge(cls.__configuration__) def test_soft_delete_mixin(self): # noinspection PyArgumentList instance = SoftDeleteCheckingModel(title='test title') DBSession.add(instance) DBSession.commit() instance.assert_is_not_deleted() self.assertRaises(ValueError, instance.assert_is_deleted) instance = SoftDeleteCheckingModel.query.one() instance.soft_delete() DBSession.commit() instance.assert_is_deleted() self.assertRaises(ValueError, instance.assert_is_not_deleted) self.assertEqual(SoftDeleteCheckingModel.filter_deleted().count(), 1) self.assertEqual(SoftDeleteCheckingModel.exclude_deleted().count(), 0) instance.soft_undelete() DBSession.commit() instance.assert_is_not_deleted() self.assertRaises(ValueError, instance.assert_is_deleted) self.assertEqual(SoftDeleteCheckingModel.filter_deleted().count(), 0) self.assertEqual(SoftDeleteCheckingModel.exclude_deleted().count(), 1) DBSession.delete(instance) self.assertRaises(HttpConflict, DBSession.commit)
class MessagingModelTestCase(WebAppTestCase): application = MockupApplication('MockupApplication', None) __configuration__ = ''' db: uri: sqlite:// # In memory DB echo: false messaging: default_sender: [email protected] default_messenger: restfulpy.testing.helpers.MockupMessenger ''' @classmethod def configure_app(cls): cls.application.configure(force=True) settings.merge(cls.__configuration__) def test_messaging_model(self): mockup_messenger = create_messenger() # noinspection PyArgumentList message = Welcome(to='*****@*****.**', subject='Test Subject', body={'msg': 'Hello'}) DBSession.add(message) DBSession.commit() message.do_({}) # noinspection PyUnresolvedReferences self.assertDictEqual( mockup_messenger.last_message, { 'body': { 'msg': 'Hello' }, 'subject': 'Test Subject', 'to': '*****@*****.**' })
class BaseModelTestCase(WebAppTestCase): application = MockupApplication('MockupApplication', Root()) __configuration__ = ''' db: uri: sqlite:// # In memory DB echo: false ''' @classmethod def configure_app(cls): cls.application.configure(force=True) settings.merge(cls.__configuration__) def test_update_from_request(self): resp, ___ = self.request('ALL', 'POST', '/', params=dict(title='test', firstName='test', lastName='test', email='*****@*****.**', password='******', birth='01-01-01', weight=1.1), doc=False) self.assertEqual(resp['title'], 'test') resp, ___ = self.request('ALL', 'GET', '/', doc=False) self.assertEqual(len(resp), 1) self.assertEqual(resp[0]['title'], 'test') # 404 self.request('ALL', 'GET', '/non-existance-user', doc=False, expected_status=404) # Plain dictionary resp, ___ = self.request('ALL', 'GET', '/me', doc=False) self.assertEqual(resp['title'], 'me') def test_iter_columns(self): columns = { c.key: c for c in Member.iter_columns( relationships=False, synonyms=False, composites=False) } self.assertEqual(len(columns), 9) self.assertNotIn('name', columns) self.assertNotIn('password', columns) self.assertIn('_password', columns) def test_iter_json_columns(self): columns = { c.key: c for c in Member.iter_json_columns(include_readonly_columns=False, include_protected_columns=False) } self.assertEqual(len(columns), 8) self.assertNotIn('name', columns) self.assertNotIn('password', columns) self.assertNotIn('_password', columns) def test_metadata(self): resp, ___ = self.request('ALL', 'METADATA', '/', doc=False) self.assertIn('firstName', resp)
class FilteringMixinTestCase(WebAppTestCase): application = MockupApplication('MockupApplication', None) __configuration__ = ''' db: uri: sqlite:// # In memory DB echo: false ''' @classmethod def configure_app(cls): cls.application.configure(force=True) settings.merge(cls.__configuration__) def test_filtering_mixin(self): for i in range(1, 6): # noinspection PyArgumentList obj = FilteringObject( title='object %s' % i, ) DBSession.add(obj) DBSession.commit() # Bad Value with Context({'QUERY_STRING': 'id=1'}, self.application) as context: context.query_string['id'] = 1 self.assertRaises(HttpBadRequest, FilteringObject.filter_by_request) # IN with Context({'QUERY_STRING': 'id=^1,2,3'}, self.application): self.assertEqual(FilteringObject.filter_by_request().count(), 3) # NOT IN with Context({'QUERY_STRING': 'id=!^1,2,3'}, self.application): self.assertEqual(FilteringObject.filter_by_request().count(), 2) # IN (error) with Context({'QUERY_STRING': 'id=^'}, self.application): self.assertRaises(HttpBadRequest, FilteringObject.filter_by_request) # Between with Context({'QUERY_STRING': 'id=~1,3'}, self.application): self.assertEqual(FilteringObject.filter_by_request().count(), 3) # IS NULL with Context({'QUERY_STRING': 'title=null'}, self.application): self.assertEqual(FilteringObject.filter_by_request().count(), 0) # IS NOT NULL with Context({'QUERY_STRING': 'title=!null'}, self.application): self.assertEqual(FilteringObject.filter_by_request().count(), 5) # LIKE with Context({'QUERY_STRING': 'title=%obj'}, self.application): self.assertEqual(FilteringObject.filter_by_request().count(), 5) with Context({'QUERY_STRING': 'title=%OBJ'}, self.application): self.assertEqual(FilteringObject.filter_by_request().count(), 0) # ILIKE with Context({'QUERY_STRING': 'title=%~obj'}, self.application): self.assertEqual(FilteringObject.filter_by_request().count(), 5) with Context({'QUERY_STRING': 'title=%~OBJ'}, self.application): self.assertEqual(FilteringObject.filter_by_request().count(), 5) # == with Context({'QUERY_STRING': 'id=1'}, self.application): self.assertEqual(FilteringObject.filter_by_request().count(), 1) # != with Context({'QUERY_STRING': 'id=!1'}, self.application): self.assertEqual(FilteringObject.filter_by_request().count(), 4) # >= with Context({'QUERY_STRING': 'id=>=2'}, self.application): self.assertEqual(FilteringObject.filter_by_request().count(), 4) # > with Context({'QUERY_STRING': 'id=>2'}, self.application): self.assertEqual(FilteringObject.filter_by_request().count(), 3) # <= with Context({'QUERY_STRING': 'id=<=3'}, self.application): self.assertEqual(FilteringObject.filter_by_request().count(), 3) # < with Context({'QUERY_STRING': 'id=<3'}, self.application): self.assertEqual(FilteringObject.filter_by_request().count(), 2)
class ValidationPatternTestCase(WebAppTestCase): application = MockupApplication('MockupApplication', Root()) @classmethod def configure_app(cls): super().configure_app() settings.merge(""" logging: loggers: default: level: info """) def test_validation_pattern(self): # Test `pattern` # role -> All self.wsgi_app.jwt_token = DummyIdentity().dump().decode() self.request('All', 'POST', '/validation', doc=False, params={ 'patternedParam1': '0123456789', 'patternedParam2': 'abcdeFGHIJ', 'patternedParam3': 'Exact' }) self.request('All', 'POST', '/validation', doc=False, params={'patternedParam1': '12345'}, expected_status=400) # ----------------------------- # role -> Client self.wsgi_app.jwt_token = DummyIdentity('client').dump().decode() self.request('Client', 'POST', '/validation', doc=False, params={ 'patternedParam1': '12345', 'patternedParam2': 'ABCDE', 'patternedParam3': 'Exact', 'patternedParam4': 'anything' }) self.request('Client', 'POST', '/validation', doc=False, params={'patternedParam1': '1'}, expected_status=400) # ----------------------------- # role -> Admin self.wsgi_app.jwt_token = DummyIdentity('admin').dump().decode() self.request('Admin', 'POST', '/validation', doc=False, params={ 'patternedParam1': '1', 'patternedParam2': 'ABCDEFGHIJ', 'patternedParam4': 'SuperAdmin' }) self.request('Admin', 'POST', '/validation', doc=False, params={'patternedParam4': 'anything'}, expected_status=400)
class StatefulAuthenticatorTestCase(WebAppTestCase): application = MockupApplication( 'MockupApplication', Root(), authenticator=MockupStatefulAuthenticator()) @classmethod def configure_app(cls): cls.application.configure(force=True) settings.merge(""" jwt: max_age: .3 refresh_token: max_age: 3 """) def test_invalidate_token(self): response, headers = self.request('ALL', 'GET', '/login', json=dict(email='*****@*****.**', password='******')) refresh_token = headers['Set-Cookie'].split('; ')[0] self.assertIn('token', response) self.assertTrue(refresh_token.startswith('refresh-token=')) # Login on client token = response['token'] self.wsgi_app.jwt_token = token # Request a protected resource to ensure authenticator is working well response, ___ = self.request(As.member, 'GET', '/me', headers={'Cookie': refresh_token}) self.assertListEqual(response['roles'], roles) # Invalidating the token by server roles.append('god') response, headers = self.request(As.member, 'GET', '/invalidate_token', headers={'Cookie': refresh_token}) self.assertListEqual(response['roles'], roles) self.assertIn('X-New-JWT-Token', headers) # Invalidating the token by server after the token has been expired expired, with appropriate cookies. time.sleep(1) response, headers = self.request(As.member, 'GET', '/invalidate_token', headers={'Cookie': refresh_token}) self.assertIn('X-New-JWT-Token', headers) self.assertIsNotNone(headers['X-New-JWT-Token']) self.wsgi_app.jwt_token = headers['X-New-JWT-Token'] self.request(As.member, 'GET', '/me', headers={'Cookie': refresh_token}) def test_logout(self): response, headers = self.request('ALL', 'POST', '/login', json=dict(email='*****@*****.**', password='******')) self.assertIn('token', response) self.assertEqual(headers['X-Identity'], '1') self.wsgi_app.jwt_token = response['token'] response, headers = self.request('ALL', 'DELETE', '/logout') self.assertNotIn('X-Identity', headers) def test_session_member(self): with Context(environ={}, application=self.application): principal = self.application.__authenticator__.login( ('*****@*****.**', 'test')) self.assertEqual( self.application.__authenticator__.get_session_member( principal.session_id), 1) @freeze_time("2017-07-13T13:11:44", tz_offset=-4) def test_session_info(self): # Login response, headers = self.request('ALL', 'GET', '/login', json=dict(email='*****@*****.**', password='******')) self.wsgi_app.jwt_token = response['token'] # Testing test cases for test_case in session_info_test_cases: # Our new session info should be updated payload, ___ = self.request(As.member, 'GET', '/me', extra_environ=test_case['environment']) info = self.application.__authenticator__.get_session_info( payload['sessionId']) self.assertDictEqual( info, { 'remoteAddress': test_case['expected_remote_address'], 'machine': test_case['expected_machine'], 'os': test_case['expected_os'], 'agent': test_case['expected_agent'], 'client': test_case['expected_client'], 'app': test_case['expected_app'], 'lastActivity': test_case['expected_last_activity'], })
class ActivationMixinTestCase(WebAppTestCase): application = MockupApplication('MockupApplication', None) __configuration__ = ''' db: url: sqlite:// # In memory DB echo: false ''' @classmethod def configure_app(cls): cls.application.configure(force=True) settings.merge(cls.__configuration__) def test_activation_mixin(self): # noinspection PyArgumentList object1 = ActiveObject(title='object 1', ) DBSession.add(object1) DBSession.commit() self.assertFalse(object1.is_active) self.assertEqual( DBSession.query(ActiveObject).filter( ActiveObject.is_active).count(), 0) object1.is_active = True self.assertTrue(object1.is_active) DBSession.commit() object1 = DBSession.query(ActiveObject).one() self.assertTrue(object1.is_active) json = object1.to_dict() self.assertIn('isActive', json) self.assertEqual( DBSession.query(ActiveObject).filter( ActiveObject.is_active).count(), 1) self.assertEqual(ActiveObject.filter_activated().count(), 1) self.assertFalse( ActiveObject.import_value(ActiveObject.is_active, 'false')) self.assertFalse( ActiveObject.import_value(ActiveObject.is_active, 'FALSE')) self.assertFalse( ActiveObject.import_value(ActiveObject.is_active, 'False')) self.assertTrue( ActiveObject.import_value(ActiveObject.is_active, 'true')) self.assertTrue( ActiveObject.import_value(ActiveObject.is_active, 'TRUE')) self.assertTrue( ActiveObject.import_value(ActiveObject.is_active, 'True')) self.assertEqual( ActiveObject.import_value(ActiveObject.title, 'title'), 'title') def test_auto_activation(self): # noinspection PyArgumentList object1 = AutoActiveObject(title='object 1', ) DBSession.add(object1) DBSession.commit() self.assertTrue(object1.is_active) self.assertEqual( 1, DBSession.query(AutoActiveObject).filter( AutoActiveObject.is_active).count()) def test_metadata(self): # Metadata object_metadata = ActiveObject.json_metadata() self.assertIn('id', object_metadata['fields']) self.assertIn('title', object_metadata['fields']) self.assertIn('isActive', object_metadata['fields'])
class ValidationExactTestCase(WebAppTestCase): application = MockupApplication('MockupApplication', Root()) @classmethod def configure_app(cls): super().configure_app() settings.merge(""" logging: loggers: default: level: info """) def test_validation_exact(self): # Test `exact` # role -> All self.wsgi_app.jwt_token = DummyIdentity().dump().decode() result, ___ = self.request('All', 'POST', '/validation', doc=False, params={'exactParamForAll': 'param'}) self.request('All', 'POST', '/validation', doc=False, expected_status=400) self.request('All', 'POST', '/validation', doc=False, params={ 'exactParamForAll': 'param', 'exactParamForCustom': 'param', }, expected_status=400) self.request('All', 'POST', '/validation', doc=False, params={'exactParamForCustom': 'param'}, expected_status=400) self.assertIn('exactParamForAll', result) self.request('All', 'POST', '/validation', doc=False, params={ 'exactParamForAll': 'param', 'exactParamForClient': 'param', }, expected_status=400) # ----------------------------- # role -> Client self.wsgi_app.jwt_token = DummyIdentity('client').dump().decode() result, ___ = self.request('Client', 'POST', '/validation', doc=False, params={ 'exactParamForAll': 'param', 'exactParamForClient': 'param', }) self.assertIn('exactParamForClient', result) self.assertIn('exactParamForAll', result) self.request('Client', 'POST', '/validation', doc=False, params={ 'exactParamForAll': 'param', 'exactParamForClient': 'param', 'exactParamForAdmin': 'param', }, expected_status=400) # ----------------------------- # role -> Admin self.wsgi_app.jwt_token = DummyIdentity('admin').dump().decode() result, ___ = self.request('Admin', 'POST', '/validation', doc=False, params={ 'exactParamForAll': 'param', 'exactParamForAdmin': 'param', }) self.assertIn('exactParamForAdmin', result) self.assertIn('exactParamForAll', result) self.request('Admin', 'POST', '/validation', doc=False, params={ 'exactParamForAll': 'param', 'exactParamForClient': 'param', 'exactParamForAdmin': 'param', }, expected_status=400) # ------------------------------------------------------------ # Test query string self.wsgi_app.jwt_token = DummyIdentity('admin').dump().decode() result, ___ = self.request('Admin', 'POST', '/validation', doc=False, query_string={ 'exactParamForAll': 'param', }, params={ 'exactParamForAdmin': 'param', }) self.assertIn('exactParamForAdmin', result) self.assertIn('exactParamForAll', result) self.request('Admin', 'POST', '/validation', doc=False, query_string={ 'exactParamForAll': 'param', 'exactParamForClient': 'param', }, params={'exactParamForAdmin': 'param'}, expected_status=400)
class StatefulAuthenticatorTestCase(WebAppTestCase): application = MockupApplication( 'MockupApplication', Root(), authenticator=MockupStatefulAuthenticator()) @classmethod def configure_app(cls): cls.application.configure(force=True) settings.merge(""" jwt: max_age: .3 refresh_token: max_age: 3 """) def test_invalidate_token(self): response, headers = self.request('ALL', 'GET', '/login', json=dict(email='*****@*****.**', password='******')) refresh_token = headers['Set-Cookie'].split('; ')[0] self.assertIn('token', response) self.assertTrue(refresh_token.startswith('refresh-token=')) # Login on client token = response['token'] self.wsgi_app.jwt_token = token # Request a protected resource to ensure authenticator is working well response, ___ = self.request(As.member, 'GET', '/me', headers={'Cookie': refresh_token}) self.assertListEqual(response['roles'], roles) # Invalidating the token by server roles.append('god') response, headers = self.request(As.member, 'GET', '/invalidate_token', headers={'Cookie': refresh_token}) self.assertListEqual(response['roles'], roles) self.assertIn('X-New-JWT-Token', headers) # Invalidating the token by server after the token has been expired expired, with appropriate cookies. time.sleep(1) response, headers = self.request(As.member, 'GET', '/invalidate_token', headers={'Cookie': refresh_token}) self.assertIn('X-New-JWT-Token', headers) self.assertIsNotNone(headers['X-New-JWT-Token']) self.wsgi_app.jwt_token = headers['X-New-JWT-Token'] self.request(As.member, 'GET', '/me', headers={'Cookie': refresh_token}) def test_logout(self): response, headers = self.request('ALL', 'POST', '/login', json=dict(email='*****@*****.**', password='******')) self.assertIn('token', response) self.assertEqual(headers['X-Identity'], '1') self.wsgi_app.jwt_token = response['token'] response, headers = self.request('ALL', 'DELETE', '/logout') self.assertEqual(headers['X-Identity'], '')
class BaseModelTestCase(WebAppTestCase): application = MockupApplication('MockupApplication', Root()) __configuration__ = ''' db: url: sqlite:// # In memory DB echo: false ''' @classmethod def configure_app(cls): cls.application.configure(force=True) settings.merge(cls.__configuration__) settings.merge(''' logging: loggers: default: level: debug ''') def test_update_from_request(self): resp, ___ = self.request('ALL', 'POST', '/', params=dict( title='test', firstName='test', lastName='test', email='*****@*****.**', password='******', birth='2001-01-01', weight=1.1, visible='false', lastLoginTime='2017-10-10T15:44:30.000', isActive=True), doc=False) self.assertEqual(resp['title'], 'test') resp, ___ = self.request('ALL', 'GET', '/', query_string=dict(take=1), doc=False) self.assertEqual(len(resp), 1) self.assertEqual(resp[0]['title'], 'test') self.assertEqual(resp[0]['visible'], False) # 404 self.request('ALL', 'GET', '/non-existence-user', doc=False, expected_status=404) # Plain dictionary resp, ___ = self.request('ALL', 'GET', '/me', doc=False) self.assertEqual(resp['title'], 'me') # 400 for sending relationship attribute self.request('ALL', 'POST', '/', json=dict(title='test', firstName='test', lastName='test', email='*****@*****.**', password='******', birth='2001-01-01', weight=1.1, visible='false', lastLoginTime='2017-10-10T15:44:30.000', isActive=True, books=[]), doc=False, expected_status=400) def test_iter_columns(self): columns = { c.key: c for c in Member.iter_columns( relationships=False, synonyms=False, composites=False) } self.assertEqual(len(columns), 15) self.assertNotIn('name', columns) self.assertNotIn('password', columns) self.assertIn('_password', columns) def test_iter_json_columns(self): columns = { c.key: c for c in Member.iter_json_columns(include_readonly_columns=False, include_protected_columns=False) } self.assertEqual(len(columns), 13) self.assertNotIn('name', columns) self.assertNotIn('password', columns) self.assertNotIn('_password', columns) def test_metadata(self): resp, ___ = self.request('ALL', 'METADATA', '/', doc=False) self.assertIn('fields', resp) self.assertIn('name', resp) self.assertIn('primaryKeys', resp) self.assertIn('id', resp['primaryKeys']) self.assertEqual(resp['name'], 'Member') fields = resp['fields'] self.assertIn('id', fields) self.assertIn('firstName', fields) self.assertEqual(fields['id']['primaryKey'], True) self.assertEqual(fields['email']['primaryKey'], False) self.assertEqual(fields['title']['primaryKey'], False) self.assertEqual(fields['title']['minLength'], 2) self.assertEqual(fields['title']['maxLength'], 50) self.assertEqual(fields['email']['pattern'], '(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)') self.assertEqual(fields['firstName']['name'], 'firstName') self.assertEqual(fields['firstName']['key'], 'first_name') self.assertEqual(fields['firstName']['type_'], 'str') self.assertEqual(fields['birth']['type_'], 'date') self.assertEqual(fields['weight']['default_'], 50) self.assertEqual(fields['visible']['optional'], True) self.assertEqual(fields['email']['message'], 'Invalid email address, please be accurate!') self.assertEqual(fields['email']['watermark'], 'Email') self.assertEqual(fields['email']['label'], 'Email') self.assertEqual(fields['email']['icon'], 'email.svg') self.assertEqual(fields['email']['example'], '*****@*****.**') def test_datetime_format(self): # datetime allows contain microseconds self.request('ALL', 'POST', '/', params=dict(title='test', firstName='test', lastName='test', email='*****@*****.**', password='******', birth='2001-01-01', weight=1.1, visible='false', lastLoginTime='2017-10-10T10:10:00.12313'), doc=False) self.request('ALL', 'POST', '/', params=dict(title='test', firstName='test', lastName='test', email='*****@*****.**', password='******', birth='2001-01-01', weight=1.1, visible='false', lastLoginTime='2017-10-10T10:10:00.'), doc=False, expected_status=400) self.request('ALL', 'POST', '/', params=dict(title='test', firstName='test', lastName='test', email='*****@*****.**', password='******', birth='2001-01-01', weight=1.1, visible='false', lastLoginTime='2017-10-10T10:10:00'), doc=False) # Invalid month value self.request('ALL', 'POST', '/', params=dict(title='test', firstName='test', lastName='test', email='*****@*****.**', password='******', birth='2001-01-01', weight=1.1, visible='false', lastLoginTime='2017-13-10T10:10:00'), doc=False, expected_status=400) # Invalid datetime format self.request('ALL', 'POST', '/', params=dict(title='test', firstName='test', lastName='test', email='*****@*****.**', password='******', birth='2001-01-01', weight=1.1, visible='false', lastLoginTime='InvalidDatetime'), doc=False, expected_status=400) # datetime might not have ending Z resp, ___ = self.request('ALL', 'POST', '/', params=dict( title='test', firstName='test', lastName='test', email='*****@*****.**', password='******', birth='2001-01-01', weight=1.1, visible='false', lastLoginTime='2017-10-10T10:10:00.4546'), doc=False) self.assertEqual(resp['lastLoginTime'], '2017-10-10T10:10:00.004546Z') # datetime containing ending Z resp, ___ = self.request( 'ALL', 'POST', '/', params=dict(title='test', firstName='test', lastName='test', email='*****@*****.**', password='******', birth='2001-01-01', weight=1.1, visible='false', lastLoginTime='2017-10-10T10:10:00.4546Z'), doc=False) self.assertEqual(resp['lastLoginTime'], '2017-10-10T10:10:00.004546Z') def test_date_format(self): # iso date format resp, ___ = self.request( 'ALL', 'POST', '/', params=dict(title='test', firstName='test', lastName='test', email='*****@*****.**', password='******', birth='2001-01-01', weight=1.1, visible='false', lastLoginTime='2017-10-10T10:10:00.4546Z'), doc=False) self.assertEqual(resp['birth'], '2001-01-01') self.assertEqual(resp['lastLoginTime'], '2017-10-10T10:10:00.004546Z') # posix timestamp resp, ___ = self.request('ALL', 'POST', '/', params=dict(title='test', firstName='test', lastName='test', email='*****@*****.**', password='******', birth='1513434403', weight=1.1, visible='false', lastLoginTime=datetime( 2017, 10, 10, 10, 10, 0, 4546).timestamp()), doc=False) self.assertEqual(resp['birth'], '2017-12-16') self.assertEqual(resp['lastLoginTime'], '2017-10-10T10:10:00.004546Z') # none iso date format self.request('ALL', 'POST', '/', params=dict(title='test', firstName='test', lastName='test', email='*****@*****.**', password='******', birth='01-01-01', weight=1.1, visible='false', lastLoginTime='2017-10-10T10:10:00.4546Z'), doc=False, expected_status=400) # none iso date format self.request('ALL', 'POST', '/', params=dict(title='test', firstName='test', lastName='test', email='*****@*****.**', password='******', birth='2001/01/01', weight=1.1, visible='false', lastLoginTime='2017-10-10T10:10:00.4546Z'), doc=False, expected_status=400)
class ValidationFilterTestCase(WebAppTestCase): application = MockupApplication('MockupApplication', Root()) @classmethod def configure_app(cls): super().configure_app() settings.merge(""" logging: loggers: default: level: info """) def test_validation_filter(self): # Test `filter` # role -> All self.wsgi_app.jwt_token = DummyIdentity().dump().decode() result, ___ = self.request('All', 'POST', '/validation', doc=False, params={ 'customParam': 'param', 'filteredParamForAll': 'param', 'filteredParamForClient': 'param', 'filteredParamForAdmin': 'param', }) self.assertNotIn('customParam', result) self.assertNotIn('filteredParamForClient', result) self.assertNotIn('filteredParamForAdmin', result) self.assertIn('filteredParamForAll', result) # ----------------------------- # role -> Client self.wsgi_app.jwt_token = DummyIdentity('client').dump().decode() result, ___ = self.request('Client', 'POST', '/validation', doc=False, params={ 'customParam': 'param', 'filteredParamForAll': 'param', 'filteredParamForClient': 'param', 'filteredParamForAdmin': 'param', }) self.assertNotIn('customParam', result) self.assertIn('filteredParamForClient', result) self.assertNotIn('filteredParamForAdmin', result) self.assertIn('filteredParamForAll', result) # ----------------------------- # role -> Admin self.wsgi_app.jwt_token = DummyIdentity('admin').dump().decode() result, ___ = self.request('Admin', 'POST', '/validation', doc=False, params={ 'customParam': 'param', 'filteredParamForAll': 'param', 'filteredParamForClient': 'param', 'filteredParamForAdmin': 'param', }) self.assertNotIn('customParam', result) self.assertNotIn('filteredParamForClient', result) self.assertIn('filteredParamForAdmin', result) self.assertIn('filteredParamForAll', result)
class ValidationTypesTestCase(WebAppTestCase): application = MockupApplication('MockupApplication', Root()) @classmethod def configure_app(cls): super().configure_app() settings.merge(""" logging: loggers: default: level: info """) def test_validation_types(self): # Test `type` # role -> All self.wsgi_app.jwt_token = DummyIdentity().dump().decode() result, ___ = self.request( 'All', 'POST', '/validation', doc=False, params={ 'typedParam1': '1', 'typedParam2': '2', 'typedParam3': '3', 'typedParam4': '4' } ) self.assertEqual(type(result['typedParam1']), float) self.assertEqual(type(result['typedParam2']), float) self.assertEqual(type(result['typedParam3']), float) self.assertEqual(type(result['typedParam4']), str) self.request( 'All', 'POST', '/validation', doc=False, params={'typedParam1': 'not_convertible'}, expected_status=400 ) # ----------------------------- # role -> Client self.wsgi_app.jwt_token = DummyIdentity('client').dump().decode() result, ___ = self.request( 'Client', 'POST', '/validation', doc=False, params={ 'typedParam1': '1', 'typedParam2': '2', 'typedParam3': '3', 'typedParam4': '4' } ) self.assertEqual(type(result['typedParam1']), int) self.assertEqual(type(result['typedParam2']), int) self.assertEqual(type(result['typedParam3']), float) self.assertEqual(type(result['typedParam4']), str) self.request( 'Client', 'POST', '/validation', doc=False, params={'typedParam1': 'not_convertible'}, expected_status=400 ) # ----------------------------- # role -> Admin self.wsgi_app.jwt_token = DummyIdentity('admin').dump().decode() result, ___ = self.request( 'Admin', 'POST', '/validation', doc=False, params={ 'typedParam1': '1', 'typedParam2': '2', 'typedParam3': '3', 'typedParam4': '4' } ) # type complex is dict self.assertEqual(type(result['typedParam1']), dict) self.assertEqual(type(result['typedParam2']), float) self.assertEqual(type(result['typedParam3']), float) self.assertEqual(type(result['typedParam4']), dict) self.request( 'Admin', 'POST', '/validation', doc=False, params={'typedParam1': 'not_convertible'}, expected_status=400 )
class AuthenticatorTestCase(WebAppTestCase): application = MockupApplication( 'MockupApplication', Root(), authenticator=MockupStatelessAuthenticator()) @classmethod def configure_app(cls): cls.application.configure(force=True) settings.merge(""" jwt: max_age: .8 refresh_token: max_age: 2.2 """) def test_login(self): response, headers = self.request('ALL', 'POST', '/login', json=dict(email='*****@*****.**', password='******')) self.assertIn('token', response) self.assertEqual(headers['X-Identity'], '1') self.wsgi_app.jwt_token = response['token'] response, headers = self.request('ALL', 'GET', '/', json=dict(a='a', b='b')) self.assertEqual(headers['X-Identity'], '1') # Broken token self.wsgi_app.jwt_token = self.wsgi_app.jwt_token[:-10] self.request('ALL', 'GET', '/', expected_status=400) # Empty self.wsgi_app.jwt_token = ' ' self.request('ALL', 'GET', '/me', expected_status=401) # Bad Password self.wsgi_app.jwt_token = None self.request('ALL', 'POST', '/login', json=dict(email='*****@*****.**', password='******'), expected_status=400) def test_logout(self): response, headers = self.request('ALL', 'POST', '/login', json=dict(email='*****@*****.**', password='******')) self.assertIn('token', response) self.assertEqual(headers['X-Identity'], '1') self.wsgi_app.jwt_token = response['token'] response, headers = self.request('ALL', 'DELETE', '/logout') self.assertEqual(headers['X-Identity'], '') def test_refresh_token(self): self.wsgi_app.jwt_token = None response, headers = self.request('ALL', 'POST', '/login', json=dict(email='*****@*****.**', password='******')) refresh_token = headers['Set-Cookie'].split('; ')[0] self.assertIn('token', response) self.assertTrue(refresh_token.startswith('refresh-token=')) # Login on client token = response['token'] self.wsgi_app.jwt_token = token time.sleep(1.1) # Request a protected resource after the token has been expired expired, with broken cookies self.request(As.member, 'GET', '/me', headers={'Cookie': 'refresh-token=broken-data'}, expected_status=400) # Request a protected resource after the token has been expired expired, with empty cookies self.request(As.member, 'GET', '/me', headers={'Cookie': 'refresh-token='}, expected_status=401) # Request a protected resource after the token has been expired expired, without the cookies self.request(As.member, 'GET', '/me', expected_status=401) # Request a protected resource after the token has been expired expired, with appropriate cookies. response, response_headers = self.request( As.member, 'GET', '/me', headers={'Cookie': refresh_token}) self.assertIn('X-New-JWT-Token', response_headers) self.assertIsNotNone(response_headers['X-New-JWT-Token']) # Test with invalid Refresh Token self.request(As.member, 'GET', '/me', headers={'Cookie': 'refresh-token=InvalidToken'}, expected_status=400) # Waiting until expire refresh token time.sleep(3) # Request a protected resource after the refresh-token has been expired. self.request(As.member, 'GET', '/me', headers={'Cookie': refresh_token}, expected_status=401) def test_authorization(self): response, headers = self.request('ALL', 'POST', '/login', json=dict(email='*****@*****.**', password='******')) self.assertIn('token', response) self.assertEqual(headers['X-Identity'], '1') self.wsgi_app.jwt_token = response['token'] response, headers = self.request('ALL', 'GET', '/kill', expected_status=403) self.assertEqual(headers['X-Identity'], '1')
class ValidationBlackListTestCase(WebAppTestCase): application = MockupApplication('MockupApplication', Root()) @classmethod def configure_app(cls): super().configure_app() settings.merge(""" logging: loggers: default: level: info """) def test_validation_blacklist(self): # Test `blacklist` # role -> All self.wsgi_app.jwt_token = DummyIdentity().dump().decode() self.request('All', 'POST', '/validation', doc=False) self.request('All', 'POST', '/validation', doc=False, params={'customParam': 'param'}) self.request('All', 'POST', '/validation', doc=False, params={'blacklistParamForAll': 'param'}, expected_status=400) self.request('All', 'POST', '/validation', doc=False, params={'blacklistParamForClient': 'param'}) self.request('All', 'POST', '/validation', doc=False, params={'blacklistParamForAdmin': 'param'}) # ----------------------------- # role -> Client self.wsgi_app.jwt_token = DummyIdentity('client').dump().decode() self.request('Client', 'POST', '/validation', doc=False) self.request('Client', 'POST', '/validation', doc=False, params={'customParam': 'param'}) self.request('Client', 'POST', '/validation', doc=False, params={'blacklistParamForAll': 'param'}, expected_status=400) self.request('Client', 'POST', '/validation', doc=False, params={'blacklistParamForClient': 'param'}, expected_status=400) self.request('Client', 'POST', '/validation', doc=False, params={'blacklistParamForAdmin': 'param'}) # ----------------------------- # role -> Admin self.wsgi_app.jwt_token = DummyIdentity('admin').dump().decode() self.request('Admin', 'POST', '/validation', doc=False) self.request('Admin', 'POST', '/validation', doc=False, params={'customParam': 'param'}) self.request('Admin', 'POST', '/validation', doc=False, params={'blacklistParamForAll': 'param'}, expected_status=400) self.request('Admin', 'POST', '/validation', doc=False, params={'blacklistParamForClient': 'param'}) self.request('Admin', 'POST', '/validation', doc=False, params={'blacklistParamForAdmin': 'param'}, expected_status=400)