def test_get_example(self): S = string('A string.', example='Foo') assert 'Foo' == S.get_example() # Default example S = string('A string.') assert 'string' == S.get_example()
def test_type(self): S = string('Starts with S.', pattern=r'^S.*') T = string('Starts with T.', pattern=r'^T.*') class SOrT(UnionType): description = 'S or T.' types = [S, T] @classmethod def validate(cls, value): if not value.endswith('ring'): raise TypeSystemError('Value does not end with ring') # No exception, matches `S` type. SOrT('S string') # No exception, matches `T` type. SOrT('T string') # Neither `S` or `T` type. with pytest.raises(TypeSystemError, match='Value is not one of'): SOrT('B') # Custom validation with pytest.raises(TypeSystemError, match='Value does not end with '): SOrT('S foo')
class PropertyDependenciesObject(Object): description = 'An item.' properties = { 'type': string('Type of item'), 'name': string('Name of item'), 'category': string('Category of item'), } property_dependencies = { 'type': ['name'], 'name': ['category'], }
def test_additional_items(self): A = array('a', items=[ string('string', max_length=1), integer('int', maximum=1234)], additional_items=True) # no exception A(['a', 2, 3, 4, True]) A = array('a', items=[ string('string', max_length=1), integer('int', maximum=1234)], additional_items=False) with pytest.raises(TypeSystemError, match='Too many items.'): A(['a', 2, 3, 4, True])
def test_min_length(self): S = string('a string', min_length=2) # No exception S('foo') # String too short with pytest.raises(TypeSystemError, match='Must have at least 2 characters'): S('f') # Empty string when min_length is 1 S = string('empty', min_length=1) with pytest.raises(TypeSystemError, match='Must not be blank.'): S('')
def test_get_example(self): A = array('No example of items') assert [1] == A.get_example() A = array('Defined example', example=['a', 'b']) assert ['a', 'b'] == A.get_example() A = array('No example, defined items', items=string('letter')) assert ['string'] == A.get_example() A = array('a', items=[ string('string', max_length=1, example='foo'), integer('int', maximum=1234, example=123)]) assert ['foo', 123] == A.get_example()
def test_pattern(self): S = string('a regex', pattern=r'^foo*') # No exception S('foo bar') # Does not begin with `foo` with pytest.raises(TypeSystemError): S('bar')
def test_format_email(self): S = string('email', format='email') # No exception S('*****@*****.**') # Invalid email with pytest.raises(TypeSystemError, match='Not a valid email address.'): S('foo.net')
class RequiredPropsObject(Object): description = 'required' properties = { 'foo': string('foo property', min_length=2), 'bar': integer('an int'), } required = ['bar']
def test_max_length(self): S = string('a string', max_length=2) # No exception S('12') # String too long with pytest.raises(TypeSystemError, match='Must have no more than 2 characters'): S('123')
def test_format_uri(self): S = string('uri', format='uri') # no exception S('https://doctor.com') # Invalid uri expected_msg = "'foo' is not a valid 'URI'." with pytest.raises(TypeSystemError, match=expected_msg): S('foo')
class A(Array): description = 'Array with 2 items' items = string('string') @classmethod def validate(cls, value): if len(value) != 2: raise TypeSystemError('Length must be 2')
def test_items(self): A = array('array', items=string('string', max_length=1)) # no exception A(['a', 'b']) # Invalid type of items with pytest.raises(TypeSystemError, match="{0: 'Must have no more than 1 characters.'}"): A(['aa', 'b'])
def test_format_date(self): S = string('date', format='date') # No exception s = S('2018-10-22') assert s == date(2018, 10, 22) # Invalid date expected_msg = "time data 'foo' does not match format '%Y-%m-%d'" with pytest.raises(TypeSystemError, match=expected_msg): S('foo')
def test_items_multiple_types(self): A = array('a', items=[ string('string', max_length=1, example='foo'), integer('int', maximum=1234, example=123)]) # no exception A(['b', 1234]) # Invalid type with pytest.raises(TypeSystemError, match="{1: 'Must be less than or equal to 1234.'}"): A(['b', 1235])
def test_format_date_time(self): S = string('date-time', format='date-time') # No exception s = S('2018-10-22T11:12:00') assert s == datetime(2018, 10, 22, 11, 12, 0) # Invalid datetime expected_msg = ("ISO 8601 time designator 'T' missing. Unable to parse " "datetime string 'foo'") with pytest.raises(TypeSystemError, match=expected_msg): S('foo')
def test_format_time(self): S = string('time', format='time') # no exception s = S('13:10:00') assert 13 == s.hour assert 10 == s.minute assert 0 == s.second # invalid expected_msg = "time data 'foo' does not match format '%H:%M:%S" with pytest.raises(TypeSystemError, match=expected_msg): S('foo')
def test_parse_form_and_query_params_with_custom_parser_not_callable(self): """ This test verifies if a parser is provided that isn't callable that we warn the user and fallback to the default parser. """ A = string('str', parser='foo') def f(a: A): pass sig = inspect.signature(f) query_params = {'a': 'a'} with pytest.warns(UserWarning, match='Parser `foo` is not callable'): actual = parse_form_and_query_params(query_params, sig.parameters) assert {'a': 'a'} == actual
def test_native_type(self): B = boolean('A bool.') S = string('A string.') class Item(UnionType): description = 'B or S.' types = [B, S] # Should be the first native_type in the types attribute. assert Item.native_type == bool # After instantiating with a value, it should update the native_type # to match the value. assert 'S' == Item('S') assert Item.native_type == str assert Item(True) assert Item.native_type == bool
def test_type(self): S = string('string') assert type(S('string')) is str
def test_new_type_allows_custom_description(): S = string('A string', example='Foo') N = new_type(S, description='A different description') assert 'A different description' == N.description
def test_new_type_copies_and_overrides_attrs(): S1 = string('A string', pattern=r'^s') S2 = new_type(S1, description='new', max_length=10) expected = dict(S1.__dict__, description='new', max_length=10) assert expected == dict(S2.__dict__)
def test_new_type_copies_all_attrs(): S1 = string('A string', pattern=r'^s') S2 = new_type(S1) assert S1.__dict__ == S2.__dict__
def test_new_type_uses_parent_description(): S = string('A string', example='Foo') N = new_type(S, nullable=True) assert 'A string' == N.description assert S.nullable is False assert N.nullable is True
def test_trim_whitespace(self): S = string('a string', trim_whitespace=True) actual = S(' foo ') assert 'foo' == actual
class FooObject(Object): additional_properties = True description = 'A Foo' properties = {'foo': string('foo property', min_length=2)}
def test_type_nullable(self): S = string('string', nullable=True) assert S(None) is None
def test_nullable(self): A = array('array', items=string('string', max_length=1), nullable=True) # Just tests that this does not raise an exception. A(None)
# Note that this file contains some inline comments starting with # --, used to # generate documentation from this file. You're probably better served reading # the actual documentation (see Using in Flask in the docs). from flask import Flask from flask_restful import Api from doctor.errors import NotFoundError from doctor.flask import create_routes from doctor.routing import Route, get, post, put, delete # -- mark-types from doctor import types # doctor provides helper functions to easily define simple types. Body = types.string('Note body', example='body') Done = types.boolean('Marks if a note is done or not.', example=False) NoteId = types.integer('Note ID', example=1) Status = types.string('API status') NoteType = types.enum('The type of note', enum=['quick', 'detailed'], example='quick') # You can also inherit from type classes to create more complex types. class Note(types.Object): description = 'A note object' additional_properties = False properties = { 'note_id': NoteId, 'body': Body, 'done': Done, }
""" This module contains custom types used by tests. """ from doctor.types import (array, boolean, enum, integer, new_type, number, string, Object, UnionType) def parse_comma_separated_str(value): return value.split(',') Age = integer('age', minimum=1, maximum=120, example=34) Auth = string('auth token', example='testtoken') Color = enum('Color', enum=['blue', 'green'], example='blue', case_insensitive=True) Colors = array('colors', items=Color, example=['green']) ExampleArray = array('ex description e', items=Auth, example=['ex', 'array']) TwoItems = array('two items', items=[Age, Color]) class ExampleObject(Object): description = 'ex description f' properties = {'str': Auth} additional_properties = False example = {'str': 'ex str'} ExampleObjects = array('ex objects', items=ExampleObject,