Merge pull request #668 from python-telegram-bot/official-test

Add an "official test"
This commit is contained in:
Noam Meltzer 2017-06-21 23:14:10 +03:00 committed by GitHub
commit 4b3b0cb4cd
4 changed files with 164 additions and 1 deletions

View file

@ -6,3 +6,4 @@ flaky
yapf
pre-commit
pre-commit-hooks
beautifulsoup4

View file

@ -534,6 +534,8 @@ class Bot(TelegramObject):
reply_to_message_id=None,
reply_markup=None,
timeout=20.,
width=None,
height=None,
**kwargs):
"""Use this method to send video files, Telegram clients support mp4
videos (other formats may be sent as telegram.Document).
@ -544,6 +546,8 @@ class Bot(TelegramObject):
already on the Telegram servers, or upload a new video file using
multipart/form-data.
duration (Optional[int]): Duration of sent video in seconds.
width (Optional[int)): Video width.
height (Optional[int]): Video height.
caption (Optional[str]): Video caption (may also be used when resending videos by
file_id).
disable_notification (Optional[bool]): Sends the message silently. iOS users will not
@ -570,6 +574,10 @@ class Bot(TelegramObject):
data['duration'] = duration
if caption:
data['caption'] = caption
if width:
data['width'] = width
if height:
data['height'] = height
return url, data

View file

@ -147,6 +147,7 @@ class Message(TelegramObject):
successful_payment=None,
bot=None,
video_note=None,
game=None,
**kwargs):
# Required
self.message_id = int(message_id)
@ -162,7 +163,7 @@ class Message(TelegramObject):
self.text = text
self.entities = entities or list()
self.audio = audio
self.game = kwargs.get('game')
self.game = game
self.document = document
self.photo = photo
self.sticker = sticker

153
tests/test_official.py Normal file
View file

@ -0,0 +1,153 @@
import sys
import inspect
import warnings
from collections import namedtuple
import certifi
import logging
from bs4 import BeautifulSoup
sys.path.append('.')
from telegram.vendor.ptb_urllib3 import urllib3
import telegram
IGNORED_OBJECTS = ('ResponseParameters', 'CallbackGame')
IGNORED_PARAMETERS = {'self', 'args', 'kwargs', 'read_latency', 'network_delay', 'timeout', 'bot'}
logger = logging.getLogger(__name__)
def find_next_sibling_until(tag, name, until):
for sibling in tag.next_siblings:
if sibling is until:
return
if sibling.name == name:
return sibling
def parse_table(h4):
table = find_next_sibling_until(h4, 'table', h4.find_next_sibling('h4'))
if not table:
return []
head = [td.text for td in table.tr.find_all('td')]
row = namedtuple('{}TableRow'.format(h4.text), ','.join(head))
t = []
for tr in table.find_all('tr')[1:]:
t.append(row(*[td.text for td in tr.find_all('td')]))
return t
def check_method(h4):
name = h4.text
method = getattr(telegram.Bot, name)
table = parse_table(h4)
# Check arguments based on source
sig = inspect.signature(method, follow_wrapped=True)
checked = []
for parameter in table:
param = sig.parameters.get(parameter.Parameters)
logger.debug(parameter)
assert param is not None
# TODO: Check type via docstring
# TODO: Check if optional or required
checked.append(parameter.Parameters)
ignored = IGNORED_PARAMETERS.copy()
if name == 'getUpdates':
ignored -= {'timeout'} # Has it's own timeout parameter that we do wanna check for
elif name == 'sendDocument':
ignored |= {'filename'} # Undocumented
elif name == 'setGameScore':
ignored |= {'edit_message'} # TODO: Now deprecated, so no longer in telegrams docs
logger.debug((sig.parameters.keys(), checked, ignored,
sig.parameters.keys() - checked - ignored))
assert (sig.parameters.keys() ^ checked) - ignored == set()
def check_object(h4):
name = h4.text
obj = getattr(telegram, name)
table = parse_table(h4)
# Check arguments based on source
sig = inspect.signature(obj, follow_wrapped=True)
checked = []
for parameter in table:
field = parameter.Field
if field == 'from':
field = 'from_user'
elif name.startswith('InlineQueryResult') and field == 'type':
continue
elif field == 'remove_keyboard':
continue
param = sig.parameters.get(field)
logger.debug(parameter)
assert param is not None
# TODO: Check type via docstring
# TODO: Check if optional or required
checked.append(field)
ignored = IGNORED_PARAMETERS.copy()
if name == 'InputFile':
ignored |= {'data'}
elif name == 'InlineQueryResult':
ignored |= {'id'}
elif name == 'User':
ignored |= {'type'} # TODO: Deprecation
if name.startswith('InlineQueryResult'):
ignored |= {'type'}
logger.debug((sig.parameters.keys(), checked, ignored,
sig.parameters.keys() - checked - ignored))
assert (sig.parameters.keys() ^ checked) - ignored == set()
def test_official():
if not sys.version_info >= (3, 5):
warnings.warn('Not running tests, since follow_wrapped is not supported on this platform'
'(python version >= 3.5 required)')
return
http = urllib3.PoolManager(
cert_reqs='CERT_REQUIRED',
ca_certs=certifi.where())
request = http.request('GET', 'https://core.telegram.org/bots/api')
soup = BeautifulSoup(request.data.decode('utf-8'), 'html.parser')
for thing in soup.select('h4 > a.anchor'):
# Methods and types don't have spaces in them, luckily all other sections of the docs do
# TODO: don't depend on that
if '-' not in thing['name']:
h4 = thing.parent
name = h4.text
test = None
# Is it a method
if h4.text[0].lower() == h4.text[0]:
test = check_method
else: # Or a type/object
if name not in IGNORED_OBJECTS:
test = check_object
if test:
def fn():
return test(h4)
fn.description = '{}({}) ({})'.format(test.__name__, h4.text, __name__)
yield fn
if __name__ == '__main__':
# Since we don't have the nice unittest asserts which show the comparison
# We turn on debugging
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.DEBUG)
for f in test_official():
f()