"""Tests for custom validations.""" import datetime from royalty_common.constants import error from royalty_common.marshalling.custom_fields import ma OPTIONS = ('Option A', 'Option B', 'Option C') VALID_DATA = { 'test_id': 1, 'money_amount': 100, 'percentage': 99, 'date': '2020-05-20', 'date_time': '2020-05-20T05:10:20.123456-0000', 'nonempty_string': 'valid string', 'enum': 'Option B' } class MockSchema(ma.Schema): """Ad-hoc schema for testing.""" test_id = ma.IntegerId(required=True) money_amount = ma.MoneyAmount(required=True) percentage = ma.Percentage(required=True) date = ma.FormattedDate(required=True) date_time = ma.FormattedDateTime(required=True) nonempty_string = ma.NonemptyString(required=True) enum = ma.Enum( options=OPTIONS, required=True ) def errors(input_data): """Load errors.""" return MockSchema().validate(input_data) def test_valid_data(): """Test VALID_DATA dict to be valid input.""" assert errors(VALID_DATA) == {} def test_integer_id_required(): """Requires a value when required=True.""" assert error.ERROR_FIELD_MISSING in \ errors({})['test_id'] def test_integer_id_must_be_valid(): """The value must be an integer.""" assert error.ERROR_MUST_BE_INT in \ errors({'test_id': 'asdf'})['test_id'] def test_percentage_required(): """Requires a value when required=True.""" assert error.ERROR_FIELD_MISSING in \ errors({})['percentage'] def test_percentage_must_be_in_range(): """Percentages must be between 0 and 100.""" assert error.ERROR_PERCENTAGE_NOT_IN_RANGE in \ errors({'percentage': -12.45})['percentage'] def test_percentage_must_be_valid(): """Percentages must be numbers.""" assert error.ERROR_INVALID_DECIMAL in \ errors({'percentage': 'asdf'})['percentage'] def test_percentage_precision_is_max_2_digits(): """Percentages cannot have more than 2 fractional digits.""" assert error.ERROR_INVALID_DECIMAL in \ errors({'percentage': 12.455})['percentage'] def test_date_required(): """Requires a value when required=True.""" assert error.ERROR_FIELD_MISSING in errors({})['date'] def test_date_must_be_valid(): """Dates must be dates.""" assert error.ERROR_INVALID_DATE in errors({'date': 'asdf'})['date'] def test_date_must_be_in_correct_format(): """Dates must be in the right format.""" assert error.ERROR_INVALID_DATE in errors({'date': '01-01-2018'})['date'] def test_datetime_must_be_valid(): """Datetime should be valid value error.""" assert error.ERROR_INVALID_DATETIME in errors( ({'date_time': '2020-05-04 05:05:05'}))['date_time'] def test_datetime_loads(): """Datetime should be load properly.""" str_datetime = '2020-05-20T05:10:20.123456+00:00' input_data = {**VALID_DATA, 'date_time': str_datetime} loaded = MockSchema().load(input_data) assert isinstance(loaded['date_time'], datetime.datetime) dumped = MockSchema().dump(loaded) assert dumped['date_time'] == '2020-05-20T05:10:20.123456+0000' str_datetime = '2020-05-20T05:10:20.123456' input_data = {**VALID_DATA, 'date_time': str_datetime} loaded = MockSchema().load(input_data) assert isinstance(loaded['date_time'], datetime.datetime) dumped = MockSchema().dump(loaded) assert dumped['date_time'] == '2020-05-20T05:10:20.123456+0000' str_datetime = '2020-05-20T05:10:20.123456+0400' input_data = {**VALID_DATA, 'date_time': str_datetime} loaded = MockSchema().load(input_data) assert isinstance(loaded['date_time'], datetime.datetime) dumped = MockSchema().dump(loaded) assert dumped['date_time'] == '2020-05-20T05:10:20.123456+0400' def test_money_amount_precision_is_max_2_digits(): """Money amounts cannot have more than 2 fractional digits.""" assert error.ERROR_INVALID_DECIMAL in \ errors({'money_amount': 12.233})['money_amount'] def test_money_amount_can_be_negative(): """Money amounts can be negative.""" assert 'money_amount' not in errors({'money_amount': -12.23}) def test_money_amount_when_required(): """Money amount when required must be present.""" assert error.ERROR_FIELD_MISSING in \ errors({})['money_amount'] def test_money_amount_must_be_valid(): """Money amount must be a number.""" assert error.ERROR_INVALID_DECIMAL in \ errors({'money_amount': 'asdf'})['money_amount'] def test_nonempty_string_required(): """Money amount must be a number.""" assert error.ERROR_FIELD_MISSING in \ errors({})['nonempty_string'] def test_nonempty_string_cannot_by_empty(): """Money amount must be a number.""" assert error.ERROR_FIELD_MISSING in \ errors({'nonempty_string': ''})['nonempty_string'] def test_enum_required(): """Money amount must be a number.""" assert error.ERROR_FIELD_MISSING in \ errors({})['enum'] def test_enum_invalid_value(): """Money amount must be a number.""" assert error.ERROR_INVALID_OPTION.format(choices=', '.join(OPTIONS)) in \ errors({'enum': 'Option D'})['enum']