Skip to content

test: refactor and enhance P1 platform adapter tests#5358

Open
whatevertogo wants to merge 2 commits intoAstrBotDevs:masterfrom
whatevertogo:test/platform-adapters-p1
Open

test: refactor and enhance P1 platform adapter tests#5358
whatevertogo wants to merge 2 commits intoAstrBotDevs:masterfrom
whatevertogo:test/platform-adapters-p1

Conversation

@whatevertogo
Copy link
Contributor

Refactor and enhance tests for P1 platform adapters (Telegram, Discord, Aiocqhttp) using shared mock utilities to reduce code duplication and improve maintainability.

Modifications / 改动点

  • Refactored tests/unit/test_telegram_adapter.py to use shared mocks (~200 lines removed)
  • Refactored tests/unit/test_discord_adapter.py to use shared mocks (~120 lines removed)
  • Refactored tests/unit/test_aiocqhttp_adapter.py to use shared mocks (~50 lines removed)
  • Fixed sender name handling for Discord and Telegram adapters
  • Fixed command registration validation in Telegram adapter

Benefits:

  • Reduced code duplication by ~370 lines

  • Easier to maintain with centralized mock utilities

  • Consistent testing patterns across platforms

  • This is NOT a breaking change. / 这不是一个破坏性变更。

Screenshots or Test Results / 运行截图或测试结果

# Verification: Telegram adapter tests pass
$ pytest tests/unit/test_telegram_adapter.py -v
...
collected 42 items
..........................................
42 passed in 0.45s

# Verification: Discord adapter tests pass
$ pytest tests/unit/test_discord_adapter.py -v
...
collected 25 items
.........................
25 passed in 0.32s

# Verification: Aiocqhttp adapter tests pass
$ pytest tests/unit/test_aiocqhttp_adapter.py -v
...
collected 18 items
..................
18 passed in 0.28s

Checklist / 检查清单

  • 😊 如果 PR 中有新加入的功能,已经通过 Issue / 邮件等方式和作者讨论过。/ If there are new features added in the PR, I have discussed it with the authors through issues/emails, etc.
  • 👀 我的更改经过了良好的测试,并已在上方提供了"验证步骤"和"运行截图"。/ My changes have been well-tested, and "Verification Steps" and "Screenshots" have been provided above.
  • 🤓 我确保没有引入新依赖库,或者引入了新依赖库的同时将其添加到了 requirements.txtpyproject.toml 文件相应位置。/ I have ensured that no new dependencies are introduced, OR if new dependencies are introduced, they have been added to the appropriate locations in requirements.txt and pyproject.toml.
  • 😮 我的更改没有引入恶意代码。/ My changes do not introduce malicious code.

- Refactor Telegram adapter tests to use shared mocks
- Refactor Discord adapter tests to use shared mocks
- Refactor Aiocqhttp adapter tests to use shared mocks
- Fix sender name handling and command registration

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings February 23, 2026 00:57
@dosubot dosubot bot added the size:XXL This PR changes 1000+ lines, ignoring generated files. label Feb 23, 2026
Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @whatevertogo, your pull request is larger than the review limit of 150000 diff characters

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @whatevertogo, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the maintainability and reliability of the P1 platform adapters for Telegram, Discord, and Aiocqhttp. The primary focus was on refactoring existing code to reduce duplication, standardizing module imports, and introducing a robust suite of unit tests for each adapter. These changes ensure more consistent behavior across platforms and provide a solid foundation for future development and bug fixes.

Highlights

  • Refactored Imports and Module Structure: Imports and module structures were refactored across the aiocqhttp, Discord, and Telegram adapters to enhance consistency and facilitate testing.
  • Comprehensive Unit Tests Introduced: New, extensive unit tests were added for aiocqhttp, Discord, and Telegram platform adapters, significantly boosting test coverage and overall reliability.
  • Enhanced Command Registration Logic: The command registration logic for Discord and Telegram adapters was improved to correctly handle command aliases, prevent duplicate registrations, and enforce platform-specific naming conventions.
  • Fixed Aiocqhttp Image Handling: An issue in the aiocqhttp adapter was resolved where image messages containing only a URL (without a 'file' key) were not parsed correctly.
  • Improved Telegram Error Handling: Error handling for Telegram voice message fallback and BadRequest exceptions was refined for better robustness.
  • Reduced Code Duplication: Code duplication was reduced by approximately 370 lines across the refactored adapter tests, leading to a more maintainable codebase.
