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)