-
Notifications
You must be signed in to change notification settings - Fork 0
feat(composer): support full bot configuration #135
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,130 @@ | ||
| """Focused tests for Composer-to-Bot option parity.""" | ||
| from __future__ import annotations | ||
|
|
||
| from unittest.mock import MagicMock, patch | ||
|
|
||
| import discord | ||
| import pytest | ||
|
|
||
| from easycord.composer import Composer | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 1. Tests import easycord.composer tests/test_composer.py imports Composer from the internal submodule easycord.composer instead of the public top-level easycord API. This breaks the public-API-only import rule for code outside the easycord/ package and increases coupling to internal module layout. Agent Prompt
|
||
|
|
||
|
|
||
| def test_build_preserves_bot_option_defaults() -> None: | ||
| with patch("easycord.composer.Bot") as bot_class: | ||
| Composer().build() | ||
|
|
||
| bot_class.assert_called_once_with( | ||
| intents=None, | ||
| auto_sync=True, | ||
| sync_guild_id=None, | ||
| load_builtin_plugins=False, | ||
| database=None, | ||
| db_backend=None, | ||
| db_path=None, | ||
| db_auto_sync_guilds=None, | ||
| guild_sync_timeout=30.0, | ||
| localization=None, | ||
| default_locale="en-US", | ||
| translations=None, | ||
| auto_translator=None, | ||
| ai_provider=None, | ||
| enable_conversation_memory=False, | ||
| enable_health_command=False, | ||
| cooldown_cleanup_interval=600.0, | ||
| ) | ||
|
|
||
|
|
||
| def test_new_bot_and_client_options_are_forwarded() -> None: | ||
| provider = MagicMock() | ||
| activity = discord.Game(name="EasyCord") | ||
| mentions = discord.AllowedMentions.none() | ||
|
|
||
| with patch("easycord.composer.Bot") as bot_class: | ||
| result = ( | ||
| Composer() | ||
| .sync_guild_id(123) | ||
| .guild_sync_timeout(None) | ||
| .ai_provider(provider) | ||
| .conversation_memory() | ||
| .health_command() | ||
| .cooldown_cleanup_interval(45.0) | ||
| .client_options(activity=activity, allowed_mentions=mentions) | ||
| .build() | ||
| ) | ||
|
|
||
| assert result is bot_class.return_value | ||
| kwargs = bot_class.call_args.kwargs | ||
| assert kwargs["sync_guild_id"] == 123 | ||
| assert kwargs["guild_sync_timeout"] is None | ||
| assert kwargs["ai_provider"] is provider | ||
| assert kwargs["enable_conversation_memory"] is True | ||
| assert kwargs["enable_health_command"] is True | ||
| assert kwargs["cooldown_cleanup_interval"] == 45.0 | ||
| assert kwargs["activity"] is activity | ||
| assert kwargs["allowed_mentions"] is mentions | ||
|
|
||
|
|
||
| def test_boolean_options_can_be_disabled_explicitly() -> None: | ||
| with patch("easycord.composer.Bot") as bot_class: | ||
| ( | ||
| Composer() | ||
| .conversation_memory(True) | ||
| .conversation_memory(False) | ||
| .health_command(True) | ||
| .health_command(False) | ||
| .build() | ||
| ) | ||
|
|
||
| kwargs = bot_class.call_args.kwargs | ||
| assert kwargs["enable_conversation_memory"] is False | ||
| assert kwargs["enable_health_command"] is False | ||
|
|
||
|
|
||
| def test_repeated_client_options_merge_with_later_values_winning() -> None: | ||
| first_activity = discord.Game(name="First") | ||
| second_activity = discord.Game(name="Second") | ||
|
|
||
| with patch("easycord.composer.Bot") as bot_class: | ||
| ( | ||
| Composer() | ||
| .client_options(activity=first_activity, max_messages=100) | ||
| .client_options(activity=second_activity) | ||
| .build() | ||
| ) | ||
|
|
||
| kwargs = bot_class.call_args.kwargs | ||
| assert kwargs["activity"] is second_activity | ||
| assert kwargs["max_messages"] == 100 | ||
|
|
||
|
|
||
| @pytest.mark.parametrize( | ||
| ("option", "method"), | ||
| [ | ||
| ("intents", "intents"), | ||
| ("auto_sync", "auto_sync"), | ||
| ("sync_guild_id", "sync_guild_id"), | ||
| ("load_builtin_plugins", "builtin_plugins"), | ||
| ("database", "database"), | ||
| ("db_backend", "db_backend"), | ||
| ("db_path", "db_path"), | ||
| ("db_auto_sync_guilds", "db_auto_sync_guilds"), | ||
| ("guild_sync_timeout", "guild_sync_timeout"), | ||
| ("localization", "localization"), | ||
| ("default_locale", "default_locale"), | ||
| ("translations", "translations"), | ||
| ("auto_translator", "auto_translator"), | ||
| ("ai_provider", "ai_provider"), | ||
| ("enable_conversation_memory", "conversation_memory"), | ||
| ("enable_health_command", "health_command"), | ||
| ("cooldown_cleanup_interval", "cooldown_cleanup_interval"), | ||
| ], | ||
| ) | ||
| def test_client_options_reject_easycord_owned_options( | ||
| option: str, | ||
| method: str, | ||
| ) -> None: | ||
| with pytest.raises( | ||
| ValueError, | ||
| match=rf"{option!r}.*Composer\.{method}", | ||
| ): | ||
| Composer().client_options(**{option: object()}) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -211,7 +211,8 @@ def test_cli_new_template_options( | |
| assert (project / "bot.py").exists() | ||
| assert (project / "tests" / "test_bot.py").exists() | ||
| assert (project / "plugins" / f"{template}_bot.py").exists() is has_plugin | ||
| assert "auto_sync=False" in (project / "bot.py").read_text(encoding="utf-8") | ||
| bot_source = (project / "bot.py").read_text(encoding="utf-8") | ||
| assert "auto_sync=False" in bot_source or ".auto_sync(False)" in bot_source | ||
|
|
||
|
|
||
| def test_cli_new_defaults_to_plugin_template(tmp_path: Path, capsys) -> None: | ||
|
|
@@ -244,7 +245,10 @@ def test_cli_new_community_template_is_composition_first(tmp_path: Path, capsys) | |
| bot_source = (project / "bot.py").read_text(encoding="utf-8") | ||
| test_source = (project / "tests" / "test_bot.py").read_text(encoding="utf-8") | ||
|
|
||
| assert "load_builtin_plugins=True" in bot_source | ||
| assert "Composer()" in bot_source | ||
| assert ".auto_sync(False)" in bot_source | ||
| assert ".builtin_plugins()" in bot_source | ||
| assert ".build()" in bot_source | ||
|
Comment on lines
+248
to
+251
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion (testing): Add an assertion that the community template does not instantiate The current assertions ensure the template uses |
||
| assert "ModerationPlugin()" in bot_source | ||
| assert "EconomyPlugin()" in bot_source | ||
| assert "ReminderPlugin()" in bot_source | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2. Guild_id not mapped
🐞 Bug≡ CorrectnessAgent Prompt
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools