| """ |
| Unit tests for input validation functions |
| """ |
|
|
| import pytest |
| from app import validate_inputs, validate_component_id |
|
|
|
|
| class TestComponentIDValidation: |
| """Test component ID validation""" |
| |
| def test_valid_component_ids(self): |
| """Test that valid component IDs pass validation""" |
| valid_ids = [ |
| "api-service", |
| "auth-service", |
| "payment-service-v2", |
| "db-01", |
| "cache", |
| "a", |
| "api-gateway-prod-001", |
| ] |
| |
| for component_id in valid_ids: |
| is_valid, msg = validate_component_id(component_id) |
| assert is_valid is True, f"'{component_id}' should be valid but got: {msg}" |
| assert msg == "" |
| |
| def test_invalid_uppercase(self): |
| """Test that uppercase letters are rejected""" |
| invalid_ids = ["API-SERVICE", "Auth-Service", "PaymentService"] |
| |
| for component_id in invalid_ids: |
| is_valid, msg = validate_component_id(component_id) |
| assert is_valid is False |
| assert "lowercase" in msg.lower() |
| |
| def test_invalid_underscore(self): |
| """Test that underscores are rejected""" |
| is_valid, msg = validate_component_id("api_service") |
| assert is_valid is False |
| assert "lowercase" in msg.lower() or "hyphen" in msg.lower() |
| |
| def test_invalid_special_characters(self): |
| """Test that special characters are rejected""" |
| invalid_ids = [ |
| "api@service", |
| "api.service", |
| "api service", |
| "api/service", |
| "api&service", |
| ] |
| |
| for component_id in invalid_ids: |
| is_valid, msg = validate_component_id(component_id) |
| assert is_valid is False, f"'{component_id}' should be invalid" |
| |
| def test_empty_string(self): |
| """Test that empty string is rejected""" |
| is_valid, msg = validate_component_id("") |
| assert is_valid is False |
| assert "1-255" in msg or "character" in msg.lower() |
| |
| def test_too_long(self): |
| """Test that component IDs longer than 255 chars are rejected""" |
| long_id = "a" * 256 |
| is_valid, msg = validate_component_id(long_id) |
| assert is_valid is False |
| assert "255" in msg |
| |
| def test_non_string_type(self): |
| """Test that non-string types are rejected""" |
| is_valid, msg = validate_component_id(123) |
| assert is_valid is False |
| assert "string" in msg.lower() |
|
|
|
|
| class TestNumericInputValidation: |
| """Test numeric input validation""" |
| |
| def test_valid_inputs(self): |
| """Test that valid inputs pass validation""" |
| is_valid, msg = validate_inputs( |
| latency=150.0, |
| error_rate=0.05, |
| throughput=1000.0, |
| cpu_util=0.7, |
| memory_util=0.6 |
| ) |
| |
| assert is_valid is True |
| assert msg == "" |
| |
| def test_valid_inputs_with_none_optionals(self): |
| """Test that None is valid for optional fields""" |
| is_valid, msg = validate_inputs( |
| latency=150.0, |
| error_rate=0.05, |
| throughput=1000.0, |
| cpu_util=None, |
| memory_util=None |
| ) |
| |
| assert is_valid is True |
| assert msg == "" |
|
|
|
|
| class TestLatencyValidation: |
| """Test latency validation""" |
| |
| def test_valid_latency(self): |
| """Test valid latency values""" |
| valid_values = [0, 1, 100, 500, 1000, 9999] |
| |
| for latency in valid_values: |
| is_valid, msg = validate_inputs(latency, 0.05, 1000, None, None) |
| assert is_valid is True, f"Latency {latency} should be valid" |
| |
| def test_negative_latency(self): |
| """Test that negative latency is rejected""" |
| is_valid, msg = validate_inputs(-10, 0.05, 1000, None, None) |
| assert is_valid is False |
| assert "latency" in msg.lower() |
| |
| def test_excessive_latency(self): |
| """Test that excessive latency is rejected""" |
| is_valid, msg = validate_inputs(20000, 0.05, 1000, None, None) |
| assert is_valid is False |
| assert "latency" in msg.lower() |
| |
| def test_non_numeric_latency(self): |
| """Test that non-numeric latency is rejected""" |
| is_valid, msg = validate_inputs("invalid", 0.05, 1000, None, None) |
| assert is_valid is False |
| assert "latency" in msg.lower() |
|
|
|
|
| class TestErrorRateValidation: |
| """Test error rate validation""" |
| |
| def test_valid_error_rates(self): |
| """Test valid error rate values""" |
| valid_values = [0, 0.01, 0.05, 0.5, 0.99, 1.0] |
| |
| for error_rate in valid_values: |
| is_valid, msg = validate_inputs(100, error_rate, 1000, None, None) |
| assert is_valid is True, f"Error rate {error_rate} should be valid" |
| |
| def test_negative_error_rate(self): |
| """Test that negative error rate is rejected""" |
| is_valid, msg = validate_inputs(100, -0.1, 1000, None, None) |
| assert is_valid is False |
| assert "error rate" in msg.lower() |
| |
| def test_error_rate_exceeds_one(self): |
| """Test that error rate > 1 is rejected""" |
| is_valid, msg = validate_inputs(100, 1.5, 1000, None, None) |
| assert is_valid is False |
| assert "error rate" in msg.lower() |
| |
| def test_non_numeric_error_rate(self): |
| """Test that non-numeric error rate is rejected""" |
| is_valid, msg = validate_inputs(100, "high", 1000, None, None) |
| assert is_valid is False |
| assert "error rate" in msg.lower() |
|
|
|
|
| class TestThroughputValidation: |
| """Test throughput validation""" |
| |
| def test_valid_throughput(self): |
| """Test valid throughput values""" |
| valid_values = [0, 1, 100, 1000, 10000] |
| |
| for throughput in valid_values: |
| is_valid, msg = validate_inputs(100, 0.05, throughput, None, None) |
| assert is_valid is True, f"Throughput {throughput} should be valid" |
| |
| def test_negative_throughput(self): |
| """Test that negative throughput is rejected""" |
| is_valid, msg = validate_inputs(100, 0.05, -500, None, None) |
| assert is_valid is False |
| assert "throughput" in msg.lower() |
| |
| def test_non_numeric_throughput(self): |
| """Test that non-numeric throughput is rejected""" |
| is_valid, msg = validate_inputs(100, 0.05, "many", None, None) |
| assert is_valid is False |
| assert "throughput" in msg.lower() |
|
|
|
|
| class TestCPUUtilizationValidation: |
| """Test CPU utilization validation""" |
| |
| def test_valid_cpu_util(self): |
| """Test valid CPU utilization values""" |
| valid_values = [0, 0.1, 0.5, 0.85, 1.0] |
| |
| for cpu_util in valid_values: |
| is_valid, msg = validate_inputs(100, 0.05, 1000, cpu_util, None) |
| assert is_valid is True, f"CPU util {cpu_util} should be valid" |
| |
| def test_negative_cpu_util(self): |
| """Test that negative CPU utilization is rejected""" |
| is_valid, msg = validate_inputs(100, 0.05, 1000, -0.1, None) |
| assert is_valid is False |
| assert "cpu" in msg.lower() |
| |
| def test_cpu_util_exceeds_one(self): |
| """Test that CPU utilization > 1 is rejected""" |
| is_valid, msg = validate_inputs(100, 0.05, 1000, 1.5, None) |
| assert is_valid is False |
| assert "cpu" in msg.lower() |
| |
| def test_non_numeric_cpu_util(self): |
| """Test that non-numeric CPU utilization is rejected""" |
| is_valid, msg = validate_inputs(100, 0.05, 1000, "high", None) |
| assert is_valid is False |
| assert "cpu" in msg.lower() |
|
|
|
|
| class TestMemoryUtilizationValidation: |
| """Test memory utilization validation""" |
| |
| def test_valid_memory_util(self): |
| """Test valid memory utilization values""" |
| valid_values = [0, 0.1, 0.5, 0.85, 1.0] |
| |
| for memory_util in valid_values: |
| is_valid, msg = validate_inputs(100, 0.05, 1000, None, memory_util) |
| assert is_valid is True, f"Memory util {memory_util} should be valid" |
| |
| def test_negative_memory_util(self): |
| """Test that negative memory utilization is rejected""" |
| is_valid, msg = validate_inputs(100, 0.05, 1000, None, -0.1) |
| assert is_valid is False |
| assert "memory" in msg.lower() |
| |
| def test_memory_util_exceeds_one(self): |
| """Test that memory utilization > 1 is rejected""" |
| is_valid, msg = validate_inputs(100, 0.05, 1000, None, 1.5) |
| assert is_valid is False |
| assert "memory" in msg.lower() |
| |
| def test_non_numeric_memory_util(self): |
| """Test that non-numeric memory utilization is rejected""" |
| is_valid, msg = validate_inputs(100, 0.05, 1000, None, "full") |
| assert is_valid is False |
| assert "memory" in msg.lower() |
|
|
|
|
| class TestEdgeCases: |
| """Test edge cases and boundary conditions""" |
| |
| def test_zero_values(self): |
| """Test that zero is valid for all metrics""" |
| is_valid, msg = validate_inputs(0, 0, 0, 0, 0) |
| assert is_valid is True |
| |
| def test_maximum_values(self): |
| """Test maximum boundary values""" |
| is_valid, msg = validate_inputs(10000, 1.0, 999999, 1.0, 1.0) |
| assert is_valid is True |
| |
| def test_float_precision(self): |
| """Test that high-precision floats are handled""" |
| is_valid, msg = validate_inputs( |
| latency=123.456789, |
| error_rate=0.123456, |
| throughput=1234.56, |
| cpu_util=0.87654321, |
| memory_util=0.76543210 |
| ) |
| assert is_valid is True |
| |
| def test_integer_inputs(self): |
| """Test that integer inputs are accepted""" |
| is_valid, msg = validate_inputs(100, 0, 1000, 1, 1) |
| assert is_valid is True |
| |
| def test_string_numbers(self): |
| """Test that string numbers are converted""" |
| is_valid, msg = validate_inputs("100", "0.05", "1000", "0.7", "0.6") |
| assert is_valid is True |
|
|
|
|
| class TestErrorMessages: |
| """Test that error messages are helpful""" |
| |
| def test_error_message_contains_field_name(self): |
| """Test that error messages identify the problematic field""" |
| |
| is_valid, msg = validate_inputs(-10, 0.05, 1000, None, None) |
| assert "latency" in msg.lower() |
| |
| |
| is_valid, msg = validate_inputs(100, 2.0, 1000, None, None) |
| assert "error rate" in msg.lower() |
| |
| |
| is_valid, msg = validate_inputs(100, 0.05, -100, None, None) |
| assert "throughput" in msg.lower() |
| |
| def test_error_message_has_emoji(self): |
| """Test that error messages include emoji for visibility""" |
| is_valid, msg = validate_inputs(-10, 0.05, 1000, None, None) |
| assert "❌" in msg |
| |
| def test_error_message_provides_guidance(self): |
| """Test that error messages provide guidance""" |
| is_valid, msg = validate_inputs(-10, 0.05, 1000, None, None) |
| assert "between" in msg.lower() or "range" in msg.lower() or "0-10000" in msg |
|
|
|
|
| if __name__ == "__main__": |
| pytest.main([__file__, "-v", "--tb=short"]) |