blob: d23a7edc8a339fad06795cad97b5b2200697d011 [file] [log] [blame]
from unittest import TestCase, main
from uritemplate import URITemplate, expand, partial
from uritemplate import variable
def merge_dicts(*args):
d = {}
for arg in args:
d.update(arg)
return d
class RFCTemplateExamples(type):
var = {'var': 'value'}
hello = {'hello': 'Hello World!'}
path = {'path': '/foo/bar'}
x = {'x': '1024'}
y = {'y': '768'}
empty = {'empty': ''}
merged_x_y = merge_dicts(x, y)
list_ex = {'list': ['red', 'green', 'blue']}
keys = {'keys': [('semi', ';'), ('dot', '.'), ('comma', ',')]}
## Level 1
# Simple string expansion
level1_examples = {
'{var}': {
'expansion': var,
'expected': 'value',
},
'{hello}': {
'expansion': hello,
'expected': 'Hello%20World%21',
},
}
## Level 2
# Reserved string expansion
level2_reserved_examples = {
'{+var}': {
'expansion': var,
'expected': 'value',
},
'{+hello}': {
'expansion': hello,
'expected': 'Hello%20World!',
},
'{+path}/here': {
'expansion': path,
'expected': '/foo/bar/here',
},
'here?ref={+path}': {
'expansion': path,
'expected': 'here?ref=/foo/bar',
},
}
# Fragment expansion, crosshatch-prefixed
level2_fragment_examples = {
'X{#var}': {
'expansion': var,
'expected': 'X#value',
},
'X{#hello}': {
'expansion': hello,
'expected': 'X#Hello%20World!'
},
}
## Level 3
# String expansion with multiple variables
level3_multiple_variable_examples = {
'map?{x,y}': {
'expansion': merged_x_y,
'expected': 'map?1024,768',
},
'{x,hello,y}': {
'expansion': merge_dicts(x, y, hello),
'expected': '1024,Hello%20World%21,768',
},
}
# Reserved expansion with multiple variables
level3_reserved_examples = {
'{+x,hello,y}': {
'expansion': merge_dicts(x, y, hello),
'expected': '1024,Hello%20World!,768',
},
'{+path,x}/here': {
'expansion': merge_dicts(path, x),
'expected': '/foo/bar,1024/here',
},
}
# Fragment expansion with multiple variables
level3_fragment_examples = {
'{#x,hello,y}': {
'expansion': merge_dicts(x, y, hello),
'expected': '#1024,Hello%20World!,768',
},
'{#path,x}/here': {
'expansion': merge_dicts(path, x),
'expected': '#/foo/bar,1024/here'
},
}
# Label expansion, dot-prefixed
level3_label_examples = {
'X{.var}': {
'expansion': var,
'expected': 'X.value',
},
'X{.x,y}': {
'expansion': merged_x_y,
'expected': 'X.1024.768',
}
}
# Path segments, slash-prefixed
level3_path_segment_examples = {
'{/var}': {
'expansion': var,
'expected': '/value',
},
'{/var,x}/here': {
'expansion': merge_dicts(var, x),
'expected': '/value/1024/here',
},
}
# Path-style parameters, semicolon-prefixed
level3_path_semi_examples = {
'{;x,y}': {
'expansion': merged_x_y,
'expected': ';x=1024;y=768',
},
'{;x,y,empty}': {
'expansion': merge_dicts(x, y, empty),
'expected': ';x=1024;y=768;empty',
},
}
# Form-style query, ampersand-separated
level3_form_amp_examples = {
'{?x,y}': {
'expansion': merged_x_y,
'expected': '?x=1024&y=768',
},
'{?x,y,empty}': {
'expansion': merge_dicts(x, y, empty),
'expected': '?x=1024&y=768&empty=',
},
}
# Form-style query continuation
level3_form_cont_examples = {
'?fixed=yes{&x}': {
'expansion': x,
'expected': '?fixed=yes&x=1024',
},
'{&x,y,empty}': {
'expansion': merge_dicts(x, y, empty),
'expected': '&x=1024&y=768&empty=',
}
}
## Level 4
# String expansion with value modifiers
level4_value_modifier_examples = {
'{var:3}': {
'expansion': var,
'expected': 'val',
},
'{var:30}': {
'expansion': var,
'expected': 'value',
},
'{list}': {
'expansion': list_ex,
'expected': 'red,green,blue',
},
'{list*}': {
'expansion': list_ex,
'expected': 'red,green,blue',
},
'{keys}': {
'expansion': keys,
'expected': 'semi,%3B,dot,.,comma,%2C',
},
'{keys*}': {
'expansion': keys,
'expected': 'semi=%3B,dot=.,comma=%2C',
},
}
# Reserved expansion with value modifiers
level4_reserved_examples = {
'{+path:6}/here': {
'expansion': path,
'expected': '/foo/b/here',
},
'{+list}': {
'expansion': list_ex,
'expected': 'red,green,blue',
},
'{+list*}': {
'expansion': list_ex,
'expected': 'red,green,blue',
},
'{+keys}': {
'expansion': keys,
'expected': 'semi,;,dot,.,comma,,',
},
'{+keys*}': {
'expansion': keys,
'expected': 'semi=;,dot=.,comma=,',
},
}
# Fragment expansion with value modifiers
level4_fragment_examples = {
'{#path:6}/here': {
'expansion': path,
'expected': '#/foo/b/here',
},
'{#list}': {
'expansion': list_ex,
'expected': '#red,green,blue',
},
'{#list*}': {
'expansion': list_ex,
'expected': '#red,green,blue',
},
'{#keys}': {
'expansion': keys,
'expected': '#semi,;,dot,.,comma,,'
},
'{#keys*}': {
'expansion': keys,
'expected': '#semi=;,dot=.,comma=,'
},
}
# Label expansion, dot-prefixed
level4_label_examples = {
'X{.var:3}': {
'expansion': var,
'expected': 'X.val',
},
'X{.list}': {
'expansion': list_ex,
'expected': 'X.red,green,blue',
},
'X{.list*}': {
'expansion': list_ex,
'expected': 'X.red.green.blue',
},
'X{.keys}': {
'expansion': keys,
'expected': 'X.semi,%3B,dot,.,comma,%2C',
},
'X{.keys*}': {
'expansion': keys,
'expected': 'X.semi=%3B.dot=..comma=%2C',
},
}
# Path segments, slash-prefixed
level4_path_slash_examples = {
'{/var:1,var}': {
'expansion': var,
'expected': '/v/value',
},
'{/list}': {
'expansion': list_ex,
'expected': '/red,green,blue',
},
'{/list*}': {
'expansion': list_ex,
'expected': '/red/green/blue',
},
'{/list*,path:4}': {
'expansion': merge_dicts(list_ex, path),
'expected': '/red/green/blue/%2Ffoo',
},
'{/keys}': {
'expansion': keys,
'expected': '/semi,%3B,dot,.,comma,%2C',
},
'{/keys*}': {
'expansion': keys,
'expected': '/semi=%3B/dot=./comma=%2C',
},
}
# Path-style parameters, semicolon-prefixed
level4_path_semi_examples = {
'{;hello:5}': {
'expansion': hello,
'expected': ';hello=Hello',
},
'{;list}': {
'expansion': list_ex,
'expected': ';list=red,green,blue',
},
'{;list*}': {
'expansion': list_ex,
'expected': ';list=red;list=green;list=blue',
},
'{;keys}': {
'expansion': keys,
'expected': ';keys=semi,%3B,dot,.,comma,%2C',
},
'{;keys*}': {
'expansion': keys,
'expected': ';semi=%3B;dot=.;comma=%2C',
},
}
# Form-style query, ampersand-separated
level4_form_amp_examples = {
'{?var:3}': {
'expansion': var,
'expected': '?var=val',
},
'{?list}': {
'expansion': list_ex,
'expected': '?list=red,green,blue',
},
'{?list*}': {
'expansion': list_ex,
'expected': '?list=red&list=green&list=blue',
},
'{?keys}': {
'expansion': keys,
'expected': '?keys=semi,%3B,dot,.,comma,%2C',
},
'{?keys*}': {
'expansion': keys,
'expected': '?semi=%3B&dot=.&comma=%2C',
},
}
# Form-style query continuation
level4_form_query_examples = {
'{&var:3}': {
'expansion': var,
'expected': '&var=val',
},
'{&list}': {
'expansion': list_ex,
'expected': '&list=red,green,blue',
},
'{&list*}': {
'expansion': list_ex,
'expected': '&list=red&list=green&list=blue',
},
'{&keys}': {
'expansion': keys,
'expected': '&keys=semi,%3B,dot,.,comma,%2C',
},
'{&keys*}': {
'expansion': keys,
'expected': '&semi=%3B&dot=.&comma=%2C',
},
}
def __new__(cls, name, bases, attrs):
def make_test(d):
def _test_(self):
for k, v in d.items():
t = URITemplate(k)
self.assertEqual(t.expand(v['expansion']), v['expected'])
return _test_
examples = [
(
n, getattr(RFCTemplateExamples, n)
) for n in dir(RFCTemplateExamples) if n.startswith('level')
]
for name, value in examples:
testname = 'test_%s' % name
attrs[testname] = make_test(value)
return type.__new__(cls, name, bases, attrs)
class TestURITemplate(RFCTemplateExamples('RFCMeta', (TestCase,), {})):
def test_no_variables_in_uri(self):
"""
This test ensures that if there are no variables present, the
template evaluates to itself.
"""
uri = 'https://api.github.com/users'
t = URITemplate(uri)
self.assertEqual(t.expand(), uri)
self.assertEqual(t.expand(users='foo'), uri)
def test_all_variables_parsed(self):
"""
This test ensures that all variables are parsed.
"""
uris = [
'https://api.github.com',
'https://api.github.com/users{/user}',
'https://api.github.com/repos{/user}{/repo}',
'https://api.github.com/repos{/user}{/repo}/issues{/issue}'
]
for i, uri in enumerate(uris):
t = URITemplate(uri)
self.assertEqual(len(t.variables), i)
def test_expand(self):
"""
This test ensures that expansion works as expected.
"""
# Single
t = URITemplate('https://api.github.com/users{/user}')
expanded = 'https://api.github.com/users/sigmavirus24'
self.assertEqual(t.expand(user='sigmavirus24'), expanded)
v = t.variables[0]
self.assertEqual(v.expand({'user': None}), {'/user': ''})
# Multiple
t = URITemplate('https://api.github.com/users{/user}{/repo}')
expanded = 'https://api.github.com/users/sigmavirus24/github3.py'
self.assertEqual(
t.expand({'repo': 'github3.py'}, user='sigmavirus24'),
expanded
)
def test_str_repr(self):
uri = 'https://api.github.com{/endpoint}'
t = URITemplate(uri)
self.assertEqual(str(t), uri)
self.assertEqual(str(t.variables[0]), '/endpoint')
self.assertEqual(repr(t), 'URITemplate("%s")' % uri)
self.assertEqual(repr(t.variables[0]), 'URIVariable(/endpoint)')
def test_hash(self):
uri = 'https://api.github.com{/endpoint}'
self.assertEqual(hash(URITemplate(uri)), hash(uri))
def test_default_value(self):
uri = 'https://api.github.com/user{/user=sigmavirus24}'
t = URITemplate(uri)
self.assertEqual(t.expand(),
'https://api.github.com/user/sigmavirus24')
self.assertEqual(t.expand(user='lukasa'),
'https://api.github.com/user/lukasa')
def test_query_expansion(self):
t = URITemplate('{foo}')
self.assertEqual(
t.variables[0]._query_expansion('foo', None, False, False), None
)
def test_label_path_expansion(self):
t = URITemplate('{foo}')
self.assertEqual(
t.variables[0]._label_path_expansion('foo', None, False, False),
None
)
def test_semi_path_expansion(self):
t = URITemplate('{foo}')
v = t.variables[0]
self.assertEqual(
v._semi_path_expansion('foo', None, False, False),
None
)
t.variables[0].operator = '?'
self.assertEqual(
v._semi_path_expansion('foo', ['bar', 'bogus'], True, False),
'foo=bar&foo=bogus'
)
def test_string_expansion(self):
t = URITemplate('{foo}')
self.assertEqual(
t.variables[0]._string_expansion('foo', None, False, False),
None
)
def test_hashability(self):
t = URITemplate('{foo}')
u = URITemplate('{foo}')
d = {t: 1}
d[u] += 1
self.assertEqual(d, {t: 2})
class TestURIVariable(TestCase):
def setUp(self):
self.v = variable.URIVariable('{foo}')
def test_post_parse(self):
v = self.v
self.assertEqual(v.join_str, ',')
self.assertEqual(v.operator, '')
self.assertEqual(v.safe, '')
self.assertEqual(v.start, '')
def test_post_parse_plus(self):
v = self.v
v.operator = '+'
v.post_parse()
self.assertEqual(v.join_str, ',')
self.assertEqual(v.safe, variable.URIVariable.reserved)
self.assertEqual(v.start, '')
def test_post_parse_octothorpe(self):
v = self.v
v.operator = '#'
v.post_parse()
self.assertEqual(v.join_str, ',')
self.assertEqual(v.safe, variable.URIVariable.reserved)
self.assertEqual(v.start, '#')
def test_post_parse_question(self):
v = self.v
v.operator = '?'
v.post_parse()
self.assertEqual(v.join_str, '&')
self.assertEqual(v.safe, '')
self.assertEqual(v.start, '?')
def test_post_parse_ampersand(self):
v = self.v
v.operator = '&'
v.post_parse()
self.assertEqual(v.join_str, '&')
self.assertEqual(v.safe, '')
self.assertEqual(v.start, '&')
class TestVariableModule(TestCase):
def test_is_list_of_tuples(self):
l = [(1, 2), (3, 4)]
self.assertEqual(variable.is_list_of_tuples(l), (True, l))
l = [1, 2, 3, 4]
self.assertEqual(variable.is_list_of_tuples(l), (False, None))
def test_list_test(self):
l = [1, 2, 3, 4]
self.assertEqual(variable.list_test(l), True)
l = str([1, 2, 3, 4])
self.assertEqual(variable.list_test(l), False)
def test_list_of_tuples_test(self):
l = [(1, 2), (3, 4)]
self.assertEqual(variable.dict_test(l), False)
d = dict(l)
self.assertEqual(variable.dict_test(d), True)
class TestAPI(TestCase):
uri = 'https://api.github.com{/endpoint}'
def test_expand(self):
self.assertEqual(expand(self.uri, {'endpoint': 'users'}),
'https://api.github.com/users')
def test_partial(self):
self.assertEqual(partial(self.uri), URITemplate(self.uri))
uri = self.uri + '/sigmavirus24{/other}'
self.assertEqual(
partial(uri, endpoint='users'),
URITemplate('https://api.github.com/users/sigmavirus24{/other}')
)
if __name__ == '__main__':
main()