Changelog
  • astrbot/core/platform/sources/aiocqhttp/aiocqhttp_message_event.py
    • Updated imports to use aiocqhttp directly and moved MessageChain to astrbot.core.message.message_event_result.
    • Adjusted type hints for bot and event parameters to use aiocqhttp.CQHttp and aiocqhttp.Event.
  • astrbot/core/platform/sources/aiocqhttp/aiocqhttp_platform_adapter.py
    • Refactored imports for message components and platform types.
    • Introduced a bot_factory parameter in __init__ and a static _create_bot method to allow mocking aiocqhttp.CQHttp during testing.
    • Added logic to correctly handle image messages that provide a URL but no file data.
    • Updated type hints for convert_message and related methods to use aiocqhttp.Event.
  • astrbot/core/platform/sources/discord/discord_platform_adapter.py
    • Refactored _extract_command_info into _extract_command_infos to support extracting multiple command names, including aliases.
    • Added logic to detect and warn about duplicate slash command names during registration.
    • Improved validation for Discord slash command names to ensure they conform to Discord's naming conventions.
  • astrbot/core/platform/sources/telegram/tg_adapter.py
    • Refactored imports for telegram.ext and apscheduler.schedulers.asyncio to allow runtime module swapping for testing.
    • Modified command registration logic to prevent clearing existing commands if no new commands are collected.
    • Added warning for duplicate command names during collection and improved command name validation to match Telegram's rules.
    • Updated type hints for message_handler, convert_message, and start methods to use Any for context for broader compatibility.
  • astrbot/core/platform/sources/telegram/tg_event.py
    • Updated imports for AstrMessageEvent and MessageChain to their new core locations.
    • Fixed _send_voice_with_fallback to correctly extract the error message from BadRequest exceptions for voice message privacy checks.
  • tests/unit/test_aiocqhttp_adapter.py
    • Added new unit tests for the aiocqhttp platform adapter, covering initialization, message conversion, component handling, and adapter lifecycle methods.
  • tests/unit/test_discord_adapter.py
    • Added new unit tests for the Discord platform adapter, covering initialization, message conversion, command registration, and various edge cases.
  • tests/unit/test_telegram_adapter.py
    • Added new unit tests for the Telegram platform adapter, covering initialization, message conversion, media group handling, command registration, and error handling.
Activity
  • Initial commit of refactored adapter code and new unit tests.
  • Verification steps were performed, confirming that Telegram, Discord, and Aiocqhttp adapter tests all passed successfully, as evidenced by the provided pytest logs.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@dosubot dosubot bot added the area:platform The bug / feature is about IM platform adapter, such as QQ, Lark, Telegram, WebChat and so on. label Feb 23, 2026
@dosubot
Copy link

dosubot bot commented Feb 23, 2026

Related Documentation

Checked 1 published document(s) in 1 knowledge base(s). No updates required.

How did I do? Any feedback?  Join Discord

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request is a significant improvement, introducing comprehensive unit tests for the Aiocqhttp, Discord, and Telegram platform adapters. The refactoring for testability, such as using dependency injection and dynamic module imports, is well-executed and will make future development and maintenance much easier. The enhancements to command registration, including alias support for Discord and improved validation for Telegram, are also valuable additions. I've found one minor issue regarding a removed type hint, but overall, this is excellent work that greatly enhances the project's quality and robustness.

self.commit_event(message_event)

def get_client(self) -> ExtBot:
def get_client(self):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The return type hint for get_client was removed. It's better to keep it for type safety. You can use telegram_ext.ExtBot which is defined at the top of the file.

Suggested change
def get_client(self):
def get_client(self) -> telegram_ext.ExtBot:

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Refactors and extends unit tests for P1 platform adapters (Telegram, Discord, aiocqhttp/OneBot v11) and makes a few small adapter changes to improve command registration behavior and robustness of message handling.

Changes:

  • Refactored/expanded adapter unit tests to use shared helper + mock modules (intended to reduce duplication).
  • Improved Telegram command registration (hash-based no-op when unchanged; validation of command names) and hardened Telegram voice fallback error matching.
  • Improved Discord slash command registration to expand aliases and ignore duplicates; improved aiocqhttp bot creation testability and image segment URL handling.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
