diff --git a/docs/testing.rst b/docs/testing.rst index 0b6a88e..024a297 100644 --- a/docs/testing.rst +++ b/docs/testing.rst @@ -4,6 +4,21 @@ Testing .. automodule:: sqlalchemy_utils.asserts +assert_min_value +---------------- + +.. autofunction:: assert_min_value + +assert_max_length +----------------- + +.. autofunction:: assert_max_length + +assert_max_value +---------------- + +.. autofunction:: assert_max_value + assert_nullable --------------- @@ -13,8 +28,3 @@ assert_non_nullable ------------------- .. autofunction:: assert_non_nullable - -assert_max_length ------------------ - -.. autofunction:: assert_max_length diff --git a/sqlalchemy_utils/__init__.py b/sqlalchemy_utils/__init__.py index 0022f95..f774d3a 100644 --- a/sqlalchemy_utils/__init__.py +++ b/sqlalchemy_utils/__init__.py @@ -1,5 +1,11 @@ from .aggregates import aggregated -from .asserts import assert_nullable, assert_non_nullable, assert_max_length +from .asserts import ( + assert_min_value, + assert_max_length, + assert_max_value, + assert_nullable, + assert_non_nullable +) from .batch import batch_fetch, with_backrefs from .decorators import generates from .exceptions import ImproperlyConfigured diff --git a/sqlalchemy_utils/asserts.py b/sqlalchemy_utils/asserts.py index 6970495..934bea9 100644 --- a/sqlalchemy_utils/asserts.py +++ b/sqlalchemy_utils/asserts.py @@ -44,9 +44,7 @@ class raises(object): return self def __exit__(self, exc_type, exc_val, exc_tb): - if exc_type != self.expected_exc: - return False - return True + return exc_type == self.expected_exc def _update_field(obj, field, value): @@ -101,6 +99,31 @@ def assert_max_length(obj, column, max_length): :param obj: SQLAlchemy declarative model object :param column: Name of the column + :param max_length: Maximum length of given column """ _expect_successful_update(obj, column, u'a' * max_length, DataError) _expect_failing_update(obj, column, u'a' * (max_length + 1), DataError) + + +def assert_min_value(obj, column, min_value): + """ + Assert that the given column must have a minimum value of `min_value`. + + :param obj: SQLAlchemy declarative model object + :param column: Name of the column + :param min_value: The minimum allowed value for given column + """ + _expect_successful_update(obj, column, min_value, IntegrityError) + _expect_failing_update(obj, column, min_value - 1, IntegrityError) + + +def assert_max_value(obj, column, min_value): + """ + Assert that the given column must have a minimum value of `max_value`. + + :param obj: SQLAlchemy declarative model object + :param column: Name of the column + :param max_value: The maximum allowed value for given column + """ + _expect_successful_update(obj, column, min_value, IntegrityError) + _expect_failing_update(obj, column, min_value + 1, IntegrityError) diff --git a/sqlalchemy_utils/listeners.py b/sqlalchemy_utils/listeners.py index d6d4c1f..29970ff 100644 --- a/sqlalchemy_utils/listeners.py +++ b/sqlalchemy_utils/listeners.py @@ -113,7 +113,8 @@ def auto_delete_orphans(attr): """ Delete orphans for given SQLAlchemy model attribute. This function can be used for deleting many-to-many associated orphans easily. For more - information see https://bitbucket.org/zzzeek/sqlalchemy/wiki/UsageRecipes/ManyToManyOrphan. + information see + https://bitbucket.org/zzzeek/sqlalchemy/wiki/UsageRecipes/ManyToManyOrphan. Consider the following model definition: diff --git a/tests/test_asserts.py b/tests/test_asserts.py index 286d0f9..3384341 100644 --- a/tests/test_asserts.py +++ b/tests/test_asserts.py @@ -1,9 +1,11 @@ import sqlalchemy as sa import pytest from sqlalchemy_utils import ( + assert_min_value, + assert_max_length, + assert_max_value, assert_nullable, - assert_non_nullable, - assert_max_length + assert_non_nullable ) from sqlalchemy_utils.asserts import raises @@ -33,6 +35,10 @@ class AssertionTestCase(TestCase): age = sa.Column(sa.Integer, nullable=False) email = sa.Column(sa.String(200), nullable=False, unique=True) + __table_args__ = ( + sa.CheckConstraint(sa.and_(age >= 0, age <= 150)), + ) + self.User = User def setup_method(self, method): @@ -88,3 +94,39 @@ class TestAssertMaxLength(AssertionTestCase): assert_max_length(self.user, 'name', 21) with raises(AssertionError): assert_max_length(self.user, 'name', 21) + + +class TestAssertMinValue(AssertionTestCase): + def test_with_min_value(self): + assert_min_value(self.user, 'age', 0) + assert_min_value(self.user, 'age', 0) + + def test_smaller_than_min_value(self): + with raises(AssertionError): + assert_min_value(self.user, 'age', -1) + with raises(AssertionError): + assert_min_value(self.user, 'age', -1) + + def test_bigger_than_min_value(self): + with raises(AssertionError): + assert_min_value(self.user, 'age', 1) + with raises(AssertionError): + assert_min_value(self.user, 'age', 1) + + +class TestAssertMaxValue(AssertionTestCase): + def test_with_min_value(self): + assert_max_value(self.user, 'age', 150) + assert_max_value(self.user, 'age', 150) + + def test_smaller_than_max_value(self): + with raises(AssertionError): + assert_max_value(self.user, 'age', 149) + with raises(AssertionError): + assert_max_value(self.user, 'age', 149) + + def test_bigger_than_max_value(self): + with raises(AssertionError): + assert_max_value(self.user, 'age', 151) + with raises(AssertionError): + assert_max_value(self.user, 'age', 151)