dc-securex 2.29.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,297 @@
1
+ import pytest
2
+ import asyncio
3
+ import discord
4
+ import json
5
+ from securex import SecureX
6
+ from securex.models import ThreatEvent
7
+ from unittest.mock import Mock, AsyncMock, patch, MagicMock
8
+ from datetime import datetime
9
+
10
+ @pytest.mark.asyncio
11
+ class TestWorkersExhaustive:
12
+ """Exhaustive tests for securex/workers/ to reach 100% coverage"""
13
+
14
+ async def test_action_worker_full(self):
15
+ """Test ActionWorker branches for 100% coverage"""
16
+ bot = Mock(spec=discord.Client)
17
+ bot.user = Mock(id=123)
18
+ sx = SecureX(bot)
19
+ worker = sx.action_worker
20
+
21
+ # 1. Test start/stop
22
+ await worker.start()
23
+ await worker.start()
24
+ assert worker._worker_task is not None
25
+ await worker.stop()
26
+ assert worker._worker_task is None
27
+
28
+ # 2. Test _worker_loop
29
+ entry_mock = Mock(spec=discord.AuditLogEntry)
30
+ worker.action_queue = Mock(spec=asyncio.Queue)
31
+ worker.action_queue.get = AsyncMock(side_effect=[entry_mock, Exception("Queue error"), asyncio.CancelledError])
32
+ with patch.object(worker, '_process_entry', new_callable=AsyncMock) as mock_process:
33
+ await worker._worker_loop()
34
+ mock_process.assert_called_once()
35
+
36
+ # 3. Test _process_entry branches
37
+ guild = Mock(spec=discord.Guild)
38
+ guild.id = 777
39
+ guild.owner_id = 111
40
+
41
+ # A. Bot executor
42
+ entry_bot = Mock(user=Mock(id=123))
43
+ await worker._process_entry(entry_bot)
44
+
45
+ # B. Owner executor
46
+ entry_owner = Mock(user=Mock(id=111), guild=guild)
47
+ await worker._process_entry(entry_owner)
48
+
49
+ # C. Whitelisted executor
50
+ entry_wl = Mock(user=Mock(id=888), guild=guild)
51
+ sx.whitelist_cache[777] = {888}
52
+ await worker._process_entry(entry_wl)
53
+
54
+ # D. Unknown violation type
55
+ entry_unknown = Mock(user=Mock(id=999), guild=guild, action=9999)
56
+ await worker._process_entry(entry_unknown)
57
+
58
+ # E. bot_add violation (Success)
59
+ target_bot = Mock(id=555)
60
+ target_bot.name = "MyBot"
61
+ bot_member = Mock(spec=discord.Member)
62
+ bot_member.ban = AsyncMock()
63
+ guild.get_member = Mock(return_value=bot_member)
64
+ entry_bot_add = Mock(user=Mock(id=999), guild=guild, action=discord.AuditLogAction.bot_add, target=target_bot)
65
+ entry_bot_add.user.name = "Violator"
66
+ await worker._process_entry(entry_bot_add)
67
+ bot_member.ban.assert_called_once()
68
+
69
+ # F. bot_add violation (Failure)
70
+ bot_member.ban.side_effect = Exception("Ban failed")
71
+ await worker._process_entry(entry_bot_add)
72
+
73
+ # G. Instant ban types (member_kick)
74
+ entry_kick = Mock(user=Mock(id=999), guild=guild, action=discord.AuditLogAction.kick)
75
+ entry_kick.user.name = "Violator"
76
+ guild.ban = AsyncMock()
77
+ await worker._process_entry(entry_kick)
78
+ guild.ban.assert_called_once()
79
+
80
+ # H. Instant ban types failure
81
+ guild.ban.side_effect = Exception("Guild ban failed")
82
+ await worker._process_entry(entry_kick)
83
+
84
+ # I. General punishment SUCCESS
85
+ sx.punishments["role_create"] = "kick"
86
+ entry_role = Mock(user=Mock(id=999), guild=guild, action=discord.AuditLogAction.role_create, target=Mock(id=444))
87
+ entry_role.target.name = "EvilRole"
88
+ entry_role.user.name = "Violator"
89
+ sx.punisher.punish = AsyncMock(return_value="kick")
90
+ await worker._process_entry(entry_role)
91
+
92
+ # J. General punishment exception
93
+ sx.punisher.punish.side_effect = Exception("Punish failed")
94
+ await worker._process_entry(entry_role)
95
+
96
+ # K. Error path line 114
97
+ with patch.object(worker, 'action_map', side_effect=Exception("Map error")):
98
+ await worker._process_entry(None)
99
+
100
+ async def test_cleanup_worker_full(self):
101
+ """Test CleanupWorker branches for 100% coverage"""
102
+ bot = Mock(spec=discord.Client)
103
+ bot.user = Mock(id=123)
104
+ sx = SecureX(bot)
105
+ worker = sx.cleanup_worker
106
+
107
+ await worker.start()
108
+ await worker.stop()
109
+
110
+ entry_mock = Mock()
111
+ worker.cleanup_queue = Mock()
112
+ worker.cleanup_queue.get = AsyncMock(side_effect=[entry_mock, Exception("Loop error"), asyncio.CancelledError])
113
+ with patch.object(worker, '_process_entry', new_callable=AsyncMock) as mock_process:
114
+ await worker._worker_loop()
115
+ mock_process.assert_called_once()
116
+
117
+ guild = Mock(spec=discord.Guild)
118
+ guild.id = 777
119
+ guild.owner_id = 111
120
+
121
+ await worker._process_entry(Mock(user=Mock(id=123), guild=guild))
122
+ await worker._process_entry(Mock(user=Mock(id=111), guild=guild))
123
+ sx.whitelist_cache[777] = {888}
124
+ await worker._process_entry(Mock(user=Mock(id=888), guild=guild))
125
+
126
+ await worker._process_entry(Mock(user=Mock(id=999), guild=guild, action=discord.AuditLogAction.ban))
127
+ await worker._process_entry(Mock(user=Mock(id=999), guild=guild, action=discord.AuditLogAction.role_create, target=None))
128
+
129
+ role = Mock()
130
+ role.name = "EvilRole"
131
+ role.delete = AsyncMock()
132
+ guild.get_role = Mock(return_value=role)
133
+ await worker._process_entry(Mock(user=Mock(id=999), guild=guild, action=discord.AuditLogAction.role_create, target=Mock(id=55)))
134
+ role.delete.assert_called_once()
135
+
136
+ channel = Mock()
137
+ channel.name = "EvilChannel"
138
+ channel.delete = AsyncMock()
139
+ guild.get_channel = Mock(return_value=channel)
140
+ await worker._process_entry(Mock(user=Mock(id=999), guild=guild, action=discord.AuditLogAction.channel_create, target=Mock(id=66)))
141
+ channel.delete.assert_called_once()
142
+
143
+ webhook = Mock(id=77)
144
+ webhook.name = "EvilWebhook"
145
+ webhook.delete = AsyncMock()
146
+ guild.webhooks = AsyncMock(return_value=[webhook])
147
+ await worker._process_entry(Mock(user=Mock(id=999), guild=guild, action=discord.AuditLogAction.webhook_create, target=Mock(id=77)))
148
+ webhook.delete.assert_called_once()
149
+
150
+ role.delete.side_effect = discord.Forbidden(Mock(), "test")
151
+ await worker._process_entry(Mock(user=Mock(id=999), guild=guild, action=discord.AuditLogAction.role_create, target=Mock(id=55)))
152
+
153
+ role.delete.side_effect = Exception("Boom")
154
+ await worker._process_entry(Mock(user=Mock(id=999), guild=guild, action=discord.AuditLogAction.role_create, target=Mock(id=55)))
155
+
156
+ # General loop exception line 94
157
+ with patch.object(worker, 'bot', side_effect=Exception("Bot error")):
158
+ await worker._process_entry(None)
159
+
160
+ async def test_log_worker_full(self):
161
+ """Test LogWorker branches for 100% coverage"""
162
+ bot = Mock(spec=discord.Client)
163
+ bot.user = Mock(id=123)
164
+ sx = SecureX(bot)
165
+ worker = sx.log_worker
166
+
167
+ await worker.start()
168
+ await worker.stop()
169
+
170
+ entry_mock = Mock()
171
+ worker.log_queue = Mock()
172
+ worker.log_queue.get = AsyncMock(side_effect=[entry_mock, Exception("Log error"), asyncio.CancelledError])
173
+ with patch.object(worker, '_process_entry', new_callable=AsyncMock) as mock_process:
174
+ await worker._worker_loop()
175
+ mock_process.assert_called_once()
176
+
177
+ guild = Mock(id=777, owner_id=111)
178
+ await worker._process_entry(Mock(user=Mock(id=123), guild=guild))
179
+ await worker._process_entry(Mock(user=Mock(id=999), guild=guild, action=9999))
180
+
181
+ sx._trigger_callbacks = AsyncMock()
182
+ entry = Mock(user=Mock(id=999), guild=guild, action=discord.AuditLogAction.role_create, target=Mock(id=55), created_at=None)
183
+ await worker._process_entry(entry)
184
+ sx._trigger_callbacks.assert_called_once()
185
+
186
+ sx._trigger_callbacks.side_effect = Exception("Callback error")
187
+ await worker._process_entry(entry)
188
+
189
+ async def test_guild_worker_full(self, tmp_path):
190
+ """Test GuildWorker branches for 100% coverage"""
191
+ bot = Mock(spec=discord.Client)
192
+ bot.user = Mock(id=123)
193
+ sx = SecureX(bot)
194
+
195
+ tokens_file = tmp_path / "user_tokens.json"
196
+ sx.backup_dir = tmp_path
197
+
198
+ worker = sx.guild_worker
199
+ worker._tokens_file = tokens_file
200
+
201
+ # 0. Successful token load
202
+ tokens_file.write_text(json.dumps({"123": "token123"}))
203
+ await worker._load_tokens()
204
+ assert worker.get_user_token(123) == "token123"
205
+
206
+ # 1. start/stop
207
+ await worker.start()
208
+ await worker.stop()
209
+
210
+ with patch("json.load", side_effect=Exception("JSON error")):
211
+ tokens_file.write_text("{}")
212
+ await worker._load_tokens()
213
+
214
+ with patch("json.dump", side_effect=Exception("Dump error")):
215
+ await worker._save_tokens()
216
+
217
+ await worker.set_user_token(777, "token123")
218
+ assert worker.get_user_token(777) == "token123"
219
+ await worker.remove_user_token(777)
220
+ assert worker.get_user_token(777) is None
221
+ await worker.remove_user_token(999)
222
+
223
+ entry_mock = Mock(action=discord.AuditLogAction.guild_update, user=Mock(id=999))
224
+ entry_mock.changes = [Mock(key="name", before="Old", after="New")]
225
+ worker.sdk.guild_queue = Mock()
226
+ worker.sdk.guild_queue.get = AsyncMock(side_effect=[
227
+ Mock(action=discord.AuditLogAction.ban),
228
+ Mock(action=discord.AuditLogAction.guild_update, user=Mock(id=123)),
229
+ Mock(action=discord.AuditLogAction.guild_update, user=Mock(id=999), changes=[]),
230
+ entry_mock,
231
+ Exception("Loop error"),
232
+ asyncio.CancelledError
233
+ ])
234
+ with patch.object(worker, '_restore_guild_settings', new_callable=AsyncMock) as mock_restore:
235
+ await worker._restoration_loop()
236
+ mock_restore.assert_called_once()
237
+
238
+ assert await worker._restore_vanity_via_api(777, "vanity") is False
239
+ await worker.set_user_token(777, "token123")
240
+
241
+ with patch("aiohttp.ClientSession.patch") as mock_patch:
242
+ mock_resp = MagicMock()
243
+ mock_resp.status = 200
244
+ mock_patch.return_value.__aenter__.return_value = mock_resp
245
+
246
+ assert await worker._restore_vanity_via_api(777, "vanity") is True
247
+
248
+ mock_resp.status = 400
249
+ mock_resp.text = AsyncMock(return_value="error")
250
+ assert await worker._restore_vanity_via_api(777, "vanity") is False
251
+
252
+ mock_patch.side_effect = Exception("Network error")
253
+ assert await worker._restore_vanity_via_api(777, "vanity") is False
254
+
255
+ guild = Mock(spec=discord.Guild)
256
+ guild.id = 777
257
+ guild.edit = AsyncMock()
258
+
259
+ sx.backup_manager.get_guild_settings = AsyncMock(return_value=None)
260
+ await worker._restore_guild_settings({"guild": guild, "changes": {}})
261
+
262
+ backup = {
263
+ "name": "Old",
264
+ "vanity_url_code": "old-v",
265
+ "icon": "icon-data",
266
+ "banner": "banner-data",
267
+ "description": "desc"
268
+ }
269
+ sx.backup_manager.get_guild_settings = AsyncMock(return_value=backup)
270
+
271
+ changes = {
272
+ "vanity_url_code": ("old-v", "new-v"),
273
+ "name": ("Old", "New"),
274
+ "icon": ("h1", "h2"),
275
+ "banner": ("b1", "b2"),
276
+ "description": ("d1", "d2")
277
+ }
278
+
279
+ with patch.object(worker, '_restore_vanity_via_api', return_value=True):
280
+ await worker._restore_guild_settings({"guild": guild, "changes": changes})
281
+ guild.edit.assert_called_once()
282
+
283
+ # Vanity fail branch
284
+ with patch.object(worker, '_restore_vanity_via_api', return_value=False):
285
+ await worker._restore_guild_settings({"guild": guild, "changes": {"vanity_url_code": "v"}})
286
+
287
+ guild.edit.side_effect = discord.HTTPException(Mock(status=400), "Bad request")
288
+ await worker._restore_guild_settings({"guild": guild, "changes": {"name": "n"}})
289
+
290
+ mock_resp = Mock(status=400)
291
+ ex = discord.HTTPException(mock_resp, "err")
292
+ ex.code = 50035
293
+ guild.edit.side_effect = ex
294
+ await worker._restore_guild_settings({"guild": guild, "changes": {"name": "n"}})
295
+
296
+ guild.edit.side_effect = Exception("Critical fail")
297
+ await worker._restore_guild_settings({"guild": guild, "changes": {"name": "n"}})