Files
Abdelrahman Elkheir 432a67efdd Full Support for Bot API 9.3 (#5078)
Co-authored-by: Hinrich Mahler <22366557+Bibo-Joshi@users.noreply.github.com>
2026-01-24 14:20:28 +01:00

446 lines
16 KiB
Python

#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
# Copyright (C) 2015-2026
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# 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 as dtm
import pytest
from telegram import (
Chat,
Checklist,
ChecklistTask,
ChecklistTasksAdded,
ChecklistTasksDone,
Dice,
MessageEntity,
User,
)
from telegram._utils.datetime import UTC, to_timestamp
from telegram.constants import ZERO_DATE
from tests.auxil.build_messages import make_message
from tests.auxil.slots import mro_slots
class ChecklistTaskTestBase:
id = 42
text = "here is a text"
text_entities = [
MessageEntity(type="bold", offset=0, length=4),
MessageEntity(type="italic", offset=5, length=2),
]
completed_by_user = User(id=1, first_name="Test", last_name="User", is_bot=False)
completed_by_chat = Chat(id=-100, type=Chat.SUPERGROUP, title="Test Chat")
completion_date = dtm.datetime.now(tz=UTC).replace(microsecond=0)
@pytest.fixture(scope="module")
def checklist_task():
return ChecklistTask(
id=ChecklistTaskTestBase.id,
text=ChecklistTaskTestBase.text,
text_entities=ChecklistTaskTestBase.text_entities,
completed_by_user=ChecklistTaskTestBase.completed_by_user,
completed_by_chat=ChecklistTaskTestBase.completed_by_chat,
completion_date=ChecklistTaskTestBase.completion_date,
)
class TestChecklistTaskWithoutRequest(ChecklistTaskTestBase):
def test_slot_behaviour(self, checklist_task):
for attr in checklist_task.__slots__:
assert getattr(checklist_task, attr, "err") != "err", f"got extra slot '{attr}'"
assert len(mro_slots(checklist_task)) == len(set(mro_slots(checklist_task))), (
"duplicate slot"
)
def test_to_dict(self, checklist_task):
clt_dict = checklist_task.to_dict()
assert isinstance(clt_dict, dict)
assert clt_dict["id"] == self.id
assert clt_dict["text"] == self.text
assert clt_dict["text_entities"] == [entity.to_dict() for entity in self.text_entities]
assert clt_dict["completed_by_user"] == self.completed_by_user.to_dict()
assert clt_dict["completed_by_chat"] == self.completed_by_chat.to_dict()
assert clt_dict["completion_date"] == to_timestamp(self.completion_date)
def test_de_json(self, offline_bot):
json_dict = {
"id": self.id,
"text": self.text,
"text_entities": [entity.to_dict() for entity in self.text_entities],
"completed_by_user": self.completed_by_user.to_dict(),
"completed_by_chat": self.completed_by_chat.to_dict(),
"completion_date": to_timestamp(self.completion_date),
}
clt = ChecklistTask.de_json(json_dict, offline_bot)
assert isinstance(clt, ChecklistTask)
assert clt.id == self.id
assert clt.text == self.text
assert clt.text_entities == tuple(self.text_entities)
assert clt.completed_by_user == self.completed_by_user
assert clt.completed_by_chat == self.completed_by_chat
assert clt.completion_date == self.completion_date
assert clt.api_kwargs == {}
def test_de_json_required_fields(self, offline_bot):
json_dict = {
"id": self.id,
"text": self.text,
}
clt = ChecklistTask.de_json(json_dict, offline_bot)
assert isinstance(clt, ChecklistTask)
assert clt.id == self.id
assert clt.text == self.text
assert clt.text_entities == ()
assert clt.completed_by_user is None
assert clt.completion_date is None
assert clt.api_kwargs == {}
def test_de_json_localization(self, offline_bot, raw_bot, tz_bot):
json_dict = {
"id": self.id,
"text": self.text,
"completion_date": to_timestamp(self.completion_date),
}
clt_bot = ChecklistTask.de_json(json_dict, offline_bot)
clt_bot_raw = ChecklistTask.de_json(json_dict, raw_bot)
clt_bot_tz = ChecklistTask.de_json(json_dict, tz_bot)
# comparing utcoffsets because comparing tzinfo objects is not reliable
completion_date_offset = clt_bot_tz.completion_date.utcoffset()
completion_date_offset_tz = tz_bot.defaults.tzinfo.utcoffset(
clt_bot_tz.completion_date.replace(tzinfo=None)
)
assert clt_bot.completion_date.tzinfo == UTC
assert clt_bot_raw.completion_date.tzinfo == UTC
assert completion_date_offset_tz == completion_date_offset
@pytest.mark.parametrize(
("completion_date", "expected"),
[
(None, None),
(0, ZERO_DATE),
(1735689600, dtm.datetime(2025, 1, 1, tzinfo=UTC)),
],
)
def test_de_json_completion_date(self, offline_bot, completion_date, expected):
json_dict = {
"id": self.id,
"text": self.text,
"completion_date": completion_date,
}
clt = ChecklistTask.de_json(json_dict, offline_bot)
assert isinstance(clt, ChecklistTask)
assert clt.completion_date == expected
def test_parse_entity(self, checklist_task):
assert checklist_task.parse_entity(checklist_task.text_entities[0]) == "here"
def test_parse_entities(self, checklist_task):
assert checklist_task.parse_entities(MessageEntity.BOLD) == {
checklist_task.text_entities[0]: "here"
}
assert checklist_task.parse_entities() == {
checklist_task.text_entities[0]: "here",
checklist_task.text_entities[1]: "is",
}
def test_equality(self, checklist_task):
clt1 = checklist_task
clt2 = ChecklistTask(
id=self.id,
text="other text",
)
clt3 = ChecklistTask(
id=self.id + 1,
text=self.text,
)
clt4 = Dice(value=1, emoji="🎲")
assert clt1 == clt2
assert hash(clt1) == hash(clt2)
assert clt1 != clt3
assert hash(clt1) != hash(clt3)
assert clt1 != clt4
assert hash(clt1) != hash(clt4)
class ChecklistTestBase:
title = "Checklist Title"
title_entities = [
MessageEntity(type="bold", offset=0, length=9),
MessageEntity(type="italic", offset=10, length=5),
]
tasks = [
ChecklistTask(
id=1,
text="Task 1",
),
ChecklistTask(
id=2,
text="Task 2",
),
]
others_can_add_tasks = True
others_can_mark_tasks_as_done = False
@pytest.fixture(scope="module")
def checklist():
return Checklist(
title=ChecklistTestBase.title,
title_entities=ChecklistTestBase.title_entities,
tasks=ChecklistTestBase.tasks,
others_can_add_tasks=ChecklistTestBase.others_can_add_tasks,
others_can_mark_tasks_as_done=ChecklistTestBase.others_can_mark_tasks_as_done,
)
class TestChecklistWithoutRequest(ChecklistTestBase):
def test_slot_behaviour(self, checklist):
for attr in checklist.__slots__:
assert getattr(checklist, attr, "err") != "err", f"got extra slot '{attr}'"
assert len(mro_slots(checklist)) == len(set(mro_slots(checklist))), "duplicate slot"
def test_to_dict(self, checklist):
cl_dict = checklist.to_dict()
assert isinstance(cl_dict, dict)
assert cl_dict["title"] == self.title
assert cl_dict["title_entities"] == [entity.to_dict() for entity in self.title_entities]
assert cl_dict["tasks"] == [task.to_dict() for task in self.tasks]
assert cl_dict["others_can_add_tasks"] is self.others_can_add_tasks
assert cl_dict["others_can_mark_tasks_as_done"] is self.others_can_mark_tasks_as_done
def test_de_json(self, offline_bot):
json_dict = {
"title": self.title,
"title_entities": [entity.to_dict() for entity in self.title_entities],
"tasks": [task.to_dict() for task in self.tasks],
"others_can_add_tasks": self.others_can_add_tasks,
"others_can_mark_tasks_as_done": self.others_can_mark_tasks_as_done,
}
cl = Checklist.de_json(json_dict, offline_bot)
assert isinstance(cl, Checklist)
assert cl.title == self.title
assert cl.title_entities == tuple(self.title_entities)
assert cl.tasks == tuple(self.tasks)
assert cl.others_can_add_tasks is self.others_can_add_tasks
assert cl.others_can_mark_tasks_as_done is self.others_can_mark_tasks_as_done
assert cl.api_kwargs == {}
def test_de_json_required_fields(self, offline_bot):
json_dict = {
"title": self.title,
"tasks": [task.to_dict() for task in self.tasks],
}
cl = Checklist.de_json(json_dict, offline_bot)
assert isinstance(cl, Checklist)
assert cl.title == self.title
assert cl.title_entities == ()
assert cl.tasks == tuple(self.tasks)
assert not cl.others_can_add_tasks
assert not cl.others_can_mark_tasks_as_done
def test_parse_entity(self, checklist):
assert checklist.parse_entity(checklist.title_entities[0]) == "Checklist"
assert checklist.parse_entity(checklist.title_entities[1]) == "Title"
def test_parse_entities(self, checklist):
assert checklist.parse_entities(MessageEntity.BOLD) == {
checklist.title_entities[0]: "Checklist"
}
assert checklist.parse_entities() == {
checklist.title_entities[0]: "Checklist",
checklist.title_entities[1]: "Title",
}
def test_equality(self, checklist, checklist_task):
cl1 = checklist
cl2 = Checklist(
title=self.title + " other",
tasks=[ChecklistTask(id=1, text="something"), ChecklistTask(id=2, text="something")],
)
cl3 = Checklist(
title=self.title + " other",
tasks=[ChecklistTask(id=42, text="Task 2")],
)
cl4 = checklist_task
assert cl1 == cl2
assert hash(cl1) == hash(cl2)
assert cl1 != cl3
assert hash(cl1) != hash(cl3)
assert cl1 != cl4
assert hash(cl1) != hash(cl4)
class ChecklistTasksDoneTestBase:
checklist_message = make_message("Checklist message")
marked_as_done_task_ids = [1, 2, 3]
marked_as_not_done_task_ids = [4, 5]
@pytest.fixture(scope="module")
def checklist_tasks_done():
return ChecklistTasksDone(
checklist_message=ChecklistTasksDoneTestBase.checklist_message,
marked_as_done_task_ids=ChecklistTasksDoneTestBase.marked_as_done_task_ids,
marked_as_not_done_task_ids=ChecklistTasksDoneTestBase.marked_as_not_done_task_ids,
)
class TestChecklistTasksDoneWithoutRequest(ChecklistTasksDoneTestBase):
def test_slot_behaviour(self, checklist_tasks_done):
for attr in checklist_tasks_done.__slots__:
assert getattr(checklist_tasks_done, attr, "err") != "err", f"got extra slot '{attr}'"
assert len(mro_slots(checklist_tasks_done)) == len(set(mro_slots(checklist_tasks_done))), (
"duplicate slot"
)
def test_to_dict(self, checklist_tasks_done):
cltd_dict = checklist_tasks_done.to_dict()
assert isinstance(cltd_dict, dict)
assert cltd_dict["checklist_message"] == self.checklist_message.to_dict()
assert cltd_dict["marked_as_done_task_ids"] == self.marked_as_done_task_ids
assert cltd_dict["marked_as_not_done_task_ids"] == self.marked_as_not_done_task_ids
def test_de_json(self, offline_bot):
json_dict = {
"checklist_message": self.checklist_message.to_dict(),
"marked_as_done_task_ids": self.marked_as_done_task_ids,
"marked_as_not_done_task_ids": self.marked_as_not_done_task_ids,
}
cltd = ChecklistTasksDone.de_json(json_dict, offline_bot)
assert isinstance(cltd, ChecklistTasksDone)
assert cltd.checklist_message == self.checklist_message
assert cltd.marked_as_done_task_ids == tuple(self.marked_as_done_task_ids)
assert cltd.marked_as_not_done_task_ids == tuple(self.marked_as_not_done_task_ids)
assert cltd.api_kwargs == {}
def test_de_json_required_fields(self, offline_bot):
cltd = ChecklistTasksDone.de_json({}, offline_bot)
assert isinstance(cltd, ChecklistTasksDone)
assert cltd.checklist_message is None
assert cltd.marked_as_done_task_ids == ()
assert cltd.marked_as_not_done_task_ids == ()
assert cltd.api_kwargs == {}
def test_equality(self, checklist_tasks_done):
cltd1 = checklist_tasks_done
cltd2 = ChecklistTasksDone(
checklist_message=None,
marked_as_done_task_ids=[1, 2, 3],
marked_as_not_done_task_ids=[4, 5],
)
cltd3 = ChecklistTasksDone(
checklist_message=make_message("Checklist message"),
marked_as_done_task_ids=[1, 2, 3],
)
cltd4 = make_message("Not a checklist tasks done")
assert cltd1 == cltd2
assert hash(cltd1) == hash(cltd2)
assert cltd1 != cltd3
assert hash(cltd1) != hash(cltd3)
assert cltd1 != cltd4
assert hash(cltd1) != hash(cltd4)
class ChecklistTasksAddedTestBase:
checklist_message = make_message("Checklist message")
tasks = [
ChecklistTask(id=1, text="Task 1"),
ChecklistTask(id=2, text="Task 2"),
ChecklistTask(id=3, text="Task 3"),
]
@pytest.fixture(scope="module")
def checklist_tasks_added():
return ChecklistTasksAdded(
checklist_message=ChecklistTasksAddedTestBase.checklist_message,
tasks=ChecklistTasksAddedTestBase.tasks,
)
class TestChecklistTasksAddedWithoutRequest(ChecklistTasksAddedTestBase):
def test_slot_behaviour(self, checklist_tasks_added):
for attr in checklist_tasks_added.__slots__:
assert getattr(checklist_tasks_added, attr, "err") != "err", f"got extra slot '{attr}'"
assert len(mro_slots(checklist_tasks_added)) == len(
set(mro_slots(checklist_tasks_added))
), "duplicate slot"
def test_to_dict(self, checklist_tasks_added):
clta_dict = checklist_tasks_added.to_dict()
assert isinstance(clta_dict, dict)
assert clta_dict["checklist_message"] == self.checklist_message.to_dict()
assert clta_dict["tasks"] == [task.to_dict() for task in self.tasks]
def test_de_json(self, offline_bot):
json_dict = {
"checklist_message": self.checklist_message.to_dict(),
"tasks": [task.to_dict() for task in self.tasks],
}
clta = ChecklistTasksAdded.de_json(json_dict, offline_bot)
assert isinstance(clta, ChecklistTasksAdded)
assert clta.checklist_message == self.checklist_message
assert clta.tasks == tuple(self.tasks)
assert clta.api_kwargs == {}
def test_de_json_required_fields(self, offline_bot):
clta = ChecklistTasksAdded.de_json(
{"tasks": [task.to_dict() for task in self.tasks]}, offline_bot
)
assert isinstance(clta, ChecklistTasksAdded)
assert clta.checklist_message is None
assert clta.tasks == tuple(self.tasks)
assert clta.api_kwargs == {}
def test_equality(self, checklist_tasks_added):
clta1 = checklist_tasks_added
clta2 = ChecklistTasksAdded(
checklist_message=None,
tasks=[
ChecklistTask(id=1, text="Other Task 1"),
ChecklistTask(id=2, text="Other Task 2"),
ChecklistTask(id=3, text="Other Task 3"),
],
)
clta3 = ChecklistTasksAdded(
checklist_message=make_message("Checklist message"),
tasks=[ChecklistTask(id=1, text="Task 1")],
)
clta4 = make_message("Not a checklist tasks added")
assert clta1 == clta2
assert hash(clta1) == hash(clta2)
assert clta1 != clta3
assert hash(clta1) != hash(clta3)
assert clta1 != clta4
assert hash(clta1) != hash(clta4)