paijo77 commited on
Commit
54fe6ef
·
verified ·
1 Parent(s): cf24390

update tests/unit/test_validator.py

Browse files
Files changed (1) hide show
  1. tests/unit/test_validator.py +531 -0
tests/unit/test_validator.py ADDED
@@ -0,0 +1,531 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pytest
2
+ import aiohttp
3
+ from unittest.mock import AsyncMock, patch, MagicMock
4
+ from app.validator import ProxyValidator, ValidationResult
5
+
6
+
7
+ class TestProxyValidator:
8
+ """Comprehensive test suite for ProxyValidator"""
9
+
10
+ @pytest.fixture
11
+ def validator(self):
12
+ return ProxyValidator(timeout=5, max_concurrent=10)
13
+
14
+ # ==================== FORMAT VALIDATION ====================
15
+
16
+ @pytest.mark.asyncio
17
+ async def test_validate_format_valid_http(self, validator):
18
+ """Test format validation for valid HTTP proxy"""
19
+ assert await validator.validate_format("http://192.168.1.1:8080")
20
+ assert await validator.validate_format("192.168.1.1:8080")
21
+
22
+ @pytest.mark.asyncio
23
+ async def test_validate_format_valid_https(self, validator):
24
+ """Test format validation for valid HTTPS proxy"""
25
+ assert await validator.validate_format("https://10.0.0.1:3128")
26
+
27
+ @pytest.mark.asyncio
28
+ async def test_validate_format_valid_socks4(self, validator):
29
+ """Test format validation for valid SOCKS4 proxy"""
30
+ assert await validator.validate_format("socks4://127.0.0.1:1080")
31
+
32
+ @pytest.mark.asyncio
33
+ async def test_validate_format_valid_socks5(self, validator):
34
+ """Test format validation for valid SOCKS5 proxy"""
35
+ assert await validator.validate_format("socks5://172.16.0.1:9050")
36
+
37
+ @pytest.mark.asyncio
38
+ async def test_validate_format_invalid_ip(self, validator):
39
+ """Test format validation rejects invalid IP"""
40
+ assert not await validator.validate_format("999.999.999.999:8080")
41
+ assert not await validator.validate_format("192.168.1:8080")
42
+ assert not await validator.validate_format("192.168.1.256:8080")
43
+
44
+ @pytest.mark.asyncio
45
+ async def test_validate_format_invalid_port(self, validator):
46
+ """Test format validation rejects invalid port"""
47
+ assert not await validator.validate_format("192.168.1.1:99999")
48
+ assert not await validator.validate_format("192.168.1.1:")
49
+ assert not await validator.validate_format("192.168.1.1")
50
+
51
+ @pytest.mark.asyncio
52
+ async def test_validate_format_edge_cases(self, validator):
53
+ """Test format validation edge cases"""
54
+ assert not await validator.validate_format("")
55
+ assert not await validator.validate_format("not-a-proxy")
56
+ assert not await validator.validate_format("http://")
57
+ assert not await validator.validate_format("://192.168.1.1:8080")
58
+
59
+ # ==================== CONNECTIVITY VALIDATION ====================
60
+
61
+ @pytest.mark.asyncio
62
+ async def test_validate_connectivity_success(self, validator):
63
+ """Test successful proxy connectivity validation"""
64
+ mock_resp = AsyncMock()
65
+ mock_resp.status = 200
66
+ mock_resp.__aenter__.return_value = mock_resp
67
+ mock_resp.__aexit__.return_value = None
68
+
69
+ mock_session = MagicMock()
70
+ mock_session.get.return_value.__aenter__.return_value = mock_resp
71
+ mock_session.__aenter__.return_value = mock_session
72
+ mock_session.__aexit__.return_value = None
73
+
74
+ with patch("aiohttp.ClientSession", return_value=mock_session):
75
+ is_valid, latency, error = await validator.validate_connectivity(
76
+ "http://192.168.1.1:8080"
77
+ )
78
+
79
+ assert is_valid is True
80
+ assert latency is not None
81
+ assert latency >= 0
82
+ assert error is None
83
+
84
+ @pytest.mark.asyncio
85
+ async def test_validate_connectivity_timeout(self, validator):
86
+ """Test proxy connectivity timeout"""
87
+ mock_session = MagicMock()
88
+ mock_session.get.side_effect = TimeoutError()
89
+ mock_session.__aenter__.return_value = mock_session
90
+ mock_session.__aexit__.return_value = None
91
+
92
+ with patch("aiohttp.ClientSession", return_value=mock_session):
93
+ is_valid, latency, error = await validator.validate_connectivity(
94
+ "http://192.168.1.1:8080"
95
+ )
96
+
97
+ assert is_valid is False
98
+ assert latency is None
99
+ assert "timeout" in error.lower()
100
+
101
+ @pytest.mark.asyncio
102
+ async def test_validate_connectivity_proxy_error(self, validator):
103
+ """Test proxy connection error"""
104
+ mock_session = MagicMock()
105
+ mock_session.get.side_effect = aiohttp.ClientProxyConnectionError(
106
+ MagicMock(), MagicMock()
107
+ )
108
+ mock_session.__aenter__.return_value = mock_session
109
+ mock_session.__aexit__.return_value = None
110
+
111
+ with patch("aiohttp.ClientSession", return_value=mock_session):
112
+ is_valid, latency, error = await validator.validate_connectivity(
113
+ "http://192.168.1.1:8080"
114
+ )
115
+
116
+ assert is_valid is False
117
+ assert latency is None
118
+ assert "connection failed" in error.lower()
119
+
120
+ @pytest.mark.asyncio
121
+ async def test_validate_connectivity_http_error(self, validator):
122
+ """Test HTTP error status codes"""
123
+ test_cases = [
124
+ (403, "HTTP 403"),
125
+ (404, "HTTP 404"),
126
+ (500, "HTTP 500"),
127
+ (502, "HTTP 502"),
128
+ ]
129
+
130
+ for status_code, expected_error in test_cases:
131
+ mock_resp = AsyncMock()
132
+ mock_resp.status = status_code
133
+ mock_resp.__aenter__.return_value = mock_resp
134
+ mock_resp.__aexit__.return_value = None
135
+
136
+ mock_session = MagicMock()
137
+ mock_session.get.return_value.__aenter__.return_value = mock_resp
138
+ mock_session.__aenter__.return_value = mock_session
139
+ mock_session.__aexit__.return_value = None
140
+
141
+ with patch("aiohttp.ClientSession", return_value=mock_session):
142
+ is_valid, latency, error = await validator.validate_connectivity(
143
+ "http://192.168.1.1:8080"
144
+ )
145
+
146
+ assert is_valid is False, f"Status {status_code} should fail validation"
147
+ assert latency is None
148
+ assert expected_error in error
149
+
150
+ # ==================== ANONYMITY CHECK ====================
151
+
152
+ @pytest.mark.asyncio
153
+ async def test_check_anonymity_elite(self, validator):
154
+ """Test elite proxy anonymity detection"""
155
+ mock_resp = AsyncMock()
156
+ mock_resp.status = 200
157
+ mock_resp.json.return_value = {"headers": {}}
158
+ mock_resp.__aenter__.return_value = mock_resp
159
+ mock_resp.__aexit__.return_value = None
160
+
161
+ mock_session = MagicMock()
162
+ mock_session.get.return_value.__aenter__.return_value = mock_resp
163
+ mock_session.__aenter__.return_value = mock_session
164
+ mock_session.__aexit__.return_value = None
165
+
166
+ with patch("aiohttp.ClientSession", return_value=mock_session):
167
+ anonymity = await validator.check_anonymity("http://192.168.1.1:8080")
168
+
169
+ assert anonymity == "elite"
170
+
171
+ @pytest.mark.asyncio
172
+ async def test_check_anonymity_transparent(self, validator):
173
+ """Test transparent proxy detection"""
174
+ mock_resp = AsyncMock()
175
+ mock_resp.status = 200
176
+ mock_resp.json.return_value = {
177
+ "headers": {"X-Forwarded-For": "1.2.3.4", "Via": "proxy"}
178
+ }
179
+ mock_resp.__aenter__.return_value = mock_resp
180
+ mock_resp.__aexit__.return_value = None
181
+
182
+ mock_session = MagicMock()
183
+ mock_session.get.return_value.__aenter__.return_value = mock_resp
184
+ mock_session.__aenter__.return_value = mock_session
185
+ mock_session.__aexit__.return_value = None
186
+
187
+ with patch("aiohttp.ClientSession", return_value=mock_session):
188
+ anonymity = await validator.check_anonymity("http://192.168.1.1:8080")
189
+
190
+ assert anonymity == "transparent"
191
+
192
+ @pytest.mark.asyncio
193
+ async def test_check_anonymity_anonymous(self, validator):
194
+ """Test anonymous proxy detection"""
195
+ mock_resp = AsyncMock()
196
+ mock_resp.status = 200
197
+ mock_resp.json.return_value = {"headers": {"Proxy-Connection": "keep-alive"}}
198
+ mock_resp.__aenter__.return_value = mock_resp
199
+ mock_resp.__aexit__.return_value = None
200
+
201
+ mock_session = MagicMock()
202
+ mock_session.get.return_value.__aenter__.return_value = mock_resp
203
+ mock_session.__aenter__.return_value = mock_session
204
+ mock_session.__aexit__.return_value = None
205
+
206
+ with patch("aiohttp.ClientSession", return_value=mock_session):
207
+ anonymity = await validator.check_anonymity("http://192.168.1.1:8080")
208
+
209
+ assert anonymity == "anonymous"
210
+
211
+ @pytest.mark.asyncio
212
+ async def test_check_anonymity_error(self, validator):
213
+ """Test anonymity check handles errors gracefully"""
214
+ mock_session = MagicMock()
215
+ mock_session.get.side_effect = Exception("Network error")
216
+ mock_session.__aenter__.return_value = mock_session
217
+ mock_session.__aexit__.return_value = None
218
+
219
+ with patch("aiohttp.ClientSession", return_value=mock_session):
220
+ anonymity = await validator.check_anonymity("http://192.168.1.1:8080")
221
+
222
+ assert anonymity is None
223
+
224
+ # ==================== GOOGLE ACCESS TEST ====================
225
+
226
+ @pytest.mark.asyncio
227
+ async def test_google_access_success(self, validator):
228
+ """Test successful Google access through proxy"""
229
+ mock_resp = AsyncMock()
230
+ mock_resp.status = 200
231
+ mock_resp.__aenter__.return_value = mock_resp
232
+ mock_resp.__aexit__.return_value = None
233
+
234
+ mock_session = MagicMock()
235
+ mock_session.get.return_value.__aenter__.return_value = mock_resp
236
+ mock_session.__aenter__.return_value = mock_session
237
+ mock_session.__aexit__.return_value = None
238
+
239
+ with patch("aiohttp.ClientSession", return_value=mock_session):
240
+ can_access = await validator.test_google_access("http://192.168.1.1:8080")
241
+
242
+ assert can_access is True
243
+
244
+ @pytest.mark.asyncio
245
+ async def test_google_access_failure(self, validator):
246
+ """Test failed Google access through proxy"""
247
+ mock_session = MagicMock()
248
+ mock_session.get.side_effect = Exception("Connection failed")
249
+ mock_session.__aenter__.return_value = mock_session
250
+ mock_session.__aexit__.return_value = None
251
+
252
+ with patch("aiohttp.ClientSession", return_value=mock_session):
253
+ can_access = await validator.test_google_access("http://192.168.1.1:8080")
254
+
255
+ assert can_access is False
256
+
257
+ # ==================== GEO INFO ====================
258
+
259
+ @pytest.mark.asyncio
260
+ async def test_get_geo_info_success(self, validator):
261
+ """Test successful geo info retrieval"""
262
+ mock_resp = AsyncMock()
263
+ mock_resp.status = 200
264
+ mock_resp.json.return_value = {
265
+ "country_code": "US",
266
+ "country_name": "United States",
267
+ "region": "California",
268
+ "city": "San Francisco",
269
+ }
270
+ mock_resp.__aenter__.return_value = mock_resp
271
+ mock_resp.__aexit__.return_value = None
272
+
273
+ mock_session = MagicMock()
274
+ mock_session.get.return_value.__aenter__.return_value = mock_resp
275
+ mock_session.__aenter__.return_value = mock_session
276
+ mock_session.__aexit__.return_value = None
277
+
278
+ with patch("aiohttp.ClientSession", return_value=mock_session):
279
+ geo_info = await validator.get_geo_info("8.8.8.8")
280
+
281
+ assert geo_info["country_code"] == "US"
282
+ assert geo_info["country_name"] == "United States"
283
+ assert geo_info["state"] == "California"
284
+ assert geo_info["city"] == "San Francisco"
285
+
286
+ @pytest.mark.asyncio
287
+ async def test_get_geo_info_error(self, validator):
288
+ """Test geo info retrieval error handling"""
289
+ mock_session = MagicMock()
290
+ mock_session.get.side_effect = Exception("API error")
291
+ mock_session.__aenter__.return_value = mock_session
292
+ mock_session.__aexit__.return_value = None
293
+
294
+ with patch("aiohttp.ClientSession", return_value=mock_session):
295
+ geo_info = await validator.get_geo_info("8.8.8.8")
296
+
297
+ assert geo_info["country_code"] is None
298
+ assert geo_info["country_name"] is None
299
+
300
+ # ==================== PROXY TYPE DETECTION ====================
301
+
302
+ @pytest.mark.asyncio
303
+ async def test_detect_proxy_type_datacenter(self, validator):
304
+ """Test datacenter proxy detection"""
305
+ test_cases = [
306
+ {"org": "Amazon AWS"},
307
+ {"org": "Google Cloud"},
308
+ {"org": "Microsoft Azure"},
309
+ {"org": "DigitalOcean LLC"},
310
+ {"org": "Linode Hosting"},
311
+ {"org": "OVH Datacenter"},
312
+ ]
313
+
314
+ for data in test_cases:
315
+ mock_resp = AsyncMock()
316
+ mock_resp.status = 200
317
+ mock_resp.json.return_value = data
318
+ mock_resp.__aenter__.return_value = mock_resp
319
+ mock_resp.__aexit__.return_value = None
320
+
321
+ mock_session = MagicMock()
322
+ mock_session.get.return_value.__aenter__.return_value = mock_resp
323
+ mock_session.__aenter__.return_value = mock_session
324
+ mock_session.__aexit__.return_value = None
325
+
326
+ with patch("aiohttp.ClientSession", return_value=mock_session):
327
+ proxy_type = await validator.detect_proxy_type("1.2.3.4")
328
+
329
+ assert proxy_type == "datacenter", (
330
+ f"Should detect datacenter for {data['org']}"
331
+ )
332
+
333
+ @pytest.mark.asyncio
334
+ async def test_detect_proxy_type_residential(self, validator):
335
+ """Test residential proxy detection"""
336
+ mock_resp = AsyncMock()
337
+ mock_resp.status = 200
338
+ mock_resp.json.return_value = {"org": "Comcast Cable"}
339
+ mock_resp.__aenter__.return_value = mock_resp
340
+ mock_resp.__aexit__.return_value = None
341
+
342
+ mock_session = MagicMock()
343
+ mock_session.get.return_value.__aenter__.return_value = mock_resp
344
+ mock_session.__aenter__.return_value = mock_session
345
+ mock_session.__aexit__.return_value = None
346
+
347
+ with patch("aiohttp.ClientSession", return_value=mock_session):
348
+ proxy_type = await validator.detect_proxy_type("1.2.3.4")
349
+
350
+ assert proxy_type == "residential"
351
+
352
+ @pytest.mark.asyncio
353
+ async def test_detect_proxy_type_error(self, validator):
354
+ """Test proxy type detection error handling"""
355
+ mock_session = MagicMock()
356
+ mock_session.get.side_effect = Exception("API error")
357
+ mock_session.__aenter__.return_value = mock_session
358
+ mock_session.__aexit__.return_value = None
359
+
360
+ with patch("aiohttp.ClientSession", return_value=mock_session):
361
+ proxy_type = await validator.detect_proxy_type("1.2.3.4")
362
+
363
+ assert proxy_type == "unknown"
364
+
365
+ # ==================== QUALITY SCORE CALCULATION ====================
366
+
367
+ @pytest.mark.asyncio
368
+ async def test_calculate_quality_score_perfect(self, validator):
369
+ """Test quality score for perfect proxy"""
370
+ score = await validator.calculate_quality_score(
371
+ latency_ms=50,
372
+ anonymity="elite",
373
+ can_access_google=True,
374
+ proxy_type="residential",
375
+ )
376
+ assert score == 100 # 40 + 30 + 15 + 15 = 100
377
+
378
+ @pytest.mark.asyncio
379
+ async def test_calculate_quality_score_good(self, validator):
380
+ """Test quality score for good proxy"""
381
+ score = await validator.calculate_quality_score(
382
+ latency_ms=300,
383
+ anonymity="anonymous",
384
+ can_access_google=True,
385
+ proxy_type="datacenter",
386
+ )
387
+ assert score == 70 # 30 + 20 + 15 + 5 = 70
388
+
389
+ @pytest.mark.asyncio
390
+ async def test_calculate_quality_score_poor(self, validator):
391
+ """Test quality score for poor proxy"""
392
+ score = await validator.calculate_quality_score(
393
+ latency_ms=1500,
394
+ anonymity="transparent",
395
+ can_access_google=False,
396
+ proxy_type="unknown",
397
+ )
398
+ assert score == 15 # 10 + 5 + 0 + 0 = 15
399
+
400
+ @pytest.mark.asyncio
401
+ async def test_calculate_quality_score_minimal(self, validator):
402
+ """Test quality score with minimal data"""
403
+ score = await validator.calculate_quality_score(
404
+ latency_ms=None, anonymity=None, can_access_google=None, proxy_type=None
405
+ )
406
+ assert score == 0
407
+
408
+ @pytest.mark.asyncio
409
+ async def test_calculate_quality_score_caps_at_100(self, validator):
410
+ """Test quality score never exceeds 100"""
411
+ score = await validator.calculate_quality_score(
412
+ latency_ms=10,
413
+ anonymity="elite",
414
+ can_access_google=True,
415
+ proxy_type="residential",
416
+ )
417
+ assert score <= 100
418
+
419
+ # ==================== COMPREHENSIVE VALIDATION ====================
420
+
421
+ @pytest.mark.asyncio
422
+ async def test_validate_comprehensive_success(self, validator):
423
+ """Test comprehensive validation with all checks"""
424
+ # Mock connectivity check
425
+ with patch.object(
426
+ validator,
427
+ "validate_connectivity",
428
+ return_value=(True, 150, None),
429
+ ):
430
+ # Mock other checks
431
+ with patch.object(validator, "check_anonymity", return_value="elite"):
432
+ with patch.object(validator, "test_google_access", return_value=True):
433
+ with patch.object(
434
+ validator,
435
+ "get_geo_info",
436
+ return_value={
437
+ "country_code": "US",
438
+ "country_name": "United States",
439
+ "state": "CA",
440
+ "city": "SF",
441
+ },
442
+ ):
443
+ with patch.object(
444
+ validator, "detect_proxy_type", return_value="datacenter"
445
+ ):
446
+ result = await validator.validate_comprehensive(
447
+ "http://1.2.3.4:8080", "1.2.3.4"
448
+ )
449
+
450
+ assert result.success is True
451
+ assert result.latency_ms == 150
452
+ assert result.anonymity == "elite"
453
+ assert result.can_access_google is True
454
+ assert result.country_code == "US"
455
+ assert result.proxy_type == "datacenter"
456
+
457
+ @pytest.mark.asyncio
458
+ async def test_validate_comprehensive_connectivity_failure(self, validator):
459
+ """Test comprehensive validation when connectivity fails"""
460
+ with patch.object(
461
+ validator,
462
+ "validate_connectivity",
463
+ return_value=(False, None, "Connection timeout"),
464
+ ):
465
+ result = await validator.validate_comprehensive(
466
+ "http://1.2.3.4:8080", "1.2.3.4"
467
+ )
468
+
469
+ assert result.success is False
470
+ assert result.error_message == "Connection timeout"
471
+ assert result.latency_ms is None
472
+
473
+ # ==================== BATCH VALIDATION ====================
474
+
475
+ @pytest.mark.asyncio
476
+ async def test_validate_batch_success(self, validator):
477
+ """Test batch validation of multiple proxies"""
478
+ proxies = [
479
+ ("http://1.2.3.4:8080", "1.2.3.4"),
480
+ ("http://5.6.7.8:3128", "5.6.7.8"),
481
+ ]
482
+
483
+ mock_result = ValidationResult(success=True, latency_ms=100, anonymity="elite")
484
+
485
+ with patch.object(
486
+ validator, "validate_comprehensive", return_value=mock_result
487
+ ):
488
+ results = await validator.validate_batch(proxies)
489
+
490
+ assert len(results) == 2
491
+ assert all(result[1].success for result in results)
492
+
493
+ @pytest.mark.asyncio
494
+ async def test_validate_batch_mixed_results(self, validator):
495
+ """Test batch validation with mixed success/failure"""
496
+
497
+ async def mock_validate(proxy_url, ip):
498
+ if "1.2.3.4" in proxy_url:
499
+ return ValidationResult(success=True, latency_ms=100)
500
+ else:
501
+ return ValidationResult(success=False, error_message="Failed")
502
+
503
+ proxies = [
504
+ ("http://1.2.3.4:8080", "1.2.3.4"),
505
+ ("http://9.9.9.9:8080", "9.9.9.9"),
506
+ ]
507
+
508
+ with patch.object(
509
+ validator, "validate_comprehensive", side_effect=mock_validate
510
+ ):
511
+ results = await validator.validate_batch(proxies)
512
+
513
+ assert len(results) == 2
514
+ assert results[0][1].success is True
515
+ assert results[1][1].success is False
516
+
517
+ @pytest.mark.asyncio
518
+ async def test_validate_batch_exception_handling(self, validator):
519
+ """Test batch validation handles exceptions gracefully"""
520
+ proxies = [
521
+ ("http://1.2.3.4:8080", "1.2.3.4"),
522
+ ]
523
+
524
+ with patch.object(
525
+ validator, "validate_comprehensive", side_effect=Exception("Test error")
526
+ ):
527
+ results = await validator.validate_batch(proxies)
528
+
529
+ assert len(results) == 1
530
+ assert results[0][1].success is False
531
+ assert "Test error" in results[0][1].error_message