From 09bdb8882273c6889633510e8bed99619c327c4e Mon Sep 17 00:00:00 2001 From: Jasmin Bom Date: Sat, 1 Sep 2018 16:58:08 +0200 Subject: [PATCH] Bot api 4.1 (#1198) * Fix passport decryption failing at random times Sometimes a decrypted secret was being treated as b64 and therefore got decoded even further. Fix by decoding b64 right before call to decrypt so we have better control of when not to do it * Bot api 4.1 Telegram passport 1.1 Added support for middle names. Added support for translations for documents Add errors for translations for documents Added support for requesting names in the language of the user's country of residence Replaced the payload parameter with the new parameter nonce NOTE: Scope stuff is NOT implemented, as we wanna STRONGLY encourage users to use the telegram provided SDKs anyway (and not generate telegram auth links in their bot, but rather on a server) * Minor fixes * Add hash to EncryptedPassportElement For use with PassportElementErrorUnspecified apparently --- examples/passportbot.py | 17 +- telegram/__init__.py | 10 +- telegram/files/file.py | 9 +- telegram/passport/credentials.py | 45 +++--- telegram/passport/data.py | 12 +- telegram/passport/encryptedpassportelement.py | 64 +++++--- telegram/passport/passportelementerrors.py | 105 +++++++++++++ telegram/passport/passportfile.py | 2 +- tests/test_passport.py | 147 ++++++++++++------ ...est_passportelementerrortranslationfile.py | 80 ++++++++++ ...st_passportelementerrortranslationfiles.py | 81 ++++++++++ tests/test_passportelementerrorunspecified.py | 79 ++++++++++ 12 files changed, 550 insertions(+), 101 deletions(-) create mode 100644 tests/test_passportelementerrortranslationfile.py create mode 100644 tests/test_passportelementerrortranslationfiles.py create mode 100644 tests/test_passportelementerrorunspecified.py diff --git a/examples/passportbot.py b/examples/passportbot.py index f9a239c5d..aa57605d8 100644 --- a/examples/passportbot.py +++ b/examples/passportbot.py @@ -24,9 +24,9 @@ def msg(bot, update): # If we received any passport data passport_data = update.message.passport_data if passport_data: - # If our payload doesn't match what we think, this Update did not originate from us - # Ideally you would randomize the payload on the server - if passport_data.decrypted_credentials.payload != 'thisisatest': + # If our nonce doesn't match what we think, this Update did not originate from us + # Ideally you would randomize the nonce on the server + if passport_data.decrypted_credentials.nonce != 'thisisatest': return # Print the decrypted credential data @@ -39,7 +39,7 @@ def msg(bot, update): elif data.type == 'email': print('Email: ', data.email) if data.type in ('personal_details', 'passport', 'driver_license', 'identity_card', - 'identity_passport', 'address'): + 'internal_passport', 'address'): print(data.type, data.data) if data.type in ('utility_bill', 'bank_statement', 'rental_agreement', 'passport_registration', 'temporary_registration'): @@ -65,6 +65,15 @@ def msg(bot, update): file = data.selfie.get_file() print(data.type, file) file.download() + if data.type in ('passport', 'driver_license', 'identity_card', + 'internal_passport', 'utility_bill', 'bank_statement', + 'rental_agreement', 'passport_registration', + 'temporary_registration'): + print(data.type, len(data.translation), 'translation') + for file in data.translation: + actual_file = file.get_file() + print(actual_file) + actual_file.download() def error(bot, update, error): diff --git a/telegram/__init__.py b/telegram/__init__.py index cde4547ff..cf45c6997 100644 --- a/telegram/__init__.py +++ b/telegram/__init__.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +''#!/usr/bin/env python # # A library that provides a Python interface to the Telegram Bot API # Copyright (C) 2015-2018 @@ -109,7 +109,10 @@ from .passport.passportelementerrors import (PassportElementError, PassportElementErrorFiles, PassportElementErrorFrontSide, PassportElementErrorReverseSide, - PassportElementErrorSelfie) + PassportElementErrorSelfie, + PassportElementErrorTranslationFile, + PassportElementErrorTranslationFiles, + PassportElementErrorUnspecified) from .passport.credentials import (Credentials, DataCredentials, SecureData, @@ -148,5 +151,6 @@ __all__ = [ 'Credentials', 'DataCredentials', 'SecureData', 'FileCredentials', 'IdDocumentData', 'PersonalDetails', 'ResidentialAddress', 'InputMediaVideo', 'InputMediaAnimation', 'InputMediaAudio', 'InputMediaDocument', 'TelegramDecryptionError', - 'PassportElementErrorSelfie' + 'PassportElementErrorSelfie', 'PassportElementErrorTranslationFile', + 'PassportElementErrorTranslationFiles', 'PassportElementErrorUnspecified' ] diff --git a/telegram/files/file.py b/telegram/files/file.py index c3f7f7544..33fa3cb61 100644 --- a/telegram/files/file.py +++ b/telegram/files/file.py @@ -17,6 +17,7 @@ # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram File.""" +from base64 import b64decode from os.path import basename from future.backports.urllib import parse as urllib_parse @@ -107,7 +108,9 @@ class File(TelegramObject): if out: buf = self.bot.request.retrieve(url) if self._credentials: - buf = decrypt(self._credentials.secret, self._credentials.hash, buf, file=True) + buf = decrypt(b64decode(self._credentials.secret), + b64decode(self._credentials.hash), + buf) out.write(buf) return out else: @@ -118,7 +121,9 @@ class File(TelegramObject): buf = self.bot.request.retrieve(url, timeout=timeout) if self._credentials: - buf = decrypt(self._credentials.secret, self._credentials.hash, buf, file=True) + buf = decrypt(b64decode(self._credentials.secret), + b64decode(self._credentials.hash), + buf) with open(filename, 'wb') as fobj: fobj.write(buf) return filename diff --git a/telegram/passport/credentials.py b/telegram/passport/credentials.py index 35c236d8f..638944d5a 100644 --- a/telegram/passport/credentials.py +++ b/telegram/passport/credentials.py @@ -16,7 +16,6 @@ # # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. -import binascii try: import ujson as json except ImportError: @@ -44,7 +43,7 @@ class TelegramDecryptionError(TelegramError): "{}".format(message)) -def decrypt(secret, hash, data, file=False): +def decrypt(secret, hash, data): """ Decrypt per telegram docs at https://core.telegram.org/passport. @@ -65,20 +64,6 @@ def decrypt(secret, hash, data, file=False): :obj:`bytes`: The decrypted data as bytes. """ - # First make sure that if secret, hash, or data was base64 encoded, to decode it into bytes - try: - secret = b64decode(secret) - except (binascii.Error, TypeError): - pass - try: - hash = b64decode(hash) - except (binascii.Error, TypeError): - pass - if not file: - try: - data = b64decode(data) - except (binascii.Error, TypeError): - pass # Make a SHA512 hash of secret + update digest = Hash(SHA512(), backend=default_backend()) digest.update(secret + hash) @@ -113,14 +98,14 @@ class EncryptedCredentials(TelegramObject): Attributes: data (:class:`telegram.Credentials` or :obj:`str`): Decrypted data with unique user's - payload, data hashes and secrets used for EncryptedPassportElement decryption and + nonce, data hashes and secrets used for EncryptedPassportElement decryption and authentication or base64 encrypted data. hash (:obj:`str`): Base64-encoded data hash for data authentication. secret (:obj:`str`): Decrypted or encrypted secret used for decryption. Args: data (:class:`telegram.Credentials` or :obj:`str`): Decrypted data with unique user's - payload, data hashes and secrets used for EncryptedPassportElement decryption and + nonce, data hashes and secrets used for EncryptedPassportElement decryption and authentication or base64 encrypted data. hash (:obj:`str`): Base64-encoded data hash for data authentication. secret (:obj:`str`): Decrypted or encrypted secret used for decryption. @@ -184,8 +169,8 @@ class EncryptedCredentials(TelegramObject): def decrypted_data(self): """ :class:`telegram.Credentials`: Lazily decrypt and return credentials data. This object - also contains the user specified payload as - `decrypted_data.payload`. + also contains the user specified nonce as + `decrypted_data.nonce`. Raises: telegram.TelegramDecryptionError: Decryption failed. Usually due to bad @@ -193,8 +178,8 @@ class EncryptedCredentials(TelegramObject): """ if self._decrypted_data is None: self._decrypted_data = Credentials.de_json(decrypt_json(self.decrypted_secret, - self.hash, - self.data), + b64decode(self.hash), + b64decode(self.data)), self.bot) return self._decrypted_data @@ -203,13 +188,13 @@ class Credentials(TelegramObject): """ Attributes: secure_data (:class:`telegram.SecureData`): Credentials for encrypted data - payload (:obj:`str`): Bot-specified payload + nonce (:obj:`str`): Bot-specified nonce """ - def __init__(self, secure_data, payload, bot=None, **kwargs): + def __init__(self, secure_data, nonce, bot=None, **kwargs): # Required self.secure_data = secure_data - self.payload = payload + self.nonce = nonce self.bot = bot @@ -319,7 +304,11 @@ class SecureValue(TelegramObject): selfie (:class:`telegram.FileCredentials`, optional): Credentials for encrypted selfie of the user with a document. Can be available for "passport", "driver_license", "identity_card" and "internal_passport". - files (:class:`telegram.Array of FileCredentials`, optional): Credentials for encrypted + translation (List[:class:`telegram.FileCredentials`], optional): Credentials for an + encrypted translation of the document. Available for "passport", "driver_license", + "identity_card", "internal_passport", "utility_bill", "bank_statement", + "rental_agreement", "passport_registration" and "temporary_registration". + files (List[:class:`telegram.FileCredentials`], optional): Credentials for encrypted files. Available for "utility_bill", "bank_statement", "rental_agreement", "passport_registration" and "temporary_registration" types. @@ -331,6 +320,7 @@ class SecureValue(TelegramObject): reverse_side=None, selfie=None, files=None, + translation=None, bot=None, **kwargs): self.data = data @@ -338,6 +328,7 @@ class SecureValue(TelegramObject): self.reverse_side = reverse_side self.selfie = selfie self.files = files + self.translation = translation self.bot = bot @@ -351,6 +342,7 @@ class SecureValue(TelegramObject): data['reverse_side'] = FileCredentials.de_json(data.get('reverse_side'), bot=bot) data['selfie'] = FileCredentials.de_json(data.get('selfie'), bot=bot) data['files'] = FileCredentials.de_list(data.get('files'), bot=bot) + data['translation'] = FileCredentials.de_list(data.get('translation'), bot=bot) return cls(bot=bot, **data) @@ -358,6 +350,7 @@ class SecureValue(TelegramObject): data = super(SecureValue, self).to_dict() data['files'] = [p.to_dict() for p in self.files] + data['translation'] = [p.to_dict() for p in self.translation] return data diff --git a/telegram/passport/data.py b/telegram/passport/data.py index dd7d6d3b4..08658be77 100644 --- a/telegram/passport/data.py +++ b/telegram/passport/data.py @@ -25,23 +25,33 @@ class PersonalDetails(TelegramObject): Attributes: first_name (:obj:`str`): First Name. + middle_name (:obj:`str`): Optional. First Name. last_name (:obj:`str`): Last Name. birth_date (:obj:`str`): Date of birth in DD.MM.YYYY format. gender (:obj:`str`): Gender, male or female. country_code (:obj:`str`): Citizenship (ISO 3166-1 alpha-2 country code). residence_country_code (:obj:`str`): Country of residence (ISO 3166-1 alpha-2 country code). + first_name (:obj:`str`): First Name in the language of the user's country of residence. + middle_name (:obj:`str`): Optional. Middle Name in the language of the user's country of + residence. + last_name (:obj:`str`): Last Name in the language of the user's country of residence. """ def __init__(self, first_name, last_name, birth_date, gender, country_code, - residence_country_code, bot=None, **kwargs): + residence_country_code, first_name_native, last_name_native, middle_name=None, + middle_name_native=None, bot=None, **kwargs): # Required self.first_name = first_name self.last_name = last_name + self.middle_name = middle_name self.birth_date = birth_date self.gender = gender self.country_code = country_code self.residence_country_code = residence_country_code + self.first_name_native = first_name_native + self.last_name_native = last_name_native + self.middle_name_native = middle_name_native self.bot = bot diff --git a/telegram/passport/encryptedpassportelement.py b/telegram/passport/encryptedpassportelement.py index 7402fc43c..db7332f17 100644 --- a/telegram/passport/encryptedpassportelement.py +++ b/telegram/passport/encryptedpassportelement.py @@ -17,6 +17,7 @@ # You should have received a copy of the GNU Lesser Public License # along with this program. If not, see [http://www.gnu.org/licenses/]. """This module contains an object that represents a Telegram EncryptedPassportElement.""" +from base64 import b64decode from telegram import (TelegramObject, PassportFile, PersonalDetails, IdDocumentData, ResidentialAddress) @@ -43,15 +44,22 @@ class EncryptedPassportElement(TelegramObject): files (List[:class:`telegram.PassportFile`]): Optional. Array of encrypted/decrypted files with documents provided by the user, available for "utility_bill", "bank_statement", "rental_agreement", "passport_registration" and "temporary_registration" types. - front_side (:class:`PassportFile`): Optional. Encrypted/decrypted file with the front side - of the document, provided by the user. Available for "passport", "driver_license", - "identity_card" and "internal_passport". - reverse_side (:class:`PassportFile`): Optional. Encrypted/decrypted file with the reverse - side of the document, provided by the user. Available for "driver_license" and - "identity_card". - selfie (:class:`PassportFile`): Optional. Encrypted/decrypted file with the selfie of the - user holding a document, provided by the user; available for "passport", + front_side (:class:`telegram.PassportFile`): Optional. Encrypted/decrypted file with the + front side of the document, provided by the user. Available for "passport", "driver_license", "identity_card" and "internal_passport". + reverse_side (:class:`telegram.PassportFile`): Optional. Encrypted/decrypted file with the + reverse side of the document, provided by the user. Available for "driver_license" and + "identity_card". + selfie (:class:`telegram.PassportFile`): Optional. Encrypted/decrypted file with the + selfie of the user holding a document, provided by the user; available for "passport", + "driver_license", "identity_card" and "internal_passport". + translation (List[:class:`telegram.PassportFile`]): Optional. Array of encrypted/decrypted + files with translated versions of documents provided by the user. Available if + requested for "passport", "driver_license", "identity_card", "internal_passport", + "utility_bill", "bank_statement", "rental_agreement", "passport_registration" and + "temporary_registration" types. + hash (:obj:`str`): Base64-encoded element hash for using in + :class:`telegram.PassportElementErrorUnspecified`. bot (:class:`telegram.Bot`): Optional. The Bot to use for instance methods. Args: @@ -69,15 +77,22 @@ class EncryptedPassportElement(TelegramObject): files (List[:class:`telegram.PassportFile`], optional): Array of encrypted/decrypted files with documents provided by the user, available for "utility_bill", "bank_statement", "rental_agreement", "passport_registration" and "temporary_registration" types. - front_side (:class:`PassportFile`, optional): Encrypted/decrypted file with the front side - of the document, provided by the user. Available for "passport", "driver_license", - "identity_card" and "internal_passport". - reverse_side (:class:`PassportFile`, optional): Encrypted/decrypted file with the reverse - side of the document, provided by the user. Available for "driver_license" and - "identity_card". - selfie (:class:`PassportFile`, optional): Encrypted/decrypted file with the selfie of the - user holding a document, provided by the user; available for "passport", + front_side (:class:`telegram.PassportFile`, optional): Encrypted/decrypted file with the + front side of the document, provided by the user. Available for "passport", "driver_license", "identity_card" and "internal_passport". + reverse_side (:class:`telegram.PassportFile`, optional): Encrypted/decrypted file with the + reverse side of the document, provided by the user. Available for "driver_license" and + "identity_card". + selfie (:class:`telegram.PassportFile`, optional): Encrypted/decrypted file with the + selfie of the user holding a document, provided by the user; available for "passport", + "driver_license", "identity_card" and "internal_passport". + translation (List[:class:`telegram.PassportFile`], optional): Array of encrypted/decrypted + files with translated versions of documents provided by the user. Available if + requested for "passport", "driver_license", "identity_card", "internal_passport", + "utility_bill", "bank_statement", "rental_agreement", "passport_registration" and + "temporary_registration" types. + hash (:obj:`str`): Base64-encoded element hash for using in + :class:`telegram.PassportElementErrorUnspecified`. bot (:class:`telegram.Bot`, optional): The Bot to use for instance methods. **kwargs (:obj:`dict`): Arbitrary keyword arguments. @@ -95,6 +110,8 @@ class EncryptedPassportElement(TelegramObject): front_side=None, reverse_side=None, selfie=None, + translation=None, + hash=None, bot=None, credentials=None, **kwargs): @@ -108,6 +125,8 @@ class EncryptedPassportElement(TelegramObject): self.front_side = front_side self.reverse_side = reverse_side self.selfie = selfie + self.translation = translation + self.hash = hash self._id_attrs = (self.type, self.data, self.phone_number, self.email, self.files, self.front_side, self.reverse_side, self.selfie) @@ -125,6 +144,7 @@ class EncryptedPassportElement(TelegramObject): data['front_side'] = PassportFile.de_json(data.get('front_side'), bot) data['reverse_side'] = PassportFile.de_json(data.get('reverse_side'), bot) data['selfie'] = PassportFile.de_json(data.get('selfie'), bot) + data['translation'] = PassportFile.de_list(data.get('translation'), bot) or None return cls(bot=bot, **data) @@ -141,9 +161,9 @@ class EncryptedPassportElement(TelegramObject): if secure_data.data is not None: # If not already decrypted if not isinstance(data['data'], dict): - data['data'] = decrypt_json(secure_data.data.secret, - secure_data.data.hash, - data['data']) + data['data'] = decrypt_json(b64decode(secure_data.data.secret), + b64decode(secure_data.data.hash), + b64decode(data['data'])) if data['type'] == 'personal_details': data['data'] = PersonalDetails.de_json(data['data'], bot=bot) elif data['type'] in ('passport', 'internal_passport', @@ -153,13 +173,15 @@ class EncryptedPassportElement(TelegramObject): data['data'] = ResidentialAddress.de_json(data['data'], bot=bot) data['files'] = PassportFile.de_list_decrypted(data.get('files'), bot, - secure_data) or None + secure_data.files) or None data['front_side'] = PassportFile.de_json_decrypted(data.get('front_side'), bot, secure_data.front_side) data['reverse_side'] = PassportFile.de_json_decrypted(data.get('reverse_side'), bot, secure_data.reverse_side) data['selfie'] = PassportFile.de_json_decrypted(data.get('selfie'), bot, secure_data.selfie) + data['translation'] = PassportFile.de_list_decrypted(data.get('translation'), bot, + secure_data.translation) or None return cls(bot=bot, **data) @@ -179,5 +201,7 @@ class EncryptedPassportElement(TelegramObject): if self.files: data['files'] = [p.to_dict() for p in self.files] + if self.translation: + data['translation'] = [p.to_dict() for p in self.translation] return data diff --git a/telegram/passport/passportelementerrors.py b/telegram/passport/passportelementerrors.py index 7ee63a998..d69935b49 100644 --- a/telegram/passport/passportelementerrors.py +++ b/telegram/passport/passportelementerrors.py @@ -250,3 +250,108 @@ class PassportElementErrorSelfie(PassportElementError): self.file_hash = file_hash self._id_attrs = (self.source, self.type, self.file_hash, self.message) + + +class PassportElementErrorTranslationFile(PassportElementError): + """ + Represents an issue with one of the files that constitute the translation of a document. + The error is considered resolved when the file changes. + + Attributes: + type (:obj:`str`): Type of element of the user's Telegram Passport which has the issue, + one of "passport", "driver_license", "identity_card", "internal_passport", + "utility_bill", "bank_statement", "rental_agreement", "passport_registration", + "temporary_registration". + file_hash (:obj:`str`): Base64-encoded hash of the file. + message (:obj:`str`): Error message. + + Args: + type (:obj:`str`): Type of element of the user's Telegram Passport which has the issue, + one of "passport", "driver_license", "identity_card", "internal_passport", + "utility_bill", "bank_statement", "rental_agreement", "passport_registration", + "temporary_registration". + file_hash (:obj:`str`): Base64-encoded hash of the file. + message (:obj:`str`): Error message. + **kwargs (:obj:`dict`): Arbitrary keyword arguments. + + """ + + def __init__(self, + type, + file_hash, + message, + **kwargs): + # Required + super(PassportElementErrorTranslationFile, self).__init__('translation_file', + type, message) + self.file_hash = file_hash + + self._id_attrs = (self.source, self.type, self.file_hash, self.message) + + +class PassportElementErrorTranslationFiles(PassportElementError): + """ + Represents an issue with the translated version of a document. The error is considered + resolved when a file with the document translation change. + + Attributes: + type (:obj:`str`): Type of element of the user's Telegram Passport which has the issue, + one of "passport", "driver_license", "identity_card", "internal_passport", + "utility_bill", "bank_statement", "rental_agreement", "passport_registration", + "temporary_registration" + file_hash (:obj:`str`): Base64-encoded file hash. + message (:obj:`str`): Error message. + + Args: + type (:obj:`str`): Type of element of the user's Telegram Passport which has the issue, + one of "passport", "driver_license", "identity_card", "internal_passport", + "utility_bill", "bank_statement", "rental_agreement", "passport_registration", + "temporary_registration" + file_hashes (List[:obj:`str`]): List of base64-encoded file hashes. + message (:obj:`str`): Error message. + **kwargs (:obj:`dict`): Arbitrary keyword arguments. + + """ + + def __init__(self, + type, + file_hashes, + message, + **kwargs): + # Required + super(PassportElementErrorTranslationFiles, self).__init__('translation_files', + type, message) + self.file_hashes = file_hashes + + self._id_attrs = ((self.source, self.type, self.message) + + tuple([file_hash for file_hash in file_hashes])) + + +class PassportElementErrorUnspecified(PassportElementError): + """ + Represents an issue in an unspecified place. The error is considered resolved when new + data is added. + + Attributes: + type (:obj:`str`): Type of element of the user's Telegram Passport which has the issue. + element_hash (:obj:`str`): Base64-encoded element hash. + message (:obj:`str`): Error message. + + Args: + type (:obj:`str`): Type of element of the user's Telegram Passport which has the issue. + element_hash (:obj:`str`): Base64-encoded element hash. + message (:obj:`str`): Error message. + **kwargs (:obj:`dict`): Arbitrary keyword arguments. + + """ + + def __init__(self, + type, + element_hash, + message, + **kwargs): + # Required + super(PassportElementErrorUnspecified, self).__init__('unspecified', type, message) + self.element_hash = element_hash + + self._id_attrs = (self.source, self.type, self.element_hash, self.message) diff --git a/telegram/passport/passportfile.py b/telegram/passport/passportfile.py index 2d5fe4a48..4b8229467 100644 --- a/telegram/passport/passportfile.py +++ b/telegram/passport/passportfile.py @@ -84,7 +84,7 @@ class PassportFile(TelegramObject): if not data: return [] - return [cls.de_json_decrypted(passport_file, bot, credentials.files[i]) + return [cls.de_json_decrypted(passport_file, bot, credentials[i]) for i, passport_file in enumerate(data)] def get_file(self, timeout=None, **kwargs): diff --git a/tests/test_passport.py b/tests/test_passport.py index b5e9eaf19..91ef6c477 100644 --- a/tests/test_passport.py +++ b/tests/test_passport.py @@ -22,29 +22,63 @@ import pytest from telegram import (PassportData, PassportFile, Bot, File, PassportElementErrorSelfie, PassportElementErrorDataField, Credentials, TelegramDecryptionError) - -RAW_PASSPORT_DATA = {'data': [{'type': 'personal_details', - 'data': 'tj3pNwOpN+ZHsyb6F3aJcNmEyPxrOtGTbu3waBlCQDNaQ9oJlkbXpw+HI3y9faq/+TCeB/WsS/2TxRXTKZw4zXvGP2UsfdRkJ2SQq6x+Ffe/oTF9/q8sWp2BwU3hHUOz7ec1/QrdPBhPJjbwSykEBNggPweiBVDZ0x/DWJ0guCkGT9smYGqog1vqlqbIWG7AWcxVy2fpUy9w/zDXjxj5WQ3lRpHJmi46s9xIHobNGGBvWw6/bGBCInMoovgqRCEu1sgz2QXF3wNiUzGFycEzLz7o+1htLys5n8Pdi9MG4RY='}, - {'type': 'driver_license', - 'data': 'hOXQ/iHSGRDFXqql3yETA4AiP0mdlwmo9RtGS+Qg9E5okrN/yTcPNtBKb2fLA0posk35bvevN53cyJMHZnxErEFTSw/FQjPyRFdJUyjGNPeu4yOI73uk5eRVLTAlA2G0eN2azzfS/QOBGL19N3pHk9hMTZYPCBTDt89MHhRQS7Z3YWRSzFcFiEhktHv/ezgcg3EWtsUQ8K4J2445uoZzbB8wsQ6RM4ibn08RfjV6dNyVrj8jBGUpCBlA6iY60rFQM+LZ9ByI3OeS53bnIAMQC2rHHbV/wkzS6PbufOzjZgJq26aCLmC5YDomrpcrdvk0fvi6aEuBJEI3zcteh2fh/Q==', - 'selfie': {'file_id': 'DgADBAADEQQAAkopgFNr6oi-wISRtAI', - 'file_date': 1534074942}, - 'reverse_side': {'file_id': 'DgADBAADNQQAAtoagFPf4wwmFZdmyQI', - 'file_date': 1534074942}, - 'front_side': {'file_id': 'DgADBAADxwMAApnQgVPK2-ckL2eXVAI', - 'file_date': 1534074942}}, - {'type': 'address', - 'data': 'j9SksVkSj128DBtZA+3aNjSFNirzv+R97guZaMgae4Gi0oDVNAF7twPR7j9VSmPedfJrEwL3O889Ei+a5F1xyLLyEI/qEBljvL70GFIhYGitS0JmNabHPHSZrjOl8b4s/0Z0Px2GpLO5siusTLQonimdUvu4UPjKquYISmlKEKhtmGATy+h+JDjNCYuOkhakeNw0Rk0BHgj0C3fCb7WZNQSyVb+2GTu6caR6eXf/AFwFp0TV3sRz3h0WIVPW8bna'}, - {'type': 'utility_bill', 'files': [ - {'file_id': 'DgADBAADLAMAAhwfgVMyfGa5Nr0LvAI', - 'file_date': 1534074988}, - {'file_id': 'DgADBAADaQQAAsFxgVNVfLZuT-_3ZQI', - 'file_date': 1534074988}]}, - {'type': 'email', 'email': 'fb3e3i47zt@dispostable.com'}], - 'credentials': { - 'data': 'uI/g4fJLVO6132t+yuBvKExTpTubinscH8KLVc8YPuo1SiXaBg4A6AaVdv60CPViMw8n+ShVWOTL6oN5Ye0+CC2/URZ0eeTMJcKkvJYRI0Q6YJ3aeeEzslwORKho0mGk5xSWpPV5LHXhRIlgUMA32NkbNuzzoij7OhqxvuDE50/0pAUKtxy69h+heAzxFj8+jYjgnOwgwNa6FGUG59oUozgpB98XrEeWGW+JIGE9fux8dSGkFhF2SjkmdW6b2Gexuq94TVGDgFigSvqMOgZj9slZI4UEdZBIGldPHrc8/+EuqWv+WKWU8hdj1dxfY/pNvonb8MwrQLrUyNafdLq5QD4Kmg3XPnZTI9bsYUf6xO9oKU42JezDcRbaCqKkUn6UceCkV2hHMJP4aVVI9Bad1k/rXlaDh8PUV19n6tct5UH8JfkBbGlj2uASI5lPasIvnWg9s1vsZ4ynE2YAuU4iotMkgmJkj1+JCl8Ul9uqzfiK0IbYfu57V82gUEjn6c4br49pY+Cvo9od9Lx8fSJCiXq+DdmDTfIZxpeFqEYeJi6/v0CjJni4CtS6LJGo9JUR2MlqOAiyOLomadlrZyzC2eLm8X6ouIXTxtbUqzxcTiH/r23NQ44YwJpMdDiulNuPg0tEyio8TokHj9APORiW+QIw4zLZFyBEorT1DSALm4AV3A1lIg0svOncDkOqa3ZydQ0tdysoXOq5Zsu+Qu/DQZQz5IGEHb7N9QW6KZDGfBOP5Ok04OxlXO8UaTZr6mysmu8jDOlqtitLKSCGDy0LPfITzehTqfEu0KrtmxI91zJtiNyE4g+oBzQsLwQ7vSp1xl7YNViDi8ea/upZdRavh3NthVD5TxyNJedt4Xp8q5/H5kSV/EFhumONPwBwOuN4KrsCQEaNtHZfYB8SH1v0rWbiFw3/owxCrCPqgEwMGnx+JTqsAplurIZfxsNz+57O4Hq10VVt7yhroq9UpIAR8C8hW58dcZBflWyQwG8HalYAaPJ/teSLP9o6MyiqrUkADbNBOFsUhIerq6Q0BnQ941sWg10BkG8oQSU6JdnZ1Ygml2gpFabBRXeeF5ijD0Y9Bq8D3vDnHKx/GziAK0aKlwSqfTmJ+C1hm7AxBekjtDavkBeq5bIOtxYaXqrtunQFAZRCrcnQEg0DPY6dYw+jmOR0NuHRMU5KeZ0994Jn0TRGiUoJtLp1MHJ2D1MCWG3Fp9Ps7vB46a9XiGYHJUdJjHsavvQRQjhB1FvA/kxXNubCjAOPLUz1j+BEkUUgwi65l8+pViHUCIhBdLJn7S0J9HbMsc3jXOFusfvXfyvf2SZMqyhEscs/2HzYgbCWVSxQ//6eimZqvW4an1JIEfH1xUH3zS0HvS7NWChtX1Cj+jJ2vw+lyom6a84wJ2mjSRVcaV6JZa2mIf2IuXfX8EMZwKMDJagkQ7effV3MrnCNrXUXJYn8wVm6l+uP+R9mCn9qbGlvWToCID0YM8MTxJ0LgzC0n93m46OxxCI1p1hPzVrmDCcKt3OASe0zVat415/sz9TTU7Rm8oNSAVUVNo7FRg2XwEb9VupGMjlscu4cJKLCtUpImSKDKlVLH2KYy6s4NdPjOjqtjQLRoaCxbYHmNXXE+vVwPiNBK/bBVT3XJgdmbkXa/PDnMjhAmn0huuxBu8GsTOs+bBdyDCbr6mY6EzN9lCPspEoOPs7nmREpjrvGmEqEddRteyAebpVqZQhqklyT3RVl8AmfoF7QgTi0NYqFb50Lgou3hFrMg7isG5EKU+IKI5WfzyWONlHNSpvO', - 'hash': 'qyi5vQ2HCVjVMHb+l6HQ4krmvwwEkvm6Qmdf5GJcohM=', - 'secret': 'kgJE0VLB9enOq5fhhX8gOv0xoaMIcskmRPOG1eMbiC/Q8slr7kur12H/YIoOfd7/DQ0ggE7TAAe34PypFvtmwt5fDVtqtPl9YoCeAOCFWxHxLgTCLbzoJ0lTJXoJkdHjlvR2lKaP+rMtaU1w8WOpYOGiNXyblQoWwFRrWNTHmHnmwBfGBFCj/vp89+C1viEYHeWPPUkBhf1vT31L70BEoe8hxORJEDg+jY+80W2nFdIWNBF+o9GSmbMWFtd7UFiuLPp2JUBCy8XuHozk8xFk/PN6m6DgSu32rC4YBJv/sWGUo/MmH0nxR3gaiEkj+9rWIybCNAwgfdQpk/KH2RCF8g=='}} +# Generated using the scope: +# { +# data: [ +# { +# type: 'personal_details', +# native_names: true +# }, +# { +# type: 'id_document', +# selfie: true, +# translation: true +# }, +# { +# type: 'address_document', +# translation: true +# }, +# 'address', +# 'email' +# ], +# v: 1 +# } +RAW_PASSPORT_DATA = {'credentials': {'hash': 'qB4hz2LMcXYhglwz6EvXMMyI3PURisWLXl/iCmCXcSk=', + 'secret': 'O6x3X2JrLO1lUIhw48os1gaenDuZLhesoZMKXehZwtM3vsxOdtxHKWQyLNwtbyy4snYpARXDwf8f1QHNmQ/M1PwBQvk1ozrZBXb4a6k/iYj+P4v8Xw2M++CRHqZv0LaxFtyHOXnNYZ6dXpNeF0ZvYYTmm0FsYvK+/3/F6VDB3Oe6xWlXFLwaCCP/jA9i2+dKp6iq8NLOo4VnxenPKWWYz20RZ50MdAbS3UR+NCx4AHM2P5DEGrHNW0tMXJ+FG3jpVrit5BuCbB/eRgKxNGRWNxEGV5hun5MChdxKCEHCimnUA97h7MZdoTgYxkrfvDSZ/V89BnFXLdr87t/NLvVE0Q==', + 'data': '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'}, + 'data': [ + { + 'data': 'QRfzWcCN4WncvRO3lASG+d+c5gzqXtoCinQ1PgtYiZMKXCksx9eB9Ic1bOt8C/un9/XaX220PjJSO7Kuba+nXXC51qTsjqP9rnLKygnEIWjKrfiDdklzgcukpRzFSjiOAvhy86xFJZ1PfPSrFATy/Gp1RydLzbrBd2ZWxZqXrxcMoA0Q2UTTFXDoCYerEAiZoD69i79tB/6nkLBcUUvN5d52gKd/GowvxWqAAmdO6l1N7jlo6aWjdYQNBAK1KHbJdbRZMJLxC1MqMuZXAYrPoYBRKr5xAnxDTmPn/LEZKLc3gwwZyEgR5x7e9jp5heM6IEMmsv3O/6SUeEQs7P0iVuRSPLMJLfDdwns8Tl3fF2M4IxKVovjCaOVW+yHKsADDAYQPzzH2RcrWVD0TP5I64mzpK64BbTOq3qm3Hn51SV9uA/+LvdGbCp7VnzHx4EdUizHsVyilJULOBwvklsrDRvXMiWmh34ZSR6zilh051tMEcRf0I+Oe7pIxVJd/KKfYA2Z/eWVQTCn5gMuAInQNXFSqDIeIqBX+wca6kvOCUOXB7J2uRjTpLaC4DM9s/sNjSBvFixcGAngt+9oap6Y45rQc8ZJaNN/ALqEJAmkphW8=', + 'type': 'personal_details' + }, { + 'reverse_side': {'file_date': 1534074942, + 'file_id': 'DgADBAADNQQAAtoagFPf4wwmFZdmyQI'}, + 'translation': [{'file_size': 28640, 'file_date': 1535630933, + 'file_id': 'DgADBAADswMAAisqQVAmooP-kVgLgAI'}, + {'file_size': 28672, 'file_date': 1535630933, + 'file_id': 'DgADBAAD1QMAAnrpQFBMZsT3HysjwwI'}], + 'front_side': {'file_size': 28624, 'file_date': 1534074942, + 'file_id': 'DgADBAADxwMAApnQgVPK2-ckL2eXVAI'}, + 'type': 'driver_license', + 'selfie': {'file_size': 28592, 'file_date': 1534074942, + 'file_id': 'DgADBAADEQQAAkopgFNr6oi-wISRtAI'}, + 'data': 'eJUOFuY53QKmGqmBgVWlLBAQCUQJ79n405SX6M5aGFIIodOPQqnLYvMNqTwTrXGDlW+mVLZcbu+y8luLVO8WsJB/0SB7q5WaXn/IMt1G9lz5G/KMLIZG/x9zlnimsaQLg7u8srG6L4KZzv+xkbbHjZdETrxU8j0N/DoS4HvLMRSJAgeFUrY6v2YW9vSRg+fSxIqQy1jR2VKpzAT8OhOz7A==' + }, { + 'translation': [{'file_size': 28480, 'file_date': 1535630939, + 'file_id': 'DgADBAADyQUAAqyqQVC_eoX_KwNjJwI'}, + {'file_size': 28528, 'file_date': 1535630939, + 'file_id': 'DgADBAADsQQAAubTQVDRO_FN3lOwWwI'}], + 'files': [{'file_size': 28640, 'file_date': 1534074988, + 'file_id': 'DgADBAADLAMAAhwfgVMyfGa5Nr0LvAI'}, + {'file_size': 28480, 'file_date': 1534074988, + 'file_id': 'DgADBAADaQQAAsFxgVNVfLZuT-_3ZQI'}], + 'type': 'utility_bill' + }, { + 'data': 'j9SksVkSj128DBtZA+3aNjSFNirzv+R97guZaMgae4Gi0oDVNAF7twPR7j9VSmPedfJrEwL3O889Ei+a5F1xyLLyEI/qEBljvL70GFIhYGitS0JmNabHPHSZrjOl8b4s/0Z0Px2GpLO5siusTLQonimdUvu4UPjKquYISmlKEKhtmGATy+h+JDjNCYuOkhakeNw0Rk0BHgj0C3fCb7WZNQSyVb+2GTu6caR6eXf/AFwFp0TV3sRz3h0WIVPW8bna', + 'type': 'address' + }, { + 'email': 'fb3e3i47zt@dispostable.com', 'type': 'email' + }]} @pytest.fixture(scope='function') @@ -54,33 +88,42 @@ def all_passport_data(): {'type': 'passport', 'data': RAW_PASSPORT_DATA['data'][1]['data'], 'front_side': RAW_PASSPORT_DATA['data'][1]['front_side'], - 'selfie': RAW_PASSPORT_DATA['data'][1]['selfie']}, + 'selfie': RAW_PASSPORT_DATA['data'][1]['selfie'], + 'translation': RAW_PASSPORT_DATA['data'][1]['translation']}, {'type': 'internal_passport', 'data': RAW_PASSPORT_DATA['data'][1]['data'], 'front_side': RAW_PASSPORT_DATA['data'][1]['front_side'], - 'selfie': RAW_PASSPORT_DATA['data'][1]['selfie']}, + 'selfie': RAW_PASSPORT_DATA['data'][1]['selfie'], + 'translation': RAW_PASSPORT_DATA['data'][1]['translation']}, {'type': 'driver_license', 'data': RAW_PASSPORT_DATA['data'][1]['data'], 'front_side': RAW_PASSPORT_DATA['data'][1]['front_side'], 'reverse_side': RAW_PASSPORT_DATA['data'][1]['reverse_side'], - 'selfie': RAW_PASSPORT_DATA['data'][1]['selfie']}, + 'selfie': RAW_PASSPORT_DATA['data'][1]['selfie'], + 'translation': RAW_PASSPORT_DATA['data'][1]['translation']}, {'type': 'identity_card', 'data': RAW_PASSPORT_DATA['data'][1]['data'], 'front_side': RAW_PASSPORT_DATA['data'][1]['front_side'], 'reverse_side': RAW_PASSPORT_DATA['data'][1]['reverse_side'], - 'selfie': RAW_PASSPORT_DATA['data'][1]['selfie']}, - {'type': 'address', - 'data': RAW_PASSPORT_DATA['data'][2]['data']}, + 'selfie': RAW_PASSPORT_DATA['data'][1]['selfie'], + 'translation': RAW_PASSPORT_DATA['data'][1]['translation']}, {'type': 'utility_bill', - 'files': RAW_PASSPORT_DATA['data'][3]['files']}, + 'files': RAW_PASSPORT_DATA['data'][2]['files'], + 'translation': RAW_PASSPORT_DATA['data'][2]['translation']}, {'type': 'bank_statement', - 'files': RAW_PASSPORT_DATA['data'][3]['files']}, + 'files': RAW_PASSPORT_DATA['data'][2]['files'], + 'translation': RAW_PASSPORT_DATA['data'][2]['translation']}, {'type': 'rental_agreement', - 'files': RAW_PASSPORT_DATA['data'][3]['files']}, + 'files': RAW_PASSPORT_DATA['data'][2]['files'], + 'translation': RAW_PASSPORT_DATA['data'][2]['translation']}, {'type': 'passport_registration', - 'files': RAW_PASSPORT_DATA['data'][3]['files']}, + 'files': RAW_PASSPORT_DATA['data'][2]['files'], + 'translation': RAW_PASSPORT_DATA['data'][2]['translation']}, {'type': 'temporary_registration', - 'files': RAW_PASSPORT_DATA['data'][3]['files']}, + 'files': RAW_PASSPORT_DATA['data'][2]['files'], + 'translation': RAW_PASSPORT_DATA['data'][2]['translation']}, + {'type': 'address', + 'data': RAW_PASSPORT_DATA['data'][3]['data']}, {'type': 'email', 'email': 'fb3e3i47zt@dispostable.com'}, {'type': 'phone_number', @@ -96,8 +139,12 @@ class TestPassport(object): driver_license_selfie_file_id = 'DgADBAADEQQAAkopgFNr6oi-wISRtAI' driver_license_front_side_file_id = 'DgADBAADxwMAApnQgVPK2-ckL2eXVAI' driver_license_reverse_side_file_id = 'DgADBAADNQQAAtoagFPf4wwmFZdmyQI' + driver_license_translation_1_file_id = 'DgADBAADswMAAisqQVAmooP-kVgLgAI' + driver_license_translation_2_file_id = 'DgADBAAD1QMAAnrpQFBMZsT3HysjwwI' utility_bill_1_file_id = 'DgADBAADLAMAAhwfgVMyfGa5Nr0LvAI' utility_bill_2_file_id = 'DgADBAADaQQAAsFxgVNVfLZuT-_3ZQI' + utility_bill_translation_1_file_id = 'DgADBAADyQUAAqyqQVC_eoX_KwNjJwI' + utility_bill_translation_2_file_id = 'DgADBAADsQQAAubTQVDRO_FN3lOwWwI' driver_license_selfie_credentials_file_hash = 'Cila/qLXSBH7DpZFbb5bRZIRxeFW2uv/ulL0u0JNsYI=' driver_license_selfie_credentials_secret = 'tivdId6RNYNsvXYPppdzrbxOBuBOr9wXRPDcCvnXU7E=' @@ -105,7 +152,7 @@ class TestPassport(object): assert isinstance(passport_data, PassportData) def test_expected_encrypted_values(self, passport_data): - personal_details, driver_license, address, utility_bill, email = passport_data.data + personal_details, driver_license, utility_bill, address, email = passport_data.data assert personal_details.type == 'personal_details' assert personal_details.data == RAW_PASSPORT_DATA['data'][0]['data'] @@ -118,29 +165,41 @@ class TestPassport(object): assert driver_license.front_side.file_id == self.driver_license_front_side_file_id assert isinstance(driver_license.reverse_side, PassportFile) assert driver_license.reverse_side.file_id == self.driver_license_reverse_side_file_id - - assert address.type == 'address' - assert address.data == RAW_PASSPORT_DATA['data'][2]['data'] + assert isinstance(driver_license.translation[0], PassportFile) + assert driver_license.translation[0].file_id == self.driver_license_translation_1_file_id + assert isinstance(driver_license.translation[1], PassportFile) + assert driver_license.translation[1].file_id == self.driver_license_translation_2_file_id assert utility_bill.type == 'utility_bill' assert isinstance(utility_bill.files[0], PassportFile) assert utility_bill.files[0].file_id == self.utility_bill_1_file_id assert isinstance(utility_bill.files[1], PassportFile) assert utility_bill.files[1].file_id == self.utility_bill_2_file_id + assert isinstance(utility_bill.translation[0], PassportFile) + assert utility_bill.translation[0].file_id == self.utility_bill_translation_1_file_id + assert isinstance(utility_bill.translation[1], PassportFile) + assert utility_bill.translation[1].file_id == self.utility_bill_translation_2_file_id + + assert address.type == 'address' + assert address.data == RAW_PASSPORT_DATA['data'][3]['data'] assert email.type == 'email' assert email.email == 'fb3e3i47zt@dispostable.com' def test_expected_decrypted_values(self, passport_data): - (personal_details, driver_license, address, - utility_bill, email) = passport_data.decrypted_data + (personal_details, driver_license, utility_bill, address, + email) = passport_data.decrypted_data assert personal_details.type == 'personal_details' - assert personal_details.data.to_dict() == {'gender': 'female', + assert personal_details.data.to_dict() == {'first_name': 'FIRSTNAME', + 'middle_name': 'MIDDLENAME', + 'first_name_native': 'FIRSTNAMENATIVE', 'residence_country_code': 'DK', - 'country_code': 'DK', 'birth_date': '01.01.2001', - 'first_name': 'FIRSTNAME', + 'last_name_native': 'LASTNAMENATIVE', + 'gender': 'female', + 'middle_name_native': 'MIDDLENAMENATIVE', + 'country_code': 'DK', 'last_name': 'LASTNAME'} assert driver_license.type == 'driver_license' @@ -210,7 +269,7 @@ class TestPassport(object): def test_wrong_hash(self, bot): data = deepcopy(RAW_PASSPORT_DATA) - data['credentials']['hash'] = b'notcorrecthash' + data['credentials']['hash'] = 'bm90Y29ycmVjdGhhc2g=' # Not correct hash passport_data = PassportData.de_json(data, bot=bot) with pytest.raises(TelegramDecryptionError): assert passport_data.decrypted_data diff --git a/tests/test_passportelementerrortranslationfile.py b/tests/test_passportelementerrortranslationfile.py new file mode 100644 index 000000000..4bf02df6b --- /dev/null +++ b/tests/test_passportelementerrortranslationfile.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2018 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. + +import pytest + +from telegram import PassportElementErrorTranslationFile, PassportElementErrorDataField + + +@pytest.fixture(scope='class') +def passport_element_error_translation_file(): + return PassportElementErrorTranslationFile(TestPassportElementErrorTranslationFile.type, + TestPassportElementErrorTranslationFile.file_hash, + TestPassportElementErrorTranslationFile.message) + + +class TestPassportElementErrorTranslationFile(object): + source = 'translation_file' + type = 'test_type' + file_hash = 'file_hash' + message = 'Error message' + + def test_expected_values(self, passport_element_error_translation_file): + assert passport_element_error_translation_file.source == self.source + assert passport_element_error_translation_file.type == self.type + assert passport_element_error_translation_file.file_hash == self.file_hash + assert passport_element_error_translation_file.message == self.message + + def test_to_dict(self, passport_element_error_translation_file): + passport_element_error_translation_file_dict = \ + passport_element_error_translation_file.to_dict() + + assert isinstance(passport_element_error_translation_file_dict, dict) + assert (passport_element_error_translation_file_dict['source'] == + passport_element_error_translation_file.source) + assert (passport_element_error_translation_file_dict['type'] == + passport_element_error_translation_file.type) + assert (passport_element_error_translation_file_dict['file_hash'] == + passport_element_error_translation_file.file_hash) + assert (passport_element_error_translation_file_dict['message'] == + passport_element_error_translation_file.message) + + def test_equality(self): + a = PassportElementErrorTranslationFile(self.type, self.file_hash, self.message) + b = PassportElementErrorTranslationFile(self.type, self.file_hash, self.message) + c = PassportElementErrorTranslationFile(self.type, '', '') + d = PassportElementErrorTranslationFile('', self.file_hash, '') + e = PassportElementErrorTranslationFile('', '', self.message) + f = PassportElementErrorDataField(self.type, '', '', self.message) + + assert a == b + assert hash(a) == hash(b) + assert a is not b + + assert a != c + assert hash(a) != hash(c) + + assert a != d + assert hash(a) != hash(d) + + assert a != e + assert hash(a) != hash(e) + + assert a != f + assert hash(a) != hash(f) diff --git a/tests/test_passportelementerrortranslationfiles.py b/tests/test_passportelementerrortranslationfiles.py new file mode 100644 index 000000000..7198e49fe --- /dev/null +++ b/tests/test_passportelementerrortranslationfiles.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2018 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. + +import pytest + +from telegram import PassportElementErrorTranslationFiles, PassportElementErrorSelfie + + +@pytest.fixture(scope='class') +def passport_element_error_translation_files(): + return PassportElementErrorTranslationFiles(TestPassportElementErrorTranslationFiles.type, + TestPassportElementErrorTranslationFiles.file_hashes, # flake8: noqa + TestPassportElementErrorTranslationFiles.message) + + +class TestPassportElementErrorTranslationFiles(object): + source = 'translation_files' + type = 'test_type' + file_hashes = ['hash1', 'hash2'] + message = 'Error message' + + def test_expected_values(self, passport_element_error_translation_files): + assert passport_element_error_translation_files.source == self.source + assert passport_element_error_translation_files.type == self.type + assert isinstance(passport_element_error_translation_files.file_hashes, list) + assert passport_element_error_translation_files.file_hashes == self.file_hashes + assert passport_element_error_translation_files.message == self.message + + def test_to_dict(self, passport_element_error_translation_files): + passport_element_error_translation_files_dict = \ + passport_element_error_translation_files.to_dict() + + assert isinstance(passport_element_error_translation_files_dict, dict) + assert (passport_element_error_translation_files_dict['source'] == + passport_element_error_translation_files.source) + assert (passport_element_error_translation_files_dict['type'] == + passport_element_error_translation_files.type) + assert (passport_element_error_translation_files_dict['file_hashes'] == + passport_element_error_translation_files.file_hashes) + assert (passport_element_error_translation_files_dict['message'] == + passport_element_error_translation_files.message) + + def test_equality(self): + a = PassportElementErrorTranslationFiles(self.type, self.file_hashes, self.message) + b = PassportElementErrorTranslationFiles(self.type, self.file_hashes, self.message) + c = PassportElementErrorTranslationFiles(self.type, '', '') + d = PassportElementErrorTranslationFiles('', self.file_hashes, '') + e = PassportElementErrorTranslationFiles('', '', self.message) + f = PassportElementErrorSelfie(self.type, '', self.message) + + assert a == b + assert hash(a) == hash(b) + assert a is not b + + assert a != c + assert hash(a) != hash(c) + + assert a != d + assert hash(a) != hash(d) + + assert a != e + assert hash(a) != hash(e) + + assert a != f + assert hash(a) != hash(f) diff --git a/tests/test_passportelementerrorunspecified.py b/tests/test_passportelementerrorunspecified.py new file mode 100644 index 000000000..c3aee3f42 --- /dev/null +++ b/tests/test_passportelementerrorunspecified.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python +# +# A library that provides a Python interface to the Telegram Bot API +# Copyright (C) 2015-2018 +# Leandro Toledo de Souza +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser Public License for more details. +# +# You should have received a copy of the GNU Lesser Public License +# along with this program. If not, see [http://www.gnu.org/licenses/]. + +import pytest + +from telegram import PassportElementErrorUnspecified, PassportElementErrorDataField + + +@pytest.fixture(scope='class') +def passport_element_error_unspecified(): + return PassportElementErrorUnspecified(TestPassportElementErrorUnspecified.type, + TestPassportElementErrorUnspecified.element_hash, + TestPassportElementErrorUnspecified.message) + + +class TestPassportElementErrorUnspecified(object): + source = 'unspecified' + type = 'test_type' + element_hash = 'element_hash' + message = 'Error message' + + def test_expected_values(self, passport_element_error_unspecified): + assert passport_element_error_unspecified.source == self.source + assert passport_element_error_unspecified.type == self.type + assert passport_element_error_unspecified.element_hash == self.element_hash + assert passport_element_error_unspecified.message == self.message + + def test_to_dict(self, passport_element_error_unspecified): + passport_element_error_unspecified_dict = passport_element_error_unspecified.to_dict() + + assert isinstance(passport_element_error_unspecified_dict, dict) + assert (passport_element_error_unspecified_dict['source'] == + passport_element_error_unspecified.source) + assert (passport_element_error_unspecified_dict['type'] == + passport_element_error_unspecified.type) + assert (passport_element_error_unspecified_dict['element_hash'] == + passport_element_error_unspecified.element_hash) + assert (passport_element_error_unspecified_dict['message'] == + passport_element_error_unspecified.message) + + def test_equality(self): + a = PassportElementErrorUnspecified(self.type, self.element_hash, self.message) + b = PassportElementErrorUnspecified(self.type, self.element_hash, self.message) + c = PassportElementErrorUnspecified(self.type, '', '') + d = PassportElementErrorUnspecified('', self.element_hash, '') + e = PassportElementErrorUnspecified('', '', self.message) + f = PassportElementErrorDataField(self.type, '', '', self.message) + + assert a == b + assert hash(a) == hash(b) + assert a is not b + + assert a != c + assert hash(a) != hash(c) + + assert a != d + assert hash(a) != hash(d) + + assert a != e + assert hash(a) != hash(e) + + assert a != f + assert hash(a) != hash(f)