class ModelTestCase(unittest.TestCase): def setUp(self): self.session = create_session() self._old_now_func = tables.set_now_func(utc_now) self.dummy_processor = DummyProcessor() self.model_factory = ModelFactory( session=self.session, processor_factory=lambda: self.dummy_processor, settings={}, ) self.company_model = self.model_factory.create_company_model() self.customer_model = self.model_factory.create_customer_model() self.plan_model = self.model_factory.create_plan_model() self.subscription_model = self.model_factory.create_subscription_model( ) self.invoice_model = self.model_factory.create_invoice_model() self.transaction_model = self.model_factory.create_transaction_model() self.transaction_failure_model = self.model_factory.create_transaction_failure_model( ) def tearDown(self): self.session.close() self.session.remove() tables.DeclarativeBase.metadata.drop_all() self.session.bind.dispose() tables.set_now_func(self._old_now_func)
class ModelTestCase(unittest.TestCase): def setUp(self): self.session = create_session() self._old_now_func = tables.set_now_func(utc_now) self.dummy_processor = DummyProcessor() self.model_factory = ModelFactory( session=self.session, processor_factory=lambda: self.dummy_processor, settings={}, ) self.company_model = self.model_factory.create_company_model() self.customer_model = self.model_factory.create_customer_model() self.plan_model = self.model_factory.create_plan_model() self.subscription_model = self.model_factory.create_subscription_model() self.invoice_model = self.model_factory.create_invoice_model() self.transaction_model = self.model_factory.create_transaction_model() self.transaction_failure_model = self.model_factory.create_transaction_failure_model() def tearDown(self): self.session.close() self.session.remove() tables.DeclarativeBase.metadata.drop_all() self.session.bind.dispose() tables.set_now_func(self._old_now_func)
def setUp(self): self.dummy_processor = DummyProcessor() def model_factory_func(): return self.model_factory if not hasattr(self, 'settings'): self.settings = { 'billy.processor_factory': lambda: self.dummy_processor, 'model_factory_func': model_factory_func, # do not remove when a request is processed, so that we don't # have to use session.add every time 'db_session_cleanup': False, } # init database db_url = os.environ.get('BILLY_FUNC_TEST_DB', 'sqlite://') self.settings['sqlalchemy.url'] = db_url self.settings = setup_database({}, **self.settings) DeclarativeBase.metadata.bind = self.settings['engine'] DeclarativeBase.metadata.create_all() app = main({}, **self.settings) self.testapp = TestApp(app) self.testapp.session = self.settings['session'] self.dummy_request = DummyRequest() # create model factory self.model_factory = ModelFactory( session=self.testapp.session, processor_factory=lambda: self.dummy_processor, settings=self.settings, ) # create all models self.company_model = self.model_factory.create_company_model() self.customer_model = self.model_factory.create_customer_model() self.plan_model = self.model_factory.create_plan_model() self.subscription_model = self.model_factory.create_subscription_model( ) self.invoice_model = self.model_factory.create_invoice_model() self.transaction_model = self.model_factory.create_transaction_model() self.transaction_failure_model = self.model_factory.create_transaction_failure_model( )
def setUp(self): self.session = create_session() self._old_now_func = tables.set_now_func(utc_now) self.dummy_processor = DummyProcessor() self.model_factory = ModelFactory( session=self.session, processor_factory=lambda: self.dummy_processor, settings={}, ) self.company_model = self.model_factory.create_company_model() self.customer_model = self.model_factory.create_customer_model() self.plan_model = self.model_factory.create_plan_model() self.subscription_model = self.model_factory.create_subscription_model( ) self.invoice_model = self.model_factory.create_invoice_model() self.transaction_model = self.model_factory.create_transaction_model() self.transaction_failure_model = self.model_factory.create_transaction_failure_model( )
def model_factory(self): """The factory for creating data models """ settings = self.registry.settings model_factory_func = settings.get('model_factory_func') if model_factory_func is not None: return model_factory_func() processor_factory = get_processor_factory(settings) return ModelFactory( session=self.session, processor_factory=processor_factory, settings=settings, )
def main(argv=sys.argv, processor=None): logger = logging.getLogger(__name__) if len(argv) != 2: usage(argv) config_uri = argv[1] setup_logging(config_uri) settings = get_appsettings(config_uri) settings = setup_database({}, **settings) session = settings['session'] try: if processor is None: processor_factory = get_processor_factory(settings) else: processor_factory = lambda: processor factory = ModelFactory( session=session, processor_factory=processor_factory, settings=settings, ) subscription_model = factory.create_subscription_model() tx_model = factory.create_transaction_model() # yield all transactions and commit before we process them, so that # we won't double process them. with db_transaction.manager: logger.info('Yielding transaction ...') subscription_model.yield_invoices() with db_transaction.manager: logger.info('Processing transaction ...') tx_model.process_transactions() logger.info('Done') finally: session.close()
class ViewTestCase(unittest.TestCase): def setUp(self): self.dummy_processor = DummyProcessor() def model_factory_func(): return self.model_factory if not hasattr(self, "settings"): self.settings = { "billy.processor_factory": lambda: self.dummy_processor, "model_factory_func": model_factory_func, # do not remove when a request is processed, so that we don't # have to use session.add every time "db_session_cleanup": False, } # init database db_url = os.environ.get("BILLY_FUNC_TEST_DB", "sqlite://") self.settings["sqlalchemy.url"] = db_url self.settings = setup_database({}, **self.settings) DeclarativeBase.metadata.bind = self.settings["engine"] DeclarativeBase.metadata.create_all() app = main({}, **self.settings) self.testapp = TestApp(app) self.testapp.session = self.settings["session"] self.dummy_request = DummyRequest() # create model factory self.model_factory = ModelFactory( session=self.testapp.session, processor_factory=lambda: self.dummy_processor, settings=self.settings ) # create all models self.company_model = self.model_factory.create_company_model() self.customer_model = self.model_factory.create_customer_model() self.plan_model = self.model_factory.create_plan_model() self.subscription_model = self.model_factory.create_subscription_model() self.invoice_model = self.model_factory.create_invoice_model() self.transaction_model = self.model_factory.create_transaction_model() self.transaction_failure_model = self.model_factory.create_transaction_failure_model() def tearDown(self): self.testapp.session.close() self.testapp.session.remove() DeclarativeBase.metadata.drop_all() self.testapp.session.bind.dispose()
class ViewTestCase(unittest.TestCase): def setUp(self): self.dummy_processor = DummyProcessor() def model_factory_func(): return self.model_factory if not hasattr(self, 'settings'): self.settings = { 'billy.processor_factory': lambda: self.dummy_processor, 'model_factory_func': model_factory_func, } # init database db_url = os.environ.get('BILLY_FUNC_TEST_DB', 'sqlite://') self.settings['sqlalchemy.url'] = db_url self.settings = setup_database({}, **self.settings) DeclarativeBase.metadata.bind = self.settings['engine'] DeclarativeBase.metadata.create_all() app = main({}, **self.settings) self.testapp = TestApp(app) self.testapp.session = self.settings['session'] self.dummy_request = DummyRequest() # create model factory self.model_factory = ModelFactory( session=self.testapp.session, processor_factory=lambda: self.dummy_processor, settings=self.settings, ) # create all models self.company_model = self.model_factory.create_company_model() self.customer_model = self.model_factory.create_customer_model() self.plan_model = self.model_factory.create_plan_model() self.subscription_model = self.model_factory.create_subscription_model() self.invoice_model = self.model_factory.create_invoice_model() self.transaction_model = self.model_factory.create_transaction_model() self.transaction_failure_model = self.model_factory.create_transaction_failure_model() def tearDown(self): self.testapp.session.close() self.testapp.session.remove() DeclarativeBase.metadata.drop_all() self.testapp.session.bind.dispose()
def setUp(self): self.session = create_session() self._old_now_func = tables.set_now_func(utc_now) self.dummy_processor = DummyProcessor() self.model_factory = ModelFactory( session=self.session, processor_factory=lambda: self.dummy_processor, settings={}, ) self.company_model = self.model_factory.create_company_model() self.customer_model = self.model_factory.create_customer_model() self.plan_model = self.model_factory.create_plan_model() self.subscription_model = self.model_factory.create_subscription_model() self.invoice_model = self.model_factory.create_invoice_model() self.transaction_model = self.model_factory.create_transaction_model() self.transaction_failure_model = self.model_factory.create_transaction_failure_model()
def test_main_with_crash(self): dummy_processor = DummyProcessor() dummy_processor.debit = mock.Mock() tx_guids = set() debits = [] def mock_charge(transaction): if dummy_processor.debit.call_count == 2: raise KeyboardInterrupt uri = 'MOCK_DEBIT_URI_FOR_{}'.format(transaction.guid) if transaction.guid in tx_guids: return dict( processor_uri=uri, status=TransactionModel.statuses.SUCCEEDED, ) tx_guids.add(transaction.guid) debits.append(uri) return dict( processor_uri=uri, status=TransactionModel.statuses.SUCCEEDED, ) dummy_processor.debit.side_effect = mock_charge cfg_path = os.path.join(self.temp_dir, 'config.ini') with open(cfg_path, 'wt') as f: f.write( textwrap.dedent("""\ [app:main] use = egg:billy sqlalchemy.url = sqlite:///%(here)s/billy.sqlite """)) initializedb.main([initializedb.__file__, cfg_path]) settings = get_appsettings(cfg_path) settings = setup_database({}, **settings) session = settings['session'] factory = ModelFactory( session=session, processor_factory=lambda: dummy_processor, settings=settings, ) company_model = factory.create_company_model() customer_model = factory.create_customer_model() plan_model = factory.create_plan_model() subscription_model = factory.create_subscription_model() with db_transaction.manager: company = company_model.create('my_secret_key') plan = plan_model.create( company=company, plan_type=plan_model.types.DEBIT, amount=10, frequency=plan_model.frequencies.MONTHLY, ) customer = customer_model.create(company=company, ) subscription_model.create( customer=customer, plan=plan, funding_instrument_uri='/v1/cards/tester', ) subscription_model.create( customer=customer, plan=plan, funding_instrument_uri='/v1/cards/tester', ) with self.assertRaises(KeyboardInterrupt): process_transactions.main( [process_transactions.__file__, cfg_path], processor=dummy_processor) process_transactions.main([process_transactions.__file__, cfg_path], processor=dummy_processor) # here is the story, we have two subscriptions here # # Subscription1 # Subscription2 # # And the time is not advanced, so we should only have two transactions # to be yielded and processed. However, we assume bad thing happens # durring the process. We let the second call to charge of processor # raises a KeyboardInterrupt error. So, it would look like this # # charge for transaction from Subscription1 # charge for transaction from Subscription2 (Crash) # # Then, we perform the process_transactions again, if it works # correctly, the first transaction is already yield and processed. # # charge for transaction from Subscription2 # # So, there would only be two charges in processor. This is mainly # for making sure we won't duplicate charges/payouts self.assertEqual(len(debits), 2)
def test_main_with_crash(self): dummy_processor = DummyProcessor() dummy_processor.debit = mock.Mock() tx_guids = set() debits = [] def mock_charge(transaction): if dummy_processor.debit.call_count == 2: raise KeyboardInterrupt uri = 'MOCK_DEBIT_URI_FOR_{}'.format(transaction.guid) if transaction.guid in tx_guids: return dict( processor_uri=uri, status=TransactionModel.statuses.SUCCEEDED, ) tx_guids.add(transaction.guid) debits.append(uri) return dict( processor_uri=uri, status=TransactionModel.statuses.SUCCEEDED, ) dummy_processor.debit.side_effect = mock_charge cfg_path = os.path.join(self.temp_dir, 'config.ini') with open(cfg_path, 'wt') as f: f.write(textwrap.dedent("""\ [app:main] use = egg:billy sqlalchemy.url = sqlite:///%(here)s/billy.sqlite """)) initializedb.main([initializedb.__file__, cfg_path]) settings = get_appsettings(cfg_path) settings = setup_database({}, **settings) session = settings['session'] factory = ModelFactory( session=session, processor_factory=lambda: dummy_processor, settings=settings, ) company_model = factory.create_company_model() customer_model = factory.create_customer_model() plan_model = factory.create_plan_model() subscription_model = factory.create_subscription_model() with db_transaction.manager: company = company_model.create('my_secret_key') plan = plan_model.create( company=company, plan_type=plan_model.types.DEBIT, amount=10, frequency=plan_model.frequencies.MONTHLY, ) customer = customer_model.create( company=company, ) subscription_model.create( customer=customer, plan=plan, funding_instrument_uri='/v1/cards/tester', ) subscription_model.create( customer=customer, plan=plan, funding_instrument_uri='/v1/cards/tester', ) with self.assertRaises(KeyboardInterrupt): process_transactions.main([process_transactions.__file__, cfg_path], processor=dummy_processor) process_transactions.main([process_transactions.__file__, cfg_path], processor=dummy_processor) # here is the story, we have two subscriptions here # # Subscription1 # Subscription2 # # And the time is not advanced, so we should only have two transactions # to be yielded and processed. However, we assume bad thing happens # durring the process. We let the second call to charge of processor # raises a KeyboardInterrupt error. So, it would look like this # # charge for transaction from Subscription1 # charge for transaction from Subscription2 (Crash) # # Then, we perform the process_transactions again, if it works # correctly, the first transaction is already yield and processed. # # charge for transaction from Subscription2 # # So, there would only be two charges in processor. This is mainly # for making sure we won't duplicate charges/payouts self.assertEqual(len(debits), 2)