Example #1
0
    def test_member_added_event(self):

        from cocktail.schema import Schema, String

        foo = Schema("foo")
        spam = Schema("spam")
        spam.inherit(foo)

        events = EventLog()
        events.listen(foo.member_added, spam.member_added)

        bar = String("bar")
        foo.add_member(bar)

        scrum = String("scrum")
        foo.add_member(scrum)

        event = events.pop(0)
        self.assertEqual(event.slot, foo.member_added)
        self.assertEqual(event.member, bar)

        event = events.pop(0)
        self.assertEqual(event.slot, foo.member_added)
        self.assertEqual(event.member, scrum)

        self.assertFalse(events)
Example #2
0
    def test_exclusive_expression(self):

        from cocktail.schema import Schema, String, Boolean
        from cocktail.schema.exceptions import (ValueRequiredError,
                                                NoneRequiredError)

        test_schema = Schema()
        test_schema.add_member(Boolean("enabled"))
        test_schema.add_member(
            String("field", exclusive=test_schema["enabled"]))

        # Valid states
        assert test_schema.validate({"enabled": False, "field": None})
        assert test_schema.validate({"enabled": True, "field": "foo"})

        # None required error
        errors = list(
            test_schema.get_errors({
                "enabled": False,
                "field": "foo"
            }))
        assert len(errors) == 1
        error = errors[0]
        assert isinstance(error, NoneRequiredError)

        # Required error
        errors = list(test_schema.get_errors({"enabled": True, "field": None}))
        assert len(errors) == 1
        error = errors[0]
        assert isinstance(error, ValueRequiredError)
Example #3
0
    def test_disabled_implicit_schema_copy(self):

        from cocktail.schema import Schema, Adapter

        user_schema = get_user_schema()
        form_schema = Schema()
        adapter = Adapter()
        adapter.implicit_copy = False

        adapter.export_schema(user_schema, form_schema)
        self.assertFalse(form_schema.members())
Example #4
0
    def test_exclude_undefined_member(self):

        from cocktail.schema import Adapter, Schema

        adapter = Adapter()
        adapter.exclude("fictitious_member")

        user_schema = get_user_schema()
        form_schema = Schema()
        adapter.export_schema(user_schema, form_schema)
        self.assertFalse("fictitious_member" in form_schema.members())
Example #5
0
    def test_implicit_schema_copy(self):

        from cocktail.schema import Schema, Adapter

        user_schema = get_user_schema()
        adapter = Adapter()

        self.assertTrue(adapter.implicit_copy)

        # Exporting schemas
        form_schema = Schema()
        adapter.export_schema(user_schema, form_schema)

        self.assertEqual(
            len(list(user_schema.members().keys())),
            len(list(form_schema.members().keys()))
        )

        self.assertEqual(
            set(user_schema.members().keys()),
            set(form_schema.members().keys())
        )

        # Importing schemas
        user_schema_2 = Schema()
        adapter.import_schema(form_schema, user_schema_2)

        self.assertEqual(
            list(form_schema.members().keys()),
            list(user_schema_2.members().keys())
        )
Example #6
0
    def test_preserves_ordering_from_constructor(self):

        from cocktail.schema import Schema, Member

        member_list = [Member("m%d" % i) for i in range(5)]
        schema = Schema(members=member_list)
        assert schema.members_order == [m.name for m in member_list]
Example #7
0
    def __init__(self,
                 type: Union[int, Default] = default,
                 schema: Union[Member, Default] = default,
                 content: Union[media_content, Default] = default,
                 description: Union[str, Default] = default,
                 headers: Union[List[Member], Default] = default):
        self.content = self.content.copy()

        if content is not default:
            self.content.update(content)

        # Normalize member lists / mappings to a Schema object
        if isinstance(schema, (Sequence, Mapping)):
            schema = Schema(members=schema)

        if type is not default:
            self.content.update(
                {type: {
                    "schema": None if schema is default else schema
                }})

        if schema is not default and type is default:
            raise ValueError(
                "Can't specify 'schema' without supplying a default response "
                "type")

        if description is not default:
            self.description = description

        if headers is default:
            self.headers = list(self.headers)
        else:
            self.headers = list(headers)
