petter2025 commited on
Commit
0c05457
·
verified ·
1 Parent(s): b26f88e

Delete test_models.py

Browse files
Files changed (1) hide show
  1. test_models.py +0 -614
test_models.py DELETED
@@ -1,614 +0,0 @@
1
- """
2
- Unit tests for Pydantic models with validation and security tests
3
- """
4
-
5
- import pytest
6
- from datetime import datetime, timezone
7
- from pydantic import ValidationError
8
- from models import (
9
- ReliabilityEvent,
10
- EventSeverity,
11
- HealingPolicy,
12
- HealingAction,
13
- PolicyCondition,
14
- AnomalyResult,
15
- ForecastResult
16
- )
17
-
18
-
19
- class TestReliabilityEventValidation:
20
- """Test ReliabilityEvent validation"""
21
-
22
- def test_valid_event_creation(self):
23
- """Test creating a valid event"""
24
- event = ReliabilityEvent(
25
- component="api-service",
26
- latency_p99=150.0,
27
- error_rate=0.05,
28
- throughput=1000.0,
29
- cpu_util=0.7,
30
- memory_util=0.6
31
- )
32
-
33
- assert event.component == "api-service"
34
- assert event.latency_p99 == 150.0
35
- assert event.error_rate == 0.05
36
- assert isinstance(event.timestamp, datetime)
37
- assert event.severity == EventSeverity.LOW
38
-
39
- def test_component_validation_valid(self):
40
- """Test valid component IDs"""
41
- valid_ids = ["api-service", "auth-service", "payment-service-v2", "db-01"]
42
-
43
- for component_id in valid_ids:
44
- event = ReliabilityEvent(
45
- component=component_id,
46
- latency_p99=100.0,
47
- error_rate=0.01,
48
- throughput=1000.0
49
- )
50
- assert event.component == component_id
51
-
52
- def test_component_validation_invalid(self):
53
- """Test invalid component IDs are rejected"""
54
- invalid_ids = [
55
- "API-SERVICE", # Uppercase
56
- "api_service", # Underscore
57
- "api service", # Space
58
- "api@service", # Special char
59
- "", # Empty
60
- ]
61
-
62
- for component_id in invalid_ids:
63
- with pytest.raises(ValidationError) as exc_info:
64
- ReliabilityEvent(
65
- component=component_id,
66
- latency_p99=100.0,
67
- error_rate=0.01,
68
- throughput=1000.0
69
- )
70
- assert "component" in str(exc_info.value).lower()
71
-
72
- def test_latency_bounds(self):
73
- """Test latency validation bounds"""
74
- # Valid latency
75
- event = ReliabilityEvent(
76
- component="test-service",
77
- latency_p99=100.0,
78
- error_rate=0.01,
79
- throughput=1000.0
80
- )
81
- assert event.latency_p99 == 100.0
82
-
83
- # Negative latency should fail
84
- with pytest.raises(ValidationError):
85
- ReliabilityEvent(
86
- component="test-service",
87
- latency_p99=-10.0,
88
- error_rate=0.01,
89
- throughput=1000.0
90
- )
91
-
92
- # Extremely high latency should fail (> 5 minutes)
93
- with pytest.raises(ValidationError):
94
- ReliabilityEvent(
95
- component="test-service",
96
- latency_p99=400000.0, # > 300000ms limit
97
- error_rate=0.01,
98
- throughput=1000.0
99
- )
100
-
101
- def test_error_rate_bounds(self):
102
- """Test error rate validation"""
103
- # Valid error rate
104
- event = ReliabilityEvent(
105
- component="test-service",
106
- latency_p99=100.0,
107
- error_rate=0.5,
108
- throughput=1000.0
109
- )
110
- assert event.error_rate == 0.5
111
-
112
- # Negative error rate should fail
113
- with pytest.raises(ValidationError):
114
- ReliabilityEvent(
115
- component="test-service",
116
- latency_p99=100.0,
117
- error_rate=-0.1,
118
- throughput=1000.0
119
- )
120
-
121
- # Error rate > 1 should fail
122
- with pytest.raises(ValidationError):
123
- ReliabilityEvent(
124
- component="test-service",
125
- latency_p99=100.0,
126
- error_rate=1.5,
127
- throughput=1000.0
128
- )
129
-
130
- def test_resource_utilization_bounds(self):
131
- """Test CPU and memory utilization bounds"""
132
- # Valid utilization
133
- event = ReliabilityEvent(
134
- component="test-service",
135
- latency_p99=100.0,
136
- error_rate=0.01,
137
- throughput=1000.0,
138
- cpu_util=0.85,
139
- memory_util=0.75
140
- )
141
- assert event.cpu_util == 0.85
142
- assert event.memory_util == 0.75
143
-
144
- # CPU > 1 should fail
145
- with pytest.raises(ValidationError):
146
- ReliabilityEvent(
147
- component="test-service",
148
- latency_p99=100.0,
149
- error_rate=0.01,
150
- throughput=1000.0,
151
- cpu_util=1.5
152
- )
153
-
154
- # Memory < 0 should fail
155
- with pytest.raises(ValidationError):
156
- ReliabilityEvent(
157
- component="test-service",
158
- latency_p99=100.0,
159
- error_rate=0.01,
160
- throughput=1000.0,
161
- memory_util=-0.1
162
- )
163
-
164
-
165
- class TestEventFingerprint:
166
- """Test event fingerprint generation (SHA-256)"""
167
-
168
- def test_fingerprint_is_sha256(self):
169
- """Test that fingerprint uses SHA-256 (64 hex chars)"""
170
- event = ReliabilityEvent(
171
- component="test-service",
172
- latency_p99=100.0,
173
- error_rate=0.05,
174
- throughput=1000.0
175
- )
176
-
177
- # SHA-256 produces 64 hex characters
178
- assert len(event.fingerprint) == 64
179
- assert all(c in '0123456789abcdef' for c in event.fingerprint)
180
-
181
- def test_fingerprint_deterministic(self):
182
- """Test that same inputs produce same fingerprint"""
183
- event1 = ReliabilityEvent(
184
- component="test-service",
185
- service_mesh="default",
186
- latency_p99=100.0,
187
- error_rate=0.05,
188
- throughput=1000.0
189
- )
190
-
191
- event2 = ReliabilityEvent(
192
- component="test-service",
193
- service_mesh="default",
194
- latency_p99=100.0,
195
- error_rate=0.05,
196
- throughput=1000.0
197
- )
198
-
199
- # Should produce same fingerprint (timestamp not included)
200
- assert event1.fingerprint == event2.fingerprint
201
-
202
- def test_fingerprint_different_for_different_events(self):
203
- """Test that different events produce different fingerprints"""
204
- event1 = ReliabilityEvent(
205
- component="service-1",
206
- latency_p99=100.0,
207
- error_rate=0.05,
208
- throughput=1000.0
209
- )
210
-
211
- event2 = ReliabilityEvent(
212
- component="service-2",
213
- latency_p99=100.0,
214
- error_rate=0.05,
215
- throughput=1000.0
216
- )
217
-
218
- assert event1.fingerprint != event2.fingerprint
219
-
220
- def test_fingerprint_not_md5(self):
221
- """Test that fingerprint is NOT MD5 (security fix verification)"""
222
- event = ReliabilityEvent(
223
- component="test-service",
224
- latency_p99=100.0,
225
- error_rate=0.05,
226
- throughput=1000.0
227
- )
228
-
229
- # MD5 produces 32 hex chars, SHA-256 produces 64
230
- assert len(event.fingerprint) != 32
231
- assert len(event.fingerprint) == 64
232
-
233
-
234
- class TestEventImmutability:
235
- """Test that events are immutable (frozen)"""
236
-
237
- def test_event_is_frozen(self):
238
- """Test that ReliabilityEvent is frozen"""
239
- event = ReliabilityEvent(
240
- component="test-service",
241
- latency_p99=100.0,
242
- error_rate=0.05,
243
- throughput=1000.0
244
- )
245
-
246
- # Attempting to modify should raise ValidationError
247
- with pytest.raises(ValidationError):
248
- event.latency_p99 = 200.0
249
-
250
- def test_model_copy_with_update(self):
251
- """Test that model_copy creates new instance with updates"""
252
- event1 = ReliabilityEvent(
253
- component="test-service",
254
- latency_p99=100.0,
255
- error_rate=0.05,
256
- throughput=1000.0,
257
- severity=EventSeverity.LOW
258
- )
259
-
260
- # Create modified copy
261
- event2 = event1.model_copy(update={'severity': EventSeverity.HIGH})
262
-
263
- # Original unchanged
264
- assert event1.severity == EventSeverity.LOW
265
- # Copy updated
266
- assert event2.severity == EventSeverity.HIGH
267
- # Other fields same
268
- assert event2.component == event1.component
269
- assert event2.latency_p99 == event1.latency_p99
270
-
271
-
272
- class TestDependencyValidation:
273
- """Test dependency cycle detection"""
274
-
275
- def test_valid_dependencies(self):
276
- """Test valid dependency configuration"""
277
- event = ReliabilityEvent(
278
- component="api-service",
279
- latency_p99=100.0,
280
- error_rate=0.05,
281
- throughput=1000.0,
282
- upstream_deps=["auth-service", "database"],
283
- downstream_deps=["frontend", "mobile-app"]
284
- )
285
-
286
- assert "auth-service" in event.upstream_deps
287
- assert "frontend" in event.downstream_deps
288
-
289
- def test_circular_dependency_detected(self):
290
- """Test that circular dependencies are detected"""
291
- with pytest.raises(ValidationError) as exc_info:
292
- ReliabilityEvent(
293
- component="api-service",
294
- latency_p99=100.0,
295
- error_rate=0.05,
296
- throughput=1000.0,
297
- upstream_deps=["auth-service", "database"],
298
- downstream_deps=["database", "frontend"] # 'database' in both
299
- )
300
-
301
- error_msg = str(exc_info.value).lower()
302
- assert "circular" in error_msg or "database" in error_msg
303
-
304
- def test_dependency_name_validation(self):
305
- """Test that dependency names follow same rules as component IDs"""
306
- # Valid dependency names
307
- event = ReliabilityEvent(
308
- component="api-service",
309
- latency_p99=100.0,
310
- error_rate=0.05,
311
- throughput=1000.0,
312
- upstream_deps=["auth-service", "db-01", "cache-v2"]
313
- )
314
- assert len(event.upstream_deps) == 3
315
-
316
- # Invalid dependency names
317
- with pytest.raises(ValidationError):
318
- ReliabilityEvent(
319
- component="api-service",
320
- latency_p99=100.0,
321
- error_rate=0.05,
322
- throughput=1000.0,
323
- upstream_deps=["AUTH_SERVICE"] # Uppercase/underscore
324
- )
325
-
326
-
327
- class TestPolicyConditionModel:
328
- """Test PolicyCondition structured model"""
329
-
330
- def test_valid_policy_condition(self):
331
- """Test creating valid policy conditions"""
332
- condition = PolicyCondition(
333
- metric="latency_p99",
334
- operator="gt",
335
- threshold=150.0
336
- )
337
-
338
- assert condition.metric == "latency_p99"
339
- assert condition.operator == "gt"
340
- assert condition.threshold == 150.0
341
-
342
- def test_policy_condition_frozen(self):
343
- """Test that PolicyCondition is immutable"""
344
- condition = PolicyCondition(
345
- metric="error_rate",
346
- operator="gt",
347
- threshold=0.1
348
- )
349
-
350
- with pytest.raises(ValidationError):
351
- condition.threshold = 0.2
352
-
353
- def test_invalid_metric(self):
354
- """Test that invalid metrics are rejected"""
355
- with pytest.raises(ValidationError):
356
- PolicyCondition(
357
- metric="invalid_metric",
358
- operator="gt",
359
- threshold=100.0
360
- )
361
-
362
- def test_invalid_operator(self):
363
- """Test that invalid operators are rejected"""
364
- with pytest.raises(ValidationError):
365
- PolicyCondition(
366
- metric="latency_p99",
367
- operator="invalid_op",
368
- threshold=100.0
369
- )
370
-
371
- def test_negative_threshold(self):
372
- """Test that negative thresholds are rejected"""
373
- with pytest.raises(ValidationError):
374
- PolicyCondition(
375
- metric="latency_p99",
376
- operator="gt",
377
- threshold=-100.0
378
- )
379
-
380
-
381
- class TestHealingPolicyModel:
382
- """Test HealingPolicy model"""
383
-
384
- def test_valid_healing_policy(self):
385
- """Test creating valid healing policy"""
386
- policy = HealingPolicy(
387
- name="high_latency_restart",
388
- conditions=[
389
- PolicyCondition(metric="latency_p99", operator="gt", threshold=300.0)
390
- ],
391
- actions=[HealingAction.RESTART_CONTAINER, HealingAction.ALERT_TEAM],
392
- priority=1,
393
- cool_down_seconds=300
394
- )
395
-
396
- assert policy.name == "high_latency_restart"
397
- assert len(policy.conditions) == 1
398
- assert len(policy.actions) == 2
399
- assert policy.priority == 1
400
-
401
- def test_policy_frozen(self):
402
- """Test that HealingPolicy is immutable"""
403
- policy = HealingPolicy(
404
- name="test_policy",
405
- conditions=[
406
- PolicyCondition(metric="error_rate", operator="gt", threshold=0.1)
407
- ],
408
- actions=[HealingAction.ROLLBACK],
409
- priority=2
410
- )
411
-
412
- with pytest.raises(ValidationError):
413
- policy.priority = 5
414
-
415
- def test_empty_conditions_rejected(self):
416
- """Test that policies must have at least one condition"""
417
- with pytest.raises(ValidationError):
418
- HealingPolicy(
419
- name="empty_policy",
420
- conditions=[], # Empty
421
- actions=[HealingAction.ALERT_TEAM],
422
- priority=3
423
- )
424
-
425
- def test_empty_actions_rejected(self):
426
- """Test that policies must have at least one action"""
427
- with pytest.raises(ValidationError):
428
- HealingPolicy(
429
- name="empty_actions",
430
- conditions=[
431
- PolicyCondition(metric="latency_p99", operator="gt", threshold=100.0)
432
- ],
433
- actions=[], # Empty
434
- priority=3
435
- )
436
-
437
- def test_priority_bounds(self):
438
- """Test priority validation (1-5)"""
439
- # Valid priority
440
- policy = HealingPolicy(
441
- name="test",
442
- conditions=[PolicyCondition(metric="latency_p99", operator="gt", threshold=100.0)],
443
- actions=[HealingAction.ALERT_TEAM],
444
- priority=3
445
- )
446
- assert policy.priority == 3
447
-
448
- # Priority < 1 should fail
449
- with pytest.raises(ValidationError):
450
- HealingPolicy(
451
- name="test",
452
- conditions=[PolicyCondition(metric="latency_p99", operator="gt", threshold=100.0)],
453
- actions=[HealingAction.ALERT_TEAM],
454
- priority=0
455
- )
456
-
457
- # Priority > 5 should fail
458
- with pytest.raises(ValidationError):
459
- HealingPolicy(
460
- name="test",
461
- conditions=[PolicyCondition(metric="latency_p99", operator="gt", threshold=100.0)],
462
- actions=[HealingAction.ALERT_TEAM],
463
- priority=10
464
- )
465
-
466
-
467
- class TestAnomalyResultModel:
468
- """Test AnomalyResult model"""
469
-
470
- def test_valid_anomaly_result(self):
471
- """Test creating valid anomaly result"""
472
- result = AnomalyResult(
473
- is_anomaly=True,
474
- confidence=0.85,
475
- anomaly_score=0.75,
476
- affected_metrics=["latency", "error_rate"]
477
- )
478
-
479
- assert result.is_anomaly is True
480
- assert result.confidence == 0.85
481
- assert isinstance(result.detection_timestamp, datetime)
482
-
483
- def test_confidence_bounds(self):
484
- """Test confidence is bounded 0-1"""
485
- # Valid
486
- result = AnomalyResult(
487
- is_anomaly=True,
488
- confidence=0.5,
489
- anomaly_score=0.6
490
- )
491
- assert result.confidence == 0.5
492
-
493
- # Confidence > 1 should fail
494
- with pytest.raises(ValidationError):
495
- AnomalyResult(
496
- is_anomaly=True,
497
- confidence=1.5,
498
- anomaly_score=0.5
499
- )
500
-
501
-
502
- class TestForecastResultModel:
503
- """Test ForecastResult model"""
504
-
505
- def test_valid_forecast(self):
506
- """Test creating valid forecast"""
507
- result = ForecastResult(
508
- metric="latency",
509
- predicted_value=250.0,
510
- confidence=0.75,
511
- trend="increasing",
512
- time_to_threshold=15.5,
513
- risk_level="high"
514
- )
515
-
516
- assert result.metric == "latency"
517
- assert result.trend == "increasing"
518
- assert result.risk_level == "high"
519
-
520
- def test_trend_validation(self):
521
- """Test that only valid trends are accepted"""
522
- valid_trends = ["increasing", "decreasing", "stable"]
523
-
524
- for trend in valid_trends:
525
- result = ForecastResult(
526
- metric="latency",
527
- predicted_value=200.0,
528
- confidence=0.7,
529
- trend=trend,
530
- risk_level="medium"
531
- )
532
- assert result.trend == trend
533
-
534
- # Invalid trend
535
- with pytest.raises(ValidationError):
536
- ForecastResult(
537
- metric="latency",
538
- predicted_value=200.0,
539
- confidence=0.7,
540
- trend="invalid_trend",
541
- risk_level="medium"
542
- )
543
-
544
- def test_risk_level_validation(self):
545
- """Test that only valid risk levels are accepted"""
546
- valid_levels = ["low", "medium", "high", "critical"]
547
-
548
- for level in valid_levels:
549
- result = ForecastResult(
550
- metric="error_rate",
551
- predicted_value=0.08,
552
- confidence=0.8,
553
- trend="stable",
554
- risk_level=level
555
- )
556
- assert result.risk_level == level
557
-
558
- # Invalid risk level
559
- with pytest.raises(ValidationError):
560
- ForecastResult(
561
- metric="error_rate",
562
- predicted_value=0.08,
563
- confidence=0.8,
564
- trend="stable",
565
- risk_level="extreme"
566
- )
567
-
568
-
569
- class TestTimestampHandling:
570
- """Test datetime timestamp handling"""
571
-
572
- def test_timestamp_is_datetime(self):
573
- """Test that timestamp is datetime, not string"""
574
- event = ReliabilityEvent(
575
- component="test-service",
576
- latency_p99=100.0,
577
- error_rate=0.05,
578
- throughput=1000.0
579
- )
580
-
581
- # Should be datetime object
582
- assert isinstance(event.timestamp, datetime)
583
-
584
- # Should have timezone
585
- assert event.timestamp.tzinfo is not None
586
-
587
- def test_timestamp_is_utc(self):
588
- """Test that timestamp uses UTC"""
589
- event = ReliabilityEvent(
590
- component="test-service",
591
- latency_p99=100.0,
592
- error_rate=0.05,
593
- throughput=1000.0
594
- )
595
-
596
- assert event.timestamp.tzinfo == timezone.utc
597
-
598
- def test_timestamp_serialization(self):
599
- """Test that timestamp can be serialized"""
600
- event = ReliabilityEvent(
601
- component="test-service",
602
- latency_p99=100.0,
603
- error_rate=0.05,
604
- throughput=1000.0
605
- )
606
-
607
- # Can convert to ISO format
608
- iso_str = event.timestamp.isoformat()
609
- assert isinstance(iso_str, str)
610
- assert 'T' in iso_str # ISO format
611
-
612
-
613
- if __name__ == "__main__":
614
- pytest.main([__file__, "-v", "--tb=short"])