diff --git a/app/tests/test_validator.py b/app/tests/test_validator.py new file mode 100644 index 0000000..f9873b3 --- /dev/null +++ b/app/tests/test_validator.py @@ -0,0 +1,167 @@ +"""Tests for schema validation.""" + +import json +import tempfile +from pathlib import Path + +from src.confgen.validator import SchemaValidator + + +class TestSchemaValidator: + """Tests for SchemaValidator.""" + + def setup_method(self): + """Set up test fixtures.""" + self.schema_data = { + "type": "object", + "properties": { + "name": {"type": "string"}, + "port": {"type": "integer", "minimum": 1, "maximum": 65535}, + "enabled": {"type": "boolean"}, + "timeout": {"type": "number", "minimum": 0}, + }, + "required": ["name", "port"], + } + + with tempfile.NamedTemporaryFile( + mode="w", suffix=".json", delete=False + ) as f: + json.dump(self.schema_data, f) + self.schema_path = f.name + + self.validator = SchemaValidator(self.schema_path) + + def teardown_method(self): + """Clean up test fixtures.""" + Path(self.schema_path).unlink() + + def test_validate_valid_config(self): + """Test validating a valid configuration.""" + config = {"name": "myapp", "port": 8080, "enabled": True, "timeout": 30} + + is_valid, errors = self.validator.validate(config) + + assert is_valid is True + assert len(errors) == 0 + + def test_validate_missing_required_field(self): + """Test that missing required field fails validation.""" + config = {"name": "myapp"} + + is_valid, errors = self.validator.validate(config) + + assert is_valid is False + assert len(errors) > 0 + assert any("port" in error for error in errors) + + def test_validate_invalid_type(self): + """Test that invalid type fails validation.""" + config = {"name": "myapp", "port": "not_a_number"} + + is_valid, errors = self.validator.validate(config) + + assert is_valid is False + assert len(errors) > 0 + + def test_validate_integer_out_of_range(self): + """Test that integer out of range fails validation.""" + config = {"name": "myapp", "port": 70000} + + is_valid, errors = self.validator.validate(config) + + assert is_valid is False + assert any("minimum" in error or "maximum" in error for error in errors) + + def test_validate_boolean_type(self): + """Test validating boolean fields.""" + config = {"name": "myapp", "port": 8080, "enabled": "not_boolean"} + + is_valid, errors = self.validator.validate(config) + + assert is_valid is False + + def test_format_error_with_path(self): + """Test error formatting includes the path.""" + schema_with_nested = { + "type": "object", + "properties": { + "database": { + "type": "object", + "properties": { + "host": {"type": "string"}, + "port": {"type": "integer"}, + }, + "required": ["host"], + }, + }, + } + + with tempfile.NamedTemporaryFile( + mode="w", suffix=".json", delete=False + ) as f: + json.dump(schema_with_nested, f) + nested_schema_path = f.name + + validator = SchemaValidator(nested_schema_path) + config = {"database": {"port": "invalid"}} + + is_valid, errors = validator.validate(config) + + assert is_valid is False + Path(nested_schema_path).unlink() + + def test_get_schema_summary(self): + """Test getting a summary of the schema.""" + summary = self.validator.get_schema_summary() + + assert summary["type"] == "object" + assert "name" in summary["required"] + assert "port" in summary["properties"] + assert "type" in summary["properties"]["name"] + + def test_check_property_exists(self): + """Test checking if property exists in data.""" + config = {"database": {"host": "localhost"}} + + assert self.validator.check_property_exists(config, "database.host") is True + assert self.validator.check_property_exists(config, "database.port") is False + assert self.validator.check_property_exists(config, "nonexistent") is False + + def test_validate_nested_object(self): + """Test validating nested objects.""" + schema = { + "type": "object", + "properties": { + "database": { + "type": "object", + "properties": { + "host": {"type": "string"}, + "port": {"type": "integer"}, + }, + "required": ["host", "port"], + }, + }, + "required": ["database"], + } + + with tempfile.NamedTemporaryFile( + mode="w", suffix=".json", delete=False + ) as f: + json.dump(schema, f) + nested_schema_path = f.name + + validator = SchemaValidator(nested_schema_path) + config = {"database": {"host": "localhost", "port": 5432}} + + is_valid, errors = validator.validate(config) + + assert is_valid is True + Path(nested_schema_path).unlink() + + def test_validate_with_default_values(self): + """Test that validation ignores missing optional fields.""" + config = {"name": "myapp", "port": 8080} + + is_valid, errors = self.validator.validate(config) + + assert is_valid is True