2020-05-12 17:02:34 +02:00
|
|
|
from unittest import TestCase
|
2020-05-18 14:28:23 +02:00
|
|
|
from textwrap import dedent
|
2020-09-03 21:02:59 +02:00
|
|
|
from lbry.service.api import Paginated, Wallet, expander
|
2020-05-12 17:02:34 +02:00
|
|
|
from lbry.service.parser import (
|
|
|
|
parse_method, get_expanders, get_api_definitions,
|
|
|
|
generate_options
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2020-09-03 21:02:59 +02:00
|
|
|
@expander
|
|
|
|
def test_kwargs(somevalue=1):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
@expander
|
2020-09-03 22:13:10 +02:00
|
|
|
def another_test_kwargs(
|
2020-09-04 05:35:38 +02:00
|
|
|
somevalue=1,
|
|
|
|
repeated=2,
|
|
|
|
bad_description=3, # using linebreaks makes docopt very very --angry
|
|
|
|
angry=4
|
2020-09-03 22:13:10 +02:00
|
|
|
):
|
2020-09-03 21:02:59 +02:00
|
|
|
pass
|
|
|
|
|
|
|
|
|
2020-05-12 17:02:34 +02:00
|
|
|
class FakeAPI:
|
|
|
|
|
|
|
|
THING_DOC = "thing doc"
|
|
|
|
|
|
|
|
def thing_create(
|
|
|
|
self,
|
|
|
|
name: str, # the name
|
|
|
|
value1='hi', # the first value
|
2020-05-18 14:28:23 +02:00
|
|
|
value2=9, # the second value
|
|
|
|
_ignored=9
|
2020-05-12 17:02:34 +02:00
|
|
|
) -> str: # thing name
|
|
|
|
"""create command doc"""
|
|
|
|
|
|
|
|
def thing_list(
|
|
|
|
self,
|
|
|
|
value1: str = None, # the first value
|
2020-05-18 14:28:23 +02:00
|
|
|
value2: int = None, # the second value with a very very long description which needs to be wrapped
|
2020-05-12 17:02:34 +02:00
|
|
|
value3=False, # a bool
|
|
|
|
# multi-line
|
|
|
|
**pagination_kwargs
|
|
|
|
) -> Paginated[Wallet]: # list of wallets
|
|
|
|
"""list command doc"""
|
|
|
|
|
2020-05-18 14:28:23 +02:00
|
|
|
def thing_update(self, value1: str) -> Wallet: # updated wallet
|
|
|
|
"""update command doc"""
|
|
|
|
|
|
|
|
def thing_delete(self, value1: str, **tx_and_pagination_kwargs) -> Wallet: # deleted thing
|
|
|
|
"""
|
|
|
|
delete command doc
|
|
|
|
|
|
|
|
Usage:
|
|
|
|
thing delete <value1>
|
|
|
|
{kwargs}
|
|
|
|
"""
|
|
|
|
|
|
|
|
def not_grouped(self) -> str: # cheese
|
2020-05-12 17:02:34 +02:00
|
|
|
"""
|
|
|
|
group command doc
|
|
|
|
|
|
|
|
Usage:
|
|
|
|
not_grouped [--foo]
|
|
|
|
|
|
|
|
Options:
|
|
|
|
--foo : (bool) blah
|
|
|
|
|
|
|
|
Returns:
|
2020-05-18 14:28:23 +02:00
|
|
|
foo bar
|
2020-05-12 17:02:34 +02:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
2020-09-04 05:35:38 +02:00
|
|
|
class BadAPI(FakeAPI):
|
|
|
|
def thing_search(
|
|
|
|
self,
|
|
|
|
query='a',
|
|
|
|
**test_and_another_test_kwargs) -> Wallet:
|
|
|
|
"""
|
|
|
|
search command doc
|
|
|
|
|
|
|
|
Usage:
|
|
|
|
thing search [--query=<query>]
|
|
|
|
{kwargs}
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
2020-09-04 06:25:56 +02:00
|
|
|
class BadContinuationAPI(FakeAPI):
|
|
|
|
def thing_save(self, **another_test_kwargs):
|
|
|
|
"""save command doc"""
|
|
|
|
|
|
|
|
|
2020-05-12 17:02:34 +02:00
|
|
|
class TestParser(TestCase):
|
|
|
|
maxDiff = None
|
|
|
|
|
2020-09-04 05:42:52 +02:00
|
|
|
def test_parse_does_not_allow_duplicate_arguments(self):
|
2020-09-04 05:35:38 +02:00
|
|
|
with self.assertRaises(Exception) as exc:
|
|
|
|
parse_method(BadAPI.thing_search, get_expanders())
|
2020-09-04 06:25:56 +02:00
|
|
|
self.assertEqual(exc.exception.args[0], "Expander 'another_test' argument repeated: somevalue.")
|
2020-09-03 20:54:35 +02:00
|
|
|
|
2020-09-04 05:42:52 +02:00
|
|
|
def test_parse_does_not_allow_line_break_with_two_dashes(self):
|
|
|
|
# breaking with two dashes breaks docopt parsing
|
|
|
|
with self.assertRaises(Exception) as exc:
|
2020-09-04 06:25:56 +02:00
|
|
|
get_api_definitions(BadContinuationAPI)
|
2020-09-04 05:42:52 +02:00
|
|
|
self.assertEqual(
|
|
|
|
exc.exception.args[0],
|
|
|
|
"Continuation line starts with -- on thing save: \"--angry [default: 3]\""
|
|
|
|
)
|
|
|
|
|
2020-05-12 17:02:34 +02:00
|
|
|
def test_parse_method(self):
|
|
|
|
expanders = get_expanders()
|
|
|
|
self.assertEqual(
|
|
|
|
parse_method(FakeAPI.thing_create, expanders), {
|
|
|
|
'name': 'thing_create',
|
|
|
|
'desc': {'text': ['create command doc']},
|
|
|
|
'method': FakeAPI.thing_create,
|
|
|
|
'arguments': [
|
|
|
|
{'name': 'name', 'type': 'str', 'desc': ['the name']},
|
|
|
|
{'name': 'value1', 'type': 'str', 'default': "'hi'", 'desc': ['the first value']},
|
|
|
|
{'name': 'value2', 'type': 'int', 'default': 9, 'desc': ['the second value']},
|
|
|
|
],
|
|
|
|
'returns': {
|
|
|
|
'type': 'str',
|
|
|
|
'desc': ['thing name']
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)
|
|
|
|
self.assertEqual(
|
|
|
|
parse_method(FakeAPI.thing_list, expanders), {
|
|
|
|
'name': 'thing_list',
|
|
|
|
'desc': {'text': ['list command doc']},
|
|
|
|
'method': FakeAPI.thing_list,
|
|
|
|
'arguments': [
|
|
|
|
{'name': 'value1', 'type': 'str', 'desc': ['the first value']},
|
2020-05-18 14:28:23 +02:00
|
|
|
{'name': 'value2', 'type': 'int', 'desc': [
|
|
|
|
'the second value with a very very long description which needs to be wrapped']},
|
2020-05-12 17:02:34 +02:00
|
|
|
{'name': 'value3', 'type': 'bool', 'default': False, 'desc': ['a bool', 'multi-line']},
|
2020-05-18 14:28:23 +02:00
|
|
|
{'name': 'page', 'type': 'int', 'desc': ['page to return for paginating']},
|
|
|
|
{'name': 'page_size', 'type': 'int', 'desc': ['number of items on page for pagination']},
|
|
|
|
{'name': 'include_total', 'type': 'bool', 'default': False,
|
|
|
|
'desc': ['calculate total number of items and pages']},
|
|
|
|
],
|
|
|
|
'kwargs': [
|
|
|
|
{'name': 'page', 'type': 'int', 'desc': ['page to return for paginating']},
|
|
|
|
{'name': 'page_size', 'type': 'int', 'desc': ['number of items on page for pagination']},
|
|
|
|
{'name': 'include_total', 'type': 'bool', 'default': False,
|
|
|
|
'desc': ['calculate total number of items and pages']},
|
2020-05-12 17:02:34 +02:00
|
|
|
],
|
|
|
|
'returns': {
|
|
|
|
'type': 'Paginated[Wallet]',
|
|
|
|
'desc': ['list of wallets'],
|
|
|
|
'json': {
|
|
|
|
'page': 'Page number of the current items.',
|
|
|
|
'page_size': 'Number of items to show on a page.',
|
|
|
|
'total_pages': 'Total number of pages.',
|
|
|
|
'total_items': 'Total number of items.',
|
|
|
|
'items': [
|
|
|
|
{'id': 'wallet_id', 'name': 'optional wallet name'}
|
|
|
|
]
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)
|
2020-05-18 14:28:23 +02:00
|
|
|
self.assertEqual(
|
|
|
|
parse_method(FakeAPI.thing_update, expanders), {
|
|
|
|
'name': 'thing_update',
|
|
|
|
'desc': {'text': ['update command doc']},
|
|
|
|
'method': FakeAPI.thing_update,
|
|
|
|
'arguments': [
|
|
|
|
{'name': 'value1', 'type': 'str', 'desc': []},
|
|
|
|
],
|
|
|
|
'returns': {
|
|
|
|
'type': 'Wallet',
|
|
|
|
'desc': ['updated wallet'],
|
|
|
|
'json': {'id': 'wallet_id', 'name': 'optional wallet name'},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)
|
2020-05-12 17:02:34 +02:00
|
|
|
self.assertEqual(
|
|
|
|
parse_method(FakeAPI.not_grouped, expanders), {
|
|
|
|
'name': 'not_grouped',
|
|
|
|
'desc': {
|
|
|
|
'text': ['group command doc'],
|
|
|
|
'usage': [' not_grouped [--foo]'],
|
|
|
|
'options': [' --foo : (bool) blah'],
|
2020-05-18 14:28:23 +02:00
|
|
|
'returns': [' foo bar']
|
2020-05-12 17:02:34 +02:00
|
|
|
},
|
|
|
|
'method': FakeAPI.not_grouped,
|
|
|
|
'arguments': [],
|
2020-05-18 14:28:23 +02:00
|
|
|
'returns': {'desc': ['cheese'], 'type': 'str'}
|
2020-05-12 17:02:34 +02:00
|
|
|
}
|
|
|
|
)
|
|
|
|
|
2020-05-18 14:28:23 +02:00
|
|
|
|
|
|
|
class TestGenerator(TestCase):
|
|
|
|
maxDiff = None
|
|
|
|
|
|
|
|
def test_generate_options(self):
|
|
|
|
expanders = get_expanders()
|
|
|
|
self.assertEqual(
|
|
|
|
generate_options(parse_method(FakeAPI.thing_list, expanders), indent=' '), [
|
2020-07-07 16:52:41 +02:00
|
|
|
' --value1=<value1> : (str) the first value',
|
|
|
|
' --value2=<value2> : (int) the second value with a very very long description which',
|
|
|
|
' needs to be wrapped',
|
|
|
|
' --value3 : (bool) a bool multi-line',
|
|
|
|
' --page=<page> : (int) page to return for paginating',
|
|
|
|
' --page_size=<page_size> : (int) number of items on page for pagination',
|
|
|
|
' --include_total : (bool) calculate total number of items and pages',
|
2020-05-18 14:28:23 +02:00
|
|
|
]
|
|
|
|
)
|
|
|
|
|
2020-05-12 17:02:34 +02:00
|
|
|
def test_get_api_definitions(self):
|
|
|
|
defs = get_api_definitions(FakeAPI)
|
|
|
|
self.assertEqual({'groups', 'commands'}, set(defs))
|
|
|
|
self.assertEqual(defs['groups'], {'thing': 'thing doc'})
|
|
|
|
self.assertEqual(defs['commands']['thing_create']['group'], 'thing')
|
|
|
|
self.assertEqual(defs['commands']['thing_create']['name'], 'create')
|
|
|
|
self.assertEqual(defs['commands']['thing_list']['group'], 'thing')
|
|
|
|
self.assertEqual(defs['commands']['thing_list']['name'], 'list')
|
|
|
|
self.assertEqual(defs['commands']['not_grouped']['name'], 'not_grouped')
|
|
|
|
self.assertNotIn('group', defs['commands']['not_grouped'])
|
2020-05-18 14:28:23 +02:00
|
|
|
self.assertEqual(
|
|
|
|
defs['commands']['thing_create']['help'],
|
|
|
|
dedent("""\
|
|
|
|
create command doc
|
2020-05-12 17:02:34 +02:00
|
|
|
|
2020-05-18 14:28:23 +02:00
|
|
|
Usage:
|
|
|
|
thing create
|
2020-05-12 17:02:34 +02:00
|
|
|
|
2020-05-18 14:28:23 +02:00
|
|
|
Options:
|
2020-07-07 16:52:41 +02:00
|
|
|
--name=<name> : (str) the name
|
|
|
|
--value1=<value1> : (str) the first value [default: 'hi']
|
|
|
|
--value2=<value2> : (int) the second value [default: 9]
|
2020-05-12 17:02:34 +02:00
|
|
|
|
2020-05-18 14:28:23 +02:00
|
|
|
Returns:
|
|
|
|
(str) thing name""")
|
|
|
|
)
|
2020-05-12 17:02:34 +02:00
|
|
|
self.assertEqual(
|
2020-05-18 14:28:23 +02:00
|
|
|
defs['commands']['thing_delete']['help'],
|
|
|
|
dedent("""\
|
|
|
|
delete command doc
|
|
|
|
|
|
|
|
Usage:
|
|
|
|
thing delete <value1>
|
2020-09-04 06:25:56 +02:00
|
|
|
[--change_account_id=<change_account_id>]
|
2020-07-07 16:52:41 +02:00
|
|
|
[--fund_account_id=<fund_account_id>...] [--preview] [--no_wait]
|
2020-05-18 14:28:23 +02:00
|
|
|
[--page=<page>] [--page_size=<page_size>] [--include_total]
|
|
|
|
|
|
|
|
Options:
|
2020-07-07 16:52:41 +02:00
|
|
|
--value1=<value1> : (str)
|
|
|
|
--change_account_id=<change_account_id> : (str) account to send excess change (LBC)
|
|
|
|
--fund_account_id=<fund_account_id> : (str, list) accounts to fund the
|
|
|
|
transaction
|
|
|
|
--preview : (bool) do not broadcast the transaction
|
|
|
|
--no_wait : (bool) do not wait for mempool confirmation
|
|
|
|
--page=<page> : (int) page to return for paginating
|
|
|
|
--page_size=<page_size> : (int) number of items on page for
|
|
|
|
pagination
|
|
|
|
--include_total : (bool) calculate total number of items and
|
|
|
|
pages
|
2020-05-18 14:28:23 +02:00
|
|
|
|
|
|
|
Returns:
|
|
|
|
(Wallet) deleted thing
|
|
|
|
{
|
|
|
|
"id": "wallet_id",
|
|
|
|
"name": "optional wallet name"
|
|
|
|
}""")
|
|
|
|
)
|
|
|
|
self.assertEqual(
|
|
|
|
defs['commands']['thing_list']['help'],
|
|
|
|
dedent("""\
|
|
|
|
list command doc
|
|
|
|
|
|
|
|
Usage:
|
|
|
|
thing list
|
|
|
|
|
|
|
|
Options:
|
2020-07-07 16:52:41 +02:00
|
|
|
--value1=<value1> : (str) the first value
|
|
|
|
--value2=<value2> : (int) the second value with a very very long description
|
|
|
|
which needs to be wrapped
|
|
|
|
--value3 : (bool) a bool multi-line
|
|
|
|
--page=<page> : (int) page to return for paginating
|
|
|
|
--page_size=<page_size> : (int) number of items on page for pagination
|
|
|
|
--include_total : (bool) calculate total number of items and pages
|
2020-05-18 14:28:23 +02:00
|
|
|
|
|
|
|
Returns:
|
|
|
|
(Paginated[Wallet]) list of wallets
|
|
|
|
{
|
|
|
|
"page": "Page number of the current items.",
|
|
|
|
"page_size": "Number of items to show on a page.",
|
|
|
|
"total_pages": "Total number of pages.",
|
|
|
|
"total_items": "Total number of items.",
|
|
|
|
"items": [
|
|
|
|
{
|
|
|
|
"id": "wallet_id",
|
|
|
|
"name": "optional wallet name"
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}""")
|
|
|
|
)
|
|
|
|
self.assertEqual(
|
|
|
|
defs['commands']['not_grouped']['help'],
|
|
|
|
dedent("""\
|
|
|
|
group command doc
|
|
|
|
|
|
|
|
Usage:
|
|
|
|
not_grouped [--foo]
|
|
|
|
|
|
|
|
Options:
|
|
|
|
--foo : (bool) blah
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
(str) cheese
|
|
|
|
foo bar""")
|
2020-05-12 17:02:34 +02:00
|
|
|
)
|