Example #8
0
    def test_simple_schema_copy(self):

        from cocktail.schema import Adapter, Schema, Copy

        adapter = Adapter()
        adapter.implicit_copy = False
        adapter.copy("id")
        adapter.copy("name")

        user_schema = get_user_schema()
        form_schema = Schema()

        adapter.export_schema(user_schema, form_schema)
        self.assertEqual(len(form_schema.members()), 2)
        self.assertEqual(
            set(form_schema.members().keys()),
            set(["id", "name"])
        )
Example #9
0
    def test_fails_if_member_specifies_conflicting_relative_positions(self):

        from cocktail.schema import Schema, Member

        schema = Schema(members=[
            Member("m1"),
            Member("m2", after_member="m1", before_member="m1")
        ])

        assert_raises(ValueError, schema.ordered_members)
Example #10
0
    def test_base_isolation(self):

        from cocktail.schema import Schema

        base = Schema()

        derived = Schema()
        self._add_error(derived, "derived_error")
        derived.inherit(base)

        assert not list(base.get_errors({}))
Example #11
0
    def test_members_can_specify_relative_positions(self):

        from cocktail.schema import Schema, Member

        m1 = Member("m1")
        m2 = Member("m2")
        m3 = Member("m3")
        m4 = Member("m4", after_member="m5")
        m5 = Member("m5", before_member="m1")
        m6 = Member("m6", before_member="m3")

        schema = Schema()
        schema.members_order = ["m3", "m2"]
        schema.add_member(m1)
        schema.add_member(m2)
        schema.add_member(m3)
        schema.add_member(m4)
        schema.add_member(m5)
        schema.add_member(m6)

        ordered_members = schema.ordered_members()
        assert ordered_members == [m6, m3, m2, m5, m4, m1]
Example #12
0
    def test_match_one_to_one(self):

        from cocktail.schema import Schema, Reference
        from cocktail.schema.exceptions import SchemaIntegrityError

        a = Schema("a")
        b = Schema("b")

        a.add_member(Reference("rel_b", type=b, bidirectional=True))
        b.add_member(Reference("rel_a", type=a, bidirectional=True))

        self.assertTrue(a["rel_b"].related_end is b["rel_a"])
        self.assertTrue(a["rel_b"].related_type is b)
        self.assertTrue(b["rel_a"].related_end is a["rel_b"])
        self.assertTrue(b["rel_a"].related_type is a)
Example #13
0
    def test_match_many_to_many(self):

        from cocktail.schema import Schema, Reference, Collection
        from cocktail.schema.exceptions import SchemaIntegrityError

        a = Schema("a")
        b = Schema("b")

        a.add_member(
            Collection("rel_b", items=Reference(type=b), bidirectional=True))
        b.add_member(
            Collection("rel_a", items=Reference(type=a), bidirectional=True))

        self.assertTrue(a["rel_b"].related_end is b["rel_a"])
        self.assertTrue(a["rel_b"].related_type is b)
        self.assertTrue(b["rel_a"].related_end is a["rel_b"])
        self.assertTrue(b["rel_a"].related_type is a)
Example #14
0
    def test_no_match(self):

        from cocktail.schema import Schema, Reference
        from cocktail.schema.exceptions import SchemaIntegrityError

        a = Schema("a")
        b = Schema("b")

        a.add_member(Reference("rel_b", type=b, bidirectional=True))
        b.add_member(Reference("rel_a", type=a))

        def resolve_relation():
            print(a["rel_b"].related_end)

        self.assertRaises(SchemaIntegrityError, resolve_relation)

        self.assertTrue(b["rel_a"].related_end is None)
Example #15
0
    def test_follows_explicit_ordering(self):

        from cocktail.schema import Schema, Member

        m1 = Member("m1")
        m2 = Member("m2")
        m3 = Member("m3")
        m4 = Member("m4")

        schema = Schema()
        schema.members_order = ["m1", "m4", "m3", "m2"]
        schema.add_member(m1)
        schema.add_member(m2)
        schema.add_member(m3)
        schema.add_member(m4)

        assert schema.ordered_members() == [m1, m4, m3, m2]
Example #16
0
    def test_callable(self):

        from cocktail.schema import Schema, String, Boolean
        from cocktail.schema.exceptions import ValueRequiredError

        test_schema = Schema()
        test_schema.add_member(Boolean("enabled"))
        test_schema.add_member(
            String("field", required=lambda ctx: ctx.get_value("enabled")))

        assert test_schema.validate({"enabled": False, "field": None})
        assert test_schema.validate({"enabled": True, "field": "foo"})

        errors = list(test_schema.get_errors({"enabled": True, "field": None}))
        assert len(errors) == 1
        error = errors[0]
        assert isinstance(error, ValueRequiredError)
