#!/usr/bin/env python # # A library that provides a Python interface to the Telegram Bot API # Copyright (C) 2015-2022 # 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 datetime import logging import os import pickle import gzip import signal from pathlib import Path from time import sleep import pytest from telegram.warnings import PTBUserWarning try: import ujson as json except ImportError: import json from telegram import Update, Message, User, Chat, MessageEntity, Bot, TelegramObject from telegram.ext import ( BasePersistence, ConversationHandler, MessageHandler, filters, PicklePersistence, CommandHandler, DictPersistence, TypeHandler, JobQueue, ContextTypes, PersistenceInput, UpdaterBuilder, CallbackDataCache, ) from telegram.ext._callbackdatacache import _KeyboardData @pytest.fixture(autouse=True) def change_directory(tmp_path: Path): orig_dir = Path.cwd() # Switch to a temporary directory, so we don't have to worry about cleaning up files os.chdir(tmp_path) yield # Go back to original directory os.chdir(orig_dir) @pytest.fixture(autouse=True) def reset_callback_data_cache(bot): yield bot.callback_data_cache.clear_callback_data() bot.callback_data_cache.clear_callback_queries() bot.arbitrary_callback_data = False class OwnPersistence(BasePersistence): def get_bot_data(self): raise NotImplementedError def get_chat_data(self): raise NotImplementedError def get_user_data(self): raise NotImplementedError def get_conversations(self, name): raise NotImplementedError def update_bot_data(self, data): raise NotImplementedError def update_chat_data(self, chat_id, data): raise NotImplementedError def update_conversation(self, name, key, new_state): raise NotImplementedError def update_user_data(self, user_id, data): raise NotImplementedError def get_callback_data(self): raise NotImplementedError def drop_user_data(self, user_id): raise NotImplementedError def drop_chat_data(self, chat_id): raise NotImplementedError def refresh_user_data(self, user_id, user_data): raise NotImplementedError def refresh_chat_data(self, chat_id, chat_data): raise NotImplementedError def refresh_bot_data(self, bot_data): raise NotImplementedError def update_callback_data(self, data): raise NotImplementedError def flush(self): raise NotImplementedError @pytest.fixture(scope="function") def base_persistence(): return OwnPersistence() @pytest.fixture(scope="function") def bot_persistence(): class BotPersistence(BasePersistence): __slots__ = ('bot_data', 'chat_data', 'user_data', 'callback_data') def __init__(self): super().__init__() self.bot_data = None self.chat_data = {} self.user_data = {} self.callback_data = None def get_bot_data(self): return self.bot_data def get_chat_data(self): return self.chat_data def get_user_data(self): return self.user_data def get_callback_data(self): return self.callback_data def get_conversations(self, name): raise NotImplementedError def update_bot_data(self, data): self.bot_data = data def update_chat_data(self, chat_id, data): self.chat_data[chat_id] = data def update_user_data(self, user_id, data): self.user_data[user_id] = data def update_callback_data(self, data): self.callback_data = data def drop_user_data(self, user_id): self.user_data.pop(user_id, None) def drop_chat_data(self, chat_id): self.chat_data.pop(chat_id, None) def update_conversation(self, name, key, new_state): raise NotImplementedError def refresh_user_data(self, user_id, user_data): pass def refresh_chat_data(self, chat_id, chat_data): pass def refresh_bot_data(self, bot_data): pass def flush(self): pass return BotPersistence() @pytest.fixture(scope="function") def bot_data(): return {'test1': 'test2', 'test3': {'test4': 'test5'}} @pytest.fixture(scope="function") def chat_data(): return {-12345: {'test1': 'test2', 'test3': {'test4': 'test5'}}, -67890: {3: 'test4'}} @pytest.fixture(scope="function") def user_data(): return {12345: {'test1': 'test2', 'test3': {'test4': 'test5'}}, 67890: {3: 'test4'}} @pytest.fixture(scope="function") def callback_data(): return [('test1', 1000, {'button1': 'test0', 'button2': 'test1'})], {'test1': 'test2'} @pytest.fixture(scope='function') def conversations(): return { 'name1': {(123, 123): 3, (456, 654): 4}, 'name2': {(123, 321): 1, (890, 890): 2}, 'name3': {(123, 321): 1, (890, 890): 2}, } @pytest.fixture(scope="function") def updater(bot, base_persistence): base_persistence.store_data = PersistenceInput(False, False, False, False) u = UpdaterBuilder().bot(bot).persistence(base_persistence).build() base_persistence.store_data = PersistenceInput() return u @pytest.fixture(scope='function') def job_queue(bot): jq = JobQueue() yield jq jq.stop() def assert_data_in_cache(callback_data_cache: CallbackDataCache, data): for val in callback_data_cache._keyboard_data.values(): if data in val.button_data.values(): return data return False class TestBasePersistence: test_flag = False @pytest.fixture(scope='function', autouse=True) def reset(self): self.test_flag = False def test_slot_behaviour(self, bot_persistence, mro_slots): inst = bot_persistence for attr in inst.__slots__: assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" # The below test fails if the child class doesn't define __slots__ (not a cause of concern) assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" def test_creation(self, base_persistence): assert base_persistence.store_data.chat_data assert base_persistence.store_data.user_data assert base_persistence.store_data.bot_data assert base_persistence.store_data.callback_data def test_abstract_methods(self, base_persistence): with pytest.raises( TypeError, match=( 'drop_chat_data, drop_user_data, flush, get_bot_data, get_callback_data, ' 'get_chat_data, get_conversations, ' 'get_user_data, refresh_bot_data, refresh_chat_data, ' 'refresh_user_data, update_bot_data, update_callback_data, ' 'update_chat_data, update_conversation, update_user_data' ), ): BasePersistence() with pytest.raises(NotImplementedError): base_persistence.get_callback_data() with pytest.raises(NotImplementedError): base_persistence.update_callback_data((None, {'foo': 'bar'})) def test_implementation(self, updater, base_persistence): dp = updater.dispatcher assert dp.persistence == base_persistence def test_conversationhandler_addition(self, dp, base_persistence): with pytest.raises(ValueError, match="when handler is unnamed"): ConversationHandler([], [], [], persistent=True) with pytest.raises(ValueError, match="if dispatcher has no persistence"): dp.add_handler(ConversationHandler([], {}, [], persistent=True, name="My Handler")) dp.persistence = base_persistence def test_dispatcher_integration_init( self, bot, base_persistence, chat_data, user_data, bot_data, callback_data ): # Bad data testing- def bad_get_bot_data(): return "test" def bad_get_callback_data(): return "test" # Good data testing- def good_get_user_data(): return user_data def good_get_chat_data(): return chat_data def good_get_bot_data(): return bot_data def good_get_callback_data(): return callback_data base_persistence.get_user_data = good_get_user_data # No errors to be tested so base_persistence.get_chat_data = good_get_chat_data base_persistence.get_bot_data = bad_get_bot_data base_persistence.get_callback_data = bad_get_callback_data with pytest.raises(ValueError, match="bot_data must be of type dict"): UpdaterBuilder().bot(bot).persistence(base_persistence).build() base_persistence.get_bot_data = good_get_bot_data with pytest.raises(ValueError, match="callback_data must be a tuple of length 2"): UpdaterBuilder().bot(bot).persistence(base_persistence).build() base_persistence.bot = None base_persistence.get_callback_data = good_get_callback_data u = UpdaterBuilder().bot(bot).persistence(base_persistence).build() assert u.dispatcher.bot is base_persistence.bot assert u.dispatcher.bot_data == bot_data assert u.dispatcher.chat_data == chat_data assert u.dispatcher.user_data == user_data assert u.dispatcher.bot.callback_data_cache.persistence_data == callback_data u.dispatcher.chat_data[442233]['test5'] = 'test6' assert u.dispatcher.chat_data[442233]['test5'] == 'test6' @pytest.mark.parametrize('run_async', [True, False], ids=['run_async', 'synchronous']) def test_dispatcher_integration_handlers( self, dp, caplog, bot, base_persistence, chat_data, user_data, bot_data, callback_data, run_async, ): def get_user_data(): return user_data def get_chat_data(): return chat_data def get_bot_data(): return bot_data def get_callback_data(): return callback_data base_persistence.get_user_data = get_user_data base_persistence.get_chat_data = get_chat_data base_persistence.get_bot_data = get_bot_data base_persistence.get_callback_data = get_callback_data base_persistence.refresh_bot_data = lambda x: x base_persistence.refresh_chat_data = lambda x, y: x base_persistence.refresh_user_data = lambda x, y: x updater = UpdaterBuilder().bot(bot).persistence(base_persistence).build() dp = updater.dispatcher def callback_known_user(update, context): if not context.user_data['test1'] == 'test2': pytest.fail('user_data corrupt') if not context.bot_data == bot_data: pytest.fail('bot_data corrupt') def callback_known_chat(update, context): if not context.chat_data[3] == 'test4': pytest.fail('chat_data corrupt') if not context.bot_data == bot_data: pytest.fail('bot_data corrupt') def callback_unknown_user_or_chat(update, context): if not context.user_data == {}: pytest.fail('user_data corrupt') if not context.chat_data == {}: pytest.fail('chat_data corrupt') if not context.bot_data == bot_data: pytest.fail('bot_data corrupt') context.user_data[1] = 'test7' context.chat_data[2] = 'test8' context.bot_data['test0'] = 'test0' # Let's now delete user1 and chat1 context.dispatcher.drop_chat_data(-67890) context.dispatcher.drop_user_data(12345) # Test setting new keyboard callback data- context.bot.callback_data_cache._keyboard_data['id'] = _KeyboardData( 'id', button_data={'button3': 'test3'} ) known_user = MessageHandler(filters.User(user_id=12345), callback_known_user) # user1 known_chat = MessageHandler(filters.Chat(chat_id=-67890), callback_known_chat) # chat1 unknown = MessageHandler(filters.ALL, callback_unknown_user_or_chat) # user2 and chat2 dp.add_handler(known_user) dp.add_handler(known_chat) dp.add_handler(unknown) user1 = User(id=12345, first_name='test user', is_bot=False) user2 = User(id=54321, first_name='test user', is_bot=False) chat1 = Chat(id=-67890, type='group') chat2 = Chat(id=-987654, type='group') m = Message(1, None, chat2, from_user=user1) u_known_user = Update(0, m) dp.process_update(u_known_user) # 4 errors which arise since update_*_data are raising NotImplementedError here. assert len(caplog.records) == 4 m.from_user = user2 m.chat = chat1 u_known_chat = Update(1, m) dp.process_update(u_known_chat) m.chat = chat2 u_unknown_user_or_chat = Update(2, m) def save_bot_data(data): if 'test0' not in data: pytest.fail() def save_chat_data(_id, data): if 2 not in data: # data should be: {2: 'test8'} pytest.fail() def save_user_data(_id, data): if 1 not in data: # data should be: {1: 'test7'} pytest.fail() def save_callback_data(data): if not assert_data_in_cache(dp.bot.callback_data_cache, 'test3'): pytest.fail() # Functions to check deletion- def delete_user_data(user_id): if 12345 != user_id: pytest.fail("The id being deleted is not of user1's") user_data.pop(user_id, None) def delete_chat_data(chat_id): if -67890 != chat_id: pytest.fail("The chat id being deleted is not of chat1's") chat_data.pop(chat_id, None) base_persistence.update_chat_data = save_chat_data base_persistence.update_user_data = save_user_data base_persistence.update_bot_data = save_bot_data base_persistence.update_callback_data = save_callback_data base_persistence.drop_chat_data = delete_chat_data base_persistence.drop_user_data = delete_user_data dp.process_update(u_unknown_user_or_chat) # Test callback_unknown_user_or_chat worked correctly- assert dp.user_data[54321][1] == 'test7' assert dp.chat_data[-987654][2] == 'test8' assert dp.bot_data['test0'] == 'test0' assert assert_data_in_cache(dp.bot.callback_data_cache, 'test3') assert 12345 not in dp.user_data # Tests if dp.drop_user_data worked or not assert -67890 not in dp.chat_data assert len(caplog.records) == 8 # Errors double since new update is processed. for r in caplog.records: assert issubclass(r.exc_info[0], NotImplementedError) assert r.getMessage() == 'No error handlers are registered, logging exception.' assert r.levelname == 'ERROR' def test_dispatcher_integration_migrate_chat_data(self, dp, bot_persistence): dp.persistence = bot_persistence dp.chat_data[1]['key'] = 'value' dp.update_persistence() assert bot_persistence.chat_data == {1: {'key': 'value'}} dp.migrate_chat_data(old_chat_id=1, new_chat_id=2) assert bot_persistence.chat_data == {2: {'key': 'value'}} @pytest.mark.parametrize( 'store_user_data', [True, False], ids=['store_user_data-True', 'store_user_data-False'] ) @pytest.mark.parametrize( 'store_chat_data', [True, False], ids=['store_chat_data-True', 'store_chat_data-False'] ) @pytest.mark.parametrize( 'store_bot_data', [True, False], ids=['store_bot_data-True', 'store_bot_data-False'] ) @pytest.mark.parametrize('run_async', [True, False], ids=['run_async', 'synchronous']) def test_persistence_dispatcher_integration_refresh_data( self, dp, base_persistence, chat_data, bot_data, user_data, store_bot_data, store_chat_data, store_user_data, run_async, ): base_persistence.refresh_bot_data = lambda x: x.setdefault( 'refreshed', x.get('refreshed', 0) + 1 ) # x is the user/chat_id base_persistence.refresh_chat_data = lambda x, y: y.setdefault('refreshed', x) base_persistence.refresh_user_data = lambda x, y: y.setdefault('refreshed', x) base_persistence.store_data = PersistenceInput( bot_data=store_bot_data, chat_data=store_chat_data, user_data=store_user_data ) dp.persistence = base_persistence self.test_flag = True def callback_with_user_and_chat(update, context): if store_user_data: if context.user_data.get('refreshed') != update.effective_user.id: self.test_flag = 'user_data was not refreshed' else: if 'refreshed' in context.user_data: self.test_flag = 'user_data was wrongly refreshed' if store_chat_data: if context.chat_data.get('refreshed') != update.effective_chat.id: self.test_flag = 'chat_data was not refreshed' else: if 'refreshed' in context.chat_data: self.test_flag = 'chat_data was wrongly refreshed' if store_bot_data: if context.bot_data.get('refreshed') != 1: self.test_flag = 'bot_data was not refreshed' else: if 'refreshed' in context.bot_data: self.test_flag = 'bot_data was wrongly refreshed' def callback_without_user_and_chat(_, context): if store_bot_data: if context.bot_data.get('refreshed') != 1: self.test_flag = 'bot_data was not refreshed' else: if 'refreshed' in context.bot_data: self.test_flag = 'bot_data was wrongly refreshed' with_user_and_chat = MessageHandler( filters.User(user_id=12345), callback_with_user_and_chat, run_async=run_async, ) without_user_and_chat = MessageHandler( filters.ALL, callback_without_user_and_chat, run_async=run_async, ) dp.add_handler(with_user_and_chat) dp.add_handler(without_user_and_chat) user = User(id=12345, first_name='test user', is_bot=False) chat = Chat(id=-987654, type='group') m = Message(1, None, chat, from_user=user) # has user and chat u = Update(0, m) dp.process_update(u) assert self.test_flag is True # has neither user nor hat m.from_user = None m.chat = None u = Update(1, m) dp.process_update(u) assert self.test_flag is True sleep(0.1) def test_persistence_dispatcher_arbitrary_update_types(self, dp, base_persistence, caplog): # Updates used with TypeHandler doesn't necessarily have the proper attributes for # persistence, makes sure it works anyways dp.persistence = base_persistence class MyUpdate: pass dp.add_handler(TypeHandler(MyUpdate, lambda *_: None)) with caplog.at_level(logging.ERROR): dp.process_update(MyUpdate()) assert 'An uncaught error was raised while processing the update' not in caplog.text def test_set_bot_exception(self, bot): non_ext_bot = Bot(bot.token) persistence = OwnPersistence() with pytest.raises(TypeError, match='callback_data can only be stored'): persistence.set_bot(non_ext_bot) @pytest.fixture(scope='function') def pickle_persistence(): return PicklePersistence( filepath='pickletest', single_file=False, on_flush=False, ) @pytest.fixture(scope='function') def pickle_persistence_only_bot(): return PicklePersistence( filepath='pickletest', store_data=PersistenceInput(callback_data=False, user_data=False, chat_data=False), single_file=False, on_flush=False, ) @pytest.fixture(scope='function') def pickle_persistence_only_chat(): return PicklePersistence( filepath='pickletest', store_data=PersistenceInput(callback_data=False, user_data=False, bot_data=False), single_file=False, on_flush=False, ) @pytest.fixture(scope='function') def pickle_persistence_only_user(): return PicklePersistence( filepath='pickletest', store_data=PersistenceInput(callback_data=False, chat_data=False, bot_data=False), single_file=False, on_flush=False, ) @pytest.fixture(scope='function') def pickle_persistence_only_callback(): return PicklePersistence( filepath='pickletest', store_data=PersistenceInput(user_data=False, chat_data=False, bot_data=False), single_file=False, on_flush=False, ) @pytest.fixture(scope='function') def bad_pickle_files(): for name in [ 'pickletest_user_data', 'pickletest_chat_data', 'pickletest_bot_data', 'pickletest_callback_data', 'pickletest_conversations', 'pickletest', ]: Path(name).write_text('(())') yield True @pytest.fixture(scope='function') def invalid_pickle_files(): for name in [ 'pickletest_user_data', 'pickletest_chat_data', 'pickletest_bot_data', 'pickletest_callback_data', 'pickletest_conversations', 'pickletest', ]: # Just a random way to trigger pickle.UnpicklingError # see https://stackoverflow.com/a/44422239/10606962 with gzip.open(name, 'wb') as file: pickle.dump([1, 2, 3], file) yield True @pytest.fixture(scope='function') def good_pickle_files(user_data, chat_data, bot_data, callback_data, conversations): data = { 'user_data': user_data, 'chat_data': chat_data, 'bot_data': bot_data, 'callback_data': callback_data, 'conversations': conversations, } with Path('pickletest_user_data').open('wb') as f: pickle.dump(user_data, f) with Path('pickletest_chat_data').open('wb') as f: pickle.dump(chat_data, f) with Path('pickletest_bot_data').open('wb') as f: pickle.dump(bot_data, f) with Path('pickletest_callback_data').open('wb') as f: pickle.dump(callback_data, f) with Path('pickletest_conversations').open('wb') as f: pickle.dump(conversations, f) with Path('pickletest').open('wb') as f: pickle.dump(data, f) yield True @pytest.fixture(scope='function') def pickle_files_wo_bot_data(user_data, chat_data, callback_data, conversations): data = { 'user_data': user_data, 'chat_data': chat_data, 'conversations': conversations, 'callback_data': callback_data, } with Path('pickletest_user_data').open('wb') as f: pickle.dump(user_data, f) with Path('pickletest_chat_data').open('wb') as f: pickle.dump(chat_data, f) with Path('pickletest_callback_data').open('wb') as f: pickle.dump(callback_data, f) with Path('pickletest_conversations').open('wb') as f: pickle.dump(conversations, f) with Path('pickletest').open('wb') as f: pickle.dump(data, f) yield True @pytest.fixture(scope='function') def pickle_files_wo_callback_data(user_data, chat_data, bot_data, conversations): data = { 'user_data': user_data, 'chat_data': chat_data, 'bot_data': bot_data, 'conversations': conversations, } with Path('pickletest_user_data').open('wb') as f: pickle.dump(user_data, f) with Path('pickletest_chat_data').open('wb') as f: pickle.dump(chat_data, f) with Path('pickletest_bot_data').open('wb') as f: pickle.dump(bot_data, f) with Path('pickletest_conversations').open('wb') as f: pickle.dump(conversations, f) with Path('pickletest').open('wb') as f: pickle.dump(data, f) yield True @pytest.fixture(scope='function') def update(bot): user = User(id=321, first_name='test_user', is_bot=False) chat = Chat(id=123, type='group') message = Message(1, datetime.datetime.now(), chat, from_user=user, text="Hi there", bot=bot) return Update(0, message=message) class TestPicklePersistence: class DictSub(TelegramObject): # Used for testing our custom (Un)Pickler. def __init__(self, private, normal, b): self._private = private self.normal = normal self._bot = b class SlotsSub(TelegramObject): __slots__ = ('new_var', '_private') def __init__(self, new_var, private): self.new_var = new_var self._private = private class NormalClass: def __init__(self, my_var): self.my_var = my_var def test_slot_behaviour(self, mro_slots, pickle_persistence): inst = pickle_persistence for attr in inst.__slots__: assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" def test_pickle_behaviour_with_slots(self, pickle_persistence): bot_data = pickle_persistence.get_bot_data() bot_data['message'] = Message(3, datetime.datetime.now(), Chat(2, type='supergroup')) pickle_persistence.update_bot_data(bot_data) retrieved = pickle_persistence.get_bot_data() assert retrieved == bot_data def test_no_files_present_multi_file(self, pickle_persistence): assert pickle_persistence.get_user_data() == {} assert pickle_persistence.get_chat_data() == {} assert pickle_persistence.get_bot_data() == {} assert pickle_persistence.get_callback_data() is None assert pickle_persistence.get_conversations('noname') == {} def test_no_files_present_single_file(self, pickle_persistence): pickle_persistence.single_file = True assert pickle_persistence.get_user_data() == {} assert pickle_persistence.get_chat_data() == {} assert pickle_persistence.get_bot_data() == {} assert pickle_persistence.get_callback_data() is None assert pickle_persistence.get_conversations('noname') == {} def test_with_bad_multi_file(self, pickle_persistence, bad_pickle_files): with pytest.raises(TypeError, match='pickletest_user_data'): pickle_persistence.get_user_data() with pytest.raises(TypeError, match='pickletest_chat_data'): pickle_persistence.get_chat_data() with pytest.raises(TypeError, match='pickletest_bot_data'): pickle_persistence.get_bot_data() with pytest.raises(TypeError, match='pickletest_callback_data'): pickle_persistence.get_callback_data() with pytest.raises(TypeError, match='pickletest_conversations'): pickle_persistence.get_conversations('name') def test_with_invalid_multi_file(self, pickle_persistence, invalid_pickle_files): with pytest.raises(TypeError, match='pickletest_user_data does not contain'): pickle_persistence.get_user_data() with pytest.raises(TypeError, match='pickletest_chat_data does not contain'): pickle_persistence.get_chat_data() with pytest.raises(TypeError, match='pickletest_bot_data does not contain'): pickle_persistence.get_bot_data() with pytest.raises(TypeError, match='pickletest_callback_data does not contain'): pickle_persistence.get_callback_data() with pytest.raises(TypeError, match='pickletest_conversations does not contain'): pickle_persistence.get_conversations('name') def test_with_bad_single_file(self, pickle_persistence, bad_pickle_files): pickle_persistence.single_file = True with pytest.raises(TypeError, match='pickletest'): pickle_persistence.get_user_data() with pytest.raises(TypeError, match='pickletest'): pickle_persistence.get_chat_data() with pytest.raises(TypeError, match='pickletest'): pickle_persistence.get_bot_data() with pytest.raises(TypeError, match='pickletest'): pickle_persistence.get_callback_data() with pytest.raises(TypeError, match='pickletest'): pickle_persistence.get_conversations('name') def test_with_invalid_single_file(self, pickle_persistence, invalid_pickle_files): pickle_persistence.single_file = True with pytest.raises(TypeError, match='pickletest does not contain'): pickle_persistence.get_user_data() with pytest.raises(TypeError, match='pickletest does not contain'): pickle_persistence.get_chat_data() with pytest.raises(TypeError, match='pickletest does not contain'): pickle_persistence.get_bot_data() with pytest.raises(TypeError, match='pickletest does not contain'): pickle_persistence.get_callback_data() with pytest.raises(TypeError, match='pickletest does not contain'): pickle_persistence.get_conversations('name') def test_with_good_multi_file(self, pickle_persistence, good_pickle_files): user_data = pickle_persistence.get_user_data() assert isinstance(user_data, dict) assert user_data[12345]['test1'] == 'test2' assert user_data[67890][3] == 'test4' chat_data = pickle_persistence.get_chat_data() assert isinstance(chat_data, dict) assert chat_data[-12345]['test1'] == 'test2' assert chat_data[-67890][3] == 'test4' bot_data = pickle_persistence.get_bot_data() assert isinstance(bot_data, dict) assert bot_data['test1'] == 'test2' assert bot_data['test3']['test4'] == 'test5' assert 'test0' not in bot_data callback_data = pickle_persistence.get_callback_data() assert isinstance(callback_data, tuple) assert callback_data[0] == [('test1', 1000, {'button1': 'test0', 'button2': 'test1'})] assert callback_data[1] == {'test1': 'test2'} conversation1 = pickle_persistence.get_conversations('name1') assert isinstance(conversation1, dict) assert conversation1[(123, 123)] == 3 assert conversation1[(456, 654)] == 4 with pytest.raises(KeyError): conversation1[(890, 890)] conversation2 = pickle_persistence.get_conversations('name2') assert isinstance(conversation1, dict) assert conversation2[(123, 321)] == 1 assert conversation2[(890, 890)] == 2 with pytest.raises(KeyError): conversation2[(123, 123)] def test_with_good_single_file(self, pickle_persistence, good_pickle_files): pickle_persistence.single_file = True user_data = pickle_persistence.get_user_data() assert isinstance(user_data, dict) assert user_data[12345]['test1'] == 'test2' assert user_data[67890][3] == 'test4' chat_data = pickle_persistence.get_chat_data() assert isinstance(chat_data, dict) assert chat_data[-12345]['test1'] == 'test2' assert chat_data[-67890][3] == 'test4' bot_data = pickle_persistence.get_bot_data() assert isinstance(bot_data, dict) assert bot_data['test1'] == 'test2' assert bot_data['test3']['test4'] == 'test5' assert 'test0' not in bot_data callback_data = pickle_persistence.get_callback_data() assert isinstance(callback_data, tuple) assert callback_data[0] == [('test1', 1000, {'button1': 'test0', 'button2': 'test1'})] assert callback_data[1] == {'test1': 'test2'} conversation1 = pickle_persistence.get_conversations('name1') assert isinstance(conversation1, dict) assert conversation1[(123, 123)] == 3 assert conversation1[(456, 654)] == 4 with pytest.raises(KeyError): conversation1[(890, 890)] conversation2 = pickle_persistence.get_conversations('name2') assert isinstance(conversation1, dict) assert conversation2[(123, 321)] == 1 assert conversation2[(890, 890)] == 2 with pytest.raises(KeyError): conversation2[(123, 123)] def test_with_multi_file_wo_bot_data(self, pickle_persistence, pickle_files_wo_bot_data): user_data = pickle_persistence.get_user_data() assert isinstance(user_data, dict) assert user_data[12345]['test1'] == 'test2' assert user_data[67890][3] == 'test4' chat_data = pickle_persistence.get_chat_data() assert isinstance(chat_data, dict) assert chat_data[-12345]['test1'] == 'test2' assert chat_data[-67890][3] == 'test4' bot_data = pickle_persistence.get_bot_data() assert isinstance(bot_data, dict) assert not bot_data.keys() callback_data = pickle_persistence.get_callback_data() assert isinstance(callback_data, tuple) assert callback_data[0] == [('test1', 1000, {'button1': 'test0', 'button2': 'test1'})] assert callback_data[1] == {'test1': 'test2'} conversation1 = pickle_persistence.get_conversations('name1') assert isinstance(conversation1, dict) assert conversation1[(123, 123)] == 3 assert conversation1[(456, 654)] == 4 with pytest.raises(KeyError): conversation1[(890, 890)] conversation2 = pickle_persistence.get_conversations('name2') assert isinstance(conversation1, dict) assert conversation2[(123, 321)] == 1 assert conversation2[(890, 890)] == 2 with pytest.raises(KeyError): conversation2[(123, 123)] def test_with_multi_file_wo_callback_data( self, pickle_persistence, pickle_files_wo_callback_data ): user_data = pickle_persistence.get_user_data() assert isinstance(user_data, dict) assert user_data[12345]['test1'] == 'test2' assert user_data[67890][3] == 'test4' chat_data = pickle_persistence.get_chat_data() assert isinstance(chat_data, dict) assert chat_data[-12345]['test1'] == 'test2' assert chat_data[-67890][3] == 'test4' bot_data = pickle_persistence.get_bot_data() assert isinstance(bot_data, dict) assert bot_data['test1'] == 'test2' assert bot_data['test3']['test4'] == 'test5' assert 'test0' not in bot_data callback_data = pickle_persistence.get_callback_data() assert callback_data is None conversation1 = pickle_persistence.get_conversations('name1') assert isinstance(conversation1, dict) assert conversation1[(123, 123)] == 3 assert conversation1[(456, 654)] == 4 with pytest.raises(KeyError): conversation1[(890, 890)] conversation2 = pickle_persistence.get_conversations('name2') assert isinstance(conversation1, dict) assert conversation2[(123, 321)] == 1 assert conversation2[(890, 890)] == 2 with pytest.raises(KeyError): conversation2[(123, 123)] def test_with_single_file_wo_bot_data(self, pickle_persistence, pickle_files_wo_bot_data): pickle_persistence.single_file = True user_data = pickle_persistence.get_user_data() assert isinstance(user_data, dict) assert user_data[12345]['test1'] == 'test2' assert user_data[67890][3] == 'test4' chat_data = pickle_persistence.get_chat_data() assert isinstance(chat_data, dict) assert chat_data[-12345]['test1'] == 'test2' assert chat_data[-67890][3] == 'test4' bot_data = pickle_persistence.get_bot_data() assert isinstance(bot_data, dict) assert not bot_data.keys() callback_data = pickle_persistence.get_callback_data() assert isinstance(callback_data, tuple) assert callback_data[0] == [('test1', 1000, {'button1': 'test0', 'button2': 'test1'})] assert callback_data[1] == {'test1': 'test2'} conversation1 = pickle_persistence.get_conversations('name1') assert isinstance(conversation1, dict) assert conversation1[(123, 123)] == 3 assert conversation1[(456, 654)] == 4 with pytest.raises(KeyError): conversation1[(890, 890)] conversation2 = pickle_persistence.get_conversations('name2') assert isinstance(conversation1, dict) assert conversation2[(123, 321)] == 1 assert conversation2[(890, 890)] == 2 with pytest.raises(KeyError): conversation2[(123, 123)] def test_with_single_file_wo_callback_data( self, pickle_persistence, pickle_files_wo_callback_data ): user_data = pickle_persistence.get_user_data() assert isinstance(user_data, dict) assert user_data[12345]['test1'] == 'test2' assert user_data[67890][3] == 'test4' chat_data = pickle_persistence.get_chat_data() assert isinstance(chat_data, dict) assert chat_data[-12345]['test1'] == 'test2' assert chat_data[-67890][3] == 'test4' bot_data = pickle_persistence.get_bot_data() assert isinstance(bot_data, dict) assert bot_data['test1'] == 'test2' assert bot_data['test3']['test4'] == 'test5' assert 'test0' not in bot_data callback_data = pickle_persistence.get_callback_data() assert callback_data is None conversation1 = pickle_persistence.get_conversations('name1') assert isinstance(conversation1, dict) assert conversation1[(123, 123)] == 3 assert conversation1[(456, 654)] == 4 with pytest.raises(KeyError): conversation1[(890, 890)] conversation2 = pickle_persistence.get_conversations('name2') assert isinstance(conversation1, dict) assert conversation2[(123, 321)] == 1 assert conversation2[(890, 890)] == 2 with pytest.raises(KeyError): conversation2[(123, 123)] def test_updating_multi_file(self, pickle_persistence, good_pickle_files): user_data = pickle_persistence.get_user_data() user_data[12345]['test3']['test4'] = 'test6' assert not pickle_persistence.user_data == user_data pickle_persistence.update_user_data(12345, user_data[12345]) user_data[12345]['test3']['test4'] = 'test7' assert not pickle_persistence.user_data == user_data pickle_persistence.update_user_data(12345, user_data[12345]) assert pickle_persistence.user_data == user_data with Path('pickletest_user_data').open('rb') as f: user_data_test = dict(pickle.load(f)) assert user_data_test == user_data pickle_persistence.drop_user_data(67890) assert 67890 not in pickle_persistence.get_user_data() chat_data = pickle_persistence.get_chat_data() chat_data[-12345]['test3']['test4'] = 'test6' assert not pickle_persistence.chat_data == chat_data pickle_persistence.update_chat_data(-12345, chat_data[-12345]) chat_data[-12345]['test3']['test4'] = 'test7' assert not pickle_persistence.chat_data == chat_data pickle_persistence.update_chat_data(-12345, chat_data[-12345]) assert pickle_persistence.chat_data == chat_data with Path('pickletest_chat_data').open('rb') as f: chat_data_test = dict(pickle.load(f)) assert chat_data_test == chat_data pickle_persistence.drop_chat_data(-67890) assert -67890 not in pickle_persistence.get_chat_data() bot_data = pickle_persistence.get_bot_data() bot_data['test3']['test4'] = 'test6' assert not pickle_persistence.bot_data == bot_data pickle_persistence.update_bot_data(bot_data) bot_data['test3']['test4'] = 'test7' assert not pickle_persistence.bot_data == bot_data pickle_persistence.update_bot_data(bot_data) assert pickle_persistence.bot_data == bot_data with Path('pickletest_bot_data').open('rb') as f: bot_data_test = pickle.load(f) assert bot_data_test == bot_data callback_data = pickle_persistence.get_callback_data() callback_data[1]['test3'] = 'test4' assert not pickle_persistence.callback_data == callback_data pickle_persistence.update_callback_data(callback_data) callback_data[1]['test3'] = 'test5' assert not pickle_persistence.callback_data == callback_data pickle_persistence.update_callback_data(callback_data) assert pickle_persistence.callback_data == callback_data with Path('pickletest_callback_data').open('rb') as f: callback_data_test = pickle.load(f) assert callback_data_test == callback_data conversation1 = pickle_persistence.get_conversations('name1') conversation1[(123, 123)] = 5 assert not pickle_persistence.conversations['name1'] == conversation1 pickle_persistence.update_conversation('name1', (123, 123), 5) assert pickle_persistence.conversations['name1'] == conversation1 assert pickle_persistence.get_conversations('name1') == conversation1 with Path('pickletest_conversations').open('rb') as f: conversations_test = dict(pickle.load(f)) assert conversations_test['name1'] == conversation1 pickle_persistence.conversations = None pickle_persistence.update_conversation('name1', (123, 123), 5) assert pickle_persistence.conversations['name1'] == {(123, 123): 5} assert pickle_persistence.get_conversations('name1') == {(123, 123): 5} def test_updating_single_file(self, pickle_persistence, good_pickle_files): pickle_persistence.single_file = True user_data = pickle_persistence.get_user_data() user_data[12345]['test3']['test4'] = 'test6' assert not pickle_persistence.user_data == user_data pickle_persistence.update_user_data(12345, user_data[12345]) user_data[12345]['test3']['test4'] = 'test7' assert not pickle_persistence.user_data == user_data pickle_persistence.update_user_data(12345, user_data[12345]) assert pickle_persistence.user_data == user_data with Path('pickletest').open('rb') as f: user_data_test = dict(pickle.load(f))['user_data'] assert user_data_test == user_data pickle_persistence.drop_user_data(67890) assert 67890 not in pickle_persistence.get_user_data() chat_data = pickle_persistence.get_chat_data() chat_data[-12345]['test3']['test4'] = 'test6' assert not pickle_persistence.chat_data == chat_data pickle_persistence.update_chat_data(-12345, chat_data[-12345]) chat_data[-12345]['test3']['test4'] = 'test7' assert not pickle_persistence.chat_data == chat_data pickle_persistence.update_chat_data(-12345, chat_data[-12345]) assert pickle_persistence.chat_data == chat_data with Path('pickletest').open('rb') as f: chat_data_test = dict(pickle.load(f))['chat_data'] assert chat_data_test == chat_data pickle_persistence.drop_chat_data(-67890) assert -67890 not in pickle_persistence.get_chat_data() bot_data = pickle_persistence.get_bot_data() bot_data['test3']['test4'] = 'test6' assert not pickle_persistence.bot_data == bot_data pickle_persistence.update_bot_data(bot_data) bot_data['test3']['test4'] = 'test7' assert not pickle_persistence.bot_data == bot_data pickle_persistence.update_bot_data(bot_data) assert pickle_persistence.bot_data == bot_data with Path('pickletest').open('rb') as f: bot_data_test = pickle.load(f)['bot_data'] assert bot_data_test == bot_data callback_data = pickle_persistence.get_callback_data() callback_data[1]['test3'] = 'test4' assert not pickle_persistence.callback_data == callback_data pickle_persistence.update_callback_data(callback_data) callback_data[1]['test3'] = 'test5' assert not pickle_persistence.callback_data == callback_data pickle_persistence.update_callback_data(callback_data) assert pickle_persistence.callback_data == callback_data with Path('pickletest').open('rb') as f: callback_data_test = pickle.load(f)['callback_data'] assert callback_data_test == callback_data conversation1 = pickle_persistence.get_conversations('name1') conversation1[(123, 123)] = 5 assert not pickle_persistence.conversations['name1'] == conversation1 pickle_persistence.update_conversation('name1', (123, 123), 5) assert pickle_persistence.conversations['name1'] == conversation1 assert pickle_persistence.get_conversations('name1') == conversation1 with Path('pickletest').open('rb') as f: conversations_test = dict(pickle.load(f))['conversations'] assert conversations_test['name1'] == conversation1 pickle_persistence.conversations = None pickle_persistence.update_conversation('name1', (123, 123), 5) assert pickle_persistence.conversations['name1'] == {(123, 123): 5} assert pickle_persistence.get_conversations('name1') == {(123, 123): 5} def test_updating_single_file_no_data(self, pickle_persistence): pickle_persistence.single_file = True assert not any( [ pickle_persistence.user_data, pickle_persistence.chat_data, pickle_persistence.bot_data, pickle_persistence.callback_data, pickle_persistence.conversations, ] ) pickle_persistence.flush() with pytest.raises(FileNotFoundError, match='pickletest'): open('pickletest', 'rb') def test_save_on_flush_multi_files(self, pickle_persistence, good_pickle_files): # Should run without error pickle_persistence.flush() pickle_persistence.on_flush = True user_data = pickle_persistence.get_user_data() user_data[54321] = {} user_data[54321]['test9'] = 'test 10' assert not pickle_persistence.user_data == user_data pickle_persistence.update_user_data(54321, user_data[54321]) assert pickle_persistence.user_data == user_data pickle_persistence.drop_user_data(0) assert pickle_persistence.user_data == user_data with Path('pickletest_user_data').open('rb') as f: user_data_test = dict(pickle.load(f)) assert not user_data_test == user_data chat_data = pickle_persistence.get_chat_data() chat_data[54321] = {} chat_data[54321]['test9'] = 'test 10' assert not pickle_persistence.chat_data == chat_data pickle_persistence.update_chat_data(54321, chat_data[54321]) assert pickle_persistence.chat_data == chat_data pickle_persistence.drop_chat_data(0) assert pickle_persistence.user_data == user_data with Path('pickletest_chat_data').open('rb') as f: chat_data_test = dict(pickle.load(f)) assert not chat_data_test == chat_data bot_data = pickle_persistence.get_bot_data() bot_data['test6'] = 'test 7' assert not pickle_persistence.bot_data == bot_data pickle_persistence.update_bot_data(bot_data) assert pickle_persistence.bot_data == bot_data with Path('pickletest_bot_data').open('rb') as f: bot_data_test = pickle.load(f) assert not bot_data_test == bot_data callback_data = pickle_persistence.get_callback_data() callback_data[1]['test3'] = 'test4' assert not pickle_persistence.callback_data == callback_data pickle_persistence.update_callback_data(callback_data) assert pickle_persistence.callback_data == callback_data with Path('pickletest_callback_data').open('rb') as f: callback_data_test = pickle.load(f) assert not callback_data_test == callback_data conversation1 = pickle_persistence.get_conversations('name1') conversation1[(123, 123)] = 5 assert not pickle_persistence.conversations['name1'] == conversation1 pickle_persistence.update_conversation('name1', (123, 123), 5) assert pickle_persistence.conversations['name1'] == conversation1 with Path('pickletest_conversations').open('rb') as f: conversations_test = dict(pickle.load(f)) assert not conversations_test['name1'] == conversation1 pickle_persistence.flush() with Path('pickletest_user_data').open('rb') as f: user_data_test = dict(pickle.load(f)) assert user_data_test == user_data with Path('pickletest_chat_data').open('rb') as f: chat_data_test = dict(pickle.load(f)) assert chat_data_test == chat_data with Path('pickletest_bot_data').open('rb') as f: bot_data_test = pickle.load(f) assert bot_data_test == bot_data with Path('pickletest_conversations').open('rb') as f: conversations_test = dict(pickle.load(f)) assert conversations_test['name1'] == conversation1 def test_save_on_flush_single_files(self, pickle_persistence, good_pickle_files): # Should run without error pickle_persistence.flush() pickle_persistence.on_flush = True pickle_persistence.single_file = True user_data = pickle_persistence.get_user_data() user_data[54321] = {} user_data[54321]['test9'] = 'test 10' assert not pickle_persistence.user_data == user_data pickle_persistence.update_user_data(54321, user_data[54321]) assert pickle_persistence.user_data == user_data with Path('pickletest').open('rb') as f: user_data_test = dict(pickle.load(f))['user_data'] assert not user_data_test == user_data chat_data = pickle_persistence.get_chat_data() chat_data[54321] = {} chat_data[54321]['test9'] = 'test 10' assert not pickle_persistence.chat_data == chat_data pickle_persistence.update_chat_data(54321, chat_data[54321]) assert pickle_persistence.chat_data == chat_data with Path('pickletest').open('rb') as f: chat_data_test = dict(pickle.load(f))['chat_data'] assert not chat_data_test == chat_data bot_data = pickle_persistence.get_bot_data() bot_data['test6'] = 'test 7' assert not pickle_persistence.bot_data == bot_data pickle_persistence.update_bot_data(bot_data) assert pickle_persistence.bot_data == bot_data with Path('pickletest').open('rb') as f: bot_data_test = pickle.load(f)['bot_data'] assert not bot_data_test == bot_data callback_data = pickle_persistence.get_callback_data() callback_data[1]['test3'] = 'test4' assert not pickle_persistence.callback_data == callback_data pickle_persistence.update_callback_data(callback_data) assert pickle_persistence.callback_data == callback_data with Path('pickletest').open('rb') as f: callback_data_test = pickle.load(f)['callback_data'] assert not callback_data_test == callback_data conversation1 = pickle_persistence.get_conversations('name1') conversation1[(123, 123)] = 5 assert not pickle_persistence.conversations['name1'] == conversation1 pickle_persistence.update_conversation('name1', (123, 123), 5) assert pickle_persistence.conversations['name1'] == conversation1 with Path('pickletest').open('rb') as f: conversations_test = dict(pickle.load(f))['conversations'] assert not conversations_test['name1'] == conversation1 pickle_persistence.flush() with Path('pickletest').open('rb') as f: user_data_test = dict(pickle.load(f))['user_data'] assert user_data_test == user_data with Path('pickletest').open('rb') as f: chat_data_test = dict(pickle.load(f))['chat_data'] assert chat_data_test == chat_data with Path('pickletest').open('rb') as f: bot_data_test = pickle.load(f)['bot_data'] assert bot_data_test == bot_data with Path('pickletest').open('rb') as f: conversations_test = dict(pickle.load(f))['conversations'] assert conversations_test['name1'] == conversation1 def test_custom_pickler_unpickler_simple( self, pickle_persistence, update, good_pickle_files, bot, recwarn ): pickle_persistence.bot = bot # assign the current bot to the persistence data_with_bot = {'current_bot': update.message} pickle_persistence.update_chat_data(12345, data_with_bot) # also calls BotPickler.dumps() # Test that regular pickle load fails - err_msg = ( "A load persistent id instruction was encountered,\nbut no persistent_load " "function was specified." ) with pytest.raises(pickle.UnpicklingError, match=err_msg): with open('pickletest_chat_data', 'rb') as f: pickle.load(f) # Test that our custom unpickler works as intended -- inserts the current bot # We have to create a new instance otherwise unpickling is skipped pp = PicklePersistence("pickletest", single_file=False, on_flush=False) pp.bot = bot # Set the bot assert pp.get_chat_data()[12345]['current_bot'].get_bot() is bot # Now test that pickling of unknown bots in TelegramObjects will be replaced by None- assert not len(recwarn) data_with_bot['unknown_bot_in_user'] = User(1, 'Dev', False, bot=Bot('1234:abcd')) pickle_persistence.update_chat_data(12345, data_with_bot) assert len(recwarn) == 1 assert recwarn[-1].category is PTBUserWarning assert str(recwarn[-1].message).startswith("Unknown bot instance found.") pp = PicklePersistence("pickletest", single_file=False, on_flush=False) pp.bot = bot assert pp.get_chat_data()[12345]['unknown_bot_in_user']._bot is None def test_custom_pickler_unpickler_with_custom_objects( self, bot, pickle_persistence, good_pickle_files ): dict_s = self.DictSub("private", 'normal', bot) slot_s = self.SlotsSub("new_var", 'private_var') regular = self.NormalClass(12) pickle_persistence.bot = bot pickle_persistence.update_user_data( 1232, {'sub_dict': dict_s, 'sub_slots': slot_s, 'r': regular} ) pp = PicklePersistence("pickletest", single_file=False, on_flush=False) pp.bot = bot # Set the bot data = pp.get_user_data()[1232] sub_dict = data['sub_dict'] sub_slots = data['sub_slots'] sub_regular = data['r'] assert sub_dict._bot is bot assert sub_dict.normal == dict_s.normal assert sub_dict._private == dict_s._private assert sub_slots.new_var == slot_s.new_var assert sub_slots._private == slot_s._private assert sub_slots._bot is None # We didn't set the bot, so it shouldn't have it here. assert sub_regular.my_var == regular.my_var def test_custom_pickler_unpickler_with_handler_integration( self, bot, update, pickle_persistence, good_pickle_files, recwarn ): u = UpdaterBuilder().bot(bot).persistence(pickle_persistence).build() dp = u.dispatcher bot_id = None def first(update, context): nonlocal bot_id bot_id = update.message.get_bot() # Test pickling a message object, which has the current bot context.user_data['msg'] = update.message # Test pickling a bot, which is not known. Directly serializing bots will fail. new_chat = Chat(1, 'private', bot=Bot('1234:abcd')) context.chat_data['unknown_bot_in_chat'] = new_chat def second(_, context): msg = context.user_data['msg'] assert bot_id is msg.get_bot() # Tests if the same bot is inserted by the unpickler new_none_bot = context.chat_data['unknown_bot_in_chat']._bot assert new_none_bot is None h1 = MessageHandler(None, first) h2 = MessageHandler(None, second) dp.add_handler(h1) assert not len(recwarn) dp.process_update(update) assert len(recwarn) == 1 assert recwarn[-1].category is PTBUserWarning assert str(recwarn[-1].message).startswith("Unknown bot instance found.") pickle_persistence_2 = PicklePersistence( # initialize a new persistence for unpickling filepath='pickletest', single_file=False, on_flush=False, ) u = UpdaterBuilder().bot(bot).persistence(pickle_persistence_2).build() dp = u.dispatcher dp.add_handler(h2) dp.process_update(update) def test_with_handler(self, bot, update, bot_data, pickle_persistence, good_pickle_files): u = UpdaterBuilder().bot(bot).persistence(pickle_persistence).build() dp = u.dispatcher bot.callback_data_cache.clear_callback_data() bot.callback_data_cache.clear_callback_queries() def first(_, context): if not context.user_data == {}: pytest.fail() if not context.chat_data == {}: pytest.fail() if not context.bot_data == bot_data: pytest.fail() if not context.bot.callback_data_cache.persistence_data == ([], {}): pytest.fail() context.user_data['test1'] = 'test2' context.chat_data['test3'] = 'test4' context.bot_data['test1'] = 'test0' context.bot.callback_data_cache._callback_queries['test1'] = 'test0' def second(_, context): if not context.user_data['test1'] == 'test2': pytest.fail() if not context.chat_data['test3'] == 'test4': pytest.fail() if not context.bot_data['test1'] == 'test0': pytest.fail() if not context.bot.callback_data_cache.persistence_data == ([], {'test1': 'test0'}): pytest.fail() h1 = MessageHandler(None, first) h2 = MessageHandler(None, second) dp.add_handler(h1) dp.process_update(update) pickle_persistence_2 = PicklePersistence( filepath='pickletest', single_file=False, on_flush=False, ) u = UpdaterBuilder().bot(bot).persistence(pickle_persistence_2).build() dp = u.dispatcher dp.add_handler(h2) dp.process_update(update) def test_flush_on_stop(self, bot, update, pickle_persistence): u = UpdaterBuilder().bot(bot).persistence(pickle_persistence).build() dp = u.dispatcher u.running = True dp.user_data[4242424242]['my_test'] = 'Working!' dp.chat_data[-4242424242]['my_test2'] = 'Working2!' dp.bot_data['test'] = 'Working3!' dp.bot.callback_data_cache._callback_queries['test'] = 'Working4!' u._signal_handler(signal.SIGINT, None) pickle_persistence_2 = PicklePersistence( filepath='pickletest', single_file=False, on_flush=False, ) assert pickle_persistence_2.get_user_data()[4242424242]['my_test'] == 'Working!' assert pickle_persistence_2.get_chat_data()[-4242424242]['my_test2'] == 'Working2!' assert pickle_persistence_2.get_bot_data()['test'] == 'Working3!' data = pickle_persistence_2.get_callback_data()[1] assert data['test'] == 'Working4!' def test_flush_on_stop_only_bot(self, bot, update, pickle_persistence_only_bot): u = UpdaterBuilder().bot(bot).persistence(pickle_persistence_only_bot).build() dp = u.dispatcher u.running = True dp.user_data[4242424242]['my_test'] = 'Working!' dp.chat_data[-4242424242]['my_test2'] = 'Working2!' dp.bot_data['my_test3'] = 'Working3!' dp.bot.callback_data_cache._callback_queries['test'] = 'Working4!' u._signal_handler(signal.SIGINT, None) pickle_persistence_2 = PicklePersistence( filepath='pickletest', store_data=PersistenceInput(callback_data=False, chat_data=False, user_data=False), single_file=False, on_flush=False, ) assert pickle_persistence_2.get_user_data() == {} assert pickle_persistence_2.get_chat_data() == {} assert pickle_persistence_2.get_bot_data()['my_test3'] == 'Working3!' assert pickle_persistence_2.get_callback_data() is None def test_flush_on_stop_only_chat(self, bot, update, pickle_persistence_only_chat): u = UpdaterBuilder().bot(bot).persistence(pickle_persistence_only_chat).build() dp = u.dispatcher u.running = True dp.user_data[4242424242]['my_test'] = 'Working!' dp.chat_data[-4242424242]['my_test2'] = 'Working2!' dp.bot_data['my_test3'] = 'Working3!' dp.bot.callback_data_cache._callback_queries['test'] = 'Working4!' u._signal_handler(signal.SIGINT, None) pickle_persistence_2 = PicklePersistence( filepath='pickletest', store_data=PersistenceInput(callback_data=False, user_data=False, bot_data=False), single_file=False, on_flush=False, ) assert pickle_persistence_2.get_user_data() == {} assert pickle_persistence_2.get_chat_data()[-4242424242]['my_test2'] == 'Working2!' assert pickle_persistence_2.get_bot_data() == {} assert pickle_persistence_2.get_callback_data() is None def test_flush_on_stop_only_user(self, bot, update, pickle_persistence_only_user): u = UpdaterBuilder().bot(bot).persistence(pickle_persistence_only_user).build() dp = u.dispatcher u.running = True dp.user_data[4242424242]['my_test'] = 'Working!' dp.chat_data[-4242424242]['my_test2'] = 'Working2!' dp.bot_data['my_test3'] = 'Working3!' dp.bot.callback_data_cache._callback_queries['test'] = 'Working4!' u._signal_handler(signal.SIGINT, None) pickle_persistence_2 = PicklePersistence( filepath='pickletest', store_data=PersistenceInput(callback_data=False, chat_data=False, bot_data=False), single_file=False, on_flush=False, ) assert pickle_persistence_2.get_user_data()[4242424242]['my_test'] == 'Working!' assert pickle_persistence_2.get_chat_data() == {} assert pickle_persistence_2.get_bot_data() == {} assert pickle_persistence_2.get_callback_data() is None def test_flush_on_stop_only_callback(self, bot, update, pickle_persistence_only_callback): u = UpdaterBuilder().bot(bot).persistence(pickle_persistence_only_callback).build() dp = u.dispatcher u.running = True dp.user_data[4242424242]['my_test'] = 'Working!' dp.chat_data[-4242424242]['my_test2'] = 'Working2!' dp.bot_data['my_test3'] = 'Working3!' dp.bot.callback_data_cache._callback_queries['test'] = 'Working4!' u._signal_handler(signal.SIGINT, None) del dp del u del pickle_persistence_only_callback pickle_persistence_2 = PicklePersistence( filepath='pickletest', store_data=PersistenceInput(user_data=False, chat_data=False, bot_data=False), single_file=False, on_flush=False, ) assert pickle_persistence_2.get_user_data() == {} assert pickle_persistence_2.get_chat_data() == {} assert pickle_persistence_2.get_bot_data() == {} data = pickle_persistence_2.get_callback_data()[1] assert data['test'] == 'Working4!' def test_with_conversation_handler(self, dp, update, good_pickle_files, pickle_persistence): dp.persistence = pickle_persistence NEXT, NEXT2 = range(2) def start(update, context): return NEXT start = CommandHandler('start', start) def next_callback(update, context): return NEXT2 next_handler = MessageHandler(None, next_callback) def next2(update, context): return ConversationHandler.END next2 = MessageHandler(None, next2) ch = ConversationHandler( [start], {NEXT: [next_handler], NEXT2: [next2]}, [], name='name2', persistent=True ) dp.add_handler(ch) assert ch.conversations[ch._get_key(update)] == 1 dp.process_update(update) assert ch._get_key(update) not in ch.conversations update.message.text = '/start' update.message.entities = [MessageEntity(MessageEntity.BOT_COMMAND, 0, 6)] dp.process_update(update) assert ch.conversations[ch._get_key(update)] == 0 assert ch.conversations == pickle_persistence.conversations['name2'] def test_with_nested_conversation_handler( self, dp, update, good_pickle_files, pickle_persistence ): dp.persistence = pickle_persistence NEXT2, NEXT3 = range(1, 3) def start(update, context): return NEXT2 start = CommandHandler('start', start) def next_callback(update, context): return NEXT2 next_handler = MessageHandler(None, next_callback) def next2(update, context): return ConversationHandler.END next2 = MessageHandler(None, next2) nested_ch = ConversationHandler( [next_handler], {NEXT2: [next2]}, [], name='name3', persistent=True, map_to_parent={ConversationHandler.END: ConversationHandler.END}, ) ch = ConversationHandler( [start], {NEXT2: [nested_ch], NEXT3: []}, [], name='name2', persistent=True ) dp.add_handler(ch) assert ch.conversations[ch._get_key(update)] == 1 assert nested_ch.conversations[nested_ch._get_key(update)] == 1 dp.process_update(update) assert ch._get_key(update) not in ch.conversations assert nested_ch._get_key(update) not in nested_ch.conversations update.message.text = '/start' update.message.entities = [MessageEntity(MessageEntity.BOT_COMMAND, 0, 6)] dp.process_update(update) assert ch.conversations[ch._get_key(update)] == 1 assert ch.conversations == pickle_persistence.conversations['name2'] assert nested_ch._get_key(update) not in nested_ch.conversations dp.process_update(update) assert ch.conversations[ch._get_key(update)] == 1 assert ch.conversations == pickle_persistence.conversations['name2'] assert nested_ch.conversations[nested_ch._get_key(update)] == 1 assert nested_ch.conversations == pickle_persistence.conversations['name3'] @pytest.mark.parametrize( 'filepath', ['pickletest', Path('pickletest')], ids=['str filepath', 'pathlib.Path filepath'], ) def test_filepath_argument_types(self, filepath): pick_persist = PicklePersistence( filepath=filepath, on_flush=False, ) pick_persist.update_user_data(1, 1) assert pick_persist.get_user_data()[1] == 1 assert Path(filepath).is_file() def test_with_job(self, job_queue, dp, pickle_persistence): dp.bot.arbitrary_callback_data = True def job_callback(context): context.bot_data['test1'] = '456' context.dispatcher.chat_data[123]['test2'] = '789' context.dispatcher.user_data[789]['test3'] = '123' context.bot.callback_data_cache._callback_queries['test'] = 'Working4!' dp.persistence = pickle_persistence job_queue.set_dispatcher(dp) job_queue.start() job_queue.run_once(job_callback, 0.01) sleep(0.5) bot_data = pickle_persistence.get_bot_data() assert bot_data == {'test1': '456'} chat_data = pickle_persistence.get_chat_data() assert chat_data[123] == {'test2': '789'} user_data = pickle_persistence.get_user_data() assert user_data[789] == {'test3': '123'} data = pickle_persistence.get_callback_data()[1] assert data['test'] == 'Working4!' @pytest.mark.parametrize('singlefile', [True, False]) @pytest.mark.parametrize('ud', [int, float, complex]) @pytest.mark.parametrize('cd', [int, float, complex]) @pytest.mark.parametrize('bd', [int, float, complex]) def test_with_context_types(self, ud, cd, bd, singlefile): cc = ContextTypes(user_data=ud, chat_data=cd, bot_data=bd) persistence = PicklePersistence('pickletest', single_file=singlefile, context_types=cc) assert isinstance(persistence.get_bot_data(), bd) assert persistence.get_bot_data() == 0 persistence.user_data = None persistence.chat_data = None persistence.drop_user_data(123) persistence.drop_chat_data(123) assert isinstance(persistence.get_user_data(), dict) assert isinstance(persistence.get_chat_data(), dict) persistence.user_data = None persistence.chat_data = None persistence.update_user_data(1, ud(1)) persistence.update_chat_data(1, cd(1)) persistence.update_bot_data(bd(1)) assert persistence.get_user_data()[1] == 1 assert persistence.get_chat_data()[1] == 1 assert persistence.get_bot_data() == 1 persistence.flush() persistence = PicklePersistence('pickletest', single_file=singlefile, context_types=cc) assert isinstance(persistence.get_user_data()[1], ud) assert persistence.get_user_data()[1] == 1 assert isinstance(persistence.get_chat_data()[1], cd) assert persistence.get_chat_data()[1] == 1 assert isinstance(persistence.get_bot_data(), bd) assert persistence.get_bot_data() == 1 @pytest.fixture(scope='function') def user_data_json(user_data): return json.dumps(user_data) @pytest.fixture(scope='function') def chat_data_json(chat_data): return json.dumps(chat_data) @pytest.fixture(scope='function') def bot_data_json(bot_data): return json.dumps(bot_data) @pytest.fixture(scope='function') def callback_data_json(callback_data): return json.dumps(callback_data) @pytest.fixture(scope='function') def conversations_json(conversations): return """{"name1": {"[123, 123]": 3, "[456, 654]": 4}, "name2": {"[123, 321]": 1, "[890, 890]": 2}, "name3": {"[123, 321]": 1, "[890, 890]": 2}}""" class TestDictPersistence: def test_slot_behaviour(self, mro_slots, recwarn): inst = DictPersistence() for attr in inst.__slots__: assert getattr(inst, attr, 'err') != 'err', f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" def test_no_json_given(self): dict_persistence = DictPersistence() assert dict_persistence.get_user_data() == {} assert dict_persistence.get_chat_data() == {} assert dict_persistence.get_bot_data() == {} assert dict_persistence.get_callback_data() is None assert dict_persistence.get_conversations('noname') == {} def test_bad_json_string_given(self): bad_user_data = 'thisisnojson99900()))(' bad_chat_data = 'thisisnojson99900()))(' bad_bot_data = 'thisisnojson99900()))(' bad_callback_data = 'thisisnojson99900()))(' bad_conversations = 'thisisnojson99900()))(' with pytest.raises(TypeError, match='user_data'): DictPersistence(user_data_json=bad_user_data) with pytest.raises(TypeError, match='chat_data'): DictPersistence(chat_data_json=bad_chat_data) with pytest.raises(TypeError, match='bot_data'): DictPersistence(bot_data_json=bad_bot_data) with pytest.raises(TypeError, match='callback_data'): DictPersistence(callback_data_json=bad_callback_data) with pytest.raises(TypeError, match='conversations'): DictPersistence(conversations_json=bad_conversations) def test_invalid_json_string_given(self, pickle_persistence, bad_pickle_files): bad_user_data = '["this", "is", "json"]' bad_chat_data = '["this", "is", "json"]' bad_bot_data = '["this", "is", "json"]' bad_conversations = '["this", "is", "json"]' bad_callback_data_1 = '[[["str", 3.14, {"di": "ct"}]], "is"]' bad_callback_data_2 = '[[["str", "non-float", {"di": "ct"}]], {"di": "ct"}]' bad_callback_data_3 = '[[[{"not": "a str"}, 3.14, {"di": "ct"}]], {"di": "ct"}]' bad_callback_data_4 = '[[["wrong", "length"]], {"di": "ct"}]' bad_callback_data_5 = '["this", "is", "json"]' with pytest.raises(TypeError, match='user_data'): DictPersistence(user_data_json=bad_user_data) with pytest.raises(TypeError, match='chat_data'): DictPersistence(chat_data_json=bad_chat_data) with pytest.raises(TypeError, match='bot_data'): DictPersistence(bot_data_json=bad_bot_data) for bad_callback_data in [ bad_callback_data_1, bad_callback_data_2, bad_callback_data_3, bad_callback_data_4, bad_callback_data_5, ]: with pytest.raises(TypeError, match='callback_data'): DictPersistence(callback_data_json=bad_callback_data) with pytest.raises(TypeError, match='conversations'): DictPersistence(conversations_json=bad_conversations) def test_good_json_input( self, user_data_json, chat_data_json, bot_data_json, conversations_json, callback_data_json ): dict_persistence = DictPersistence( user_data_json=user_data_json, chat_data_json=chat_data_json, bot_data_json=bot_data_json, conversations_json=conversations_json, callback_data_json=callback_data_json, ) user_data = dict_persistence.get_user_data() assert isinstance(user_data, dict) assert user_data[12345]['test1'] == 'test2' assert user_data[67890][3] == 'test4' chat_data = dict_persistence.get_chat_data() assert isinstance(chat_data, dict) assert chat_data[-12345]['test1'] == 'test2' assert chat_data[-67890][3] == 'test4' bot_data = dict_persistence.get_bot_data() assert isinstance(bot_data, dict) assert bot_data['test1'] == 'test2' assert bot_data['test3']['test4'] == 'test5' assert 'test6' not in bot_data callback_data = dict_persistence.get_callback_data() assert isinstance(callback_data, tuple) assert callback_data[0] == [('test1', 1000, {'button1': 'test0', 'button2': 'test1'})] assert callback_data[1] == {'test1': 'test2'} conversation1 = dict_persistence.get_conversations('name1') assert isinstance(conversation1, dict) assert conversation1[(123, 123)] == 3 assert conversation1[(456, 654)] == 4 with pytest.raises(KeyError): conversation1[(890, 890)] conversation2 = dict_persistence.get_conversations('name2') assert isinstance(conversation1, dict) assert conversation2[(123, 321)] == 1 assert conversation2[(890, 890)] == 2 with pytest.raises(KeyError): conversation2[(123, 123)] def test_good_json_input_callback_data_none(self): dict_persistence = DictPersistence(callback_data_json='null') assert dict_persistence.callback_data is None assert dict_persistence.callback_data_json == 'null' def test_dict_outputs( self, user_data, user_data_json, chat_data, chat_data_json, bot_data, bot_data_json, callback_data_json, conversations, conversations_json, ): dict_persistence = DictPersistence( user_data_json=user_data_json, chat_data_json=chat_data_json, bot_data_json=bot_data_json, callback_data_json=callback_data_json, conversations_json=conversations_json, ) assert dict_persistence.user_data == user_data assert dict_persistence.chat_data == chat_data assert dict_persistence.bot_data == bot_data assert dict_persistence.bot_data == bot_data assert dict_persistence.conversations == conversations def test_json_outputs( self, user_data_json, chat_data_json, bot_data_json, callback_data_json, conversations_json ): dict_persistence = DictPersistence( user_data_json=user_data_json, chat_data_json=chat_data_json, bot_data_json=bot_data_json, callback_data_json=callback_data_json, conversations_json=conversations_json, ) assert dict_persistence.user_data_json == user_data_json assert dict_persistence.chat_data_json == chat_data_json assert dict_persistence.callback_data_json == callback_data_json assert dict_persistence.conversations_json == conversations_json def test_updating( self, user_data_json, chat_data_json, bot_data_json, callback_data, callback_data_json, conversations, conversations_json, ): dict_persistence = DictPersistence( user_data_json=user_data_json, chat_data_json=chat_data_json, bot_data_json=bot_data_json, callback_data_json=callback_data_json, conversations_json=conversations_json, ) user_data = dict_persistence.get_user_data() user_data[12345]['test3']['test4'] = 'test6' assert not dict_persistence.user_data == user_data assert not dict_persistence.user_data_json == json.dumps(user_data) dict_persistence.update_user_data(12345, user_data[12345]) user_data[12345]['test3']['test4'] = 'test7' assert not dict_persistence.user_data == user_data assert not dict_persistence.user_data_json == json.dumps(user_data) dict_persistence.update_user_data(12345, user_data[12345]) assert dict_persistence.user_data == user_data assert dict_persistence.user_data_json == json.dumps(user_data) dict_persistence.drop_user_data(67890) assert 67890 not in dict_persistence.user_data dict_persistence._user_data = None dict_persistence.drop_user_data(123) assert isinstance(dict_persistence.get_user_data(), dict) chat_data = dict_persistence.get_chat_data() chat_data[-12345]['test3']['test4'] = 'test6' assert not dict_persistence.chat_data == chat_data assert not dict_persistence.chat_data_json == json.dumps(chat_data) dict_persistence.update_chat_data(-12345, chat_data[-12345]) chat_data[-12345]['test3']['test4'] = 'test7' assert not dict_persistence.chat_data == chat_data assert not dict_persistence.chat_data_json == json.dumps(chat_data) dict_persistence.update_chat_data(-12345, chat_data[-12345]) assert dict_persistence.chat_data == chat_data assert dict_persistence.chat_data_json == json.dumps(chat_data) dict_persistence.drop_chat_data(-67890) assert -67890 not in dict_persistence.chat_data dict_persistence._chat_data = None dict_persistence.drop_chat_data(123) assert isinstance(dict_persistence.get_chat_data(), dict) bot_data = dict_persistence.get_bot_data() bot_data['test3']['test4'] = 'test6' assert not dict_persistence.bot_data == bot_data assert not dict_persistence.bot_data_json == json.dumps(bot_data) dict_persistence.update_bot_data(bot_data) bot_data['test3']['test4'] = 'test7' assert not dict_persistence.bot_data == bot_data assert not dict_persistence.bot_data_json == json.dumps(bot_data) dict_persistence.update_bot_data(bot_data) assert dict_persistence.bot_data == bot_data assert dict_persistence.bot_data_json == json.dumps(bot_data) callback_data = dict_persistence.get_callback_data() callback_data[1]['test3'] = 'test4' callback_data[0][0][2]['button2'] = 'test41' assert not dict_persistence.callback_data == callback_data assert not dict_persistence.callback_data_json == json.dumps(callback_data) dict_persistence.update_callback_data(callback_data) callback_data[1]['test3'] = 'test5' callback_data[0][0][2]['button2'] = 'test42' assert not dict_persistence.callback_data == callback_data assert not dict_persistence.callback_data_json == json.dumps(callback_data) dict_persistence.update_callback_data(callback_data) assert dict_persistence.callback_data == callback_data assert dict_persistence.callback_data_json == json.dumps(callback_data) conversation1 = dict_persistence.get_conversations('name1') conversation1[(123, 123)] = 5 assert not dict_persistence.conversations['name1'] == conversation1 dict_persistence.update_conversation('name1', (123, 123), 5) assert dict_persistence.conversations['name1'] == conversation1 conversations['name1'][(123, 123)] = 5 assert ( dict_persistence.conversations_json == DictPersistence._encode_conversations_to_json(conversations) ) assert dict_persistence.get_conversations('name1') == conversation1 dict_persistence._conversations = None dict_persistence.update_conversation('name1', (123, 123), 5) assert dict_persistence.conversations['name1'] == {(123, 123): 5} assert dict_persistence.get_conversations('name1') == {(123, 123): 5} assert ( dict_persistence.conversations_json == DictPersistence._encode_conversations_to_json({"name1": {(123, 123): 5}}) ) def test_with_handler(self, bot, update): dict_persistence = DictPersistence() u = UpdaterBuilder().bot(bot).persistence(dict_persistence).build() dp = u.dispatcher def first(update, context): if not context.user_data == {}: pytest.fail() if not context.chat_data == {}: pytest.fail() if not context.bot_data == {}: pytest.fail() if not context.bot.callback_data_cache.persistence_data == ([], {}): pytest.fail() context.user_data['test1'] = 'test2' context.chat_data[3] = 'test4' context.bot_data['test1'] = 'test0' context.bot.callback_data_cache._callback_queries['test1'] = 'test0' def second(update, context): if not context.user_data['test1'] == 'test2': pytest.fail() if not context.chat_data[3] == 'test4': pytest.fail() if not context.bot_data['test1'] == 'test0': pytest.fail() if not context.bot.callback_data_cache.persistence_data == ([], {'test1': 'test0'}): pytest.fail() h1 = MessageHandler(filters.ALL, first) h2 = MessageHandler(filters.ALL, second) dp.add_handler(h1) dp.process_update(update) user_data = dict_persistence.user_data_json chat_data = dict_persistence.chat_data_json bot_data = dict_persistence.bot_data_json callback_data = dict_persistence.callback_data_json dict_persistence_2 = DictPersistence( user_data_json=user_data, chat_data_json=chat_data, bot_data_json=bot_data, callback_data_json=callback_data, ) u = UpdaterBuilder().bot(bot).persistence(dict_persistence_2).build() dp = u.dispatcher dp.add_handler(h2) dp.process_update(update) def test_with_conversationHandler(self, dp, update, conversations_json): dict_persistence = DictPersistence(conversations_json=conversations_json) dp.persistence = dict_persistence NEXT, NEXT2 = range(2) def start(update, context): return NEXT start = CommandHandler('start', start) def next_callback(update, context): return NEXT2 next_handler = MessageHandler(None, next_callback) def next2(update, context): return ConversationHandler.END next2 = MessageHandler(None, next2) ch = ConversationHandler( [start], {NEXT: [next_handler], NEXT2: [next2]}, [], name='name2', persistent=True ) dp.add_handler(ch) assert ch.conversations[ch._get_key(update)] == 1 dp.process_update(update) assert ch._get_key(update) not in ch.conversations update.message.text = '/start' update.message.entities = [MessageEntity(MessageEntity.BOT_COMMAND, 0, 6)] dp.process_update(update) assert ch.conversations[ch._get_key(update)] == 0 assert ch.conversations == dict_persistence.conversations['name2'] def test_with_nested_conversationHandler(self, dp, update, conversations_json): dict_persistence = DictPersistence(conversations_json=conversations_json) dp.persistence = dict_persistence NEXT2, NEXT3 = range(1, 3) def start(update, context): return NEXT2 start = CommandHandler('start', start) def next_callback(update, context): return NEXT2 next_handler = MessageHandler(None, next_callback) def next2(update, context): return ConversationHandler.END next2 = MessageHandler(None, next2) nested_ch = ConversationHandler( [next_handler], {NEXT2: [next2]}, [], name='name3', persistent=True, map_to_parent={ConversationHandler.END: ConversationHandler.END}, ) ch = ConversationHandler( [start], {NEXT2: [nested_ch], NEXT3: []}, [], name='name2', persistent=True ) dp.add_handler(ch) assert ch.conversations[ch._get_key(update)] == 1 assert nested_ch.conversations[nested_ch._get_key(update)] == 1 dp.process_update(update) assert ch._get_key(update) not in ch.conversations assert nested_ch._get_key(update) not in nested_ch.conversations update.message.text = '/start' update.message.entities = [MessageEntity(MessageEntity.BOT_COMMAND, 0, 6)] dp.process_update(update) assert ch.conversations[ch._get_key(update)] == 1 assert ch.conversations == dict_persistence.conversations['name2'] assert nested_ch._get_key(update) not in nested_ch.conversations dp.process_update(update) assert ch.conversations[ch._get_key(update)] == 1 assert ch.conversations == dict_persistence.conversations['name2'] assert nested_ch.conversations[nested_ch._get_key(update)] == 1 assert nested_ch.conversations == dict_persistence.conversations['name3'] def test_with_job(self, job_queue, dp): dp.bot.arbitrary_callback_data = True def job_callback(context): context.bot_data['test1'] = '456' context.dispatcher.chat_data[123]['test2'] = '789' context.dispatcher.user_data[789]['test3'] = '123' context.bot.callback_data_cache._callback_queries['test'] = 'Working4!' dict_persistence = DictPersistence() dp.persistence = dict_persistence job_queue.set_dispatcher(dp) job_queue.start() job_queue.run_once(job_callback, 0.01) sleep(0.8) bot_data = dict_persistence.get_bot_data() assert bot_data == {'test1': '456'} chat_data = dict_persistence.get_chat_data() assert chat_data[123] == {'test2': '789'} user_data = dict_persistence.get_user_data() assert user_data[789] == {'test3': '123'} data = dict_persistence.get_callback_data()[1] assert data['test'] == 'Working4!'