def test_get_current_user(self, req, db_session, config): """ It should return the authenticated user's exports """ import mock from occams_datastore import models as datastore from occams_studies import models req.registry.settings['studies.export.dir'] = '/tmp' blame = datastore.User(key=u'joe') db_session.add(blame) db_session.add(datastore.User(key='jane')) db_session.flush() db_session.info['blame'] = blame export1 = models.Export(owner_user=(db_session.query( datastore.User).filter_by(key='joe').one()), contents=[], status='pending') export2 = models.Export(owner_user=(db_session.query( datastore.User).filter_by(key='jane').one()), contents=[], status='pending') db_session.add_all([export1, export2]) db_session.flush() config.testing_securitypolicy(userid='joe') req.redis = mock.Mock() context = models.ExportFactory(req) export1.__parent__ = context export2.__parent__ = context res = self._call_fut(models.ExportFactory(req), req) exports = res['exports'] assert len(exports) == 1
def populate(self, request, db_session): import os import transaction from occams_studies import models from occams_datastore import models as datastore # Any view-dependent data goes here # Webtests will use a different scope for its transaction with transaction.manager: user = datastore.User(key=USERID) db_session.info['blame'] = user db_session.add(user) export = models.Export(name='myexport', status='complete', owner_user=user) db_session.add(export) db_session.flush() self.url = self.url_fmt.format(export=export.id) with open('/tmp/myexport', 'w+') as fp: self.export_file_name = fp.name def rm(): os.unlink(self.export_file_name) request.addfinalizer(rm)
def db_session(config): """ (Integartion Testing) Instantiates a database session. :param config: The pyramid testing configuartion :returns: An instantiated sqalchemy database session """ from occams_datastore import models as datastore import occams_datastore.models.events import zope.sqlalchemy db_session = config.registry['dbsession_factory']() occams_datastore.models.events.register(db_session) zope.sqlalchemy.register(db_session) # Pre-configure with a blame user blame = datastore.User(key=USERID) db_session.add(blame) db_session.flush() db_session.info['blame'] = blame # Other expected settings db_session.info['settings'] = config.registry.settings return db_session
def test_zip(self): """ It should generate a zip file containing the specified contents """ from zipfile import ZipFile from occams.celery import Session from occams_datastore import models as datastore from occams_studies import models, tasks from occams_studies.exports.pid import PidPlan owner = datastore.User(key=u'joe') Session.info['blame'] = owner Session.add(owner) Session.flush() export = models.Export(owner_user=owner, contents=[{ 'name': 'pid', 'title': 'PID', 'versions': [] }], status='complete') Session.add(export) Session.flush() tasks.app.settings['studies.export.plans'] = [PidPlan] tasks.make_export(export.name) # @in_transaction removes the session metadata, so we gotta do this export = Session.merge(export) with ZipFile(export.path, 'r') as zfp: file_names = zfp.namelist() assert sorted(['pid.csv', 'codebook.csv']) == sorted(file_names)
def test_not_owner(self, app, db_session): import transaction from occams_datastore import models as datastore with transaction.manager: db_session.add(datastore.User(key='somebody_else')) environ = make_environ(userid='somebody_else') app.get(self.url, extra_environ=environ, status=403)
def test_delete(self, req, db_session, config, check_csrf_token): """ It should allow the owner of the export to cancel/delete the export """ import mock from pyramid.httpexceptions import HTTPOk from occams_datastore import models as datastore from occams_studies import models blame = datastore.User(key=u'joe') db_session.add(blame) db_session.flush() db_session.info['blame'] = blame export = models.Export(owner_user=(db_session.query( datastore.User).filter_by(key='joe').one()), contents=[], status='complete') db_session.add(export) db_session.flush() export_id = export.id export_name = export.name db_session.expunge_all() config.testing_securitypolicy(userid='joe') with mock.patch('occams_studies.tasks.app.control.revoke') as revoke: res = self._call_fut(export, req) check_csrf_token.assert_called_with(req) assert isinstance(res, HTTPOk) assert db_session.query(models.Export).get(export_id) is None revoke.assert_called_with(export_name)
def populate(self, app, db_session): from datetime import date import transaction from occams_datastore import models as datastore from occams_studies import models as studies # Any view-dependent data goes here with transaction.manager: user = datastore.User(key=USERID) drsc = studies.Study(name=u'drsc', title=u'DRSC', short_title=u'dr', code=u'drs', consent_date=date.today(), is_randomized=False) ucsd = studies.Study(name=u'ucsd', title=u'UCSD', short_title=u'ucsd', code=u'ucsd', consent_date=date.today(), is_randomized=False) db_session.add(user) db_session.add(drsc) db_session.add(ucsd) db_session.flush()
def populate(self, app, db_session): import transaction from occams_studies import models as studies from occams_datastore import models as datastore from datetime import date # Any view-dependent data goes here # Webtests will use a different scope for its transaction with transaction.manager: user = datastore.User(key=USERID) db_session.info['blame'] = user db_session.add(user) db_session.flush() site = studies.Site(name=u'UCSD', title=u'UCSD', description=u'UCSD Campus', create_date=date.today()) patient = studies.Patient(initials=u'ian', nurse=u'*****@*****.**', site=site, pid=u'123') study = studies.Study( name=u'test_study', code=u'test_code', consent_date=date(2014, 12, 23), is_randomized=False, title=u'test_title', short_title=u'test_short', ) db_session.add(study) db_session.add(patient)
def db_session(config): """ (Integartion Testing) Instantiates a database session. :param config: The pyramid testing configuartion :returns: An instantiated sqalchemy database session """ from occams_datastore import models as datastore db_session = config.registry['db_sessionmaker']() # Pre-configure with a blame user blame = datastore.User(key=USERID) db_session.add(blame) db_session.flush() db_session.info['blame'] = blame # Other expected settings db_session.info['settings'] = config.registry.settings # Hardcoded workflow db_session.add_all([ datastore.State(name=u'pending-entry', title=u'Pending Entry'), datastore.State(name=u'pending-review', title=u'Pending Review'), datastore.State(name=u'pending-correction', title=u'Pending Correction'), datastore.State(name=u'complete', title=u'Complete') ]) return db_session
def populate(self, app, db_session): import transaction from occams_datastore import models as datastore # Any view-dependent data goes here # Webtests will use a different scope for its transaction with transaction.manager: db_session.add(datastore.User(key=USERID))
def db_session(sessionmaker): from occams_datastore import models session = sessionmaker() blame = models.User(key=u'tester') session.add(blame) session.flush() session.info['blame'] = blame yield session session.rollback()
def populate(self, app, db_session): import transaction from occams_studies import models as studies from occams_datastore import models as datastore from datetime import date # Any view-dependent data goes here # Webtests will use a different scope for its transaction with transaction.manager: user = datastore.User(key=USERID) db_session.info['blame'] = user db_session.add(user) db_session.flush() site = studies.Site(name=u'UCSD', title=u'UCSD', description=u'UCSD Campus', create_date=date.today()) patient = studies.Patient(initials=u'ian', nurse=u'*****@*****.**', site=site, pid=u'123') form = datastore.Schema(name=u'test_schema', title=u'test_title', publish_date=date(2015, 1, 1)) study = studies.Study(name=u'test_study', code=u'test_code', consent_date=date(2014, 12, 23), is_randomized=False, title=u'test_title', short_title=u'test_short', schemata=set([form])) cycle = studies.Cycle(name=u'TestCycle', title=u'TestCycle', week=39, study=study) visit = studies.Visit(patient=patient, cycles=[cycle], visit_date='2015-01-01') entity = datastore.Entity(schema=form, collect_date=date(2015, 1, 1)) db_session.add(study) db_session.add(patient) db_session.add(visit) db_session.add(entity) patient.entities.add(entity)
def test_not_owner(self, app, db_session): import transaction from occams_datastore import models as datastore with transaction.manager: db_session.add(datastore.User(key='somebody_else')) environ = make_environ(userid='somebody_else') csrf_token = get_csrf_token(app, environ) app.delete(self.url, extra_environ=environ, headers={'X-CSRF-Token': csrf_token}, xhr=True, status=403)
def populate(self, app, db_session): import transaction from occams_datastore import models as datastore from occams_studies import models as studies # Any view-dependent data goes here # Webtests will use a different scope for its transaction with transaction.manager: blame = datastore.User(key=USERID) db_session.info['blame'] = blame db_session.add(blame) db_session.flush() db_session.add(studies.Site(name=u'ucsd', title=u'UCSD')) db_session.add(studies.Site(name=u'ucla', title=u'UCSD'))
def populate(self, app, db_session): import transaction from occams_datastore import models as datastore from occams_studies import models # Any view-dependent data goes here # Webtests will use a different scope for its transaction with transaction.manager: user = datastore.User(key=USERID) db_session.info['blame'] = user db_session.add(user) export = models.Export(owner_user=user) db_session.add(export) db_session.flush() self.url = self.url_fmt.format(export=export.id)
def populate(self, app, db_session): import transaction from occams_studies import models as studies from occams_datastore import models as datastore from datetime import date # Any view-dependent data goes here # Webtests will use a different scope for its transaction with transaction.manager: user = datastore.User(key=USERID) db_session.info['blame'] = user db_session.add(user) db_session.flush() site = studies.Site(name=u'UCSD', title=u'UCSD', description=u'UCSD Campus', create_date=date.today()) patient = studies.Patient(initials=u'ian', nurse=u'*****@*****.**', site=site, pid=u'123') form = datastore.Schema(name=u'test_schema', title=u'test_title', publish_date=date(2015, 1, 1)) study = studies.Study(name=u'test_study', code=u'test_code', consent_date=date(2014, 12, 23), is_randomized=False, title=u'test_title', short_title=u'test_short', schemata=set([form])) state = (db_session.query( datastore.State).filter_by(name=u'pending-entry').one()) db_session.add( datastore.Entity(state=state, schema=form, collect_date=date(2015, 2, 1))) db_session.add( studies.Enrollment(patient=patient, study=study, consent_date=date(2014, 12, 22)))
def app(request, wsgi, db_session): """ (Functional Testing) Initiates a user request against a WSGI stack :param request: The pytest context :param wsgi: An initialized WSGI stack :param db_session: A database session for seting up pre-existing data :returns: a test app request against the WSGI instance """ import transaction from webtest import TestApp from zope.sqlalchemy import mark_changed from occams_datastore import models as datastore # Save all changes up tho this point (db_session does some configuration) with transaction.manager: blame = datastore.User(key='workflow@localhost') db_session.add(blame) db_session.flush() db_session.info['blame'] = blame db_session.add_all([ datastore.State(name=u'pending-entry', title=u'Pending Entry'), datastore.State(name=u'pending-review', title=u'Pending Review'), datastore.State(name=u'pending-correction', title=u'Pending Correction'), datastore.State(name=u'complete', title=u'Complete') ]) app = TestApp(wsgi) yield app with transaction.manager: # DELETE is dramatically faster than TRUNCATE # http://stackoverflow.com/a/11423886/148781 # We also have to do this as a raw query becuase SA does # not have a way to invoke server-side cascade db_session.execute('DELETE FROM "study" CASCADE') db_session.execute('DELETE FROM "patient" CASCADE') db_session.execute('DELETE FROM "site" CASCADE') db_session.execute('DELETE FROM "schema" CASCADE') db_session.execute('DELETE FROM "export" CASCADE') db_session.execute('DELETE FROM "state" CASCADE') db_session.execute('DELETE FROM "user" CASCADE') mark_changed(db_session)
def test_ignore_expired(self, req, db_session, config): """ It should not render expired exports. """ from datetime import datetime, timedelta import mock from occams_datastore import models as datastore from occams_studies import models EXPIRE_DAYS = 10 req.registry.settings['studies.export.expire'] = EXPIRE_DAYS req.registry.settings['studies.export.dir'] = '/tmp' blame = datastore.User(key=u'joe') db_session.add(blame) db_session.flush() db_session.info['blame'] = blame now = datetime.now() export = models.Export(owner_user=(db_session.query( datastore.User).filter_by(key='joe').one()), contents=[], status='pending', create_date=now, modify_date=now) db_session.add(export) db_session.flush() config.testing_securitypolicy(userid='joe') req.redis = mock.Mock() context = models.ExportFactory(req) export.__parent__ = context res = self._call_fut(context, req) exports = res['exports'] assert len(exports) == 1 export.create_date = export.modify_date = \ now - timedelta(EXPIRE_DAYS + 1) db_session.flush() context = models.ExportFactory(req) export.__parent__ = context res = self._call_fut(context, req) exports = res['exports'] assert len(exports) == 0
def populate(self, request, app, db_session): import os import transaction from occams_datastore import models as datastore # Any view-dependent data goes here # Webtests will use a different scope for its transaction with transaction.manager: db_session.add(datastore.User(key=USERID)) # XXX: need to somehow get the settings so we can consitently # get the correct directory with open('/tmp/codebook.csv', 'w+') as fp: self.codebook_file_name = fp.name def rm(): os.unlink(self.codebook_file_name) request.addfinalizer(rm)
def populate(self, app, db_session): import transaction from occams_studies import models as studies from occams_datastore import models as datastore from datetime import date # Any view-dependent data goes here # Webtests will use a different scope for its transaction with transaction.manager: user = datastore.User(key=USERID) db_session.info['blame'] = user db_session.add(user) db_session.flush() db_session.add( studies.Site(name=u'UCSD', title=u'UCSD', description=u'UCSD Campus', create_date=date.today())) db_session.flush()
def populate(self, app, db_session): import transaction from occams_studies import models as studies from occams_datastore import models as datastore from datetime import date # Any view-dependent data goes here # Webtests will use a different scope for its transaction with transaction.manager: user = datastore.User(key=USERID) db_session.info['blame'] = user db_session.add(user) db_session.flush() db_session.add(studies.Study( name=u'test', title=u'test', short_title=u'test', code=u'test', consent_date=date.today(), is_randomized=False))
def celery(request): """ (Function Testing) Sets up a celery application for testing :param request: The pytest context """ import shutil import tempfile import mock from redis import StrictRedis from sqlalchemy import create_engine from occams.celery import Session from occams_datastore import models as datastore from occams_studies import tasks settings = { 'studies.export.dir': tempfile.mkdtemp(), 'celery.blame': USERID } tasks.app.userid = settings['celery.blame'] tasks.app.redis = StrictRedis.from_url(REDIS_URL) tasks.app.settings = settings db_url = request.config.getoption('--db') engine = create_engine(db_url) Session.configure(bind=engine, info={'settings': settings}) Session.add(datastore.User(key=settings['celery.blame'])) Session.flush() commitmock = mock.patch('occams_imports.tasks.Session.commit') commitmock.start() def cleanup(): commitmock.stop() shutil.rmtree(settings['studies.export.dir']) Session.remove() request.addfinalizer(cleanup)
def test_exceed_limit(self, req, db_session, config): """ It should not let the user exceed their allocated export limit """ from datetime import date from webob.multidict import MultiDict from occams_datastore import models as datastore from occams_studies import models from occams_studies.exports.schema import SchemaPlan config.registry.settings['app.export.limit'] = 0 req.registry.settings['studies.export.plans'] = [SchemaPlan.list_all] blame = datastore.User(key=u'joe') db_session.add(blame) db_session.flush() db_session.info['blame'] = blame previous_export = models.Export(owner_user=(db_session.query( datastore.User).filter_by(key='joe').one()), contents=[{ u'name': u'vitals', u'title': u'Vitals', u'versions': [str(date.today())] }]) db_session.add(previous_export) db_session.flush() # The renderer should know about it config.testing_securitypolicy(userid='joe') res = self._call_fut(models.ExportFactory(req), req) assert res['exceeded'] # If the user insists, they'll get a validation error as well config.testing_securitypolicy(userid='joe') req.method = 'POST' req.POST = MultiDict([('contents', 'vitals')]) assert res['exceeded']
def test_valid(self, req, db_session, config, check_csrf_token): """ It should add an export record and initiate an async task """ from datetime import date import mock from pyramid.httpexceptions import HTTPFound from webob.multidict import MultiDict from occams_datastore import models as datastore from occams_studies import models from occams_studies.exports.schema import SchemaPlan req.registry.settings['app.export.dir'] = '/tmp' req.registry.settings['studies.export.plans'] = [SchemaPlan.list_all] blame = datastore.User(key=u'joe') db_session.add(blame) db_session.flush() db_session.info['blame'] = blame schema = datastore.Schema(name=u'vitals', title=u'Vitals', publish_date=date.today()) db_session.add(schema) db_session.flush() config.testing_securitypolicy(userid='joe') req.method = 'POST' req.POST = MultiDict([('contents', str('vitals'))]) # Don't invoke subtasks with mock.patch('occams_studies.tasks.make_export'): res = self._call_fut(models.ExportFactory(req), req) check_csrf_token.assert_called_with(req) assert isinstance(res, HTTPFound) assert res.location == req.route_path('studies.exports_status') export = db_session.query(models.Export).one() assert export.owner_user.key == 'joe'
def test_get_not_found_status(self, req, db_session, status): """ It should return 404 if the record is not ready """ from pyramid.httpexceptions import HTTPBadRequest from occams_datastore import models as datastore from occams_studies import models blame = datastore.User(key=u'joe') db_session.add(blame) db_session.flush() db_session.info['blame'] = blame export = models.Export(id=123, owner_user=(db_session.query( datastore.User).filter_by(key='joe').one()), contents=[], status=status) db_session.add(export) db_session.flush() with pytest.raises(HTTPBadRequest): self._call_fut(export, req)
def login(request): db_session = request.db_session form = LoginForm(request.POST) if request.method == 'POST' and form.validate(): # XXX: Hack for this to work on systems that have not set the # environ yet. Pyramid doesn't give us access to the policy # publicly, put it's still available throught this private # variable and it's usefule in leveraging repoze.who's # login mechanisms... who_api = request._get_authentication_policy()._getAPI(request) authenticated, headers = who_api.login(form.data) if not authenticated: request.session.flash(_(u'Invalid credentials'), 'danger') else: user = (db_session.query( datastore.User).filter_by(key=form.login.data).first()) if not user: user = datastore.User(key=form.login.data) db_session.add(user) referrer = request.GET.get('referrer') if not referrer or request.route_path( 'accounts.login') in referrer: # TODO: Maybe send the user to their user dashboard instead? referrer = request.route_path('occams.index') return HTTPFound(location=referrer, headers=headers) # forcefully forget any credentials request.response.headerlist.extend(forget(request)) return {'form': form}