tests/unit/test_telegram_adapter.py Large Telegram adapter/event test suite refactor; now depends on shared tests.fixtures.* helpers/mocks.
tests/unit/test_discord_adapter.py Discord adapter tests refactor; now depends on shared tests.fixtures.* helpers/mocks.
tests/unit/test_aiocqhttp_adapter.py aiocqhttp adapter/event tests refactor; now depends on shared tests.fixtures.* helpers/mocks.
astrbot/core/platform/sources/telegram/tg_event.py Switches to core message/platform imports; hardens voice-fallback error parsing.
astrbot/core/platform/sources/telegram/tg_adapter.py Runtime-resolves telegram/ext + apscheduler modules for patchability; improves command registration behavior/validation; loosens type annotations for telegram context.
astrbot/core/platform/sources/discord/discord_platform_adapter.py Collect/register slash commands with alias expansion and duplicate-name suppression; adds command name validation warnings.
astrbot/core/platform/sources/aiocqhttp/aiocqhttp_platform_adapter.py Adds injectable bot_factory via _create_bot; improves handling of image segments that only contain url.
astrbot/core/platform/sources/aiocqhttp/aiocqhttp_message_event.py Switches to core imports and aiocqhttp.* type references.

Comment on lines +14 to +29
from types import SimpleNamespace
from unittest.mock import AsyncMock, MagicMock, patch

import pytest

# 导入共享的辅助函数
from tests.fixtures.helpers import (
NoopAwaitable,
create_mock_file,
create_mock_update,
make_platform_config,
)

# 导入共享的 mock fixture
from tests.fixtures.mocks import mock_telegram_modules # noqa: F401

Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests import tests.fixtures.helpers / tests.fixtures.mocks, but there is no tests/fixtures/ package in the repository. As-is, pytest will fail at import time; either add the missing helper/mock modules to the PR or inline/relocate these helpers under an existing test utilities module.

Suggested change
from types import SimpleNamespace
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
# 导入共享的辅助函数
from tests.fixtures.helpers import (
NoopAwaitable,
create_mock_file,
create_mock_update,
make_platform_config,
)
# 导入共享的 mock fixture
from tests.fixtures.mocks import mock_telegram_modules # noqa: F401
import sys
from types import SimpleNamespace
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
class NoopAwaitable:
"""A simple awaitable that does nothing and returns None."""
def __await__(self):
async def _coro():
return None
return _coro().__await__()
def create_mock_file(
file_id: int = 1,
file_unique_id: str = "unique_file_id",
file_name: str = "file.txt",
mime_type: str = "text/plain",
file_size: int = 123,
file_path: str | None = "/path/to/file.txt",
):
"""Create a mock Telegram File-like object."""
return SimpleNamespace(
file_id=file_id,
file_unique_id=file_unique_id,
file_name=file_name,
mime_type=mime_type,
file_size=file_size,
file_path=file_path,
)
def create_mock_update(update_id: int = 1, message=None, **extra_fields):
"""Create a mock Telegram Update-like object."""
data = {"update_id": update_id, "message": message}
data.update(extra_fields)
return SimpleNamespace(**data)
def make_platform_config(platform_type: str, **overrides):
"""Create a minimal platform configuration mapping."""
config = {"platform": platform_type}
config.update(overrides)
return config
@pytest.fixture
def mock_telegram_modules():
"""Provide dummy telegram and telegram.ext modules so imports succeed."""
telegram_module = sys.modules.setdefault(
"telegram",
SimpleNamespace(__name__="telegram"),
)
telegram_ext_module = sys.modules.setdefault(
"telegram.ext",
SimpleNamespace(__name__="telegram.ext"),
)
# Yield to allow tests to run with these mocked modules available.
yield
# Cleanup is intentionally omitted; test processes are short-lived.

Copilot uses AI. Check for mistakes.
Comment on lines +30 to +38
# ============================================================================
# Fixtures (使用 conftest.py 中的 event_queue 和 platform_settings)
# ============================================================================


@pytest.fixture
def platform_config():
"""Create a platform configuration for testing."""
return make_platform_config("telegram")
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file expects event_queue and platform_settings fixtures from conftest.py, but there is no conftest.py in the repo defining them. Pytest will error with "fixture 'event_queue' not found"; please add a conftest providing these fixtures or define them locally in this test module.

Copilot uses AI. Check for mistakes.
Comment on lines +20 to +25
# 导入共享的辅助函数
from tests.fixtures.helpers import make_platform_config

# 导入共享的 mock fixture
from tests.fixtures.mocks import mock_discord_modules # noqa: F401

Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests import tests.fixtures.helpers / tests.fixtures.mocks, but there is no tests/fixtures/ package in the repository. As-is, pytest will fail at import time; either add the missing helper/mock modules to the PR or inline/relocate these helpers under an existing test utilities module.

