text: """tests/test_types.py.
  
  Tests the type validators included with hug_core
  
  Copyright (C) 2016 Timothy Edmund Crosley
  
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
  documentation files (the "Software"), to deal in the Software without restriction, including without limitation
  the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
  to permit persons to whom the Software is furnished to do so, subject to the following conditions:
  
  The above copyright notice and this permission notice shall be included in all copies or
  substantial portions of the Software.
  
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
  TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
  CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  OTHER DEALINGS IN THE SOFTWARE.
  
  """
  import json
  import urllib
  from datetime import datetime
  from decimal import Decimal
  from uuid import UUID
  
  import pytest
  from marshmallow import Schema, ValidationError, fields
  from marshmallow.decorators import validates_schema
  
  import hug_core
  from hug_core.exceptions import InvalidTypeData
  
  api = hug_core.API(__name__)
  
  
  def test_type():
      """Test to ensure the abstract Type object can't be used"""
      with pytest.raises(NotImplementedError):
          hug_core.types.Type()("value")
  
  
  def test_number():
      """Tests that hug_core's number type correctly converts and validates input"""
      assert hug_core.types.number("1") == 1
      assert hug_core.types.number(1) == 1
      with pytest.raises(ValueError):
          hug_core.types.number("bacon")
  
  
  def test_range():
      """Tests that hug_core's range type successfully handles ranges of numbers"""
      assert hug_core.types.in_range(1, 10)("1") == 1
      assert hug_core.types.in_range(1, 10)(1) == 1
      assert "1" in hug_core.types.in_range(1, 10).__doc__
      with pytest.raises(ValueError):
          hug_core.types.in_range(1, 10)("bacon")
      with pytest.raises(ValueError):
          hug_core.types.in_range(1, 10)("15")
      with pytest.raises(ValueError):
          hug_core.types.in_range(1, 10)(-34)
  
  
  def test_less_than():
      """Tests that hug_core's less than type successfully limits the values passed in"""
      assert hug_core.types.less_than(10)("1") == 1
      assert hug_core.types.less_than(10)(1) == 1
      assert hug_core.types.less_than(10)(-10) == -10
      assert "10" in hug_core.types.less_than(10).__doc__
      with pytest.raises(ValueError):
          assert hug_core.types.less_than(10)(10)
  
  
  def test_greater_than():
      """Tests that hug_core's greater than type succefully limis the values passed in"""
      assert hug_core.types.greater_than(10)("11") == 11
      assert hug_core.types.greater_than(10)(11) == 11
      assert hug_core.types.greater_than(10)(1000) == 1000
      assert "10" in hug_core.types.greater_than(10).__doc__
      with pytest.raises(ValueError):
          assert hug_core.types.greater_than(10)(9)
  
  
  def test_multiple():
      """Tests that hug_core's multile type correctly forces values to come back as lists, but not lists of lists"""
      assert hug_core.types.multiple("value") == ["value"]
      assert hug_core.types.multiple(["value1", "value2"]) == ["value1", "value2"]
  
  
  def test_delimited_list():
      """Test to ensure hug_core's custom delimited list type function works as expected"""
      assert hug_core.types.delimited_list(",")("value1,value2") == ["value1", "value2"]
      assert hug_core.types.DelimitedList[int](",")("1,2") == [1, 2]
      assert hug_core.types.delimited_list(",")(["value1", "value2"]) == ["value1", "value2"]
      assert hug_core.types.delimited_list("|-|")("value1|-|value2|-|value3,value4") == [
          "value1",
          "value2",
          "value3,value4",
      ]
      assert "," in hug_core.types.delimited_list(",").__doc__
  
  
  def test_comma_separated_list():
      """Tests that hug_core's comma separated type correctly converts into a Python list"""
      assert hug_core.types.comma_separated_list("value") == ["value"]
      assert hug_core.types.comma_separated_list("value1,value2") == ["value1", "value2"]
  
  
  def test_float_number():
      """Tests to ensure the float type correctly allows floating point values"""
      assert hug_core.types.float_number("1.1") == 1.1
      assert hug_core.types.float_number("1") == float(1)
      assert hug_core.types.float_number(1.1) == 1.1
      with pytest.raises(ValueError):
          hug_core.types.float_number("bacon")
  
  
  def test_decimal():
      """Tests to ensure the decimal type correctly allows decimal values"""
      assert hug_core.types.decimal("1.1") == Decimal("1.1")
      assert hug_core.types.decimal("1") == Decimal("1")
      assert hug_core.types.decimal(1.1) == Decimal(1.1)
      with pytest.raises(ValueError):
          hug_core.types.decimal("bacon")
  
  
  def test_boolean():
      """Test to ensure the custom boolean type correctly supports boolean conversion"""
      assert hug_core.types.boolean("1")
      assert hug_core.types.boolean("T")
      assert not hug_core.types.boolean("")
      assert hug_core.types.boolean("False")
      assert not hug_core.types.boolean(False)
  
  
  def test_mapping():
      """Test to ensure the mapping type works as expected"""
      mapping_type = hug_core.types.mapping({"n": None, "l": [], "s": set()})
      assert mapping_type("n") is None
      assert mapping_type("l") == []
      assert mapping_type("s") == set()
      assert "n" in mapping_type.__doc__
      with pytest.raises(KeyError):
          mapping_type("bacon")
  
  
  def test_smart_boolean():
      """Test to ensure that the smart boolean type works as expected"""
      assert hug_core.types.smart_boolean("true")
      assert hug_core.types.smart_boolean("t")
      assert hug_core.types.smart_boolean("1")
      assert hug_core.types.smart_boolean(1)
      assert not hug_core.types.smart_boolean("")
      assert not hug_core.types.smart_boolean("false")
      assert not hug_core.types.smart_boolean("f")
      assert not hug_core.types.smart_boolean("0")
      assert not hug_core.types.smart_boolean(0)
      assert hug_core.types.smart_boolean(True)
      assert not hug_core.types.smart_boolean(None)
      assert not hug_core.types.smart_boolean(False)
      with pytest.raises(KeyError):
          hug_core.types.smart_boolean("bacon")
  
  
  def test_text():
      """Tests that hug_core's text validator correctly handles basic values"""
      assert hug_core.types.text("1") == "1"
      assert hug_core.types.text(1) == "1"
      assert hug_core.types.text("text") == "text"
      with pytest.raises(ValueError):
          hug_core.types.text(["one", "two"])
  
  
  def test_uuid():
      """Tests that hug_core's text validator correctly handles UUID values
         Examples were taken from https://docs.python.org/3/library/uuid.html"""
  
      assert hug_core.types.uuid("{12345678-1234-5678-1234-567812345678}") == UUID(
          "12345678-1234-5678-1234-567812345678"
      )
      assert hug_core.types.uuid("12345678-1234-5678-1234-567812345678") == UUID(
          "12345678123456781234567812345678"
      )
      assert hug_core.types.uuid("12345678123456781234567812345678") == UUID(
          "12345678-1234-5678-1234-567812345678"
      )
      assert hug_core.types.uuid("urn:uuid:12345678-1234-5678-1234-567812345678") == UUID(
          "12345678-1234-5678-1234-567812345678"
      )
  
      with pytest.raises(ValueError):
          hug_core.types.uuid(1)
  
      with pytest.raises(ValueError):
          # Invalid HEX character
          hug_core.types.uuid("12345678-1234-5678-1234-56781234567G")
  
      with pytest.raises(ValueError):
          # One character added
          hug_core.types.uuid("12345678-1234-5678-1234-5678123456781")
      with pytest.raises(ValueError):
          # One character removed
          hug_core.types.uuid("12345678-1234-5678-1234-56781234567")
  
  
  def test_length():
      """Tests that hug_core's length type successfully handles a length range"""
      assert hug_core.types.length(1, 10)("bacon") == "bacon"
      assert hug_core.types.length(1, 10)(42) == "42"
      assert "42" in hug_core.types.length(1, 42).__doc__
      with pytest.raises(ValueError):
          hug_core.types.length(1, 10)("bacon is the greatest food known to man")
      with pytest.raises(ValueError):
          hug_core.types.length(1, 10)("")
      with pytest.raises(ValueError):
          hug_core.types.length(1, 10)("bacon is th")
  
  
  def test_shorter_than():
      """Tests that hug_core's shorter than type successfully limits the values passed in"""
      assert hug_core.types.shorter_than(10)("hi there") == "hi there"
      assert hug_core.types.shorter_than(10)(1) == "1"
      assert hug_core.types.shorter_than(10)("") == ""
      assert "10" in hug_core.types.shorter_than(10).__doc__
      with pytest.raises(ValueError):
          assert hug_core.types.shorter_than(10)(
              "there is quite a bit of text here, in fact way more than allowed"
          )
  
  
  def test_longer_than():
      """Tests that hug_core's greater than type succefully limis the values passed in"""
      assert (
          hug_core.types.longer_than(10)("quite a bit of text here should be")
          == "quite a bit of text here should be"
      )
      assert hug_core.types.longer_than(10)(12345678910) == "12345678910"
      assert hug_core.types.longer_than(10)(100123456789100) == "100123456789100"
      assert "10" in hug_core.types.longer_than(10).__doc__
      with pytest.raises(ValueError):
          assert hug_core.types.longer_than(10)("short")
  
  
  def test_cut_off():
      """Test to ensure that hug_core's cut_off type works as expected"""
      assert hug_core.types.cut_off(10)("text") == "text"
      assert hug_core.types.cut_off(10)(10) == "10"
      assert hug_core.types.cut_off(10)("some really long text") == "some reall"
      assert "10" in hug_core.types.cut_off(10).__doc__
  
  
  def test_inline_dictionary():
      """Tests that inline dictionary values are correctly handled"""
      int_dict = hug_core.types.InlineDictionary[int, int]()
      assert int_dict("1:2") == {1: 2}
      assert int_dict("1:2|3:4") == {1: 2, 3: 4}
      assert hug_core.types.inline_dictionary("1:2") == {"1": "2"}
      assert hug_core.types.inline_dictionary("1:2|3:4") == {"1": "2", "3": "4"}
      with pytest.raises(ValueError):
          hug_core.types.inline_dictionary("1")
  
      int_dict = hug_core.types.InlineDictionary[int]()
      assert int_dict("1:2") == {1: "2"}
  
      int_dict = hug_core.types.InlineDictionary[int, int, int]()
      assert int_dict("1:2") == {1: 2}
  
  
  def test_one_of():
      """Tests that hug_core allows limiting a value to one of a list of values"""
      assert hug_core.types.one_of(("bacon", "sausage", "pancakes"))("bacon") == "bacon"
      assert hug_core.types.one_of(["bacon", "sausage", "pancakes"])("sausage") == "sausage"
      assert hug_core.types.one_of({"bacon", "sausage", "pancakes"})("pancakes") == "pancakes"
      assert "bacon" in hug_core.types.one_of({"bacon", "sausage", "pancakes"}).__doc__
      with pytest.raises(KeyError):
          hug_core.types.one_of({"bacon", "sausage", "pancakes"})("syrup")
  
  
  def test_accept():
      """Tests to ensure the accept type wrapper works as expected"""
      custom_converter = lambda value: value + " converted"
      custom_type = hug_core.types.accept(custom_converter, "A string Value")
      with pytest.raises(TypeError):
          custom_type(1)
  
  
  def test_accept_custom_exception_text():
      """Tests to ensure it's easy to custom the exception text using the accept wrapper"""
      custom_converter = lambda value: value + " converted"
      custom_type = hug_core.types.accept(custom_converter, "A string Value", "Error occurred")
      assert custom_type("bacon") == "bacon converted"
      with pytest.raises(ValueError):
          custom_type(1)
  
  
  def test_accept_custom_exception_handlers():
      """Tests to ensure it's easy to custom the exception text using the accept wrapper"""
      custom_converter = lambda value: (str(int(value)) if value else value) + " converted"
      custom_type = hug_core.types.accept(
          custom_converter, "A string Value", exception_handlers={TypeError: "0 provided"}
      )
      assert custom_type("1") == "1 converted"
      with pytest.raises(ValueError):
          custom_type("bacon")
      with pytest.raises(ValueError):
          custom_type(0)
  
      custom_type = hug_core.types.accept(
          custom_converter, "A string Value", exception_handlers={TypeError: KeyError}
      )
      with pytest.raises(KeyError):
          custom_type(0)
  
  
  def test_json():
      """Test to ensure that the json type correctly handles url encoded json, as well as direct json"""
      assert hug_core.types.json({"this": "works"}) == {"this": "works"}
      assert hug_core.types.json(json.dumps({"this": "works"})) == {"this": "works"}
      with pytest.raises(ValueError):
          hug_core.types.json("Invalid JSON")
  
      assert hug_core.types.json(json.dumps(["a", "b"]).split(",")) == ["a", "b"]
      with pytest.raises(ValueError):
          assert hug_core.types.json(["Invalid JSON", "Invalid JSON"])
  
  
  def test_multi():
      """Test to ensure that the multi type correctly handles a variety of value types"""
      multi_type = hug_core.types.multi(hug_core.types.json, hug_core.types.smart_boolean)
      assert multi_type({"this": "works"}) == {"this": "works"}
      assert multi_type(json.dumps({"this": "works"})) == {"this": "works"}
      assert multi_type("t")
      with pytest.raises(ValueError):
          multi_type("Bacon!")
  
  
  def test_chain():
      """Test to ensure that chaining together multiple types works as expected"""
      chain_type = hug_core.types.Chain(hug_core.types.text, hug_core.types.LongerThan(10))
      assert chain_type(12345678901) == "12345678901"
      with pytest.raises(ValueError):
          chain_type(1)
  
  
  def test_nullable():
      """Test the concept of a nullable type"""
      nullable_type = hug_core.types.Nullable(hug_core.types.text, hug_core.types.LongerThan(10))
      assert nullable_type(12345678901) == "12345678901"
      assert nullable_type(None) is None
      with pytest.raises(ValueError):
          nullable_type(1)
  
  
  def test_schema_type():
      """Test hug_core's complex schema types"""
  
      class User(hug_core.types.Schema):
          username = hug_core.types.text
          password = hug_core.types.Chain(hug_core.types.text, hug_core.types.LongerThan(10))
  
      user_one = User({"username": "brandon", "password": "password123"})
      user_two = User(user_one)
      with pytest.raises(ValueError):
          user_three = User({"username": "brandon", "password": "123"})
      user_three = User({"username": "brandon", "password": "123"}, force=True)
      with pytest.raises(AttributeError):
          del user_one.username
      assert "username" in User.__slots__
      assert "_username" in User.__slots__
      assert user_one._username == "brandon"
      assert user_two == user_one
      assert user_three._username == "brandon"
      assert user_one.username == "brandon"
      assert user_two.username == "brandon"
      assert user_three.username == "brandon"
      assert user_one.password == "password123"
      with pytest.raises(ValueError):
          user_one.password = "test"
      assert user_one.password == "password123"
  
  
  def test_marshmallow_schema():
      """Test hug_core's marshmallow schema support"""
  
      class UserSchema(Schema):
          name = fields.Int()
  
      schema_type = hug_core.types.MarshmallowInputSchema(UserSchema())
      assert schema_type({"name": 23}, {}) == {"name": 23}
      assert schema_type("""{"name": 23}""", {}) == {"name": 23}
      assert schema_type.__doc__ == "UserSchema"
      with pytest.raises(InvalidTypeData):
          schema_type({"name": "test"}, {})
  
      schema_type = hug_core.types.MarshmallowReturnSchema(UserSchema())
      assert schema_type({"name": 23}) == {"name": 23}
      assert schema_type.__doc__ == "UserSchema"
      with pytest.raises(InvalidTypeData):
          schema_type({"name": "test"})
  
  
  def test_create_type():
      """Test hug_core's new type creation decorator works as expected"""
  
      @hug_core.type(
          extend=hug_core.types.text,
          exception_handlers={TypeError: ValueError, LookupError: "Hi!"},
          error_text="Invalid",
      )
      def prefixed_string(value):
          if value == "hi":
              raise TypeError("Repeat of prefix")
          elif value == "bye":
              raise LookupError("Never say goodbye!")
          elif value == "1+1":
              raise ArithmeticError("Testing different error types")
          return "hi-" + value
  
      assert prefixed_string("there") == "hi-there"
      with pytest.raises(ValueError):
          prefixed_string([])
      with pytest.raises(ValueError):
          prefixed_string("hi")
      with pytest.raises(ValueError):
          prefixed_string("bye")
  
      @hug_core.type(extend=hug_core.types.text, exception_handlers={TypeError: ValueError})
      def prefixed_string(value):
          if value == "1+1":
              raise ArithmeticError("Testing different error types")
          return "hi-" + value
  
      with pytest.raises(ArithmeticError):
          prefixed_string("1+1")
  
      @hug_core.type(extend=hug_core.types.text)
      def prefixed_string(value):
          return "hi-" + value
  
      assert prefixed_string("there") == "hi-there"
  
      @hug_core.type(extend=hug_core.types.one_of)
      def numbered(value):
          return int(value)
  
      assert numbered(["1", "2", "3"])("1") == 1
  
  
  def test_marshmallow_custom_context():
      custom_context = dict(context="global", factory=0, delete=0, marshmallow=0)
  
      @hug_core.context_factory(apply_globally=True)
      def create_context(*args, **kwargs):
          custom_context["factory"] += 1
          return custom_context
  
      @hug_core.delete_context(apply_globally=True)
      def delete_context(context, *args, **kwargs):
          assert context == custom_context
          custom_context["delete"] += 1
  
      class MarshmallowContextSchema(Schema):
          name = fields.String()
  
          @validates_schema
          def check_context(self, data):
              assert self.context == custom_context
              self.context["marshmallow"] += 1
  
      @hug_core.local()
      def made_up_hello(test: MarshmallowContextSchema()):
          return "hi"
  
      assert made_up_hello({"name": "test"}) == "hi"
      assert custom_context["factory"] == 1
      assert custom_context["delete"] == 1
      assert custom_context["marshmallow"] == 1
  
  
  def test_extending_types_with_context_with_no_error_messages():
      custom_context = dict(context="global", the_only_right_number=42)
  
      @hug_core.context_factory()
      def create_context(*args, **kwargs):
          return custom_context
  
      @hug_core.delete_context()
      def delete_context(*args, **kwargs):
          pass
  
      @hug_core.type(chain=True, extend=hug_core.types.number)
      def check_if_positive(value):
          if value < 0:
              raise ValueError("Not positive")
          return value
  
      @hug_core.type(chain=True, extend=check_if_positive, accept_context=True)
      def check_if_near_the_right_number(value, context):
          the_only_right_number = context["the_only_right_number"]
          if value not in [
              the_only_right_number - 1,
              the_only_right_number,
              the_only_right_number + 1,
          ]:
              raise ValueError("Not near the right number")
          return value
  
      @hug_core.type(chain=True, extend=check_if_near_the_right_number, accept_context=True)
      def check_if_the_only_right_number(value, context):
          if value != context["the_only_right_number"]:
              raise ValueError("Not the right number")
          return value
  
      @hug_core.type(chain=False, extend=hug_core.types.number, accept_context=True)
      def check_if_string_has_right_value(value, context):
          if str(context["the_only_right_number"]) not in value:
              raise ValueError("The value does not contain the only right number")
          return value
  
      @hug_core.type(chain=False, extend=hug_core.types.number)
      def simple_check(value):
          if value != "simple":
              raise ValueError("This is not simple")
          return value
  
      @hug_core.local("/check_the_types")
      def check_the_types(
          first: check_if_positive,
          second: check_if_near_the_right_number,
          third: check_if_the_only_right_number,
          forth: check_if_string_has_right_value,
          fifth: simple_check,
      ):
          return "hi"
  
      test_cases = [
          ((42, 42, 42, "42", "simple"), (None, None, None, None, None)),
          ((43, 43, 43, "42", "simple"), (None, None, "Not the right number", None, None)),
          (
              (40, 40, 40, "42", "simple"),
              (None, "Not near the right number", "Not near the right number", None, None),
          ),
          (
              (-42, -42, -42, "53", "not_simple"),
              (
                  "Not positive",
                  "Not positive",
                  "Not positive",
                  "The value does not contain the only right number",
                  "This is not simple",
              ),
          ),
      ]
  
      for provided_values, expected_results in test_cases:
          response = check_the_types(
              **{
                  "first": provided_values[0],
                  "second": provided_values[1],
                  "third": provided_values[2],
                  "forth": provided_values[3],
                  "fifth": provided_values[4],
              }
          )
          if response == "hi":
              errors = (None, None, None, None, None)
          else:
              errors = []
              for key in ["first", "second", "third", "forth", "fifth"]:
                  if key in response.data["errors"]:
                      errors.append(response.data["errors"][key])
                  else:
                      errors.append(None)
              errors = tuple(errors)
          assert errors == expected_results
  
  
  def test_extending_types_with_context_with_error_messages():
      custom_context = dict(context="global", the_only_right_number=42)
  
      @hug_core.context_factory()
      def create_context(*args, **kwargs):
          return custom_context
  
      @hug_core.delete_context()
      def delete_context(*args, **kwargs):
          pass
  
      @hug_core.type(chain=True, extend=hug_core.types.number, error_text="error 1")
      def check_if_positive(value):
          if value < 0:
              raise ValueError("Not positive")
          return value
  
      @hug_core.type(chain=True, extend=check_if_positive, accept_context=True, error_text="error 2")
      def check_if_near_the_right_number(value, context):
          the_only_right_number = context["the_only_right_number"]
          if value not in [
              the_only_right_number - 1,
              the_only_right_number,
              the_only_right_number + 1,
          ]:
              raise ValueError("Not near the right number")
          return value
  
      @hug_core.type(
          chain=True, extend=check_if_near_the_right_number, accept_context=True, error_text="error 3"
      )
      def check_if_the_only_right_number(value, context):
          if value != context["the_only_right_number"]:
              raise ValueError("Not the right number")
          return value
  
      @hug_core.type(chain=False, extend=hug_core.types.number, accept_context=True, error_text="error 4")
      def check_if_string_has_right_value(value, context):
          if str(context["the_only_right_number"]) not in value:
              raise ValueError("The value does not contain the only right number")
          return value
  
      @hug_core.type(chain=False, extend=hug_core.types.number, error_text="error 5")
      def simple_check(value):
          if value != "simple":
              raise ValueError("This is not simple")
          return value
  
      @hug_core.local("/check_the_types")
      def check_the_types(
          first: check_if_positive,
          second: check_if_near_the_right_number,
          third: check_if_the_only_right_number,
          forth: check_if_string_has_right_value,
          fifth: simple_check,
      ):
          return "hi"
  
      test_cases = [
          ((42, 42, 42, "42", "simple"), (None, None, None, None, None)),
          ((43, 43, 43, "42", "simple"), (None, None, "error 3", None, None)),
          ((40, 40, 40, "42", "simple"), (None, "error 2", "error 3", None, None)),
          (
              (-42, -42, -42, "53", "not_simple"),
              ("error 1", "error 2", "error 3", "error 4", "error 5"),
          ),
      ]
  
      for provided_values, expected_results in test_cases:
          response = check_the_types(
              **{
                  "first": provided_values[0],
                  "second": provided_values[1],
                  "third": provided_values[2],
                  "forth": provided_values[3],
                  "fifth": provided_values[4],
              }
          )
          if response == "hi":
              errors = (None, None, None, None, None)
          else:
              errors = []
              for key in ["first", "second", "third", "forth", "fifth"]:
                  if key in response.data["errors"]:
                      errors.append(response.data["errors"][key])
                  else:
                      errors.append(None)
              errors = tuple(errors)
          assert errors == expected_results
  
  
  def test_extending_types_with_exception_in_function():
      custom_context = dict(context="global", the_only_right_number=42)
  
      class CustomStrException(Exception):
          pass
  
      class CustomFunctionException(Exception):
          pass
  
      class CustomNotRegisteredException(ValueError):
          def __init__(self):
              super().__init__("not registered exception")
  
      exception_handlers = {
          CustomFunctionException: lambda exception: ValueError("function exception"),
          CustomStrException: "string exception",
      }
  
      @hug_core.context_factory()
      def create_context(*args, **kwargs):
          return custom_context
  
      @hug_core.delete_context()
      def delete_context(*args, **kwargs):
          pass
  
      @hug_core.type(chain=True, extend=hug_core.types.number, exception_handlers=exception_handlers)
      def check_simple_exception(value):
          if value < 0:
              raise CustomStrException()
          elif value == 0:
              raise CustomNotRegisteredException()
          else:
              raise CustomFunctionException()
  
      @hug_core.type(
          chain=True,
          extend=hug_core.types.number,
          exception_handlers=exception_handlers,
          accept_context=True,
      )
      def check_context_exception(value, context):
          if value < 0:
              raise CustomStrException()
          elif value == 0:
              raise CustomNotRegisteredException()
          else:
              raise CustomFunctionException()
  
      @hug_core.type(chain=True, extend=hug_core.types.number, accept_context=True)
      def no_check(value, context):
          return value
  
      @hug_core.type(
          chain=True, extend=no_check, exception_handlers=exception_handlers, accept_context=True
      )
      def check_another_context_exception(value, context):
          if value < 0:
              raise CustomStrException()
          elif value == 0:
              raise CustomNotRegisteredException()
          else:
              raise CustomFunctionException()
  
      @hug_core.type(chain=False, exception_handlers=exception_handlers, accept_context=True)
      def check_simple_no_chain_exception(value, context):
          if value == "-1":
              raise CustomStrException()
          elif value == "0":
              raise CustomNotRegisteredException()
          else:
              raise CustomFunctionException()
  
      @hug_core.type(chain=False, exception_handlers=exception_handlers, accept_context=False)
      def check_simple_no_chain_no_context_exception(value):
          if value == "-1":
              raise CustomStrException()
          elif value == "0":
              raise CustomNotRegisteredException()
          else:
              raise CustomFunctionException()
  
      @hug_core.local("/raise_exception")
      def raise_exception(
          first: check_simple_exception,
          second: check_context_exception,
          third: check_another_context_exception,
          forth: check_simple_no_chain_exception,
          fifth: check_simple_no_chain_no_context_exception,
      ):
          return {}
  
      response = raise_exception(**{"first": 1, "second": 1, "third": 1, "forth": 1, "fifth": 1})
      assert response.data["errors"] == {
          "forth": "function exception",
          "third": "function exception",
          "fifth": "function exception",
          "second": "function exception",
          "first": "function exception",
      }
      response = hug_core.test.get(
          api,
          "/raise_exception",
          **{"first": -1, "second": -1, "third": -1, "forth": -1, "fifth": -1}
      )
      assert response.data["errors"] == {
          "forth": "string exception",
          "third": "string exception",
          "fifth": "string exception",
          "second": "string exception",
          "first": "string exception",
      }
      response = hug_core.test.get(
          api, "/raise_exception", **{"first": 0, "second": 0, "third": 0, "forth": 0, "fifth": 0}
      )
      assert response.data["errors"] == {
          "second": "not registered exception",
          "forth": "not registered exception",
          "third": "not registered exception",
          "fifth": "not registered exception",
          "first": "not registered exception",
      }
  
  
  def test_validate_route_args_positive_case():
      class TestSchema(Schema):
          bar = fields.String()
  
      @hug_core.local(args={"foo": fields.Integer(), "return": TestSchema()})
      def hello(foo: int) -> dict:
          return {"bar": str(foo)}
  
      response = hello(foo=5)
      assert response == {"bar": "5"}
  
  
  def test_validate_route_args_negative_case():
      @hug_core.get("/hello", raise_on_invalid=True, args={"foo": fields.Integer()})
      def hello(foo: int):
          return str(foo)
  
      with pytest.raises(ValidationError):
          hug_core.test.get(api, "/hello", **{"foo": "a"})
  
      class TestSchema(Schema):
          bar = fields.Integer()
  
      @hug_core.local(raise_on_invalid=True, args={"return": TestSchema()})
      def foo():
          return {"bar": "a"}
  
      with pytest.raises(InvalidTypeData):
          foo()
  