Example #17
0
    def test_grouped_members(self):

        from cocktail.schema import Schema, Member

        a1 = Member("a1", member_group="a")
        a2 = Member("a2", member_group="a")
        b1 = Member("b1", member_group="b")
        b2 = Member("b2", member_group="b")
        z = Member("z")

        schema = Schema(members=[a1, b2, a2, z, b1])
        schema.members_order = ["a2", "a1", "b2", "b1"]
        schema.groups_order = "a", "b"

        groups = schema.grouped_members()

        assert len(groups) == 3
        assert all(isinstance(group, tuple) for group in groups)
        assert groups[0][0] == None
        assert groups[1][0] == "a"
        assert groups[2][0] == "b"
        assert list(groups[0][1]) == [z]
        assert list(groups[1][1]) == [a2, a1]
        assert list(groups[2][1]) == [b2, b1]
Example #18
0
    def test_scalar(self):

        from cocktail.schema import Schema, Integer, exceptions

        class Validable(object):
            def __init__(self, foo):
                self.foo = foo

            def __repr__(self):
                return "Validable(%r)" % self.foo

        self._test_validation(
            Schema(members={"foo": Integer()}),
            [Validable(None), Validable(1),
             Validable(15)],
            [Validable(""),
             Validable("hello, world!"),
             Validable(3.15)], exceptions.TypeCheckError)
Example #19
0
    def test_ignores_missing_anchors(self):

        from cocktail.schema import Schema, Member

        m1 = Member("m1", after_member="m3")
        m2 = Member("m2")

        schema = Schema()
        schema.members_order = ["m2"]
        schema.add_member(m1)
        schema.add_member(m2)

        assert schema.ordered_members() == [m2, m1]
Example #20
0
    def test_single_inheritance(self):

        from cocktail.schema import Schema

        base = Schema("base")
        self._add_error(base, "base_error")

        derived = Schema("derived")
        derived.inherit(base)

        errors = list(derived.get_errors({}))
        assert len(errors) == 1
        assert errors[0].error_id == "base_error"
Example #21
0
    def test_implicitly_includes_unspecified_members(self):

        from cocktail.schema import Schema, Member

        m1 = Member("m1")
        m2 = Member("m2")
        m3 = Member("m3")
        m4 = Member("m4")

        schema = Schema()
        schema.members_order = ["m3", "m2"]
        schema.add_member(m1)
        schema.add_member(m2)
        schema.add_member(m3)
        schema.add_member(m4)

        ordered_members = schema.ordered_members()
        assert len(ordered_members) == 4
        assert ordered_members[:2] == [m3, m2]
        assert set(ordered_members[2:]) == set([m1, m4])
Example #22
0
    def get_schema(self):

        from cocktail.schema import Schema, Reference, Collection

        schema = Schema()

        schema.add_member(Reference("parent", type=schema, bidirectional=True))

        schema.add_member(
            Collection("children",
                       items=Reference(type=schema),
                       bidirectional=True))

        return schema
Example #23
0
    def test_multiple_inheritance(self):

        from cocktail.schema import Schema

        base1 = Schema("base1")
        self._add_error(base1, "base1_error")

        base2 = Schema("base2")
        self._add_error(base2, "base2_error")

        derived = Schema("derived")
        derived.inherit(base1)
        derived.inherit(base2)

        errors = list(derived.get_errors({}))
        assert len(errors) == 2
        assert errors[0].error_id == "base1_error"
        assert errors[1].error_id == "base2_error"
