<commit_before># Copyright Least Authority Enterprises.
# See LICENSE for details.

"""
Tests for ``txkube._swagger``.
"""

from datetime import datetime

from hypothesis import given
from hypothesis.strategies import sampled_from, integers

from eliot import Message

from pyrsistent import (
    InvariantException, CheckedKeyTypeError, CheckedValueTypeError,
    PTypeError, PClass, freeze,
)

from twisted.python.filepath import FilePath

from testtools.matchers import (
    Equals, MatchesPredicate,
    IsInstance, MatchesAll, Is, raises,
)

from .._swagger import (
    NotClassLike, NoSuchDefinition, AlreadyCreatedClass,
    Swagger, _IntegerRange,
    UsePrefix, PClasses, VersionedPClasses,
)

from ..testing import TestCase
from ..testing.matchers import raises_exception


def swagger_primitive_types():
    """
    Hypothesis strategy to build Swagger *primitive* type definitions.
    """
    def _swaggered(type_format):
        (t, f) = type_format
        result = {u"type": t}
        if f is not None:
            result[u"format"] = f
        return result

    return sampled_from([
        (u"integer", u"int32"),
        (u"integer", u"int64"),
        (u"string", None),
        (u"string", u"byte"),
        (u"string", u"date-time"),
        (u"boolean", None),
    ]).map(_swaggered)



class _IntegerRangeTests(TestCase):
    """
    Tests for ``_IntegerRange``.
    """
    def test_from_signed_bits(self):
        """
        ``_IntegerRange.from_signed_bits`` returns an ``_IntegerRange`` with lower
        and upper bounds set to the smallest and largest values a signed
        integer of the given width can represent.
        """
        r = _IntegerRange.from_signed_bits(4)
        self.assertThat(r, Equals(_IntegerRange(min=-8, max=7)))


    def test_from_unsigned_bits(self):
        """
        ``_IntegerRange.from_unsigned_bits`` returns an ``_IntegerRange`` with
        lower and upper bounds set to the smallest and largest values an
        unsigned integer of the given width can represent.
        """
        r = _IntegerRange.from_unsigned_bits(4)
        self.assertThat(r, Equals(_IntegerRange(min=0, max=15)))



