import datetime import logging import itertools import time import mock import contextlib2 as contextlib import pytest from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from vcr.cassette import Cassette, CassetteContextDecorator from . import util from okcupyd import db, settings from okcupyd import util as okcupyd_util from okcupyd.db import model, adapters log = logging.getLogger(__name__) BREAK_ON_EXCEPTION = False def pytest_addoption(parser): okcupyd_util.add_command_line_options( parser.addoption, use_short_options=False ) parser.addoption('--live', dest='skip_vcrpy', action='store_true', default=False, help="Skip the patching of http requests in" " tests. USE WITH CAUTION. This will interact with the " "okcupid website and send messages with any provided user " "credentials.") parser.addoption('--scrub', dest='scrub', action='store_true', help="Scrub PII from http requests/responses. " "This is useful for recording cassetes.", default=False) parser.addoption('--resave', dest='resave', action='store_true', default=False, help="Resave cassettes. Use to retoractively scrub " "cassettes.") parser.addoption('--cassette-mode', dest='cassette_mode', action='store', default='once', help="Accept new requests in all tests.") parser.addoption('--record', dest='record', action='store_true', default=False, help="Re-record cassettes for all tests.") parser.addoption('--break-exc', dest='break_on_exception', default=False, action='store_true', help="Enter ipdb if an" " exception is encountered in a test.") def patch(option, *patches, **kwargs): negate = kwargs.get('negate', False) @pytest.yield_fixture(autouse=True, scope='session') def patch_conditionally(request): condition = bool(request.config.getoption(option)) if negate: condition = not condition if condition: with contextlib.ExitStack() as exit_stack: for patch in patches: exit_stack.enter_context(patch) yield else: yield return patch_conditionally patch_settings = patch('credential_files', mock.patch.object(settings, 'USERNAME', 'username'), mock.patch.object(settings, 'PASSWORD', 'password'), mock.patch.object(settings, 'AF_USERNAME', 'username'), mock.patch.object(settings, 'AF_PASSWORD', 'password'), negate=True) patch_save = patch( 'resave', mock.patch.object( Cassette, '_save', okcupyd_util.curry(Cassette._save)(force=True) ) ) def reraise_exception(obj, error_type, error, traceback): if error is not None: raise patch_use_cassette_enabled = patch( 'skip_vcrpy', mock.patch.object(CassetteContextDecorator, '__enter__', lambda stuff: log.debug("Skipping vcrpy patching.")), mock.patch.object(CassetteContextDecorator, '__exit__', reraise_exception) ) patch_vcrpy_filters = patch( 'scrub', mock.patch.object(util, 'SHOULD_SCRUB', True) ) patch_break_on_exception = patch( 'break_on_exception', mock.patch('okcupyd_testing.conftest.BREAK_ON_EXCEPTION', True) ) original_load = Cassette._load def new_load(self): self.dirty = False self.rewound = True patch_record = patch( 'record', mock.patch.object( Cassette, '_load', new_load ) ) @pytest.fixture(autouse=True, scope='session') def set_vcr_options(request): util.okcupyd_vcr.record_mode = request.config.getoption('cassette_mode') @pytest.fixture(autouse=True, scope='session') def process_command_line_args(request): okcupyd_util.handle_command_line_options(request.config.option) @pytest.fixture(scope='function') def engine(request): return create_engine('sqlite://', echo=request.config.getoption('echo')) @pytest.fixture(scope='function') def session(engine): new_kwargs = db.Session.kw.copy() new_kwargs['bind'] = engine return sessionmaker(**new_kwargs) @pytest.yield_fixture(autouse=True, scope='function') def setup_db(engine, session): with mock.patch.object(db, 'Session', session): orig_engine = db.Base.metadata.bind db.Base.metadata.bind = engine db.Base.metadata.drop_all() db.Base.metadata.create_all() yield db.Base.metadata.bind = orig_engine @pytest.fixture def T(mock_profile_builder, mock_message_thread_builder, mock_message_builder): class testing(object): pass testing.ensure = mock.Mock() testing.build_mock = mock.Mock() testing.factory = mock.Mock() def ensure_thread_model_resembles_okcupyd_thread( thread_model, okcupyd_thread ): assert thread_model.okc_id == okcupyd_thread.id ensure_user_model_resembles_okcupyd_profile( thread_model.initiator, okcupyd_thread.initiator ) ensure_user_model_resembles_okcupyd_profile( thread_model.respondent, okcupyd_thread.respondent ) for pair in zip(thread_model.messages, okcupyd_thread.messages): ensure_message_model_resembles_okcupyd_message(*pair) assert len(thread_model.messages) == len(okcupyd_thread.messages) def ensure_message_model_resembles_okcupyd_message( message_model, okcupyd_message ): assert message_model.okc_id == okcupyd_message.id assert message_model.sender.handle == okcupyd_message.sender.username assert message_model.recipient.handle == okcupyd_message.recipient.username assert message_model.text == okcupyd_message.content def ensure_user_model_resembles_okcupyd_profile(user_model, okcupyd_profile): assert user_model.handle == okcupyd_profile.username testing.ensure.user_model_resembles_okcupyd_profile = \ ensure_user_model_resembles_okcupyd_profile testing.ensure.message_model_resembles_okcupyd_message = \ ensure_message_model_resembles_okcupyd_message testing.ensure.thread_model_resembles_okcupyd_thread = \ ensure_thread_model_resembles_okcupyd_thread testing.build_mock.thread = mock_message_thread_builder testing.build_mock.message = mock_message_builder testing.build_mock.profile = mock_profile_builder def build_message_thread(): message_thread = testing.build_mock.thread() return adapters.ThreadAdapter(message_thread).get_thread()[0] def build_user(username): profile = testing.build_mock.profile(username) return adapters.UserAdapter(profile).get() def build_okcupyd_user(user): user_model = model.User.from_profile(user.profile) user_model.upsert_model(id_key='okc_id') okcupyd_user = model.OKCupydUser(user_id=user_model.id) okcupyd_user.upsert_model(id_key='user_id') return okcupyd_user testing.factory.message_thread = build_message_thread testing.factory.user = build_user testing.factory.okcupyd_user = build_okcupyd_user return testing @pytest.fixture def mock_profile_builder(): counter = itertools.count() next(counter) username_to_profile = {} def _build_mock_profile(username='username', age=30, id=None, location='San Francisco, CA', **kwargs): if username in username_to_profile: return username_to_profile[username] if id is None: id = next(counter) mock_profile = mock.MagicMock(id=id or next(counter), location=location, age=age, username=username, **kwargs) username_to_profile[username] = mock_profile return mock_profile return _build_mock_profile @pytest.fixture def mock_message_builder(): counter = itertools.count() next(counter) def _build_mock_message(id=None, sender='sender', recipient='recipient', content='content', **kwargs): kwargs.setdefault('time_sent', datetime.datetime(year=2014, day=2, month=4)) if id is None: id = next(counter) assert isinstance(sender, str) assert isinstance(recipient, str) return mock.MagicMock(id=id, sender=mock.Mock(username=sender), recipient=mock.Mock(username=recipient), content=content, **kwargs) return _build_mock_message @pytest.fixture def mock_message_thread_builder(mock_message_builder, mock_profile_builder): counter = itertools.count() next(counter) def _build_mock_message_thread(id=None, message_count=2, initiator='initiator', respondent='respondent', **kwargs): if id is None: id = next(counter) messages = [mock_message_builder(content='{0}'.format(i), sender=respondent if i % 2 else initiator, recipient=initiator if i % 2 else respondent) for i in range(message_count)] kwargs.setdefault('datetime', datetime.datetime(year=2014, day=2, month=4)) message_thread = mock.MagicMock(id=id, messages=messages, **kwargs) message_thread.initiator = mock_profile_builder(initiator) message_thread.respondent = mock_profile_builder(respondent) return message_thread return _build_mock_message_thread def pytest_exception_interact(): if BREAK_ON_EXCEPTION: import ipdb; ipdb.set_trace() try: models = [] for key in dir(model): prospect = getattr(model, key) if isinstance(prospect, type) and issubclass(prospect, db.Base): models.append(prospect) model_class_to_instances = {} for model_class in models: model_class_to_instances[model_class] = { instance.okc_id: instance for instance in model_class.query() } log.info(model_class_to_instances) except: pass @pytest.fixture def vcr_live_sleep(request): return (time.sleep if (request.config.getoption('record') or request.config.getoption('skip_vcrpy')) else mock.Mock())