Files
meshcore-bot/tests/test_announcements_command.py
Stacy Olivas e392468d39 test: coverage expansion — commands, web viewer, and infrastructure
New test modules:
- test_announcements_command: parse, record_trigger, execute paths
- test_aurora_command: KP index parsing, alert levels, execute paths
- test_channel_manager: generate_hashtag_key, cache lookups, validation
- test_channels_command: remaining channel info display paths
- test_dadjoke_command: format, split, length, execute
- test_graph_trace_helper: geo-location helper and graph algorithm paths
- test_hacker_command: text transform logic
- test_help_command: format list, channel filter, general/specific help
- test_i18n: fallback loops, format failure, PermissionError, get_value
- test_joke_command: seasonal, format, split, dark, execute
- test_moon_command: phase calc, execute success/error
- test_multitest_command: multi-channel test sequences
- test_stats_command: adverts leaderboard, get_stats_summary, cleanup
- test_trace_command: path extract, parse, format inline/vertical
- test_web_viewer_integration: circuit breaker, JSON serializer,
  packet capture, channel message
- test_webviewer_command: 100% coverage

Extended existing: test_command_manager, test_feed_manager,
test_message_handler, test_rate_limiter, test_repeater_manager,
test_scheduler_logic, test_security_utils, test_transmission_tracker,
test_utils, test_web_viewer
2026-03-17 17:46:44 -07:00

377 lines
15 KiB
Python

