"""Tests for product model.""" from flexmock import flexmock from oto import error as oto_error from oto import status as oto_status import pytest from sqlalchemy import exc as sqlalchemy_exc from availability.connectors import sql from availability.constants import error from availability.constants import models from availability.models import product from tests.dbutils import db_setup_function as setup_function # noqa from tests.dbutils import db_teardown_function as teardown_function # noqa PRODUCT_ID = 123 ORCHARD_PRODUCT_ID = 819224018247 UPC = '819224018247' ITUNES_VENDOR_ID = '42' VALID_PRODUCT_KWARGS = dict( product_id=PRODUCT_ID, upc=UPC, orchard_product_id=ORCHARD_PRODUCT_ID, itunes_vendor_id=ITUNES_VENDOR_ID, provider=models.ORCHARD) def _get_product_kwargs(update_dict): result = VALID_PRODUCT_KWARGS.copy() result.update(update_dict) return result @pytest.mark.parametrize( 'product_fields, expected_status', [ ({}, 200), (dict(provider=models.IODA), 200), ]) def test_create_product_success(product_fields, expected_status): """Test create_product for successfull creation.""" kwargs = _get_product_kwargs(product_fields) result = product.create_product(**kwargs) product_as_dict = result.message assert result.status == expected_status for k, v in kwargs.items(): assert product_as_dict[k] == v @pytest.mark.parametrize( 'product_fields, expected_code, expected_status', [ (dict(provider='non_existing_provider'), error.ERROR_CODE_MODEL_VALIDATION, 400), ]) def test_create_product_validation_error( product_fields, expected_code, expected_status): """Test create_product for expected validation errors.""" result = product.create_product(**_get_product_kwargs(product_fields)) assert result.errors assert result.errors['code'] == expected_code assert result.status == expected_status @pytest.mark.parametrize( 'product_fields, expected_code, expected_status', [ (dict(upc=42), error.ERROR_CODE_MODEL_VALIDATION, 400), (dict(itunes_vendor_id=1), error.ERROR_CODE_MODEL_VALIDATION, 400), (dict(product_id='not an int'), error.ERROR_CODE_MODEL_VALIDATION, 400), (dict(orchard_product_id='not an int'), error.ERROR_CODE_MODEL_VALIDATION, 400), ]) def test_create_product_db_specific_integrity_error( product_fields, expected_code, expected_status): """Test create_product for integrity errors specific to some DB engines.""" # Test DB (SQLite) does not support type checks, so this mocks is required. (flexmock(sql) .should_receive('session_scope') .and_raise(sqlalchemy_exc.IntegrityError( statement='any', params=[], orig=None))) result = product.create_product(**_get_product_kwargs(product_fields)) assert result.errors assert result.errors['code'] == expected_code assert result.status == expected_status def test_create_product_internal_error(): """Test create_product for catching internal error.""" (flexmock(sql) .should_receive('session_scope') .and_raise(sqlalchemy_exc.SQLAlchemyError)) result = product.create_product(**VALID_PRODUCT_KWARGS) assert result.status == 500 def test_product_unique_constraint_itunes_vendor_id_within_provider(): """Test Product unique constraint itunes_vendor_id within provider.""" product_1 = product.Product(**VALID_PRODUCT_KWARGS) product_2_unique_kwargs = dict( product_id=PRODUCT_ID+1, orchard_product_id=ORCHARD_PRODUCT_ID+1, upc='another') product_2 = product.Product(**_get_product_kwargs(product_2_unique_kwargs)) with sql.session_scope() as session: session.add(product_1) with pytest.raises(sqlalchemy_exc.IntegrityError): with sql.session_scope() as session: session.add(product_2) product_2.itunes_vendor_id = VALID_PRODUCT_KWARGS['itunes_vendor_id'] + '1' with sql.session_scope() as session: session.add(product_2) product_2.itunes_vendor_id = VALID_PRODUCT_KWARGS['itunes_vendor_id'] product_2.itunes_vendor_id = models.IODA with sql.session_scope() as session: session.add(product_2) @pytest.mark.parametrize( 'kwargs', [ {}, dict( provider=models.IODA, itunes_vendor_id='foo', orchard_product_id=ORCHARD_PRODUCT_ID+1, product_id=PRODUCT_ID+1) ]) def test_product_as_dict(kwargs): """Test product dictionary representation.""" product_kwargs = _get_product_kwargs(kwargs) product_instance = product.Product(**product_kwargs) assert product_instance assert product_instance.as_dict() == product_kwargs def test_get_product_by_id(): """Test get_product_by_id for existing and non-existing records.""" for incr in range(1, 3): kwargs = _get_product_kwargs(dict( product_id=PRODUCT_ID+incr, orchard_product_id=ORCHARD_PRODUCT_ID+incr, upc=str(int(UPC)+incr), itunes_vendor_id=str(incr))) create_result = product.create_product(**kwargs) assert create_result.status == 200 select_result = product.get_product_by_id(PRODUCT_ID+incr) assert select_result.status == 200 assert select_result.message == create_result.message select_result = product.get_product_by_id(PRODUCT_ID) assert select_result.errors assert select_result.status == 404 assert select_result.errors['code'] == oto_error.ERROR_CODE_NOT_FOUND def test_get_product_by_id_internal_error(): """Test get_product_by_id for internal error handling.""" (flexmock(sql) .should_receive('session_scope') .and_raise(sqlalchemy_exc.SQLAlchemyError)) result = product.get_product_by_id(1) assert result.errors['code'] == oto_error.ERROR_CODE_INTERNAL_ERROR assert result.status == 500 def test_itunes_vendor_id_is_optional(test_database): """Assert itunes_vendor_id is optional.""" response = product.create_product( product_id=1, orchard_product_id=1, upc='1', provider='orchard') assert response.status == oto_status.OK def test_itunes_vendor_id_is_set_to_upc(test_database): """Assert itunes_vendor_id is set to upc.""" response = product.create_product( product_id=1, orchard_product_id=1, upc=UPC, provider='orchard') assert response.status == oto_status.OK with sql.session_scope() as session: created_product = session.query(product.Product).first() assert created_product.itunes_vendor_id == UPC def test_itunes_vendor_id_set_correctly(test_database): """Assert itunes_vendor_id will be set if provided.""" response = product.create_product( product_id=1, orchard_product_id=1, upc='12345678', provider='orchard', itunes_vendor_id=ITUNES_VENDOR_ID) assert response.status == oto_status.OK with sql.session_scope() as session: created_product = session.query(product.Product).first() assert created_product.itunes_vendor_id == ITUNES_VENDOR_ID def test_create_product_provider_is_optional(test_database): """Assert provider is optional and set 'orchard' by default.""" response = product.create_product(1, 1, '1', itunes_vendor_id='1') assert response.status == oto_status.OK with sql.session_scope() as session: created_product = session.query(product.Product).first() assert created_product.provider == 'orchard' def test_create_product_with_empty_provider(test_database): """Assert can create product with provider == ''.""" response = product.create_product(1, 1, '1', '1', provider='') assert response.status == oto_status.OK with sql.session_scope() as session: created_product = session.query(product.Product).first() assert created_product.provider == '' def test_create_product_when_it_already_exists(test_database): """Assert existing product will be returned.""" original_data = { 'product_id': 1, 'orchard_product_id': 1, 'upc': '1', 'itunes_vendor_id': '1', 'provider': 'orchard' } create_response = product.create_product(**original_data) assert create_response.status == oto_status.OK assert create_response.message == original_data changed_data = original_data.copy() changed_data['upc'] = 'something else' create_response = product.create_product(**changed_data) assert create_response.status == oto_status.OK assert create_response.message == original_data