class SwaggerTests(TestCase):
    spec_document = {
        u"definitions": {
            u"simple-type": {
                u"type": u"string",
            },
            u"optional-description": {
                u"properties": {
                },
            },
            u"boolean": {
                u"description": u"has type boolean",
                u"properties": {
                    u"v": {
                        u"description": u"",
                        u"type": u"boolean"
                    },
                }
            },
            u"required-boolean": {
                u"description": u"has type boolean",
                u"required": [
                    u"v",
                ],
                u"properties": {
                    u"v": {
                        u"description": u"required property",
                        u"type": u"boolean"
                    },
                }
            },
            u"string.unlabeled": {
                u"description": u"has type string and no format",
                u"properties": {
                    u"s": {
                        u"description": u"",
                        u"type": u"string"
                    },
                }
            },
            u"integer.int32": {
                u"description": u"has type integer and format int32",
                u"properties": {
                    u"i": {
                        u"description": u"",
                        u"type": u"integer",
                        u"format": u"int32"
                    },
                },
            },
            u"integer.int64": {
                u"description": u"has type integer and format int64",
                u"properties": {
                    u"i": {
                        u"description": u"",
                        u"type": u"integer",
                        u"format": u"int64"
                    },
                },
            },
            u"string.date-time": {
                u"description": u"has type string and format date-time",
                u"properties": {
                    u"s": {
                        u"description": u"",
                        u"type": u"string",
                        u"format": u"date-time",
                    },
                },
            },
            u"string.int-or-string": {
                u"description": u"has type string and format int-or-string",
                u"properties": {
                    u"s": {
                        u"description": u"",
                        u"type": u"string",
                        u"format": u"int-or-string",
                    },
                },
            },
            u"object": {
                u"description": u"has type object",
                u"properties": {
                    u"o": {
                        u"type": u"object",
                        u"additionalProperties": {
                            u"type": u"string",
                        },
                    },
                },
            },
            u"object-with-simple-ref": {
                u"description": u"has type object and $ref with simple type target",
                u"properties": {
                    u"p": {
                        u"$ref": u"#/definitions/simple-type",
                    },
                },
            },
            u"object-with-complex-ref": {
                u"description": u"has type object and $ref with type target of another class",
                u"properties": {
                    u"p": {
                        u"$ref": u"#/definitions/object-with-simple-ref",
                    },
                },
            },
            u"object-with-array": {
                u"description": u"has type object and array values",
                u"properties": {
                    u"o": {
                        u"type": u"object",
                        u"additionalProperties": {
                            u"type": u"array",
                            u"items": {
                                u"type": u"string",
                            },
                        },
                    },
                },
            },
            u"object-with-property-with-default": {
                u"description": u"has property with a default value",
                u"properties": {
                    u"d": {
                        u"type": u"string",
                        u"default": u"success",
                    },
                },
            },
        },
    }

    def setUp(self):
        super(SwaggerTests, self).setUp()
        self.spec = Swagger.from_document(self.spec_document)


    def test_hashing(self):
        self.assertThat(
            hash(self.spec),
            IsInstance(int),
        )


    def test_simple_type(self):
        self.assertThat(
            lambda: self.spec.pclass_for_definition(u"simple-type"),
            raises_exception(
                NotClassLike,
                args=(u"simple-type", {u"type": u"string"}),
            ),
        )


    def test_optional_description(self):
        Type = self.spec.pclass_for_definition(u"optional-description")
        self.assertThat(Type(), IsInstance(Type))


    @given(swagger_primitive_types())
    def test_nonrequired_none_default(self, swagger_type):
        spec = Swagger.from_document({
            u"definitions": {
                u"object": {
                    u"properties": {
                        u"o": swagger_type,
                    },
                },
            },
        })
        Type = spec.pclass_for_definition(u"object")
        self.expectThat(Type().o, Is(None))
        self.expectThat(Type().serialize(), Equals({}))


    def test_boolean(self):
        Type = self.spec.pclass_for_definition(u"boolean")
        self.expectThat(
            lambda: Type(v=3),
            raises_exception(PTypeError),
        )
        self.expectThat(
            lambda: Type(v=u"true"),
            raises_exception(PTypeError),
        )
        self.expectThat(Type(v=True).v, Equals(True))
        self.expectThat(Type(v=False).v, Equals(False))
        self.expectThat(Type(v=True).serialize(), Equals({u"v": True}))
        self.expectThat(Type(v=False).serialize(), Equals({u"v": False}))


    def test_required_default_serializer(self):
        Type = self.spec.pclass_for_definition(u"required-boolean")
        self.expectThat(Type(v=True).serialize(), Equals({u"v": True}))


    def test_integer_int32_errors(self):
        Type = self.spec.pclass_for_definition(u"integer.int32")
        self.expectThat(
            lambda: Type(i=u"foo"),
            raises_exception(PTypeError),
        )
        self.expectThat(
            lambda: Type(i=2 ** 32),
            raises_exception(InvariantException),
        )
        self.expectThat(
            lambda: Type(i=-1),
            raises_exception(InvariantException),
        )


    @given(integers(min_value=0, max_value=2 ** 32 - 1))
    def test_integer_int32_valid(self, expected):
        Type = self.spec.pclass_for_definition(u"integer.int32")
        self.assertThat(
            Type(i=expected).i,
            Equals(expected),
        )
        # The property is not required so we can set it to None.
        self.expectThat(Type(i=None).i, Is(None))
        # The property is not required so it defaults to None.
        self.expectThat(Type().i, Is(None))


    def test_integer_int64_errors(self):
        Type = self.spec.pclass_for_definition(u"integer.int64")
        self.expectThat(
            lambda: Type(i=u"foo"),
            raises_exception(PTypeError),
        )
        self.expectThat(
            lambda: Type(i=2 ** 64),
            raises_exception(InvariantException),
        )
        self.expectThat(
            lambda: Type(i=-1),
            raises_exception(InvariantException),
        )


    @given(integers(min_value=0, max_value=2 ** 64 - 1))
    def test_integer_int64_valid(self, expected):
        Type = self.spec.pclass_for_definition(u"integer.int64")
        self.assertThat(
            Type(i=expected).i,
            Equals(expected),
        )


    def test_string_unlabeled(self):
        Type = self.spec.pclass_for_definition(u"string.unlabeled")
        self.expectThat(
            lambda: Type(s=b"foo"),
            raises_exception(PTypeError),
        )
        self.expectThat(Type(s=u"bar").s, Equals(u"bar"))


    def test_string_date_time(self):
        Type = self.spec.pclass_for_definition(u"string.date-time")
        self.expectThat(
            lambda: Type(s=b"foo"),
            raises_exception(PTypeError),
        )
        self.expectThat(
            lambda: Type(s=u"foo"),
            raises_exception(CheckedValueTypeError),
        )
        now = datetime.utcnow()
        self.expectThat(Type(s=now).s, Equals(now))
        self.expectThat(Type(s=now.isoformat().decode("ascii")).s, Equals(now))

        # string / date-time fields serialize back to an ISO8601 format
        # string.
        serialized = Type(s=now).serialize()
        self.expectThat(
            serialized,
            Equals({u"s": now.isoformat().decode("ascii")}),
        )
        # Thanks for making bytes and unicode compare equal, Python.
        self.expectThat(serialized[u"s"], IsInstance(unicode))
        # Missing values don't appear in the output.
        self.expectThat(
            Type(s=None).serialize(),
            Equals({}),
        )
        self.expectThat(
            Type().serialize(),
            Equals({}),
        )


    def test_string_int_or_string(self):
        Type = self.spec.pclass_for_definition(u"string.int-or-string")
        self.expectThat(
            lambda: Type(s=b"foo"),
            raises_exception(PTypeError),
        )
        self.expectThat(Type(s=u"foo").s, Equals(u"foo"))
        self.expectThat(Type(s=u"50").s, Equals(u"50"))
        self.expectThat(Type(s=50).s, Equals(50))
        self.expectThat(Type(s=50L).s, Equals(50))


    def test_object(self):
        Type = self.spec.pclass_for_definition(u"object")
        self.expectThat(
            lambda: Type(o=3),
            raises_exception(AttributeError),
        )
        self.expectThat(
            lambda: Type(o={b"foo": u"bar"}),
            raises_exception(CheckedKeyTypeError),
        )
        self.expectThat(
            lambda: Type(o={u"foo": b"bar"}),<commit_msg>Import twisted.python.compat to facilitate Python 3.<commit_after># Copyright Least Authority Enterprises.
# See LICENSE for details.

"""
Tests for ``txkube._swagger``.
"""

from datetime import datetime

from hypothesis import given
from hypothesis.strategies import sampled_from, integers

from eliot import Message

from pyrsistent import (
    InvariantException, CheckedKeyTypeError, CheckedValueTypeError,
    PTypeError, PClass, freeze,
)

from twisted.python.compat import long, unicode
from twisted.python.filepath import FilePath

from testtools.matchers import (
    Equals, MatchesPredicate,
    IsInstance, MatchesAll, Is, raises,
)

from .._swagger import (
    NotClassLike, NoSuchDefinition, AlreadyCreatedClass,
    Swagger, _IntegerRange,
    UsePrefix, PClasses, VersionedPClasses,
)

from ..testing import TestCase
from ..testing.matchers import raises_exception


def swagger_primitive_types():
    """
    Hypothesis strategy to build Swagger *primitive* type definitions.
    """
    def _swaggered(type_format):
        (t, f) = type_format
        result = {u"type": t}
        if f is not None:
            result[u"format"] = f
        return result

    return sampled_from([
        (u"integer", u"int32"),
        (u"integer", u"int64"),
        (u"string", None),
        (u"string", u"byte"),
        (u"string", u"date-time"),
        (u"boolean", None),
    ]).map(_swaggered)



class _IntegerRangeTests(TestCase):
    """
    Tests for ``_IntegerRange``.
    """
    def test_from_signed_bits(self):
        """
        ``_IntegerRange.from_signed_bits`` returns an ``_IntegerRange`` with lower
        and upper bounds set to the smallest and largest values a signed
        integer of the given width can represent.
        """
        r = _IntegerRange.from_signed_bits(4)
        self.assertThat(r, Equals(_IntegerRange(min=-8, max=7)))


    def test_from_unsigned_bits(self):
        """
        ``_IntegerRange.from_unsigned_bits`` returns an ``_IntegerRange`` with
        lower and upper bounds set to the smallest and largest values an
        unsigned integer of the given width can represent.
        """
        r = _IntegerRange.from_unsigned_bits(4)
        self.assertThat(r, Equals(_IntegerRange(min=0, max=15)))



class SwaggerTests(TestCase):
    spec_document = {
        u"definitions": {
            u"simple-type": {
                u"type": u"string",
            },
            u"optional-description": {
                u"properties": {
                },
            },
            u"boolean": {
                u"description": u"has type boolean",
                u"properties": {
                    u"v": {
                        u"description": u"",
                        u"type": u"boolean"
                    },
                }
            },
            u"required-boolean": {
                u"description": u"has type boolean",
                u"required": [
                    u"v",
                ],
                u"properties": {
                    u"v": {
                        u"description": u"required property",
                        u"type": u"boolean"
                    },
                }
            },
            u"string.unlabeled": {
                u"description": u"has type string and no format",
                u"properties": {
                    u"s": {
                        u"description": u"",
                        u"type": u"string"
                    },
                }
            },
            u"integer.int32": {
                u"description": u"has type integer and format int32",
                u"properties": {
                    u"i": {
                        u"description": u"",
                        u"type": u"integer",
                        u"format": u"int32"
                    },
                },
            },
            u"integer.int64": {
                u"description": u"has type integer and format int64",
                u"properties": {
                    u"i": {
                        u"description": u"",
                        u"type": u"integer",
                        u"format": u"int64"
                    },
                },
            },
            u"string.date-time": {
                u"description": u"has type string and format date-time",
                u"properties": {
                    u"s": {
                        u"description": u"",
                        u"type": u"string",
                        u"format": u"date-time",
                    },
                },
            },
            u"string.int-or-string": {
                u"description": u"has type string and format int-or-string",
                u"properties": {
                    u"s": {
                        u"description": u"",
                        u"type": u"string",
                        u"format": u"int-or-string",
                    },
                },
            },
            u"object": {
                u"description": u"has type object",
                u"properties": {
                    u"o": {
                        u"type": u"object",
                        u"additionalProperties": {
                            u"type": u"string",
                        },
                    },
                },
            },
            u"object-with-simple-ref": {
                u"description": u"has type object and $ref with simple type target",
                u"properties": {
                    u"p": {
                        u"$ref": u"#/definitions/simple-type",
                    },
                },
            },
            u"object-with-complex-ref": {
                u"description": u"has type object and $ref with type target of another class",
                u"properties": {
                    u"p": {
                        u"$ref": u"#/definitions/object-with-simple-ref",
                    },
                },
            },
            u"object-with-array": {
                u"description": u"has type object and array values",
                u"properties": {
                    u"o": {
                        u"type": u"object",
                        u"additionalProperties": {
                            u"type": u"array",
                            u"items": {
                                u"type": u"string",
                            },
                        },
                    },
                },
            },
            u"object-with-property-with-default": {
                u"description": u"has property with a default value",
                u"properties": {
                    u"d": {
                        u"type": u"string",
                        u"default": u"success",
                    },
                },
            },
        },
    }

    def setUp(self):
        super(SwaggerTests, self).setUp()
        self.spec = Swagger.from_document(self.spec_document)


    def test_hashing(self):
        self.assertThat(
            hash(self.spec),
            IsInstance(int),
        )


    def test_simple_type(self):
        self.assertThat(
            lambda: self.spec.pclass_for_definition(u"simple-type"),
            raises_exception(
                NotClassLike,
                args=(u"simple-type", {u"type": u"string"}),
            ),
        )


    def test_optional_description(self):
        Type = self.spec.pclass_for_definition(u"optional-description")
        self.assertThat(Type(), IsInstance(Type))


    @given(swagger_primitive_types())
    def test_nonrequired_none_default(self, swagger_type):
        spec = Swagger.from_document({
            u"definitions": {
                u"object": {
                    u"properties": {
                        u"o": swagger_type,
                    },
                },
            },
        })
        Type = spec.pclass_for_definition(u"object")
        self.expectThat(Type().o, Is(None))
        self.expectThat(Type().serialize(), Equals({}))


    def test_boolean(self):
        Type = self.spec.pclass_for_definition(u"boolean")
        self.expectThat(
            lambda: Type(v=3),
            raises_exception(PTypeError),
        )
        self.expectThat(
            lambda: Type(v=u"true"),
            raises_exception(PTypeError),
        )
        self.expectThat(Type(v=True).v, Equals(True))
        self.expectThat(Type(v=False).v, Equals(False))
        self.expectThat(Type(v=True).serialize(), Equals({u"v": True}))
        self.expectThat(Type(v=False).serialize(), Equals({u"v": False}))


    def test_required_default_serializer(self):
        Type = self.spec.pclass_for_definition(u"required-boolean")
        self.expectThat(Type(v=True).serialize(), Equals({u"v": True}))


    def test_integer_int32_errors(self):
        Type = self.spec.pclass_for_definition(u"integer.int32")
        self.expectThat(
            lambda: Type(i=u"foo"),
            raises_exception(PTypeError),
        )
        self.expectThat(
            lambda: Type(i=2 ** 32),
            raises_exception(InvariantException),
        )
        self.expectThat(
            lambda: Type(i=-1),
            raises_exception(InvariantException),
        )


    @given(integers(min_value=0, max_value=2 ** 32 - 1))
    def test_integer_int32_valid(self, expected):
        Type = self.spec.pclass_for_definition(u"integer.int32")
        self.assertThat(
            Type(i=expected).i,
            Equals(expected),
        )
        # The property is not required so we can set it to None.
        self.expectThat(Type(i=None).i, Is(None))
        # The property is not required so it defaults to None.
        self.expectThat(Type().i, Is(None))


    def test_integer_int64_errors(self):
        Type = self.spec.pclass_for_definition(u"integer.int64")
        self.expectThat(
            lambda: Type(i=u"foo"),
            raises_exception(PTypeError),
        )
        self.expectThat(
            lambda: Type(i=2 ** 64),
            raises_exception(InvariantException),
        )
        self.expectThat(
            lambda: Type(i=-1),
            raises_exception(InvariantException),
        )


    @given(integers(min_value=0, max_value=2 ** 64 - 1))
    def test_integer_int64_valid(self, expected):
        Type = self.spec.pclass_for_definition(u"integer.int64")
        self.assertThat(
            Type(i=expected).i,
            Equals(expected),
        )


    def test_string_unlabeled(self):
        Type = self.spec.pclass_for_definition(u"string.unlabeled")
        self.expectThat(
            lambda: Type(s=b"foo"),
            raises_exception(PTypeError),
        )
        self.expectThat(Type(s=u"bar").s, Equals(u"bar"))


    def test_string_date_time(self):
        Type = self.spec.pclass_for_definition(u"string.date-time")
        self.expectThat(
            lambda: Type(s=b"foo"),
            raises_exception(PTypeError),
        )
        self.expectThat(
            lambda: Type(s=u"foo"),
            raises_exception(CheckedValueTypeError),
        )
        now = datetime.utcnow()
        self.expectThat(Type(s=now).s, Equals(now))
        self.expectThat(Type(s=now.isoformat().decode("ascii")).s, Equals(now))

        # string / date-time fields serialize back to an ISO8601 format
        # string.
        serialized = Type(s=now).serialize()
        self.expectThat(
            serialized,
            Equals({u"s": now.isoformat().decode("ascii")}),
        )
        # Thanks for making bytes and unicode compare equal, Python.
        self.expectThat(serialized[u"s"], IsInstance(unicode))
        # Missing values don't appear in the output.
        self.expectThat(
            Type(s=None).serialize(),
            Equals({}),
        )
        self.expectThat(
            Type().serialize(),
            Equals({}),
        )


    def test_string_int_or_string(self):
        Type = self.spec.pclass_for_definition(u"string.int-or-string")
        self.expectThat(
            lambda: Type(s=b"foo"),
            raises_exception(PTypeError),
        )
        self.expectThat(Type(s=u"foo").s, Equals(u"foo"))
        self.expectThat(Type(s=u"50").s, Equals(u"50"))
        self.expectThat(Type(s=50).s, Equals(50))
        self.expectThat(Type(s=long(50)).s, Equals(50))


    def test_object(self):
        Type = self.spec.pclass_for_definition(u"object")
        self.expectThat(
            lambda: Type(o=3),
            raises_exception(AttributeError),
        )
        self.expectThat(
            lambda: Type(o={b"foo": u"bar"}),
            raises_exception(CheckedKeyTypeError),
        )
        self.expectThat(
            lambda: Type(o={u"foo": b"bar"}),
{'fraction_non_alphanumeric': 0.12136994949494949, 'fraction_numerical': 0.005958017676767677, 'mean_word_length': 1.4936048799685164, 'pattern_counts': {'":': 192, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1341247', 'n_tokens_mistral': 7228, 'n_tokens_neox': 6847, 'n_words': 1263}