aboutsummaryrefslogtreecommitdiffstats
path: root/tests/cogs
diff options
context:
space:
mode:
Diffstat (limited to '')
-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.py129
-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.py226
-rw-r--r--tests/cogs/test_vclogs.py33
-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
24 files changed, 576 insertions, 104 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 93031f8..28ccfe1 100644
--- a/tests/cogs/test_cron.py
+++ b/tests/cogs/test_cron.py
@@ -1,10 +1,9 @@
1import json
2import os
3import pytest 1import pytest
4from datetime import datetime 2from datetime import datetime
5from unittest.mock import MagicMock, patch 3from unittest.mock import MagicMock, patch
6 4
7from cogs.cron import Cron 5from cogs.cron import Cron
6from cogs.utils.database import _ConnectionContextManager
8 7
9 8
10@pytest.fixture 9@pytest.fixture
@@ -17,90 +16,74 @@ def mock_bot():
17 return bot 16 return bot
18 17
19 18
20def create_cog(mock_bot, tmp_path): 19def create_cog(mock_bot):
21 with patch.object(Cron, "__init__", lambda self, bot: None): 20 with patch.object(Cron, "__init__", lambda self, bot: None):
22 cog = Cron.__new__(Cron) 21 with patch("cogs.cron.init_database"):
23 cog.bot = mock_bot 22 cog = Cron.__new__(Cron)
24 cog.scheduled_tasks = [] 23 cog.bot = mock_bot
25 cog.cron_file = str(tmp_path / "cron_jobs.json") 24 cog.scheduled_tasks = []
26 cog.task = MagicMock() 25 cog.task = MagicMock()
27 return cog 26 return cog
28 27
29 28
30class TestLoadTasks: 29class TestLoadTasks:
31 def test_load_tasks_file_not_exists(self, mock_bot, tmp_path): 30 def test_load_tasks_with_data(self, mock_bot):
32 cog = create_cog(mock_bot, tmp_path) 31 mock_conn = MagicMock()
33 cog.cron_file = str(tmp_path / "nonexistent.json") 32 mock_cursor = MagicMock()
34 cog.load_tasks() 33 mock_cursor.fetchall.return_value = [
35 assert cog.scheduled_tasks == [] 34 (1, "* * * * *", "audio", 123, 1, None, "test message")
36 35 ]
37 def test_load_tasks_empty_file(self, mock_bot, tmp_path): 36 mock_conn.cursor.return_value = mock_cursor
38 cron_file = tmp_path / "empty.json"
39 cron_file.write_text("")
40 cog = create_cog(mock_bot, tmp_path)
41 cog.cron_file = str(cron_file)
42 cog.load_tasks()
43 assert cog.scheduled_tasks == []
44
45 def test_load_tasks_valid_file(self, mock_bot, tmp_path):
46 cron_file = tmp_path / "valid.json"
47 tasks = [{"id": 1, "cron_expression": "* * * * *", "type": "audio", "channel_id": 123, "active": True}]
48 cron_file.write_text(json.dumps(tasks))
49 cog = create_cog(mock_bot, tmp_path)
50 cog.cron_file = str(cron_file)
51 cog.load_tasks()
52 assert len(cog.scheduled_tasks) == 1
53 assert cog.scheduled_tasks[0]["id"] == 1
54 37
55 def test_load_tasks_io_error(self, mock_bot, tmp_path): 38 # Create a context manager mock
56 cog = create_cog(mock_bot, tmp_path) 39 mock_ctx = MagicMock()
57 with patch("builtins.open", side_effect=IOError("test error")): 40 mock_ctx.__enter__ = MagicMock(return_value=mock_conn)
58 cog.load_tasks() 41 mock_ctx.__exit__ = MagicMock(return_value=False)
59 assert cog.scheduled_tasks == []
60 42
43 with patch("cogs.cron.get_connection", return_value=mock_ctx):
44 cog = Cron.__new__(Cron)
45 cog.bot = mock_bot
46 cog.scheduled_tasks = []
47 cog.task = MagicMock()
48 cog.load_tasks()
49 assert len(cog.scheduled_tasks) == 1
50 assert cog.scheduled_tasks[0]["id"] == 1
61 51
62class TestSaveTasks: 52class TestSaveTasks:
63 def test_save_tasks_with_real_file(self, mock_bot, tmp_path): 53 def test_save_tasks(self, mock_bot):
64 cron_file = tmp_path / "cron_jobs.json" 54 cog = create_cog(mock_bot)
65 cog = create_cog(mock_bot, tmp_path) 55 cog.scheduled_tasks = [{"id": 1, "cron_expression": "* * * * *", "type": "audio", "channel_id": 123, "active": True}]
66 cog.cron_file = str(cron_file)
67 cog.scheduled_tasks = [{"id": 1, "cron_expression": "* * * * *"}]
68 cog.save_tasks()
69 assert os.path.exists(cron_file)
70 with open(cron_file) as f:
71 loaded = json.load(f)
72 assert len(loaded) == 1
73 56
74 def test_save_tasks_creates_directory_structure(self, mock_bot, tmp_path): 57 mock_conn = MagicMock()
75 cog = create_cog(mock_bot, tmp_path) 58 mock_cursor = MagicMock()
76 new_dir = tmp_path / "new_dir" 59 mock_conn.cursor.return_value = mock_cursor
77 cron_file = new_dir / "cron_jobs.json"
78 cog.cron_file = str(cron_file)
79 cog.scheduled_tasks = [{"id": 1}]
80 60
81 cog.save_tasks() 61 # Create a context manager mock
82 62 mock_ctx = MagicMock()
83 assert os.path.exists(new_dir) 63 mock_ctx.__enter__ = MagicMock(return_value=mock_conn)
64 mock_ctx.__exit__ = MagicMock(return_value=False)
84 65
66 with patch("cogs.cron.get_connection", return_value=mock_ctx):
67 cog.save_tasks()
68 assert len(cog.scheduled_tasks) == 1
85 69
86class TestShouldRunTask: 70class TestShouldRunTask:
87 def test_should_run_task_invalid_cron(self, mock_bot, tmp_path): 71 def test_should_run_task_invalid_cron(self, mock_bot):
88 cog = create_cog(mock_bot, tmp_path) 72 cog = create_cog(mock_bot)
89 task = {"cron_expression": "invalid"} 73 task = {"cron_expression": "invalid"}
90 now = datetime.now() 74 now = datetime.now()
91 result = cog.should_run_task(task, now) 75 result = cog.should_run_task(task, now)
92 assert result is False 76 assert result is False
93 77
94 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):
95 cog = create_cog(mock_bot, tmp_path) 79 cog = create_cog(mock_bot)
96 task = {} 80 task = {}
97 now = datetime.now() 81 now = datetime.now()
98 result = cog.should_run_task(task, now) 82 result = cog.should_run_task(task, now)
99 assert result is False 83 assert result is False
100 84
101
102class TestCogUnload: 85class TestCogUnload:
103 def test_cog_unload_cancels_task(self, mock_bot, tmp_path): 86 def test_cog_unload_cancels_task(self, mock_bot):
104 with patch.object(Cron, "__init__", lambda self, bot: None): 87 with patch.object(Cron, "__init__", lambda self, bot: None):
105 cog = Cron.__new__(Cron) 88 cog = Cron.__new__(Cron)
106 cog.bot = mock_bot 89 cog.bot = mock_bot
@@ -109,10 +92,9 @@ class TestCogUnload:
109 cog.cog_unload() 92 cog.cog_unload()
110 cog.task.cancel.assert_called_once() 93 cog.task.cancel.assert_called_once()
111 94
112
113class TestTaskManagement: 95class TestTaskManagement:
114 def test_add_task_increments_id(self, mock_bot, tmp_path): 96 def test_add_task_increments_id(self, mock_bot):
115 cog = create_cog(mock_bot, tmp_path) 97 cog = create_cog(mock_bot)
116 cog.scheduled_tasks = [ 98 cog.scheduled_tasks = [
117 {"id": 1, "cron_expression": "* * * * *"}, 99 {"id": 1, "cron_expression": "* * * * *"},
118 {"id": 2, "cron_expression": "0 * * * *"}, 100 {"id": 2, "cron_expression": "0 * * * *"},
@@ -121,8 +103,8 @@ class TestTaskManagement:
121 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
122 assert new_id == 3 104 assert new_id == 3
123 105
124 def test_find_task_by_id(self, mock_bot, tmp_path): 106 def test_find_task_by_id(self, mock_bot):
125 cog = create_cog(mock_bot, tmp_path) 107 cog = create_cog(mock_bot)
126 cog.scheduled_tasks = [ 108 cog.scheduled_tasks = [
127 {"id": 1, "cron_expression": "* * * * *"}, 109 {"id": 1, "cron_expression": "* * * * *"},
128 {"id": 2, "cron_expression": "0 * * * *"}, 110 {"id": 2, "cron_expression": "0 * * * *"},
@@ -132,8 +114,8 @@ class TestTaskManagement:
132 assert found_task is not None 114 assert found_task is not None
133 assert found_task["cron_expression"] == "0 * * * *" 115 assert found_task["cron_expression"] == "0 * * * *"
134 116
135 def test_remove_task_by_index(self, mock_bot, tmp_path): 117 def test_remove_task_by_index(self, mock_bot):
136 cog = create_cog(mock_bot, tmp_path) 118 cog = create_cog(mock_bot)
137 cog.scheduled_tasks = [ 119 cog.scheduled_tasks = [
138 {"id": 1, "cron_expression": "* * * * *"}, 120 {"id": 1, "cron_expression": "* * * * *"},
139 {"id": 2, "cron_expression": "0 * * * *"}, 121 {"id": 2, "cron_expression": "0 * * * *"},
@@ -143,8 +125,8 @@ class TestTaskManagement:
143 assert len(cog.scheduled_tasks) == 1 125 assert len(cog.scheduled_tasks) == 1
144 assert cog.scheduled_tasks[0]["id"] == 2 126 assert cog.scheduled_tasks[0]["id"] == 2
145 127
146 def test_toggle_task_active(self, mock_bot, tmp_path): 128 def test_toggle_task_active(self, mock_bot):
147 cog = create_cog(mock_bot, tmp_path) 129 cog = create_cog(mock_bot)
148 cog.scheduled_tasks = [{"id": 1, "active": True}] 130 cog.scheduled_tasks = [{"id": 1, "active": True}]
149 131
150 cog.scheduled_tasks[0]["active"] = False 132 cog.scheduled_tasks[0]["active"] = False
@@ -153,14 +135,13 @@ class TestTaskManagement:
153 cog.scheduled_tasks[0]["active"] = True 135 cog.scheduled_tasks[0]["active"] = True
154 assert cog.scheduled_tasks[0]["active"] is True 136 assert cog.scheduled_tasks[0]["active"] is True
155 137
156
157class TestTaskTypes: 138class TestTaskTypes:
158 def test_task_type_audio_aliases(self, mock_bot, tmp_path): 139 def test_task_type_audio_aliases(self, mock_bot):
159 audio_types = ("zagraj_audio", "audio") 140 audio_types = ("zagraj_audio", "audio")
160 for task_type in audio_types: 141 for task_type in audio_types:
161 assert task_type in ("zagraj_audio", "audio") 142 assert task_type in ("zagraj_audio", "audio")
162 143
163 def test_task_type_message_aliases(self, mock_bot, tmp_path): 144 def test_task_type_message_aliases(self, mock_bot):
164 message_types = ("wyślij_wiadomość", "send_message") 145 message_types = ("wyślij_wiadomość", "send_message")
165 for task_type in message_types: 146 for task_type in message_types:
166 assert task_type in ("wyślij_wiadomość", "send_message") 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
new file mode 100644
index 0000000..0777126
--- /dev/null
+++ b/tests/cogs/test_uploader.py
@@ -0,0 +1,226 @@
1import pytest
2from unittest.mock import AsyncMock, patch, MagicMock
3import os
4import discord
5from conftest import MockApplicationContext, MockMessage, MockUser
6
7
8@pytest.fixture
9def uploader_cog(bot):
10 with patch('cogs.utils.temp.get_temp_dir', return_value='/tmp/test'):
11 from cogs.uploader import Uploader
12 cog = Uploader(bot)
13 return cog
14
15
16@pytest.mark.asyncio
17async def test_file_autocomplete(uploader_cog):
18 mock_autocomplete_ctx = MagicMock()
19 mock_autocomplete_ctx.value = "test"
20 mock_interaction = MagicMock()
21 mock_user = MockUser(id=12345)
22 mock_interaction.user = mock_user
23 mock_autocomplete_ctx.interaction = mock_interaction
24
25 with patch('cogs.utils.file_search.UniversalSearcher.search_copyparty_autocomplete', new_callable=AsyncMock) as mock_search:
26 mock_search.return_value = [
27 "https://copyparty.local/testfile1.mp3",
28 "https://copyparty.local/testfile2.mp3"
29 ]
30 result = await uploader_cog._file_autocomplete(mock_autocomplete_ctx)
31 assert len(result) == 2
32 assert result[0].name == "testfile1.mp3"
33
34
35@pytest.mark.asyncio
36async def test_file_autocomplete_empty_query(uploader_cog):
37 mock_autocomplete_ctx = MagicMock()
38 mock_autocomplete_ctx.value = ""
39 mock_interaction = MagicMock()
40 mock_interaction.user = MockUser(id=12345)
41 mock_autocomplete_ctx.interaction = mock_interaction
42
43 with patch('cogs.utils.file_search.UniversalSearcher.search_copyparty_autocomplete', new_callable=AsyncMock) as mock_search:
44 result = await uploader_cog._file_autocomplete(mock_autocomplete_ctx)
45 mock_search.assert_called_once_with("", max_results=25)
46
47
48@pytest.mark.asyncio
49async def test_download_from_url_success(uploader_cog):
50 with patch('requests.get') as mock_get:
51 mock_response = MagicMock()
52 mock_response.status_code = 200
53 mock_response.content = b"test file content"
54 mock_get.return_value = mock_response
55
56 mock_file = MagicMock()
57 mock_file.write = AsyncMock()
58
59 mock_cm = AsyncMock()
60 mock_cm.__aenter__ = AsyncMock(return_value=mock_file)
61 mock_cm.__aexit__ = AsyncMock(return_value=None)
62
63 with patch('cogs.uploader.aiofiles.open', return_value=mock_cm):
64 result = await uploader_cog._download_from_url("https://copyparty.local/file.txt")
65
66 assert result is not None
67 assert "file.txt" in result
68 mock_file.write.assert_called_once_with(b"test file content")
69
70
71@pytest.mark.asyncio
72async def test_download_from_url_failure(uploader_cog):
73 with patch('requests.get') as mock_get:
74 mock_response = MagicMock()
75 mock_response.status_code = 404
76 mock_get.return_value = mock_response
77
78 result = await uploader_cog._download_from_url("https://copyparty.local/notfound.txt")
79
80 assert result is None
81
82
83@pytest.mark.asyncio
84async def test_upload_to_copyparty_success(uploader_cog):
85 mock_file = MagicMock()
86 mock_file.read = AsyncMock(return_value=b"test content")
87
88 mock_cm = AsyncMock()
89 mock_cm.__aenter__ = AsyncMock(return_value=mock_file)
90 mock_cm.__aexit__ = AsyncMock(return_value=None)
91
92 with patch('cogs.uploader.aiofiles.open', return_value=mock_cm):
93 with patch('requests.post') as mock_post:
94 mock_response = MagicMock()
95 mock_response.status_code = 200
96 mock_response.json.return_value = {"fileurl": "https://copyparty.local/uploaded.txt"}
97 mock_post.return_value = mock_response
98
99 with patch.dict(os.environ, {"COPYPARTY_PASSWORD": "testpass"}):
100 result = await uploader_cog._upload_to_copyparty("/tmp/testfile.txt")
101
102 assert result == "https://copyparty.local/uploaded.txt"
103
104
105@pytest.mark.asyncio
106async def test_upload_to_copyparty_failure(uploader_cog):
107 mock_file = MagicMock()
108 mock_file.read = AsyncMock(return_value=b"test content")
109
110 mock_cm = AsyncMock()
111 mock_cm.__aenter__ = AsyncMock(return_value=mock_file)
112 mock_cm.__aexit__ = AsyncMock(return_value=None)
113
114 with patch('cogs.uploader.aiofiles.open', return_value=mock_cm):
115 with patch('requests.post') as mock_post:
116 mock_response = MagicMock()
117 mock_response.status_code = 500
118 mock_post.return_value = mock_response
119
120 result = await uploader_cog._upload_to_copyparty("/tmp/testfile.txt")
121
122 assert result is None
123
124
125@pytest.mark.asyncio
126async def test_upload_file_no_url(uploader_cog):
127 uploader_cog._uploadurl = None
128
129 result = await uploader_cog._upload_file("/tmp/test.txt")
130
131 assert result is None
132
133
134@pytest.mark.asyncio
135async def test_message_upload_no_attachments(uploader_cog, mock_channel, mock_guild, bot):
136 ctx = MockApplicationContext(bot=bot, channel=mock_channel, guild=mock_guild)
137
138 mock_message = MagicMock()
139 mock_message.attachments = []
140
141 with patch.object(ctx, 'respond', new_callable=AsyncMock) as mock_respond:
142 await uploader_cog.message_upload.callback(uploader_cog, ctx, message=mock_message)
143 mock_respond.assert_called_once_with("Brak załączników w wiadomości", ephemeral=True)
144
145
146@pytest.mark.asyncio
147async def test_message_upload_success(uploader_cog, mock_channel, mock_guild, bot):
148 ctx = MockApplicationContext(bot=bot, channel=mock_channel, guild=mock_guild)
149
150 mock_attachment = MagicMock()
151 mock_attachment.filename = "test.txt"
152 mock_attachment.save = AsyncMock()
153
154 mock_message = MagicMock()
155 mock_message.attachments = [mock_attachment]
156
157 with patch.object(uploader_cog, '_upload_file', new_callable=AsyncMock) as mock_upload:
158 mock_upload.return_value = "https://copyparty.local/uploaded.txt"
159
160 with patch('os.path.exists', return_value=True):
161 with patch('os.remove'):
162 with patch.object(ctx, 'defer', new_callable=AsyncMock):
163 with patch.object(ctx, 'respond', new_callable=AsyncMock) as mock_respond:
164 await uploader_cog.message_upload.callback(uploader_cog, ctx, message=mock_message)
165 mock_respond.assert_called_once()
166 assert "Wysłano" in str(mock_respond.call_args)
167
168
169@pytest.mark.asyncio
170async def test_message_upload_all_failed(uploader_cog, mock_channel, mock_guild, bot):
171 ctx = MockApplicationContext(bot=bot, channel=mock_channel, guild=mock_guild)
172
173 mock_attachment = MagicMock()
174 mock_attachment.filename = "test.txt"
175 mock_attachment.save = AsyncMock()
176
177 mock_message = MagicMock()
178 mock_message.attachments = [mock_attachment]
179
180 with patch.object(uploader_cog, '_upload_file', new_callable=AsyncMock) as mock_upload:
181 mock_upload.return_value = None
182
183 with patch('os.path.exists', return_value=True):
184 with patch('os.remove'):
185 with patch.object(ctx, 'defer', new_callable=AsyncMock):
186 with patch.object(ctx, 'respond', new_callable=AsyncMock) as mock_respond:
187 await uploader_cog.message_upload.callback(uploader_cog, ctx, message=mock_message)
188 mock_respond.assert_called_once_with("Nie udało się wysłać żadnych plików", ephemeral=True)
189
190
191@pytest.mark.asyncio
192async def test_upload_slash_command_success(uploader_cog, mock_channel, mock_guild, bot):
193 ctx = MockApplicationContext(bot=bot, channel=mock_channel, guild=mock_guild)
194
195 mock_attachment = MagicMock()
196 mock_attachment.filename = "test.txt"
197 mock_attachment.save = AsyncMock()
198
199 with patch.object(uploader_cog, '_upload_file', new_callable=AsyncMock) as mock_upload:
200 mock_upload.return_value = "https://copyparty.local/uploaded.txt"
201
202 with patch('os.path.exists', return_value=True):
203 with patch('os.remove'):
204 with patch.object(ctx, 'defer', new_callable=AsyncMock):
205 with patch.object(ctx, 'respond', new_callable=AsyncMock) as mock_respond:
206 await uploader_cog.upload.callback(uploader_cog, ctx, zalacznik=mock_attachment)
207 mock_respond.assert_called_once_with("https://copyparty.local/uploaded.txt")
208
209
210@pytest.mark.asyncio
211async def test_upload_slash_command_failure(uploader_cog, mock_channel, mock_guild, bot):
212 ctx = MockApplicationContext(bot=bot, channel=mock_channel, guild=mock_guild)
213
214 mock_attachment = MagicMock()
215 mock_attachment.filename = "test.txt"
216 mock_attachment.save = AsyncMock()
217
218 with patch.object(uploader_cog, '_upload_file', new_callable=AsyncMock) as mock_upload:
219 mock_upload.return_value = None
220
221 with patch('os.path.exists', return_value=True):
222 with patch('os.remove'):
223 with patch.object(ctx, 'defer', new_callable=AsyncMock):
224 with patch.object(ctx, 'respond', new_callable=AsyncMock) as mock_respond:
225 await uploader_cog.upload.callback(uploader_cog, ctx, zalacznik=mock_attachment)
226 mock_respond.assert_called_once_with("Nie udało się wysłać pliku", ephemeral=True) \ No newline at end of file
diff --git a/tests/cogs/test_vclogs.py b/tests/cogs/test_vclogs.py
index 2003478..17ff839 100644
--- a/tests/cogs/test_vclogs.py
+++ b/tests/cogs/test_vclogs.py
@@ -1,7 +1,6 @@
1import pytest 1import pytest
2from unittest.mock import AsyncMock, patch, MagicMock 2from unittest.mock import AsyncMock, patch, MagicMock
3import json 3from conftest import MockApplicationContext
4from tests.conftest import MockApplicationContext
5 4
6 5
7def test_format_duration(): 6def test_format_duration():
@@ -13,20 +12,26 @@ def test_format_duration():
13 12
14 13
15def test_load_activity_data(): 14def test_load_activity_data():
16 with patch('builtins.open', MagicMock()): 15 with patch("cogs.vclogs.get_connection") as mock_conn:
17 with patch('os.path.exists', return_value=True): 16 mock_cursor = MagicMock()
18 with patch('json.load', return_value={"123": {"active": True}}): 17 mock_conn.return_value.cursor.return_value = mock_cursor
19 from cogs.vclogs import load_activity_data 18 mock_conn.return_value.close.return_value = None
20 data = load_activity_data() 19 mock_cursor.fetchall.return_value = []
21 assert isinstance(data, dict) 20
21 from cogs.vclogs import load_activity_data
22 data = load_activity_data()
23 assert isinstance(data, dict)
22 24
23 25
24def test_save_activity_data(): 26def test_save_activity_data():
25 with patch('builtins.open', MagicMock()) as mock_open: 27 with patch("cogs.vclogs.get_connection") as mock_conn:
26 with patch('json.dump'): 28 mock_cursor = MagicMock()
27 from cogs.vclogs import save_activity_data 29 mock_conn.return_value.cursor.return_value = mock_cursor
28 save_activity_data({"123": {"active": True}}) 30 mock_conn.return_value.commit.return_value = None
29 mock_open.assert_called_once() 31 mock_conn.return_value.close.return_value = None
32
33 from cogs.vclogs import save_activity_data
34 save_activity_data({"123": {"active": True}})
30 35
31 36
32@pytest.fixture 37@pytest.fixture
@@ -80,4 +85,4 @@ async def test_handle_state_change_mute_false(vclogs_cog):
80 85
81 tts_text, channel = await vclogs_cog.handle_state_change(mock_member, mock_channel, 'mute', False, None) 86 tts_text, channel = await vclogs_cog.handle_state_change(mock_member, mock_channel, 'mute', False, None)
82 87
83 assert "zrobił jajecznice" in tts_text 88 assert "zrobił jajecznice" in tts_text \ No newline at end of file
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