"""Tests for modules.commands.announcements_command — pure logic functions."""
import configparser
import time
from unittest.mock import MagicMock, Mock
import pytest
from modules.commands.announcements_command import AnnouncementsCommand
from tests.conftest import mock_message
# A valid-looking 64-char hex pubkey
VALID_PUBKEY = "a" * 64
VALID_PUBKEY2 = "b" * 64
def _make_bot(enabled=True, acl_keys=None, admin_keys=None, triggers=None):
bot = MagicMock()
bot.logger = Mock()
config = configparser.ConfigParser()
config.add_section("Bot")
config.set("Bot", "bot_name", "TestBot")
config.add_section("Channels")
config.set("Channels", "monitor_channels", "general")
config.set("Channels", "respond_to_dms", "true")
config.add_section("Keywords")
config.add_section("Announcements_Command")
config.set("Announcements_Command", "enabled", str(enabled).lower())
config.set("Announcements_Command", "default_announcement_channel", "Public")
config.set("Announcements_Command", "announcement_cooldown", "60")
if acl_keys:
config.set("Announcements_Command", "announcements_acl", ",".join(acl_keys))
if triggers:
for name, text in triggers.items():
config.set("Announcements_Command", f"announce.{name}", text)
if admin_keys:
config.add_section("Admin_ACL")
config.set("Admin_ACL", "admin_pubkeys", ",".join(admin_keys))
bot.config = config
bot.translator = MagicMock()
bot.translator.translate = Mock(side_effect=lambda key, **kw: key)
bot.command_manager = MagicMock()
bot.command_manager.monitor_channels = ["general"]
return bot
class TestLoadTriggers:
"""Tests for _load_triggers."""
def test_no_triggers_returns_empty(self):
bot = _make_bot()
cmd = AnnouncementsCommand(bot)
assert cmd.triggers == {}
def test_triggers_loaded_from_config(self):
bot = _make_bot(triggers={"welcome": "Welcome message!", "bye": "Goodbye!"})
cmd = AnnouncementsCommand(bot)
assert "welcome" in cmd.triggers
assert cmd.triggers["welcome"] == "Welcome message!"
assert "bye" in cmd.triggers
def test_only_announce_keys_loaded(self):
bot = _make_bot(triggers={"hello": "Hello message"})
# Other keys in the section shouldn't be loaded as triggers
cmd = AnnouncementsCommand(bot)
assert "hello" in cmd.triggers
assert "enabled" not in cmd.triggers
class TestCheckAnnouncementsAccess:
"""Tests for _check_announcements_access."""
def test_no_acl_returns_false(self):
bot = _make_bot(enabled=True)
cmd = AnnouncementsCommand(bot)
msg = mock_message(content="announce welcome", is_dm=True, sender_pubkey=VALID_PUBKEY)
assert cmd._check_announcements_access(msg) is False
def test_valid_pubkey_in_acl_returns_true(self):
bot = _make_bot(acl_keys=[VALID_PUBKEY])
cmd = AnnouncementsCommand(bot)
msg = mock_message(content="announce welcome", is_dm=True, sender_pubkey=VALID_PUBKEY)
assert cmd._check_announcements_access(msg) is True
def test_wrong_pubkey_returns_false(self):
bot = _make_bot(acl_keys=[VALID_PUBKEY])
cmd = AnnouncementsCommand(bot)
msg = mock_message(content="announce welcome", is_dm=True, sender_pubkey=VALID_PUBKEY2)
assert cmd._check_announcements_access(msg) is False
def test_no_pubkey_returns_false(self):
bot = _make_bot(acl_keys=[VALID_PUBKEY])
cmd = AnnouncementsCommand(bot)
msg = mock_message(content="announce welcome", is_dm=True, sender_pubkey=None)
assert cmd._check_announcements_access(msg) is False
def test_admin_acl_inherited(self):
bot = _make_bot(admin_keys=[VALID_PUBKEY])
cmd = AnnouncementsCommand(bot)
msg = mock_message(content="announce welcome", is_dm=True, sender_pubkey=VALID_PUBKEY)
assert cmd._check_announcements_access(msg) is True
def test_invalid_pubkey_format_returns_false(self):
bot = _make_bot(acl_keys=[VALID_PUBKEY])
cmd = AnnouncementsCommand(bot)
# Pubkey too short
msg = mock_message(content="announce welcome", is_dm=True, sender_pubkey="tooshort")
assert cmd._check_announcements_access(msg) is False
def test_case_insensitive_pubkey_match(self):
# ACL stored as lowercase, sender sends uppercase
bot = _make_bot(acl_keys=[VALID_PUBKEY.lower()])
cmd = AnnouncementsCommand(bot)
msg = mock_message(content="announce welcome", is_dm=True, sender_pubkey=VALID_PUBKEY.upper())
# Should match case-insensitively
assert cmd._check_announcements_access(msg) is True
class TestCanExecute:
"""Tests for can_execute."""
def test_not_dm_returns_false(self):
bot = _make_bot(acl_keys=[VALID_PUBKEY])
cmd = AnnouncementsCommand(bot)
msg = mock_message(content="announce welcome", channel="general", is_dm=False, sender_pubkey=VALID_PUBKEY)
assert cmd.can_execute(msg) is False
def test_disabled_returns_false(self):
bot = _make_bot(enabled=False, acl_keys=[VALID_PUBKEY])
cmd = AnnouncementsCommand(bot)
msg = mock_message(content="announce welcome", is_dm=True, sender_pubkey=VALID_PUBKEY)
assert cmd.can_execute(msg) is False
def test_dm_with_valid_pubkey_returns_true(self):
bot = _make_bot(acl_keys=[VALID_PUBKEY])
cmd = AnnouncementsCommand(bot)
msg = mock_message(content="announce welcome", is_dm=True, sender_pubkey=VALID_PUBKEY)
assert cmd.can_execute(msg) is True
class TestCooldownLogic:
"""Tests for cooldown tracking."""
def test_no_cooldown_when_trigger_fresh(self):
bot = _make_bot(triggers={"hello": "Hello!"})
cmd = AnnouncementsCommand(bot)
remaining = cmd._get_trigger_cooldown_remaining("hello")
assert remaining == 0
def test_cooldown_active_after_execution(self):
bot = _make_bot(triggers={"hello": "Hello!"})
cmd = AnnouncementsCommand(bot)
cmd.trigger_cooldowns["hello"] = time.time()
remaining = cmd._get_trigger_cooldown_remaining("hello")
assert remaining > 0
def test_no_cooldown_when_cooldown_seconds_zero(self):
bot = _make_bot()
bot.config.set("Announcements_Command", "announcement_cooldown", "0")
cmd = AnnouncementsCommand(bot)
cmd.trigger_cooldowns["hello"] = time.time()
remaining = cmd._get_trigger_cooldown_remaining("hello")
assert remaining == 0
class TestParseCommand:
"""Tests for _parse_command."""
def test_no_args_returns_none(self):
bot = _make_bot()
cmd = AnnouncementsCommand(bot)
assert cmd._parse_command("announce") == (None, None, False)
def test_trigger_only(self):
bot = _make_bot()
cmd = AnnouncementsCommand(bot)
name, chan, override = cmd._parse_command("announce welcome")
assert name == "welcome"
assert chan is None
assert override is False
def test_trigger_with_channel(self):
bot = _make_bot()
cmd = AnnouncementsCommand(bot)
name, chan, override = cmd._parse_command("announce welcome Public")
assert name == "welcome"
assert chan == "Public"
def test_trigger_with_override(self):
bot = _make_bot()
cmd = AnnouncementsCommand(bot)
name, chan, override = cmd._parse_command("announce welcome override")
assert override is True
def test_trigger_channel_override(self):
bot = _make_bot()
cmd = AnnouncementsCommand(bot)
name, chan, override = cmd._parse_command("announce hello Public override")
assert name == "hello"
assert chan == "Public"
assert override is True
class TestRecordTrigger:
"""Tests for _record_trigger_execution and _is_trigger_locked."""
def test_record_sets_cooldown(self):
bot = _make_bot()
cmd = AnnouncementsCommand(bot)
cmd._record_trigger_execution("hello")
assert "hello" in cmd.trigger_cooldowns
def test_fresh_trigger_not_locked(self):
bot = _make_bot()
cmd = AnnouncementsCommand(bot)
assert cmd._is_trigger_locked("nonexistent") is False
def test_just_sent_trigger_is_locked(self):
bot = _make_bot()
cmd = AnnouncementsCommand(bot)
cmd._record_trigger_execution("hello")
assert cmd._is_trigger_locked("hello") is True
def test_old_trigger_not_locked(self):
bot = _make_bot()
cmd = AnnouncementsCommand(bot)
cmd.trigger_lockouts["hello"] = time.time() - 120 # 2 min ago
assert cmd._is_trigger_locked("hello") is False
class TestExecute:
"""Tests for execute()."""
def test_no_trigger_with_configured_triggers_shows_list(self):
from unittest.mock import AsyncMock
bot = _make_bot(acl_keys=[VALID_PUBKEY], triggers={"welcome": "Hello!"})
bot.command_manager.send_response = AsyncMock(return_value=True)
cmd = AnnouncementsCommand(bot)
cmd.send_response = AsyncMock(return_value=True)
msg = mock_message(content="announce", is_dm=True, sender_pubkey=VALID_PUBKEY)
import asyncio
result = asyncio.run(cmd.execute(msg))
assert result is True
cmd.send_response.assert_called_once()
def test_no_trigger_no_configured_triggers(self):
from unittest.mock import AsyncMock
bot = _make_bot(acl_keys=[VALID_PUBKEY])
cmd = AnnouncementsCommand(bot)
cmd.send_response = AsyncMock(return_value=True)
msg = mock_message(content="announce", is_dm=True, sender_pubkey=VALID_PUBKEY)
import asyncio
result = asyncio.run(cmd.execute(msg))
assert result is True
def test_list_subcommand_with_triggers(self):
from unittest.mock import AsyncMock
bot = _make_bot(acl_keys=[VALID_PUBKEY], triggers={"welcome": "Hello!"})
cmd = AnnouncementsCommand(bot)
cmd.send_response = AsyncMock(return_value=True)
msg = mock_message(content="announce list", is_dm=True, sender_pubkey=VALID_PUBKEY)
import asyncio
result = asyncio.run(cmd.execute(msg))
assert result is True
def test_list_subcommand_no_triggers(self):
from unittest.mock import AsyncMock
bot = _make_bot(acl_keys=[VALID_PUBKEY])
cmd = AnnouncementsCommand(bot)
cmd.send_response = AsyncMock(return_value=True)
msg = mock_message(content="announce list", is_dm=True, sender_pubkey=VALID_PUBKEY)
import asyncio
result = asyncio.run(cmd.execute(msg))
assert result is True
def test_unknown_trigger(self):
from unittest.mock import AsyncMock
bot = _make_bot(acl_keys=[VALID_PUBKEY], triggers={"welcome": "Hello!"})
cmd = AnnouncementsCommand(bot)
cmd.send_response = AsyncMock(return_value=True)
msg = mock_message(content="announce unknown_trigger", is_dm=True, sender_pubkey=VALID_PUBKEY)
import asyncio
result = asyncio.run(cmd.execute(msg))
assert result is True
cmd.send_response.assert_called_once()
def test_trigger_locked_prevents_send(self):
from unittest.mock import AsyncMock
bot = _make_bot(acl_keys=[VALID_PUBKEY], triggers={"welcome": "Hello!"})
cmd = AnnouncementsCommand(bot)
cmd.send_response = AsyncMock(return_value=True)
cmd._record_trigger_execution("welcome")
msg = mock_message(content="announce welcome", is_dm=True, sender_pubkey=VALID_PUBKEY)
import asyncio
result = asyncio.run(cmd.execute(msg))
assert result is True
# Should warn about lockout
call_args = cmd.send_response.call_args[0][1]
assert "just sent" in call_args.lower() or "wait" in call_args.lower()
def test_trigger_on_cooldown(self):
from unittest.mock import AsyncMock
bot = _make_bot(acl_keys=[VALID_PUBKEY], triggers={"welcome": "Hello!"})
cmd = AnnouncementsCommand(bot)
cmd.send_response = AsyncMock(return_value=True)
# Set cooldown but not lockout
cmd.trigger_cooldowns["welcome"] = time.time()
msg = mock_message(content="announce welcome", is_dm=True, sender_pubkey=VALID_PUBKEY)
import asyncio
result = asyncio.run(cmd.execute(msg))
assert result is True
def test_trigger_override_bypasses_cooldown(self):
from unittest.mock import AsyncMock
bot = _make_bot(acl_keys=[VALID_PUBKEY], triggers={"welcome": "Hello!"})
bot.command_manager.send_channel_message = AsyncMock(return_value=True)
cmd = AnnouncementsCommand(bot)
cmd.send_response = AsyncMock(return_value=True)
# Set cooldown
cmd.trigger_cooldowns["welcome"] = time.time()
msg = mock_message(content="announce welcome override", is_dm=True, sender_pubkey=VALID_PUBKEY)
import asyncio
result = asyncio.run(cmd.execute(msg))
assert result is True
def test_successful_announcement_send(self):
from unittest.mock import AsyncMock
bot = _make_bot(acl_keys=[VALID_PUBKEY], triggers={"welcome": "Hello!"})
bot.command_manager.send_channel_message = AsyncMock(return_value=True)
cmd = AnnouncementsCommand(bot)
cmd.send_response = AsyncMock(return_value=True)
msg = mock_message(content="announce welcome", is_dm=True, sender_pubkey=VALID_PUBKEY)
import asyncio
result = asyncio.run(cmd.execute(msg))
assert result is True
bot.command_manager.send_channel_message.assert_called_once()
def test_failed_announcement_send(self):
from unittest.mock import AsyncMock
bot = _make_bot(acl_keys=[VALID_PUBKEY], triggers={"welcome": "Hello!"})
bot.command_manager.send_channel_message = AsyncMock(return_value=False)
cmd = AnnouncementsCommand(bot)
cmd.send_response = AsyncMock(return_value=True)
msg = mock_message(content="announce welcome", is_dm=True, sender_pubkey=VALID_PUBKEY)
import asyncio
result = asyncio.run(cmd.execute(msg))
assert result is True
call_text = cmd.send_response.call_args[0][1]
assert "failed" in call_text.lower()
def test_trigger_with_custom_channel(self):
from unittest.mock import AsyncMock
bot = _make_bot(acl_keys=[VALID_PUBKEY], triggers={"welcome": "Hello!"})
bot.command_manager.send_channel_message = AsyncMock(return_value=True)
cmd = AnnouncementsCommand(bot)
cmd.send_response = AsyncMock(return_value=True)
msg = mock_message(content="announce welcome Emergency", is_dm=True, sender_pubkey=VALID_PUBKEY)
import asyncio
result = asyncio.run(cmd.execute(msg))
assert result is True
channel_arg = bot.command_manager.send_channel_message.call_args[0][0]
assert channel_arg == "Emergency"
def test_execute_exception_returns_false(self):
from unittest.mock import AsyncMock
bot = _make_bot(acl_keys=[VALID_PUBKEY], triggers={"welcome": "Hello!"})
bot.command_manager.send_channel_message = AsyncMock(side_effect=RuntimeError("oops"))
cmd = AnnouncementsCommand(bot)
cmd.send_response = AsyncMock(return_value=True)
msg = mock_message(content="announce welcome", is_dm=True, sender_pubkey=VALID_PUBKEY)
import asyncio
result = asyncio.run(cmd.execute(msg))
assert result is False