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,341 @@
1
+ import pytest
2
+ import asyncio
3
+ import discord
4
+ from datetime import datetime, timezone, timedelta
5
+ from securex import SecureX
6
+ from unittest.mock import Mock, AsyncMock, patch, MagicMock
7
+
8
+ class MockAuditLog:
9
+ def __init__(self, entries):
10
+ self.entries = entries
11
+ def __aiter__(self):
12
+ return self
13
+ async def __anext__(self):
14
+ if not self.entries:
15
+ raise StopAsyncIteration
16
+ return self.entries.pop(0)
17
+
18
+ @pytest.mark.asyncio
19
+ class TestHandlersExhaustive:
20
+ """Exhaustive tests for securex/handlers/ to reach 100% coverage"""
21
+
22
+ async def test_channel_handler_full(self):
23
+ """Test ChannelHandler branches for 100% coverage"""
24
+ bot = Mock(spec=discord.Client)
25
+ bot.user = Mock(id=123)
26
+ sx = SecureX(bot)
27
+ handler = sx.channel_handler
28
+
29
+ guild = Mock(spec=discord.Guild)
30
+ guild.id = 777
31
+ guild.owner_id = 111
32
+
33
+ # 1. _is_authorized branches
34
+ sx.whitelist.is_whitelisted = AsyncMock(return_value=True)
35
+ assert await handler._is_authorized(guild, 111) is True # Owner
36
+ assert await handler._is_authorized(guild, 123) is True # Bot
37
+ assert await handler._is_authorized(guild, 888) is True # Whitelisted
38
+
39
+ sx.whitelist.is_whitelisted.return_value = False
40
+ assert await handler._is_authorized(guild, 999) is False # Not whitelisted
41
+
42
+ # 2. on_channel_delete branches
43
+ channel = Mock(spec=discord.TextChannel)
44
+ channel.guild = guild
45
+ channel.id = 555
46
+ channel.name = "test-channel"
47
+
48
+ with patch.object(sx.backup_manager, 'restore_channel', new_callable=AsyncMock) as mock_restore:
49
+ # A. Authorized deletion (Owner)
50
+ entry_auth = Mock()
51
+ entry_auth.target.id = 555
52
+ entry_auth.user = Mock()
53
+ entry_auth.user.id = 111 # Owner
54
+ entry_auth.user.name = "Owner"
55
+ entry_auth.created_at = datetime.now(timezone.utc)
56
+ guild.audit_logs.return_value = MockAuditLog([entry_auth])
57
+ await handler.on_channel_delete(channel)
58
+ mock_restore.assert_not_called()
59
+
60
+ # B. Authorized deletion (Bot)
61
+ entry_bot = Mock()
62
+ entry_bot.target.id = 555
63
+ entry_bot.user = Mock()
64
+ entry_bot.user.id = 123 # Bot
65
+ entry_bot.created_at = datetime.now(timezone.utc)
66
+ guild.audit_logs.return_value = MockAuditLog([entry_bot])
67
+ await handler.on_channel_delete(channel)
68
+ mock_restore.assert_not_called()
69
+
70
+ # C. Unauthorized deletion (Success)
71
+ entry_unauth = Mock()
72
+ entry_unauth.target.id = 555
73
+ entry_unauth.user = Mock()
74
+ entry_unauth.user.id = 999
75
+ entry_unauth.user.name = "Violator"
76
+ entry_unauth.created_at = datetime.now(timezone.utc)
77
+ guild.audit_logs.return_value = MockAuditLog([entry_unauth])
78
+ mock_restore.return_value = Mock()
79
+ await handler.on_channel_delete(channel)
80
+ mock_restore.assert_called_once()
81
+ mock_restore.reset_mock()
82
+
83
+ # D. Category channel restoration
84
+ category = Mock(spec=discord.CategoryChannel)
85
+ category.guild = guild
86
+ category.id = 666
87
+ category.name = "Test Category"
88
+ entry_cat = Mock()
89
+ entry_cat.target.id = 666
90
+ entry_cat.user = Mock()
91
+ entry_cat.user.id = 999
92
+ entry_cat.created_at = datetime.now(timezone.utc)
93
+ guild.audit_logs.return_value = MockAuditLog([entry_cat])
94
+ new_cat = Mock()
95
+ mock_restore.return_value = new_cat
96
+ with patch.object(sx.backup_manager, 'restore_category_children', new_callable=AsyncMock) as mock_cat:
97
+ await handler.on_channel_delete(category)
98
+ mock_cat.assert_called_once()
99
+
100
+ # E. Too old entry
101
+ entry_old = Mock()
102
+ entry_old.created_at = datetime.now(timezone.utc) - timedelta(seconds=60)
103
+ guild.audit_logs.return_value = MockAuditLog([entry_old])
104
+ await handler.on_channel_delete(channel)
105
+
106
+ # F. Entry not found
107
+ guild.audit_logs.return_value = MockAuditLog([])
108
+ await handler.on_channel_delete(channel)
109
+
110
+ # G. Exception path
111
+ guild.audit_logs.side_effect = Exception("Audit Log Error")
112
+ await handler.on_channel_delete(channel)
113
+
114
+ # 3. on_channel_update branches
115
+ before, after = Mock(), Mock()
116
+ after.guild = guild
117
+ after.id = 555
118
+
119
+ # No change
120
+ before.overwrites = {"a": 1}
121
+ after.overwrites = {"a": 1}
122
+ before.position = 1
123
+ after.position = 1
124
+ guild.audit_logs.side_effect = None
125
+ await handler.on_channel_update(before, after)
126
+
127
+ # Unauthorized update
128
+ after.overwrites = {"a": 2}
129
+ entry_upd = Mock()
130
+ entry_upd.action = discord.AuditLogAction.channel_update
131
+ entry_upd.target.id = 555
132
+ entry_upd.user = Mock()
133
+ entry_upd.user.id = 999
134
+ entry_upd.created_at = datetime.now(timezone.utc)
135
+ guild.audit_logs.return_value = MockAuditLog([entry_upd])
136
+ with patch.object(sx.backup_manager, 'restore_channel_permissions', new_callable=AsyncMock) as mock_upd:
137
+ await handler.on_channel_update(before, after)
138
+ mock_upd.assert_called_once()
139
+
140
+ # Too old in update
141
+ entry_upd.created_at = datetime.now(timezone.utc) - timedelta(seconds=60)
142
+ guild.audit_logs.return_value = MockAuditLog([entry_upd])
143
+ await handler.on_channel_update(before, after)
144
+
145
+ # Exception update
146
+ guild.audit_logs.side_effect = Exception("Audit Log Error")
147
+ await handler.on_channel_update(before, after)
148
+
149
+ async def test_role_handler_full(self):
150
+ """Test RoleHandler branches for 100% coverage"""
151
+ bot = Mock(spec=discord.Client)
152
+ bot.user = Mock(id=123)
153
+ sx = SecureX(bot)
154
+ handler = sx.role_handler
155
+
156
+ guild = Mock(spec=discord.Guild)
157
+ guild.id = 777
158
+ guild.owner_id = 111
159
+
160
+ # 1. _is_authorized
161
+ sx.whitelist.is_whitelisted = AsyncMock(return_value=False)
162
+ assert await handler._is_authorized(guild, 111) is True # Owner
163
+ assert await handler._is_authorized(guild, 123) is True # Bot
164
+
165
+ # 2. on_role_delete
166
+ role = Mock(spec=discord.Role)
167
+ role.guild = guild
168
+ role.id = 444
169
+ role.name = "test-role"
170
+
171
+ entry = Mock()
172
+ entry.target.id = 444
173
+ entry.user = Mock()
174
+ entry.user.id = 999
175
+ entry.created_at = datetime.now(timezone.utc)
176
+ guild.audit_logs.return_value = MockAuditLog([entry])
177
+
178
+ with patch.object(sx.backup_manager, 'restore_role', new_callable=AsyncMock) as mock_rest:
179
+ mock_rest.return_value = True
180
+ await handler.on_role_delete(role)
181
+ mock_rest.assert_called_once()
182
+
183
+ # too old
184
+ entry.created_at = datetime.now(timezone.utc) - timedelta(seconds=60)
185
+ guild.audit_logs.return_value = MockAuditLog([entry])
186
+ await handler.on_role_delete(role)
187
+
188
+ # Exception
189
+ guild.audit_logs.side_effect = Exception("Audit error")
190
+ await handler.on_role_delete(role)
191
+
192
+ # 3. on_role_update
193
+ before, after = Mock(), Mock()
194
+ after.guild = guild
195
+ after.id = 444
196
+ after.name = "Role"
197
+ before.position = 1
198
+ after.position = 1
199
+ before.permissions = discord.Permissions(0)
200
+ after.permissions = discord.Permissions(8)
201
+
202
+ entry_upd = Mock()
203
+ entry_upd.action = discord.AuditLogAction.role_update
204
+ entry_upd.target.id = 444
205
+ entry_upd.user = Mock()
206
+ entry_upd.user.id = 999
207
+ entry_upd.created_at = datetime.now(timezone.utc)
208
+ guild.audit_logs.side_effect = None
209
+ guild.audit_logs.return_value = MockAuditLog([entry_upd])
210
+
211
+ with patch.object(sx.backup_manager, 'restore_role_permissions', new_callable=AsyncMock) as mock_upd:
212
+ mock_upd.return_value = True
213
+ await handler.on_role_update(before, after)
214
+ mock_upd.assert_called_once()
215
+
216
+ # too old
217
+ entry_upd.created_at = datetime.now(timezone.utc) - timedelta(seconds=60)
218
+ guild.audit_logs.return_value = MockAuditLog([entry_upd])
219
+ await handler.on_role_update(before, after)
220
+
221
+ # No positional/perm change
222
+ after.permissions = before.permissions
223
+ await handler.on_role_update(before, after)
224
+
225
+ # Exception in update - make audit_logs raise during iteration
226
+ after.permissions = discord.Permissions(8) # Change permissions again
227
+
228
+ class ExceptionIterator:
229
+ def __init__(self):
230
+ pass
231
+ def __aiter__(self):
232
+ return self
233
+ async def __anext__(self):
234
+ raise Exception("Audit log iteration error")
235
+
236
+ guild.audit_logs.side_effect = None
237
+ guild.audit_logs.return_value = ExceptionIterator()
238
+ await handler.on_role_update(before, after)
239
+
240
+ async def test_member_handler_full(self):
241
+ """Test MemberHandler branches for 100% coverage"""
242
+ bot = Mock(spec=discord.Client)
243
+ bot.user = Mock(id=123)
244
+ sx = SecureX(bot)
245
+ handler = sx.member_handler
246
+
247
+ guild = Mock(spec=discord.Guild)
248
+ guild.id = 777
249
+ guild.owner_id = 111
250
+
251
+ # _is_authorized
252
+ sx.whitelist.is_whitelisted = AsyncMock(return_value=False)
253
+ assert await handler._is_authorized(guild, 111) is True
254
+ assert await handler._is_authorized(guild, 123) is True
255
+
256
+ user = Mock(spec=discord.User)
257
+ user.id = 222
258
+ user.name = "Victim"
259
+
260
+ # on_member_ban
261
+ entry = Mock()
262
+ entry.target.id = 222
263
+ entry.user = Mock()
264
+ entry.user.id = 999
265
+ entry.created_at = datetime.now(timezone.utc)
266
+ guild.audit_logs.return_value = MockAuditLog([entry])
267
+ guild.unban = AsyncMock()
268
+
269
+ await handler.on_member_ban(guild, user)
270
+ guild.unban.assert_called_once()
271
+
272
+ # entry not found (line 68 coverage)
273
+ guild.audit_logs.return_value = MockAuditLog([])
274
+ await handler.on_member_ban(guild, user)
275
+
276
+ # too old
277
+ entry.created_at = datetime.now(timezone.utc) - timedelta(seconds=60)
278
+ guild.audit_logs.return_value = MockAuditLog([entry])
279
+ await handler.on_member_ban(guild, user)
280
+
281
+ # unban failure (Forbidden)
282
+ entry.created_at = datetime.now(timezone.utc)
283
+ guild.audit_logs.return_value = MockAuditLog([entry])
284
+ guild.unban.side_effect = discord.Forbidden(Mock(), "test")
285
+ await handler.on_member_ban(guild, user)
286
+
287
+ # Exception
288
+ guild.audit_logs.side_effect = Exception("audit error")
289
+ await handler.on_member_ban(guild, user)
290
+
291
+ # on_member_update
292
+ before, after = Mock(), Mock()
293
+ after.guild = guild
294
+ after.id = 222
295
+ after.name = "Victim"
296
+
297
+ role_def = Mock(spec=discord.Role)
298
+ role_def.is_default = Mock(return_value=True) # Line 90 branch
299
+
300
+ role_dang = Mock(spec=discord.Role)
301
+ role_dang.is_default = Mock(return_value=False)
302
+ role_dang.permissions = discord.Permissions(administrator=True)
303
+ role_dang.name = "AdminRole"
304
+
305
+ before.roles = []
306
+ after.roles = [role_def, role_dang]
307
+
308
+ entry_upd = Mock()
309
+ entry_upd.action = discord.AuditLogAction.member_role_update
310
+ entry_upd.target.id = 222
311
+ entry_upd.user = Mock()
312
+ entry_upd.user.id = 999
313
+ entry_upd.created_at = datetime.now(timezone.utc)
314
+ guild.audit_logs.side_effect = None
315
+ guild.audit_logs.return_value = MockAuditLog([entry_upd])
316
+
317
+ after.remove_roles = AsyncMock()
318
+ await handler.on_member_update(before, after)
319
+ after.remove_roles.assert_called_once_with(role_dang, reason="SecureX: Removed dangerous roles (unauthorized update)")
320
+
321
+ # remove_roles failure (Exception path line 106)
322
+ after.remove_roles.reset_mock()
323
+ after.remove_roles.side_effect = discord.HTTPException(Mock(status=400), "Fail")
324
+ guild.audit_logs.return_value = MockAuditLog([entry_upd])
325
+ await handler.on_member_update(before, after)
326
+
327
+ # too old update
328
+ entry_upd.created_at = datetime.now(timezone.utc) - timedelta(seconds=60)
329
+ guild.audit_logs.return_value = MockAuditLog([entry_upd])
330
+ await handler.on_member_update(before, after)
331
+
332
+ # Exception in member update
333
+ guild.audit_logs.side_effect = Exception("member error")
334
+ await handler.on_member_update(before, after)
335
+
336
+ # Test roles unchanged (covers line 68)
337
+ before.roles = [role_def, role_dang]
338
+ after.roles = [role_def, role_dang] # Same roles
339
+ guild.audit_logs.side_effect = None
340
+ await handler.on_member_update(before, after)
341
+