"""Tests for the DevToolsCog /dev refractor-test command.""" from unittest.mock import AsyncMock, MagicMock, patch import discord import pytest class TestCleanupView: """Test the cleanup button view for the refractor integration test. The view presents two buttons after a test run: 'Clean Up Test Data' deletes the synthetic game/plays/decisions; 'Keep Test Data' dismisses the buttons. Only the command invoker can press them. """ @pytest.fixture def owner_interaction(self): """Interaction from the user who ran the command.""" interaction = AsyncMock(spec=discord.Interaction) interaction.user = MagicMock() interaction.user.id = 12345 interaction.response = AsyncMock() return interaction @pytest.fixture def other_interaction(self): """Interaction from a different user — should be rejected.""" interaction = AsyncMock(spec=discord.Interaction) interaction.user = MagicMock() interaction.user.id = 99999 interaction.response = AsyncMock() return interaction async def test_view_has_two_buttons(self): """View should have exactly two buttons: cleanup and keep. Must be async because discord.ui.View.__init__ calls asyncio.get_running_loop() internally and requires an event loop. """ from cogs.dev_tools import CleanupView view = CleanupView(owner_id=12345, game_id=1, embed=discord.Embed()) buttons = [ child for child in view.children if isinstance(child, discord.ui.Button) ] assert len(buttons) == 2 async def test_unauthorized_user_ignored(self, other_interaction): """Non-owner clicks should be silently ignored.""" from cogs.dev_tools import CleanupView view = CleanupView(owner_id=12345, game_id=1, embed=discord.Embed()) with patch("cogs.dev_tools.db_delete", new_callable=AsyncMock) as mock_delete: await view.cleanup_btn.callback(other_interaction) mock_delete.assert_not_called() other_interaction.response.edit_message.assert_not_called() async def test_cleanup_calls_delete_endpoints(self, owner_interaction): """Cleanup button deletes decisions, plays, then game in order.""" from cogs.dev_tools import CleanupView embed = discord.Embed(description="test") view = CleanupView(owner_id=12345, game_id=42, embed=embed) with patch("cogs.dev_tools.db_delete", new_callable=AsyncMock) as mock_delete: await view.cleanup_btn.callback(owner_interaction) assert mock_delete.call_count == 3 # Verify correct endpoints and order calls = mock_delete.call_args_list assert "decisions/game" in str(calls[0]) assert "plays/game" in str(calls[1]) assert "games" in str(calls[2]) async def test_keep_removes_buttons(self, owner_interaction): """Keep button removes buttons and updates embed.""" from cogs.dev_tools import CleanupView embed = discord.Embed(description="test") view = CleanupView(owner_id=12345, game_id=42, embed=embed) await view.keep_btn.callback(owner_interaction) owner_interaction.response.edit_message.assert_called_once()