Example #24
0
    def test_fails_on_cyclic_relative_positions(self):

        from cocktail.schema import Schema, Member

        # Self references
        schema = Schema(members=[Member("m1", after_member="m1")])
        assert_raises(ValueError, schema.ordered_members)

        schema = Schema(members=[Member("m1", before_member="m1")])
        assert_raises(ValueError, schema.ordered_members)

        # 2 step cycle
        schema = Schema(members=[
            Member("m1", before_member="m2"),
            Member("m2", before_member="m1")
        ])
        assert_raises(ValueError, schema.ordered_members)

        schema = Schema(members=[
            Member("m1", after_member="m2"),
            Member("m2", before_member="m1")
        ])
        assert_raises(ValueError, schema.ordered_members)

        schema = Schema(members=[
            Member("m1", before_member="m2"),
            Member("m2", after_member="m1")
        ])
        assert_raises(ValueError, schema.ordered_members)

        schema = Schema(members=[
            Member("m1", after_member="m2"),
            Member("m2", after_member="m1")
        ])
        assert_raises(ValueError, schema.ordered_members)

        # 3 step cycle
        schema = Schema(members=[
            Member("m1", before_member="m3"),
            Member("m2", before_member="m1"),
            Member("m3", before_member="m2")
        ])
        assert_raises(ValueError, schema.ordered_members)
Example #25
0
def get_user_schema():

    import re
    from cocktail.schema import Schema, Integer, String, Boolean

    return Schema(members = {
        "id": Integer(
            required = True,
            unique = True,
            min = 1
        ),
        "name": String(
            required = True,
            min = 4,
            max = 20,
            format = re.compile("^[a-zA-Z][a-zA-Z_0-9]*$")
        ),
        "enabled": Boolean(
            required = True,
            default = True
        )
    })
Example #26
0
    def test_translated(self):

        from cocktail.schema import Schema, String, exceptions

        schema = Schema()
        schema.add_member(String("foo", translated=True, required=True))

        validable = {"foo": {"ca": "Hola", "es": "Hola", "en": "Hello"}}

        assert not list(schema.get_errors(validable))

        validable["foo"]["fr"] = None
        validable["foo"]["es"] = None

        errors = list(schema.get_errors(validable))
        assert len(errors) == 2
        assert set([error.language for error in errors]) == set(["fr", "es"])
Example #27
0
    def test_add_duplicated_validation_to_base_schema(self):

        from cocktail.schema import Schema

        def a():
            pass

        s1 = Schema()

        s2 = Schema()
        s2.inherit(s1)
        s2.add_validation(a)

        v1 = list(s2.validations())
        s1.add_validation(a)
        v2 = list(s2.validations())

        assert v1 == v2
