def test_merge_dictof(self): ## present non-empty inner spec (optional) spec = DictOf([ (Equals('a'), IsA(int, default=1)), ]) | Equals(None) # optional missing dictionary with required key assert spec.get_default_for(None) == None # optional empty dictionary with required key assert spec.get_default_for({}) == {'a': 1} ## present non-empty inner spec (optional) spec = DictOf([ (Equals('a'), IsA(int, default=1) | Equals(None)), ]) # optional missing dictionary with optional key # XXX CHANGED #assert spec.get_default_for(None) == None assert spec.get_default_for(None) == {'a': None} # optional empty dictionary with optional key # XXX CHANGED # (if the value can be either int or None, why choose one? # here we set None not because of Equals(None) but because # we *mean* None — no value could be chosen. #assert spec.get_default_for({}) == {'a': 1} assert spec.get_default_for({}) == {'a': None} ## present non-empty inner spec (required) spec = DictOf([ (Equals('a'), IsA(int, default=1)), ]) # required missing dictionary → inner spec assert spec.get_default_for({}) == {'a': 1} # required empty dictionary → inner spec assert spec.get_default_for({}) == {'a': 1} # XXX CHANGED ## required non-empty dictionary → inner spec #fallback = lambda s, v, **kw: v #assert merge_defaults(rule, {'a': 2}, {}, fallback) == {'a': 2} #assert merge_defaults(rule, {'b': 3}, {}, fallback) == {'a': 1, 'b': 3} # bogus value; will not pass validation but should be preserved assert spec.get_default_for(123) == 123
def test_invert_combinator(): hello_or_bye = Equals('hello') | Equals('bye') hello_or_bye('hello') hello_or_bye('bye') with raises_regexp(ValidationError, "^must equal 'hello' or must equal 'bye'$"): hello_or_bye('albatross!') neither_hello_nor_bye = ~hello_or_bye with raises_regexp(ValidationError, "^not \(must equal 'hello' or must equal 'bye'\)$"): neither_hello_nor_bye('hello') with raises_regexp(ValidationError, "^not \(must equal 'hello' or must equal 'bye'\)$"): neither_hello_nor_bye('bye') neither_hello_nor_bye('albatross!')
def test_opt_key__py2_unicode(self): raw = { opt_key(unicode('foo')): int, } assert translate(raw) == DictOf([ (Equals(unicode('foo')) | ~Exists(), IsA(int)), ])
def test_opt_key__str(self): # this also means Unicode for Py3 but it's OK raw = { opt_key('foo'): int, } assert translate(raw) == DictOf([ (Equals('foo') | ~Exists(), IsA(int)), ])
def test_translate_dict(): assert translate(dict) == IsA(dict) assert translate({}) == IsA(dict) # literal as a key assert translate({'foo': 123}) == DictOf([ (Equals('foo'), IsA(int, default=123)), ]) assert translate({123: str}) == DictOf([ (Equals(123), IsA(str)), ]) # validator as a key assert translate({Equals('foo') | Equals('bar'): str}) == DictOf([ (Equals('foo') | Equals('bar'), IsA(str)), ]) # type as a key assert translate({str: int}) == DictOf([ (IsA(str), IsA(int)), ]) # function as a key func = lambda: 'foo' assert translate({func: int}) == DictOf([ (Equals('foo'), IsA(int)), ])
def test_equals(): v = Equals('foo') assert repr(v) == "must equal 'foo'" v('foo') with raises_regexp(ValidationError, "^must equal 'foo'"): v('bar')
def test_typed_optional_list(self): "A value of given type (list) or no value" spec = IsA(list) | Equals(None) # value is present spec([]) # value is missing spec(None)
def test_regression_22(): ~Anything() ~IsA(str) ~Equals(5) ~Contains('x') ~InRange(5) ~Length(5) ~ListOf(str) ~DictOf([]) ~Exists()
def test_invert_requirement(): says_hello = Equals('hello') says_hello('hello') with raises_regexp(ValidationError, "^must equal 'hello'$"): says_hello('bye') says_not_hello = ~says_hello with raises_regexp(ValidationError, "^not \(must equal 'hello'\)$"): says_not_hello('hello') says_not_hello('bye')
def test_typed_optional_dict(self): "A value of given type (dict) or no value" spec = IsA(dict) | Equals(None) # value is present spec({}) # value is missing spec(None)
def test_merge_dictof_dictof_isa(self): raw_spec = { 'content': { 'text': t('hello'), }, } spec = translate(raw_spec) # make sure translation went as expected assert spec == DictOf([ (Equals('content'), DictOf([ (Equals('text'), IsA(t, default=t('hello'))), ])), ]) # make sure merging works as expected for nested dict assert raw_spec == spec.get_default_for({'content': {}}) # make sure merging works as expected for nested *and* root dicts assert raw_spec == spec.get_default_for({})
def test_double_invert_requirement(): a = Equals('hello') a('hello') with raises_regexp(ValidationError, "^must equal 'hello'$"): a('bye') b = ~a with raises_regexp(ValidationError, "^not \(must equal 'hello'\)$"): b('hello') b('bye') c = ~b c('hello') with raises_regexp(ValidationError, "^must equal 'hello'$"): c('bye')
def test_one_of(self): # literals (behaviour implicitly turned on) assert one_of(['foo', 'bar']) == Equals('foo') | Equals('bar') v = one_of(['foo', 'bar']) v('foo') with pytest.raises(errors.ValidationError) as excinfo: v('quux') assert "AllFailed: must equal 'foo' or must equal 'bar'" in excinfo.exconly( ) # non-literals → rules (behaviour explicitly turned on) shortcut_rule = one_of(['foo', 'bar'], as_rules=True) verbose_rule = Any(['foo', 'bar']) assert shortcut_rule == verbose_rule v = one_of(['foo', 123], as_rules=True) v('hello') v(456) with pytest.raises(errors.ValidationError) as excinfo: v(5.5) assert "AllFailed: must be str or must be int" in excinfo.exconly()
def test_merge_isa__dict(self): # TODO use a single test for all types used with IsA # optional missing dictionary assert (IsA(dict) | Equals(None)).get_default_for(None) == None # XXX CHANGED ## required missing dictionary → empty dictionary #assert IsA(dict).get_default_for(None) == {} assert IsA(dict).get_default_for(None) == None # required empty dictionary assert IsA(dict).get_default_for({}) == {} # required non-empty dictionary assert IsA(dict).get_default_for({'x': 1}) == {'x': 1}
def test_typed_optional(self): "A value of given type or no value" spec = IsA(int) | Equals(None) # value is present and matches datatype spec(1) # value is present and equals None spec(None) # value is present but does not match datatype with raises_regexp(AllFailed, "must be int or must equal None"): spec('bogus') # value is missing spec(None)
def test_missing(self): # MISSING KEY dict_with_opt_key = translate({IsA(text_type) | ~Exists(): text_type}) dict_with_opt_key({}) dict_with_req_key_opt_value = translate( {'a': IsA(text_type) | Equals(None)}) with raises(MissingKeys): dict_with_req_key_opt_value({}) dict_with_req_key_req_value = translate({'a': IsA(text_type)}) with raises(MissingKeys): dict_with_req_key_req_value({}) dict_with_req_keys_req_values = translate({'a': text_type, 'b': int}) with raises(MissingKeys): dict_with_req_keys_req_values({'b': 1})
def test_magic_eq(): assert IsA(str) == IsA(str) assert IsA(str) != IsA(str, default='foo') assert IsA(str) != IsA(int) assert IsA(str) != Equals(int) # nested v1 = ListOf([DictOf([ (Equals('foo'), IsA(str)) ])]) v2 = ListOf([DictOf([ (Equals('foo'), IsA(str)) ])]) v3 = ListOf([DictOf([ (Equals('bar'), IsA(str)) ])]) v4 = ListOf([DictOf([ (Equals('foo'), IsA(int)) ])]) v5 = ListOf([DictOf([ (Equals('foo'), IsA(str, default='x')) ])]) assert v1 == v1 assert v1 == v2 assert v1 != v3 assert v1 != v4 assert v1 != v5
def test_int_in_optional_dict(self): "A required int nested in an optional dict" spec = translate({'foo': int}) | Equals(None) # outer optional value is missing spec(None) # outer optional value is present, inner key is missing with raises_regexp(AllFailed, "must have keys: 'foo' or must equal None"): spec({}) # inner key is present, inner value is missing with raises_regexp(AllFailed, "'foo' value must be int or must equal None"): spec({'foo': None}) # inner value is present spec({'foo': 123})
def test_dict(self): assert translate(dict) == IsA(dict) assert translate({'foo': 123}) == DictOf([ (Equals('foo'), IsA(int, default=123)), ])
def test_merge_list(self): ## present but empty inner spec (optional) rule = ListOf([]) | Equals(None) # optional missing list assert rule.get_default_for(None) == None ## present but empty inner spec (required) rule = ListOf([]) # required missing list → empty list assert rule.get_default_for(None) == [] ## present non-empty inner spec (optional) rule = ListOf(IsA(int, default=123)) | Equals(None) # optional missing list with required item(s) assert rule.get_default_for(None) == None # optional empty list with required item(s) assert rule.get_default_for([]) == [] ## present non-empty inner spec (optional) elem_spec = IsA(int, default=123) | Equals(None) rule = ListOf(elem_spec) | Equals(None) # optional missing list with optional item(s) assert rule.get_default_for(None) == None # optional empty list with optional item assert rule.get_default_for([]) == [] ## present non-empty inner spec (required) rule = ListOf(IsA(int, default=123)) # required missing list → inner spec assert rule.get_default_for(None) == [] # required empty list → inner spec assert rule.get_default_for([]) == [] # required non-empty list → inner spec # fallback = lambda s, v, **kw: v assert rule.get_default_for([None]) == [None] assert rule.get_default_for([456]) == [456] ## present inner spec with empty item spec rule = ListOf(Anything()) assert rule.get_default_for([456]) == [456] ## present inner spec with item spec that has an inner spec #rule = Rule(datatype=list, inner_spec=[123]) rule = ListOf(ListOf(IsA(int, default=123))) # XXX CHANGED WTF was it before!? ##assert rule.get_default_for([None]) == [123] #assert rule.get_default_for([[]]) == [[123]] # bogus value; will not pass validation but should be preserved assert rule.get_default_for(123) == 123