"""Common fixtures and py.test configurations.""" import tarfile import tempfile from unittest.mock import Mock from MySQLdb.connections import Connection from MySQLdb.cursors import Cursor import pytest from flows import g from flows import log @pytest.fixture def database_context(): """Convenient mock for testing database context manager logic. Attach the returned value to the patched namespace that contains the flows.util.database_context object. The yielded cursor and connection objects are mocked and available in the _cursor and _connection attributes. Example: flows/digital/util.py --------------------- from flows import util def example(): with datastore.context() as (cur, conn): cur.execute('select 1') tests/flows/digital/test_util.py -------------------------------- @patch('flows.digital.util.datastore') def test_example(mock_datastore, database_context): mock_datastore.context = database_context util.example() database_context._cursor.execute.assert_called_once_with( 'select 1') Returns: Mock: mocked context manager with cursor and connection mocks. """ cursor = Mock(spec=Cursor) connection = Mock(spec=Connection) context = Mock() context.__enter__ = Mock(return_value=(cursor, connection)) context.__exit__ = Mock() callable_object = Mock() callable_object.return_value = context callable_object._cursor = cursor callable_object._connection = connection return callable_object @pytest.fixture def datawarehouse_context(): """Convenient mock for testing datawarehouse context manager logic. Attach the returned value to the patched namespace that contains the flows.util.datawarehouse_context object. The yielded cursor and connection objects are mocked and available in the _cursor and _connection attributes. Example: flows/digital/util.py --------------------- from flows import util def example(): with datawarehouse.context() as (cur, conn): cur.execute('select 1') tests/flows/digital/test_util.py -------------------------------- @patch('flows.digital.util.datawarehouse') def test_example(mock_datastore, datawarehouse): mock_datastore.context = datawarehouse util.example() datawarehouse._cursor.execute.assert_called_once_with( 'select 1') Returns: Mock: mocked context manager with cursor and connection mocks. """ cursor = Mock(spec=Cursor) connection = Mock(spec=Connection) context = Mock() context.__enter__ = Mock(return_value=(cursor, connection)) context.__exit__ = Mock() callable_object = Mock() callable_object.return_value = context callable_object._cursor = cursor callable_object._connection = connection return callable_object @pytest.fixture(autouse=True) def patch_global_logger(monkeypatch): """Patch global logger.""" g.log = Mock() log._logger = Mock() def create_test_tarball(data): """Create temporary tarball io object for testing. Args: data (dict): {filename: content} mapping of file data. Returns: TemporaryFile: file handler with tarball data. """ temp_fh = tempfile.TemporaryFile(mode='w+b') tar_fh = tarfile.open(mode='w', fileobj=temp_fh) for filename, content in data.items(): if type(content) == str: file_mode = 'w+t' if type(content) == bytes: file_mode = 'w+b' with tempfile.NamedTemporaryFile(mode=file_mode) as tfh: tfh.write(content) tfh.seek(0) info = tarfile.TarInfo(name=filename) info.size = len(content) tar_fh.addfile(info, fileobj=tfh) tar_fh.close() temp_fh.seek(0) return temp_fh @pytest.fixture def vendor_contracts(): """Fixture for database results of the temporary vendor contract table. Returns: tuple: rows of (vendor_contract_id, vendor_id, upc) data. """ return ( (110, 1, 10), (220, 2, 20), (221, 2, 21), (330, 3, 30), (331, 3, 31), (332, 3, 32), (440, 4, 40), (550, 5, 50), (660, 6, 60)) @pytest.fixture def distribution_fees_regular(): """Fixture for distribution fees from art_relations. The values "sync" with the vendor_contracts fixture. Returns: tuple: rows of (vendor_id, split) data. """ return ( (1, 0.11), (2, 0.22), (3, 0.33), (4, 0.44), (5, 0.55), (6, 0.66)) @pytest.fixture def distribution_fees_regular_split(): """Fixture for regular distribution fees with upcs. The values "sync" with the vendor_contracts fixture. Returns: tuple: rows of (split, upc) data. """ return ( (0.11, 10), (0.22, 20), (0.22, 21), (0.33, 30), (0.33, 31), (0.33, 32), (0.44, 40), (0.55, 50), (0.66, 60)) @pytest.fixture def distribution_fees_territory(): """Fixture for territory distribution fees from art_relations. The values "sync" with the vendor_territory_contracts fixture. Similar to regular fees but with a vendor_id * 0.1 pattern. Country id follows odds and even alternation. Returns: tuple: rows of (vendor_id, country_id, split) data. """ return ( (1, 1, 0.1), (2, 2, 0.2), (3, 1, 0.3), (4, 2, 0.4), (5, 1, 0.5), (6, 2, 0.6)) @pytest.fixture def distribution_fees_territory_split(): """Fixture for expected insert into datastore territory distribution fees. Returns: tuple: rows of (country_id, split, upc) data. """ return ( (1, 0.1, 10), (2, 0.2, 20), (2, 0.2, 21), (1, 0.3, 30), (1, 0.3, 31), (1, 0.3, 32), (2, 0.4, 40), (1, 0.5, 50), (2, 0.6, 60)) @pytest.fixture def upc_list(): """Fixture for test UPCs.""" return ('123', '234', '345') @pytest.fixture def upc_vendor_rows(): """Fixture for UPC and vendor ID rows from art relations.""" return ( ('123', '321'), ('234', '432'), ('345', '543')) @pytest.fixture def upc_vendor_mapping(): """Fixture for UPC to vendor ID mapping.""" return { '123': '321', '234': '432', '345': '543'} @pytest.fixture def vendor_contract_ids(): """Fixture for vendor IDs and contract IDs from art relations.""" return { '321': ('c123', '321'), '432': ('c234', '432'), '543': ('c345', '543')}