Example #28
0
class SchemaCoercionTestCase(TestCase):
    def setUp(self):
        from cocktail.schema import Schema, Integer, String
        self.schema = Schema(members=[
            Integer("num", required=True, min=5, default=50),
            String("text", min=5, default="cornucopia")
        ])

    def test_can_ignore_errors(self):

        from itertools import product
        from cocktail.schema import Coercion

        for obj in [{
                "num": 3,
                "text": None
        }, {
                "num": 20,
                "text": "hum"
        }, {
                "num": 3,
                "text": "hum"
        }, {
                "num": 15,
                "text": "spam!"
        }]:
            original = obj.copy()
            result = self.schema.coerce(obj, Coercion.NONE)
            assert result is obj
            assert result == original

    def test_can_replace_invalid_values_with_none(self):

        from cocktail.schema import Coercion

        # num invalid, text valid
        obj = {"num": 3, "text": None}
        result = self.schema.coerce(obj, Coercion.SET_NONE)
        assert obj is result
        assert result == {"num": None, "text": None}

        # num valid, text invalid
        obj = {"num": 20, "text": "hum"}
        result = self.schema.coerce(obj, Coercion.SET_NONE)
        assert obj is result
        assert result == {"num": 20, "text": None}

        # both invalid
        obj = {"num": 3, "text": "hum"}
        result = self.schema.coerce(obj, Coercion.SET_NONE)
        assert obj is result
        assert result == {"num": None, "text": None}

        # both valid
        obj = {"num": 15, "text": "spam!"}
        result = self.schema.coerce(obj, Coercion.SET_NONE)
        assert obj is result
        assert result == {"num": 15, "text": "spam!"}

    def test_can_replace_invalid_values_with_default(self):

        from cocktail.schema import Coercion

        default_num = self.schema["num"].default
        default_text = self.schema["text"].default

        # num invalid, text valid
        obj = {"num": 3, "text": None}
        result = self.schema.coerce(obj, Coercion.SET_DEFAULT)
        assert obj is result
        assert result == {"num": default_num, "text": None}

        # num valid, text invalid
        obj = {"num": 20, "text": "hum"}
        result = self.schema.coerce(obj, Coercion.SET_DEFAULT)
        assert obj is result
        assert result == {"num": 20, "text": default_text}

        # both invalid
        obj = {"num": 3, "text": "hum"}
        result = self.schema.coerce(obj, Coercion.SET_DEFAULT)
        assert obj is result
        assert result == {"num": default_num, "text": default_text}

        # both valid
        obj = {"num": 15, "text": "spam!"}
        result = self.schema.coerce(obj, Coercion.SET_DEFAULT)
        assert obj is result
        assert result == {"num": 15, "text": "spam!"}

    def test_can_raise_errors_immediately(self):

        from cocktail.schema import Coercion
        from cocktail.schema.exceptions import (InputError, MinValueError,
                                                MinLengthError)

        FI = Coercion.FAIL_IMMEDIATELY

        # num invalid, text valid
        obj = {"num": 3, "text": None}
        try:
            self.schema.coerce(obj, FI)
        except InputError as e:
            assert e.member is self.schema
            assert len(e.errors) == 1
            error = e.errors[0]
            assert isinstance(error, MinValueError)
            assert error.member is self.schema["num"]
        else:
            raise AssertionError(
                f"Coercing {obj} should raise an input error on 'num'")

        # num valid, text invalid
        obj = {"num": 20, "text": "hum"}
        try:
            self.schema.coerce(obj, FI)
        except InputError as e:
            assert e.member is self.schema
            assert len(e.errors) == 1
            error = e.errors[0]
            assert isinstance(error, MinLengthError)
            assert error.member is self.schema["text"]
        else:
            raise AssertionError(
                f"Coercing {obj} should raise a validation error on 'text'")

        # both invalid
        obj = {"num": 3, "text": "hum"}
        try:
            self.schema.coerce(obj, FI)
        except InputError as e:
            assert e.member is self.schema
            assert len(e.errors) == 1
            assert e.errors[0].member in list(self.schema.members().values())
        else:
            raise AssertionError(
                f"Coercing {obj} should raise a validation error")

        # both valid
        obj = {"num": 15, "text": "spam!"}
        original = obj.copy()
        result = self.schema.coerce(obj, FI)
        assert obj is result
        assert result == original

    def test_can_aggregate_errors(self):

        from cocktail.schema import Coercion
        from cocktail.schema.exceptions import InputError

        def bad_keys(e):
            keys = set()
            for error in e.errors:
                keys.update(member.name for member in error.invalid_members)
            return keys

        # num invalid, text valid
        obj = {"num": 3, "text": None}
        try:
            self.schema.coerce(obj, Coercion.FAIL)
        except InputError as e:
            assert bad_keys(e) == {"num"}
        else:
            raise AssertionError(
                f"Coercing {obj} should raise a validation error on 'num'")

        # num valid, text invalid
        obj = {"num": 20, "text": "hum"}
        try:
            self.schema.coerce(obj, Coercion.FAIL)
        except InputError as e:
            assert bad_keys(e) == {"text"}
        else:
            raise AssertionError(
                f"Coercing {obj} should raise a validation error on 'text'")

        # both invalid
        obj = {"num": 3, "text": "hum"}
        try:
            self.schema.coerce(obj, Coercion.FAIL)
        except InputError as e:
            assert bad_keys(e) == {"num", "text"}
        else:
            raise AssertionError(
                f"Coercing {obj} should raise a validation error")

        # both valid
        obj = {"num": 15, "text": "spam!"}
        original = obj.copy()
        result = self.schema.coerce(obj, Coercion.FAIL)
        assert obj is result
        assert result == original
Example #29
0
    def test_deep_inheritance(self):

        from cocktail.schema import Schema

        s1 = Schema()
        self._add_error(s1, "s1_error")

        s2 = Schema()
        s2.inherit(s1)
        self._add_error(s2, "s2_error")

        s3 = Schema()
        s3.inherit(s2)
        self._add_error(s3, "s3_error")

        s4 = Schema()
        s4.inherit(s3)

        errors = list(s4.get_errors({}))
        assert len(errors) == 3
        assert errors[0].error_id == "s1_error"
        assert errors[1].error_id == "s2_error"
        assert errors[2].error_id == "s3_error"
Example #30
0
 def setUp(self):
     from cocktail.schema import Schema, Integer, String
     self.schema = Schema(members=[
         Integer("num", required=True, min=5, default=50),
         String("text", min=5, default="cornucopia")
     ])