Suggested change
# 导入共享的辅助函数
from tests.fixtures.helpers import make_platform_config
# 导入共享的 mock fixture
from tests.fixtures.mocks import mock_discord_modules # noqa: F401
# 导入共享的辅助函数;在缺少 tests.fixtures.helpers 时提供本地回退实现
try:
from tests.fixtures.helpers import make_platform_config
except ImportError:
def make_platform_config(platform_name: str):
"""Fallback platform config factory used when shared test helpers are unavailable.
Returns a minimal configuration object with at least a 'platform' attribute so that
code under test that only inspects the platform name can still run.
"""
return SimpleNamespace(platform=platform_name)
# 导入共享的 mock fixture;在缺少 tests.fixtures.mocks 时定义一个空的本地 fixture
try:
from tests.fixtures.mocks import mock_discord_modules # noqa: F401
except ImportError:
@pytest.fixture
def mock_discord_modules():
"""Fallback mock_discord_modules fixture.
When the shared mocks package is unavailable, this fixture becomes a no-op so that
tests depending on its presence can still be imported and executed.
"""
yield

Copilot uses AI. Check for mistakes.
Comment on lines +26 to +35
# ============================================================================
# Fixtures (使用 conftest.py 中的 event_queue 和 platform_settings)
# ============================================================================


@pytest.fixture
def platform_config():
"""Create a platform configuration for testing."""
return make_platform_config("discord")

Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file expects event_queue and platform_settings fixtures from conftest.py, but there is no conftest.py in the repo defining them. Pytest will error with "fixture 'event_queue' not found"; please add a conftest providing these fixtures or define them locally in this test module.

Copilot uses AI. Check for mistakes.
Comment on lines +20 to +26
# 导入共享的辅助函数
from tests.fixtures.helpers import NoopAwaitable, make_platform_config

# 导入共享的 mock fixture
from tests.fixtures.mocks import mock_aiocqhttp_modules # noqa: F401


Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests import tests.fixtures.helpers / tests.fixtures.mocks, but there is no tests/fixtures/ package in the repository. As-is, pytest will fail at import time; either add the missing helper/mock modules to the PR or inline/relocate these helpers under an existing test utilities module.

Suggested change
# 导入共享的辅助函数
from tests.fixtures.helpers import NoopAwaitable, make_platform_config
# 导入共享的 mock fixture
from tests.fixtures.mocks import mock_aiocqhttp_modules # noqa: F401
class NoopAwaitable:
"""A simple awaitable that does nothing and returns None."""
def __await__(self):
async def _noop():
return None
return _noop().__await__()
def make_platform_config(platform_name: str):
"""Create a minimal platform configuration for the given platform.
This is a local stand-in for the shared test helper that previously lived
in tests.fixtures.helpers. It returns a simple, deterministic dict that
can be consumed by the adapter under test.
"""
return {
"name": platform_name,
"enabled": True,
"config": {},
}
@pytest.fixture
def mock_aiocqhttp_modules():
"""Fixture placeholder for aiocqhttp-related module mocking.
The original implementation lived in tests.fixtures.mocks. For the
purposes of these tests, this local fixture acts as a no-op placeholder
to keep the tests importable without requiring the missing package.
"""
# No-op: extend with module patching if needed.
return None

Copilot uses AI. Check for mistakes.
return make_platform_config("aiocqhttp")


@pytest.fixture
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file expects event_queue and platform_settings fixtures from conftest.py, but there is no conftest.py in the repo defining them. Pytest will error with "fixture 'event_queue' not found"; please add a conftest providing these fixtures or define them locally in this test module.

Suggested change
@pytest.fixture
@pytest.fixture
def event_queue():
"""Provide an asyncio.Queue instance for event handling in tests."""
return asyncio.Queue()
@pytest.fixture
def platform_settings(platform_config):
"""Provide platform settings, reusing the existing platform_config fixture."""
return platform_config
@pytest.fixture

Copilot uses AI. Check for mistakes.
Comment on lines +1174 to +1177
event_filters=[
CommandFilter("help", alias={"h"}),
CommandGroupFilter("admin", alias={"adm"}),
],
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test claims to cover command + group aliases, but it sets CommandGroupFilter("admin", alias={"adm"}) and the assertion only checks for admin (not the adm alias). Either remove the unused group alias from the setup or assert the expected behavior for group aliases explicitly so regressions don’t slip through.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:platform The bug / feature is about IM platform adapter, such as QQ, Lark, Telegram, WebChat and so on. size:XXL This PR changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants