aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorsalmonik <raisersoft@proton.me>2026-04-29 14:22:03 +0200
committersalmonik <raisersoft@proton.me>2026-04-29 14:22:03 +0200
commitdba3388567818cb5c60be9d406d3e1cccc84b3ea (patch)
tree53a0903f7a4c381c7ad12e3e632ef8dd8ebba39f /tests
parentmultitude of changes. (diff)
downloadsenator-sperma-dba3388567818cb5c60be9d406d3e1cccc84b3ea.tar.gz
senator-sperma-dba3388567818cb5c60be9d406d3e1cccc84b3ea.zip
Refactor database connections and add integration tests
- Use context managers for sqlite3 connections in cron, vclogs, and database utils - Add URL helper utility for yt-dlp with clean_url_for_ytdlp function - Add slow integration test markers and new integration test files - Fix test imports to use conftest directly instead of tests.conftest - Improve downloader error handling and use COPYPARTY_URL_UPLOAD env var - Update pytest.ini with slow test markers and warning filters - Document slow test usage in AGENTS.md
Diffstat (limited to 'tests')
-rw-r--r--tests/cogs/test_ai.py2
-rw-r--r--tests/cogs/test_assistant.py2
-rw-r--r--tests/cogs/test_compression_conversion.py2
-rw-r--r--tests/cogs/test_cron.py85
-rw-r--r--tests/cogs/test_cron_integration.py39
-rw-r--r--tests/cogs/test_downloader.py2
-rw-r--r--tests/cogs/test_downloader_integration.py37
-rw-r--r--tests/cogs/test_math_commands.py2
-rw-r--r--tests/cogs/test_music_audio.py2
-rw-r--r--tests/cogs/test_music_external_play.py2
-rw-r--r--tests/cogs/test_music_integration.py44
-rw-r--r--tests/cogs/test_music_join.py2
-rw-r--r--tests/cogs/test_music_play.py2
-rw-r--r--tests/cogs/test_qr.py2
-rw-r--r--tests/cogs/test_say.py2
-rw-r--r--tests/cogs/test_search.py2
-rw-r--r--tests/cogs/test_trigger.py2
-rw-r--r--tests/cogs/test_trigger_integration.py54
-rw-r--r--tests/cogs/test_tts.py2
-rw-r--r--tests/cogs/test_uploader.py2
-rw-r--r--tests/cogs/test_vclogs.py2
-rw-r--r--tests/cogs/test_vclogs_integration.py86
-rw-r--r--tests/cogs/test_voice.py2
-rw-r--r--tests/cogs/test_web.py2
-rw-r--r--tests/conftest.py3
25 files changed, 325 insertions, 59 deletions
diff --git a/tests/cogs/test_ai.py b/tests/cogs/test_ai.py
index a1c3fd4..6d92f55 100644
--- a/tests/cogs/test_ai.py
+++ b/tests/cogs/test_ai.py
@@ -2,7 +2,7 @@ import pytest
2from unittest.mock import AsyncMock, patch, MagicMock 2from unittest.mock import AsyncMock, patch, MagicMock
3import discord 3import discord
4import httpx 4import httpx
5from tests.conftest import MockApplicationContext 5from conftest import MockApplicationContext
6 6
7 7
8@pytest.fixture 8@pytest.fixture
diff --git a/tests/cogs/test_assistant.py b/tests/cogs/test_assistant.py
index 9a33232..056a868 100644
--- a/tests/cogs/test_assistant.py
+++ b/tests/cogs/test_assistant.py
@@ -1,6 +1,6 @@
1import pytest 1import pytest
2from unittest.mock import AsyncMock, patch, MagicMock 2from unittest.mock import AsyncMock, patch, MagicMock
3from tests.conftest import MockApplicationContext 3from conftest import MockApplicationContext
4 4
5 5
6@pytest.fixture 6@pytest.fixture
diff --git a/tests/cogs/test_compression_conversion.py b/tests/cogs/test_compression_conversion.py
index f9d3427..892482b 100644
--- a/tests/cogs/test_compression_conversion.py
+++ b/tests/cogs/test_compression_conversion.py
@@ -2,7 +2,7 @@ import pytest
2from unittest.mock import AsyncMock, patch, MagicMock 2from unittest.mock import AsyncMock, patch, MagicMock
3import os 3import os
4import discord 4import discord
5from tests.conftest import MockApplicationContext 5from conftest import MockApplicationContext
6 6
7 7
8def test_compression_filetype_video(): 8def test_compression_filetype_video():
diff --git a/tests/cogs/test_cron.py b/tests/cogs/test_cron.py
index 91fcb82..28ccfe1 100644
--- a/tests/cogs/test_cron.py
+++ b/tests/cogs/test_cron.py
@@ -3,6 +3,7 @@ from datetime import datetime
3from unittest.mock import MagicMock, patch 3from unittest.mock import MagicMock, patch
4 4
5from cogs.cron import Cron 5from cogs.cron import Cron
6from cogs.utils.database import _ConnectionContextManager
6 7
7 8
8@pytest.fixture 9@pytest.fixture
@@ -15,35 +16,31 @@ def mock_bot():
15 return bot 16 return bot
16 17
17 18
18def create_cog(mock_bot, tmp_path): 19def create_cog(mock_bot):
19 with patch.object(Cron, "__init__", lambda self, bot: None): 20 with patch.object(Cron, "__init__", lambda self, bot: None):
20 with patch("cogs.cron.init_database"): 21 with patch("cogs.cron.init_database"):
21 mock_conn = MagicMock() 22 cog = Cron.__new__(Cron)
22 mock_cursor = MagicMock() 23 cog.bot = mock_bot
23 mock_cursor.fetchall.return_value = [] 24 cog.scheduled_tasks = []
24 mock_conn.cursor.return_value = mock_cursor 25 cog.task = MagicMock()
25 mock_conn.close.return_value = None 26 return cog
26
27 with patch("cogs.cron.get_connection", return_value=mock_conn):
28 with patch("cogs.utils.database.get_db_path", return_value=":memory:"):
29 cog = Cron.__new__(Cron)
30 cog.bot = mock_bot
31 cog.scheduled_tasks = []
32 cog.task = MagicMock()
33 return cog
34 27
35 28
36class TestLoadTasks: 29class TestLoadTasks:
37 def test_load_tasks_with_data(self, mock_bot, tmp_path): 30 def test_load_tasks_with_data(self, mock_bot):
38 mock_conn = MagicMock() 31 mock_conn = MagicMock()
39 mock_cursor = MagicMock() 32 mock_cursor = MagicMock()
40 mock_cursor.fetchall.return_value = [ 33 mock_cursor.fetchall.return_value = [
41 (1, "* * * * *", "audio", 123, 1, None, "test message") 34 (1, "* * * * *", "audio", 123, 1, None, "test message")
42 ] 35 ]
43 mock_conn.cursor.return_value = mock_cursor 36 mock_conn.cursor.return_value = mock_cursor
44 mock_conn.close.return_value = None
45 37
46 with patch("cogs.cron.get_connection", return_value=mock_conn): 38 # Create a context manager mock
39 mock_ctx = MagicMock()
40 mock_ctx.__enter__ = MagicMock(return_value=mock_conn)
41 mock_ctx.__exit__ = MagicMock(return_value=False)
42
43 with patch("cogs.cron.get_connection", return_value=mock_ctx):
47 cog = Cron.__new__(Cron) 44 cog = Cron.__new__(Cron)
48 cog.bot = mock_bot 45 cog.bot = mock_bot
49 cog.scheduled_tasks = [] 46 cog.scheduled_tasks = []
@@ -52,33 +49,41 @@ class TestLoadTasks:
52 assert len(cog.scheduled_tasks) == 1 49 assert len(cog.scheduled_tasks) == 1
53 assert cog.scheduled_tasks[0]["id"] == 1 50 assert cog.scheduled_tasks[0]["id"] == 1
54 51
55
56class TestSaveTasks: 52class TestSaveTasks:
57 def test_save_tasks(self, mock_bot, tmp_path): 53 def test_save_tasks(self, mock_bot):
58 cog = create_cog(mock_bot, tmp_path) 54 cog = create_cog(mock_bot)
59 cog.scheduled_tasks = [{"id": 1, "cron_expression": "* * * * *", "type": "audio", "channel_id": 123, "active": True}] 55 cog.scheduled_tasks = [{"id": 1, "cron_expression": "* * * * *", "type": "audio", "channel_id": 123, "active": True}]
60 cog.save_tasks()
61 assert len(cog.scheduled_tasks) == 1
62 56
57 mock_conn = MagicMock()
58 mock_cursor = MagicMock()
59 mock_conn.cursor.return_value = mock_cursor
60
61 # Create a context manager mock
62 mock_ctx = MagicMock()
63 mock_ctx.__enter__ = MagicMock(return_value=mock_conn)
64 mock_ctx.__exit__ = MagicMock(return_value=False)
65
66 with patch("cogs.cron.get_connection", return_value=mock_ctx):
67 cog.save_tasks()
68 assert len(cog.scheduled_tasks) == 1
63 69
64class TestShouldRunTask: 70class TestShouldRunTask:
65 def test_should_run_task_invalid_cron(self, mock_bot, tmp_path): 71 def test_should_run_task_invalid_cron(self, mock_bot):
66 cog = create_cog(mock_bot, tmp_path) 72 cog = create_cog(mock_bot)
67 task = {"cron_expression": "invalid"} 73 task = {"cron_expression": "invalid"}
68 now = datetime.now() 74 now = datetime.now()
69 result = cog.should_run_task(task, now) 75 result = cog.should_run_task(task, now)
70 assert result is False 76 assert result is False
71 77
72 def test_should_run_task_missing_cron_expression(self, mock_bot, tmp_path): 78 def test_should_run_task_missing_cron_expression(self, mock_bot):
73 cog = create_cog(mock_bot, tmp_path) 79 cog = create_cog(mock_bot)
74 task = {} 80 task = {}
75 now = datetime.now() 81 now = datetime.now()
76 result = cog.should_run_task(task, now) 82 result = cog.should_run_task(task, now)
77 assert result is False 83 assert result is False
78 84
79
80class TestCogUnload: 85class TestCogUnload:
81 def test_cog_unload_cancels_task(self, mock_bot, tmp_path): 86 def test_cog_unload_cancels_task(self, mock_bot):
82 with patch.object(Cron, "__init__", lambda self, bot: None): 87 with patch.object(Cron, "__init__", lambda self, bot: None):
83 cog = Cron.__new__(Cron) 88 cog = Cron.__new__(Cron)
84 cog.bot = mock_bot 89 cog.bot = mock_bot
@@ -87,10 +92,9 @@ class TestCogUnload:
87 cog.cog_unload() 92 cog.cog_unload()
88 cog.task.cancel.assert_called_once() 93 cog.task.cancel.assert_called_once()
89 94
90
91class TestTaskManagement: 95class TestTaskManagement:
92 def test_add_task_increments_id(self, mock_bot, tmp_path): 96 def test_add_task_increments_id(self, mock_bot):
93 cog = create_cog(mock_bot, tmp_path) 97 cog = create_cog(mock_bot)
94 cog.scheduled_tasks = [ 98 cog.scheduled_tasks = [
95 {"id": 1, "cron_expression": "* * * * *"}, 99 {"id": 1, "cron_expression": "* * * * *"},
96 {"id": 2, "cron_expression": "0 * * * *"}, 100 {"id": 2, "cron_expression": "0 * * * *"},
@@ -99,8 +103,8 @@ class TestTaskManagement:
99 new_id = max([t["id"] for t in cog.scheduled_tasks], default=0) + 1 103 new_id = max([t["id"] for t in cog.scheduled_tasks], default=0) + 1
100 assert new_id == 3 104 assert new_id == 3
101 105
102 def test_find_task_by_id(self, mock_bot, tmp_path): 106 def test_find_task_by_id(self, mock_bot):
103 cog = create_cog(mock_bot, tmp_path) 107 cog = create_cog(mock_bot)
104 cog.scheduled_tasks = [ 108 cog.scheduled_tasks = [
105 {"id": 1, "cron_expression": "* * * * *"}, 109 {"id": 1, "cron_expression": "* * * * *"},
106 {"id": 2, "cron_expression": "0 * * * *"}, 110 {"id": 2, "cron_expression": "0 * * * *"},
@@ -110,8 +114,8 @@ class TestTaskManagement:
110 assert found_task is not None 114 assert found_task is not None
111 assert found_task["cron_expression"] == "0 * * * *" 115 assert found_task["cron_expression"] == "0 * * * *"
112 116
113 def test_remove_task_by_index(self, mock_bot, tmp_path): 117 def test_remove_task_by_index(self, mock_bot):
114 cog = create_cog(mock_bot, tmp_path) 118 cog = create_cog(mock_bot)
115 cog.scheduled_tasks = [ 119 cog.scheduled_tasks = [
116 {"id": 1, "cron_expression": "* * * * *"}, 120 {"id": 1, "cron_expression": "* * * * *"},
117 {"id": 2, "cron_expression": "0 * * * *"}, 121 {"id": 2, "cron_expression": "0 * * * *"},
@@ -121,8 +125,8 @@ class TestTaskManagement:
121 assert len(cog.scheduled_tasks) == 1 125 assert len(cog.scheduled_tasks) == 1
122 assert cog.scheduled_tasks[0]["id"] == 2 126 assert cog.scheduled_tasks[0]["id"] == 2
123 127
124 def test_toggle_task_active(self, mock_bot, tmp_path): 128 def test_toggle_task_active(self, mock_bot):
125 cog = create_cog(mock_bot, tmp_path) 129 cog = create_cog(mock_bot)
126 cog.scheduled_tasks = [{"id": 1, "active": True}] 130 cog.scheduled_tasks = [{"id": 1, "active": True}]
127 131
128 cog.scheduled_tasks[0]["active"] = False 132 cog.scheduled_tasks[0]["active"] = False
@@ -131,14 +135,13 @@ class TestTaskManagement:
131 cog.scheduled_tasks[0]["active"] = True 135 cog.scheduled_tasks[0]["active"] = True
132 assert cog.scheduled_tasks[0]["active"] is True 136 assert cog.scheduled_tasks[0]["active"] is True
133 137
134
135class TestTaskTypes: 138class TestTaskTypes:
136 def test_task_type_audio_aliases(self, mock_bot, tmp_path): 139 def test_task_type_audio_aliases(self, mock_bot):
137 audio_types = ("zagraj_audio", "audio") 140 audio_types = ("zagraj_audio", "audio")
138 for task_type in audio_types: 141 for task_type in audio_types:
139 assert task_type in ("zagraj_audio", "audio") 142 assert task_type in ("zagraj_audio", "audio")
140 143
141 def test_task_type_message_aliases(self, mock_bot, tmp_path): 144 def test_task_type_message_aliases(self, mock_bot):
142 message_types = ("wyślij_wiadomość", "send_message") 145 message_types = ("wyślij_wiadomość", "send_message")
143 for task_type in message_types: 146 for task_type in message_types:
144 assert task_type in ("wyślij_wiadomość", "send_message") \ No newline at end of file 147 assert task_type in ("wyślij_wiadomość", "send_message")
diff --git a/tests/cogs/test_cron_integration.py b/tests/cogs/test_cron_integration.py
new file mode 100644
index 0000000..f56fc46
--- /dev/null
+++ b/tests/cogs/test_cron_integration.py
@@ -0,0 +1,39 @@
1import pytest
2import time
3from unittest.mock import MagicMock, AsyncMock, patch
4
5
6@pytest.fixture
7def cron_cog(bot):
8 # Patch create_task to prevent background task creation
9 with patch.object(bot.loop, 'create_task', return_value=MagicMock()):
10 from cogs.cron import Cron
11 cog = Cron(bot)
12 yield cog
13
14
15@pytest.mark.slow
16@pytest.mark.asyncio
17async def test_cron_job_execution(cron_cog):
18 """Test cron job executes when due."""
19 cog = cron_cog
20
21 job = {
22 "id": "test_job",
23 "cron_expression": "* * * * *",
24 "type": "message",
25 "channel_id": 456,
26 "active": True,
27 "message": "Test",
28 }
29 cog.scheduled_tasks.append(job)
30
31 # Mock the execute_task method
32 with patch.object(cog, 'execute_task', new_callable=AsyncMock) as mock_exec:
33 # Mock should_run_task to return True
34 with patch.object(cog, 'should_run_task', return_value=True):
35 # Test single iteration of schedule_checker logic
36 for task in cog.scheduled_tasks:
37 if task.get("active", True) and cog.should_run_task(task, None):
38 await cog.execute_task(task)
39 mock_exec.assert_called_once_with(job) \ No newline at end of file
diff --git a/tests/cogs/test_downloader.py b/tests/cogs/test_downloader.py
index 6ac8ae7..125a103 100644
--- a/tests/cogs/test_downloader.py
+++ b/tests/cogs/test_downloader.py
@@ -1,7 +1,7 @@
1import pytest 1import pytest
2from unittest.mock import AsyncMock, patch, MagicMock 2from unittest.mock import AsyncMock, patch, MagicMock
3import discord 3import discord
4from tests.conftest import MockApplicationContext, MockMessage 4from conftest import MockApplicationContext, MockMessage
5 5
6 6
7@pytest.fixture 7@pytest.fixture
diff --git a/tests/cogs/test_downloader_integration.py b/tests/cogs/test_downloader_integration.py
new file mode 100644
index 0000000..c7c9790
--- /dev/null
+++ b/tests/cogs/test_downloader_integration.py
@@ -0,0 +1,37 @@
1import pytest
2from unittest.mock import MagicMock, AsyncMock, patch
3
4
5@pytest.fixture
6def downloader_cog(bot):
7 from cogs.downloader import Downloader
8 cog = Downloader(bot)
9 return cog
10
11
12@pytest.mark.slow
13@pytest.mark.asyncio
14async def test_download_command_flow(downloader_cog, bot):
15 """Test download command with URL processing."""
16 cog = downloader_cog
17
18 ctx = MagicMock()
19 ctx.author = MagicMock()
20 ctx.guild = MagicMock()
21 ctx.channel = MagicMock()
22 ctx.respond = AsyncMock()
23 ctx.defer = AsyncMock()
24 ctx.followup = AsyncMock()
25 ctx.voice_client = None
26
27 # Mock the download_standalone static method
28 mock_response = {
29 "status": "success",
30 "type": "url",
31 "content": "http://example.com/video.mp4"
32 }
33
34 with patch.object(cog, 'download_standalone', new_callable=AsyncMock, return_value=mock_response):
35 await cog.download.callback(cog, ctx, url="http://youtube.com/test")
36
37 ctx.respond.assert_called_once()
diff --git a/tests/cogs/test_math_commands.py b/tests/cogs/test_math_commands.py
index 5c78027..21e9a2e 100644
--- a/tests/cogs/test_math_commands.py
+++ b/tests/cogs/test_math_commands.py
@@ -1,6 +1,6 @@
1import pytest 1import pytest
2from unittest.mock import AsyncMock, patch, MagicMock 2from unittest.mock import AsyncMock, patch, MagicMock
3from tests.conftest import MockApplicationContext 3from conftest import MockApplicationContext
4 4
5 5
6@pytest.fixture 6@pytest.fixture
diff --git a/tests/cogs/test_music_audio.py b/tests/cogs/test_music_audio.py
index 7ea902e..705f1b3 100644
--- a/tests/cogs/test_music_audio.py
+++ b/tests/cogs/test_music_audio.py
@@ -1,7 +1,7 @@
1import pytest 1import pytest
2from unittest.mock import AsyncMock, patch, MagicMock 2from unittest.mock import AsyncMock, patch, MagicMock
3import os 3import os
4from tests.conftest import MockApplicationContext 4from conftest import MockApplicationContext
5 5
6 6
7@pytest.mark.asyncio 7@pytest.mark.asyncio
diff --git a/tests/cogs/test_music_external_play.py b/tests/cogs/test_music_external_play.py
index fd4b625..2e9c5b1 100644
--- a/tests/cogs/test_music_external_play.py
+++ b/tests/cogs/test_music_external_play.py
@@ -3,7 +3,7 @@ import os
3import asyncio 3import asyncio
4import tempfile 4import tempfile
5from unittest.mock import AsyncMock, patch, MagicMock 5from unittest.mock import AsyncMock, patch, MagicMock
6from tests.conftest import MockApplicationContext 6from conftest import MockApplicationContext
7 7
8 8
9@pytest.fixture 9@pytest.fixture
diff --git a/tests/cogs/test_music_integration.py b/tests/cogs/test_music_integration.py
new file mode 100644
index 0000000..1c0260a
--- /dev/null
+++ b/tests/cogs/test_music_integration.py
@@ -0,0 +1,44 @@
1import pytest
2from unittest.mock import MagicMock, AsyncMock, patch, PropertyMock
3import sys
4sys.path.insert(0, '/home/salmon/Documents/botcode/senator-sperma/tests')
5from conftest import MockApplicationContext
6
7
8@pytest.fixture
9def music_cog(bot):
10 from cogs.music.play_commands import PlayCommands
11 cog = PlayCommands(bot)
12 return cog
13
14
15@pytest.mark.slow
16@pytest.mark.asyncio
17async def test_music_join_stop(music_cog, bot):
18 """Test music join and stop commands."""
19 from cogs.music.join import Join
20 from cogs.music.stop import Stop
21
22 join_cog = Join(bot)
23 stop_cog = Stop(bot)
24
25 ctx = MockApplicationContext(bot=bot)
26 ctx.author.voice = MagicMock()
27 ctx.author.voice.channel = MagicMock()
28 ctx.guild = MagicMock()
29
30 voice_client = AsyncMock()
31 ctx.voice_client = voice_client
32
33 # Test join
34 ctx.author.voice.channel.connect = AsyncMock(return_value=voice_client)
35 with patch.object(ctx, 'respond', new_callable=AsyncMock):
36 with patch.object(ctx.interaction, 'delete_original_response', new_callable=AsyncMock):
37 await join_cog.join.callback(join_cog, ctx)
38 ctx.author.voice.channel.connect.assert_called_once()
39
40 # Test stop
41 with patch.object(ctx, 'respond', new_callable=AsyncMock):
42 with patch.object(ctx.interaction, 'delete_original_response', new_callable=AsyncMock):
43 await stop_cog.stop.callback(stop_cog, ctx)
44 voice_client.disconnect.assert_called_once()
diff --git a/tests/cogs/test_music_join.py b/tests/cogs/test_music_join.py
index 3c7ca6e..0176759 100644
--- a/tests/cogs/test_music_join.py
+++ b/tests/cogs/test_music_join.py
@@ -1,6 +1,6 @@
1import pytest 1import pytest
2from unittest.mock import AsyncMock, patch, MagicMock 2from unittest.mock import AsyncMock, patch, MagicMock
3from tests.conftest import MockApplicationContext 3from conftest import MockApplicationContext
4 4
5 5
6@pytest.fixture 6@pytest.fixture
diff --git a/tests/cogs/test_music_play.py b/tests/cogs/test_music_play.py
index e720c9a..7684729 100644
--- a/tests/cogs/test_music_play.py
+++ b/tests/cogs/test_music_play.py
@@ -1,7 +1,7 @@
1import pytest 1import pytest
2from unittest.mock import AsyncMock, patch, MagicMock 2from unittest.mock import AsyncMock, patch, MagicMock
3import os 3import os
4from tests.conftest import MockApplicationContext 4from conftest import MockApplicationContext
5 5
6 6
7@pytest.fixture 7@pytest.fixture
diff --git a/tests/cogs/test_qr.py b/tests/cogs/test_qr.py
index 2a132ca..b5cb31f 100644
--- a/tests/cogs/test_qr.py
+++ b/tests/cogs/test_qr.py
@@ -2,7 +2,7 @@ import pytest
2from unittest.mock import AsyncMock, patch, MagicMock, mock_open 2from unittest.mock import AsyncMock, patch, MagicMock, mock_open
3import os 3import os
4from cogs.qr import QR 4from cogs.qr import QR
5from tests.conftest import MockApplicationContext 5from conftest import MockApplicationContext
6 6
7 7
8@pytest.fixture 8@pytest.fixture
diff --git a/tests/cogs/test_say.py b/tests/cogs/test_say.py
index 13cad40..76f1b94 100644
--- a/tests/cogs/test_say.py
+++ b/tests/cogs/test_say.py
@@ -1,7 +1,7 @@
1import pytest 1import pytest
2from unittest.mock import AsyncMock, patch 2from unittest.mock import AsyncMock, patch
3from cogs.say import Say 3from cogs.say import Say
4from tests.conftest import MockApplicationContext 4from conftest import MockApplicationContext
5 5
6 6
7@pytest.fixture 7@pytest.fixture
diff --git a/tests/cogs/test_search.py b/tests/cogs/test_search.py
index 8c04dfc..c9c7d0e 100644
--- a/tests/cogs/test_search.py
+++ b/tests/cogs/test_search.py
@@ -1,7 +1,7 @@
1import pytest 1import pytest
2from unittest.mock import AsyncMock, patch, MagicMock 2from unittest.mock import AsyncMock, patch, MagicMock
3import discord 3import discord
4from tests.conftest import MockApplicationContext 4from conftest import MockApplicationContext
5 5
6 6
7@pytest.fixture 7@pytest.fixture
diff --git a/tests/cogs/test_trigger.py b/tests/cogs/test_trigger.py
index 6aaaa11..a60a139 100644
--- a/tests/cogs/test_trigger.py
+++ b/tests/cogs/test_trigger.py
@@ -1,7 +1,7 @@
1import pytest 1import pytest
2from unittest.mock import AsyncMock, patch, MagicMock 2from unittest.mock import AsyncMock, patch, MagicMock
3import sqlite3 3import sqlite3
4from tests.conftest import MockApplicationContext 4from conftest import MockApplicationContext
5 5
6 6
7@pytest.fixture 7@pytest.fixture
diff --git a/tests/cogs/test_trigger_integration.py b/tests/cogs/test_trigger_integration.py
new file mode 100644
index 0000000..4828387
--- /dev/null
+++ b/tests/cogs/test_trigger_integration.py
@@ -0,0 +1,54 @@
1import pytest
2from unittest.mock import MagicMock, AsyncMock, patch
3from cogs.trigger import Trigger
4
5
6@pytest.fixture
7def trigger_cog(bot):
8 from cogs.utils.database import init_database
9 init_database()
10 from cogs.trigger import Trigger
11 cog = Trigger(bot)
12 return cog
13
14
15@pytest.mark.slow
16@pytest.mark.asyncio
17async def test_trigger_response_flow(trigger_cog):
18 """Test trigger creation and response in a simulated message flow."""
19 cog = trigger_cog
20
21 ctx = MagicMock()
22 ctx.author = MagicMock()
23 ctx.author.id = 123
24 ctx.guild = MagicMock()
25 ctx.guild.id = 456
26 ctx.respond = AsyncMock()
27
28 # Create a trigger using the create command
29 await cog.create.callback(cog, ctx, name="test_trigger")
30
31 # Add a keyword and response
32 await cog.add.callback(cog, ctx, name="test_trigger", trigger="hello", response="world")
33
34 # Verify trigger was created - check DB
35 from cogs.utils.database import get_connection
36 with get_connection() as conn:
37 c = conn.cursor()
38 c.execute("SELECT id FROM triggers WHERE name=?", ("test_trigger",))
39 result = c.fetchone()
40 assert result is not None
41
42 # Simulate a message that should trigger
43 message = MagicMock()
44 message.author = ctx.author
45 message.author.bot = False
46 message.guild = ctx.guild
47 message.content = "say hello to me"
48 message.channel.send = AsyncMock() # Make send awaitable
49
50 # Check if trigger matches using the static method
51 await Trigger.trigger(message)
52
53 # Verify message.channel.send was called with the response
54 message.channel.send.assert_called_once_with("world")
diff --git a/tests/cogs/test_tts.py b/tests/cogs/test_tts.py
index de68580..c6563e6 100644
--- a/tests/cogs/test_tts.py
+++ b/tests/cogs/test_tts.py
@@ -1,7 +1,7 @@
1import pytest 1import pytest
2from unittest.mock import AsyncMock, patch, MagicMock 2from unittest.mock import AsyncMock, patch, MagicMock
3import discord 3import discord
4from tests.conftest import MockApplicationContext 4from conftest import MockApplicationContext
5 5
6 6
7@pytest.fixture 7@pytest.fixture
diff --git a/tests/cogs/test_uploader.py b/tests/cogs/test_uploader.py
index addf87e..0777126 100644
--- a/tests/cogs/test_uploader.py
+++ b/tests/cogs/test_uploader.py
@@ -2,7 +2,7 @@ import pytest
2from unittest.mock import AsyncMock, patch, MagicMock 2from unittest.mock import AsyncMock, patch, MagicMock
3import os 3import os
4import discord 4import discord
5from tests.conftest import MockApplicationContext, MockMessage, MockUser 5from conftest import MockApplicationContext, MockMessage, MockUser
6 6
7 7
8@pytest.fixture 8@pytest.fixture
diff --git a/tests/cogs/test_vclogs.py b/tests/cogs/test_vclogs.py
index da41482..17ff839 100644
--- a/tests/cogs/test_vclogs.py
+++ b/tests/cogs/test_vclogs.py
@@ -1,6 +1,6 @@
1import pytest 1import pytest
2from unittest.mock import AsyncMock, patch, MagicMock 2from unittest.mock import AsyncMock, patch, MagicMock
3from tests.conftest import MockApplicationContext 3from conftest import MockApplicationContext
4 4
5 5
6def test_format_duration(): 6def test_format_duration():
diff --git a/tests/cogs/test_vclogs_integration.py b/tests/cogs/test_vclogs_integration.py
new file mode 100644
index 0000000..9391876
--- /dev/null
+++ b/tests/cogs/test_vclogs_integration.py
@@ -0,0 +1,86 @@
1import pytest
2import asyncio
3from unittest.mock import MagicMock, AsyncMock, patch
4
5
6@pytest.fixture
7def vclogs_cog(bot):
8 from cogs.vclogs import VcLogs
9 cog = VcLogs(bot)
10 return cog
11
12
13@pytest.mark.slow
14@pytest.mark.asyncio
15async def test_full_voice_session_tracking(vclogs_cog, monkeypatch):
16 """Test complete voice session with AFK transitions using real time sleeps."""
17 cog = vclogs_cog
18
19 # Set up environment
20 monkeypatch.setenv("STATUS_CHANNEL_ID", "123456")
21
22 member = MagicMock()
23 member.id = 123
24 member.guild.id = 456
25 member.bot = False
26 member.display_name = "TestUser"
27 member.guild.get_member.return_value = member
28
29 # Create a mock log channel
30 log_channel = MagicMock()
31 log_channel.send = AsyncMock(return_value=MagicMock())
32
33 # Mock bot.get_channel to return our log channel
34 with patch.object(vclogs_cog.bot, 'get_channel', return_value=log_channel):
35 # Join voice
36 before = MagicMock()
37 before.channel = None
38 after_join = MagicMock()
39 after_join.channel = MagicMock()
40 after_join.channel.id = 789
41 after_join.channel.name = "Test Voice"
42 after_join.self_deaf = False
43 after_join.self_mute = False
44 after_join.deaf = False
45 after_join.mute = False
46 await cog.on_voice_state_update(member, before, after_join)
47
48 await asyncio.sleep(2) # Simulate 2s active
49
50 # Go AFK (self_deaf)
51 before_afk = after_join
52 after_afk = MagicMock()
53 after_afk.channel = before_afk.channel
54 after_afk.self_deaf = True
55 after_afk.self_mute = False
56 after_afk.deaf = True
57 after_afk.mute = False
58 await cog.on_voice_state_update(member, before_afk, after_afk)
59
60 await asyncio.sleep(3) # Simulate 3s AFK
61
62 # Return from AFK
63 before_back = after_afk
64 after_back = MagicMock()
65 after_back.channel = before_back.channel
66 after_back.self_deaf = False
67 after_back.self_mute = False
68 after_back.deaf = False
69 after_back.mute = False
70 await cog.on_voice_state_update(member, before_back, after_back)
71
72 await asyncio.sleep(1) # Simulate 1s active
73
74 # Leave voice
75 before_leave = after_back
76 after_leave = MagicMock()
77 after_leave.channel = None
78 after_leave.self_deaf = False
79 after_leave.self_mute = False
80 await cog.on_voice_state_update(member, before_leave, after_leave)
81
82 # Verify activity was saved (check DB)
83 from cogs.vclogs import load_activity_data
84 activity_data = load_activity_data()
85 # After leaving, data should be deleted, so check the log channel was called
86 assert log_channel.send.called
diff --git a/tests/cogs/test_voice.py b/tests/cogs/test_voice.py
index ade29cb..104eea6 100644
--- a/tests/cogs/test_voice.py
+++ b/tests/cogs/test_voice.py
@@ -1,7 +1,7 @@
1import pytest 1import pytest
2from unittest.mock import AsyncMock, patch, MagicMock 2from unittest.mock import AsyncMock, patch, MagicMock
3import asyncio 3import asyncio
4from tests.conftest import MockApplicationContext 4from conftest import MockApplicationContext
5 5
6 6
7@pytest.mark.asyncio 7@pytest.mark.asyncio
diff --git a/tests/cogs/test_web.py b/tests/cogs/test_web.py
index e0d2208..4de2e8b 100644
--- a/tests/cogs/test_web.py
+++ b/tests/cogs/test_web.py
@@ -1,7 +1,7 @@
1import pytest 1import pytest
2from unittest.mock import AsyncMock, patch, MagicMock 2from unittest.mock import AsyncMock, patch, MagicMock
3from cogs.web import Web 3from cogs.web import Web
4from tests.conftest import MockApplicationContext 4from conftest import MockApplicationContext
5 5
6 6
7@pytest.fixture 7@pytest.fixture
diff --git a/tests/conftest.py b/tests/conftest.py
index 01ece10..6b7c3bc 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -1,5 +1,6 @@
1import asyncio 1import asyncio
2import os 2import os
3import sys
3import tempfile 4import tempfile
4from unittest.mock import AsyncMock, MagicMock 5from unittest.mock import AsyncMock, MagicMock
5import pytest 6import pytest
@@ -7,6 +8,8 @@ import pytest
7import discord 8import discord
8from discord.ext import commands 9from discord.ext import commands
9 10
11sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
12
10 13
11@pytest.fixture(autouse=True, scope="function") 14@pytest.fixture(autouse=True, scope="function")
12def setup_test_env(monkeypatch): 15def setup_test_env(monkeypatch):