""" Tests for Placeholder Metadata System Tests the normative standard implementation for placeholder metadata. """ import sys from pathlib import Path # Add backend to path sys.path.insert(0, str(Path(__file__).parent.parent)) import pytest from placeholder_metadata import ( PlaceholderMetadata, PlaceholderMetadataRegistry, PlaceholderType, TimeWindow, OutputType, SourceInfo, MissingValuePolicy, ExceptionHandling, validate_metadata, ValidationViolation ) # ── Test Fixtures ───────────────────────────────────────────────────────────── @pytest.fixture def valid_metadata(): """Create a valid metadata instance.""" return PlaceholderMetadata( key="test_placeholder", placeholder="{{test_placeholder}}", category="Test", type=PlaceholderType.ATOMIC, description="Test placeholder", semantic_contract="A test placeholder for validation", unit="kg", time_window=TimeWindow.LATEST, output_type=OutputType.NUMBER, format_hint="85.0 kg", example_output="85.0 kg", source=SourceInfo( resolver="test_resolver", module="placeholder_resolver.py", source_tables=["test_table"] ), dependencies=["profile_id"], version="1.0.0", deprecated=False ) @pytest.fixture def invalid_metadata(): """Create an invalid metadata instance.""" return PlaceholderMetadata( key="", # Invalid: empty key placeholder="{{}}", category="", # Invalid: empty category type=PlaceholderType.LEGACY_UNKNOWN, # Warning: should be resolved description="", # Invalid: empty description semantic_contract="", # Invalid: empty semantic_contract unit=None, time_window=TimeWindow.UNKNOWN, # Warning: should be resolved output_type=OutputType.UNKNOWN, # Warning: should be resolved format_hint=None, example_output=None, source=SourceInfo( resolver="unknown" # Error: resolver must be specified ), version="1.0.0", deprecated=False ) # ── Validation Tests ────────────────────────────────────────────────────────── def test_valid_metadata_passes_validation(valid_metadata): """Valid metadata should pass all validation checks.""" violations = validate_metadata(valid_metadata) errors = [v for v in violations if v.severity == "error"] assert len(errors) == 0, f"Unexpected errors: {errors}" def test_invalid_metadata_fails_validation(invalid_metadata): """Invalid metadata should fail validation.""" violations = validate_metadata(invalid_metadata) errors = [v for v in violations if v.severity == "error"] assert len(errors) > 0, "Expected validation errors" def test_empty_key_violation(invalid_metadata): """Empty key should trigger violation.""" violations = validate_metadata(invalid_metadata) key_violations = [v for v in violations if v.field == "key"] assert len(key_violations) > 0 def test_legacy_unknown_type_warning(invalid_metadata): """LEGACY_UNKNOWN type should trigger warning.""" violations = validate_metadata(invalid_metadata) type_warnings = [v for v in violations if v.field == "type" and v.severity == "warning"] assert len(type_warnings) > 0 def test_unknown_time_window_warning(invalid_metadata): """UNKNOWN time window should trigger warning.""" violations = validate_metadata(invalid_metadata) tw_warnings = [v for v in violations if v.field == "time_window" and v.severity == "warning"] assert len(tw_warnings) > 0 def test_deprecated_without_replacement_warning(): """Deprecated placeholder without replacement should trigger warning.""" metadata = PlaceholderMetadata( key="old_placeholder", placeholder="{{old_placeholder}}", category="Test", type=PlaceholderType.ATOMIC, description="Deprecated placeholder", semantic_contract="Old placeholder", unit=None, time_window=TimeWindow.LATEST, output_type=OutputType.STRING, format_hint=None, example_output=None, source=SourceInfo(resolver="old_resolver"), deprecated=True, # Deprecated replacement=None # No replacement ) violations = validate_metadata(metadata) replacement_warnings = [v for v in violations if v.field == "replacement"] assert len(replacement_warnings) > 0 # ── Registry Tests ──────────────────────────────────────────────────────────── def test_registry_registration(valid_metadata): """Test registering metadata in registry.""" registry = PlaceholderMetadataRegistry() registry.register(valid_metadata, validate=False) assert registry.count() == 1 assert registry.get("test_placeholder") is not None def test_registry_validation_rejects_invalid(): """Registry should reject invalid metadata when validation is enabled.""" registry = PlaceholderMetadataRegistry() invalid = PlaceholderMetadata( key="", # Invalid placeholder="{{}}", category="", type=PlaceholderType.ATOMIC, description="", semantic_contract="", unit=None, time_window=TimeWindow.LATEST, output_type=OutputType.STRING, format_hint=None, example_output=None, source=SourceInfo(resolver="unknown") ) with pytest.raises(ValueError): registry.register(invalid, validate=True) def test_registry_get_by_category(valid_metadata): """Test retrieving metadata by category.""" registry = PlaceholderMetadataRegistry() # Create multiple metadata in different categories meta1 = valid_metadata meta2 = PlaceholderMetadata( key="test2", placeholder="{{test2}}", category="Test", type=PlaceholderType.ATOMIC, description="Test 2", semantic_contract="Test", unit=None, time_window=TimeWindow.LATEST, output_type=OutputType.STRING, format_hint=None, example_output=None, source=SourceInfo(resolver="test2_resolver") ) meta3 = PlaceholderMetadata( key="test3", placeholder="{{test3}}", category="Other", type=PlaceholderType.ATOMIC, description="Test 3", semantic_contract="Test", unit=None, time_window=TimeWindow.LATEST, output_type=OutputType.STRING, format_hint=None, example_output=None, source=SourceInfo(resolver="test3_resolver") ) registry.register(meta1, validate=False) registry.register(meta2, validate=False) registry.register(meta3, validate=False) by_category = registry.get_by_category() assert "Test" in by_category assert "Other" in by_category assert len(by_category["Test"]) == 2 assert len(by_category["Other"]) == 1 def test_registry_get_by_type(valid_metadata): """Test retrieving metadata by type.""" registry = PlaceholderMetadataRegistry() atomic_meta = valid_metadata interpreted_meta = PlaceholderMetadata( key="interpreted_test", placeholder="{{interpreted_test}}", category="Test", type=PlaceholderType.INTERPRETED, description="Interpreted test", semantic_contract="Test", unit=None, time_window=TimeWindow.DAYS_7, output_type=OutputType.STRING, format_hint=None, example_output=None, source=SourceInfo(resolver="interpreted_resolver") ) registry.register(atomic_meta, validate=False) registry.register(interpreted_meta, validate=False) atomic_placeholders = registry.get_by_type(PlaceholderType.ATOMIC) interpreted_placeholders = registry.get_by_type(PlaceholderType.INTERPRETED) assert len(atomic_placeholders) == 1 assert len(interpreted_placeholders) == 1 def test_registry_get_deprecated(): """Test retrieving deprecated placeholders.""" registry = PlaceholderMetadataRegistry() deprecated_meta = PlaceholderMetadata( key="deprecated_test", placeholder="{{deprecated_test}}", category="Test", type=PlaceholderType.ATOMIC, description="Deprecated", semantic_contract="Old placeholder", unit=None, time_window=TimeWindow.LATEST, output_type=OutputType.STRING, format_hint=None, example_output=None, source=SourceInfo(resolver="deprecated_resolver"), deprecated=True, replacement="{{new_test}}" ) active_meta = PlaceholderMetadata( key="active_test", placeholder="{{active_test}}", category="Test", type=PlaceholderType.ATOMIC, description="Active", semantic_contract="Active placeholder", unit=None, time_window=TimeWindow.LATEST, output_type=OutputType.STRING, format_hint=None, example_output=None, source=SourceInfo(resolver="active_resolver"), deprecated=False ) registry.register(deprecated_meta, validate=False) registry.register(active_meta, validate=False) deprecated = registry.get_deprecated() assert len(deprecated) == 1 assert deprecated[0].key == "deprecated_test" # ── Serialization Tests ─────────────────────────────────────────────────────── def test_metadata_to_dict(valid_metadata): """Test converting metadata to dictionary.""" data = valid_metadata.to_dict() assert isinstance(data, dict) assert data['key'] == "test_placeholder" assert data['type'] == "atomic" # Enum converted to string assert data['time_window'] == "latest" assert data['output_type'] == "number" def test_metadata_to_json(valid_metadata): """Test converting metadata to JSON string.""" import json json_str = valid_metadata.to_json() data = json.loads(json_str) assert data['key'] == "test_placeholder" assert data['type'] == "atomic" # ── Normative Standard Compliance ───────────────────────────────────────────── def test_all_mandatory_fields_present(valid_metadata): """Test that all mandatory fields from normative standard are present.""" mandatory_fields = [ 'key', 'placeholder', 'category', 'type', 'description', 'semantic_contract', 'unit', 'time_window', 'output_type', 'source', 'version', 'deprecated' ] for field in mandatory_fields: assert hasattr(valid_metadata, field), f"Missing mandatory field: {field}" def test_type_enum_valid_values(): """Test that PlaceholderType enum has required values.""" required_types = ['atomic', 'raw_data', 'interpreted', 'legacy_unknown'] for type_value in required_types: assert any(t.value == type_value for t in PlaceholderType), \ f"Missing required type: {type_value}" def test_time_window_enum_valid_values(): """Test that TimeWindow enum has required values.""" required_windows = ['latest', '7d', '14d', '28d', '30d', '90d', 'custom', 'mixed', 'unknown'] for window_value in required_windows: assert any(w.value == window_value for w in TimeWindow), \ f"Missing required time window: {window_value}" def test_output_type_enum_valid_values(): """Test that OutputType enum has required values.""" required_types = ['string', 'number', 'integer', 'boolean', 'json', 'markdown', 'date', 'enum', 'unknown'] for type_value in required_types: assert any(t.value == type_value for t in OutputType), \ f"Missing required output type: {type_value}" # ── Run Tests ───────────────────────────────────────────────────────────────── if __name__ == "__main__": pytest.main([__file__, "-v"])