# test_properties_verification_ui.py """ Property-based tests for verification UI components. Tests universal properties that should hold across all inputs: - Property 8: Classifier Decision is Displayed - Property 9: Confidence is Formatted as Percentage - Property 10: Indicators are Displayed as Bullet Points Uses hypothesis for property-based testing with 100+ iterations. """ import pytest from hypothesis import given, strategies as st, settings from src.interface.verification_ui import VerificationUIComponents class TestClassifierDecisionDisplay: """ Property 8: Classifier Decision is Displayed **Validates: Requirements 2.3** For any classifier decision (green, yellow, red), the system should display the decision with the correct color badge (🟢 for GREEN, 🟔 for YELLOW, šŸ”“ for RED). """ @given(decision=st.sampled_from(["green", "yellow", "red"])) @settings(max_examples=100) def test_classifier_decision_displays_with_correct_badge(self, decision): """ **Feature: verification-mode, Property 8: Classifier Decision is Displayed** For any classifier decision, the badge should contain the correct emoji and the decision label. """ badge = VerificationUIComponents.get_classifier_decision_badge(decision) # Verify badge contains emoji if decision == "green": assert "🟢" in badge assert "GREEN" in badge elif decision == "yellow": assert "🟔" in badge assert "YELLOW" in badge elif decision == "red": assert "šŸ”“" in badge assert "RED" in badge # Verify badge is not empty assert len(badge) > 0 # Verify badge contains the decision label assert "Distress" in badge or "No Distress" in badge @given(decision=st.sampled_from(["green", "yellow", "red"])) @settings(max_examples=100) def test_classifier_decision_badge_is_consistent(self, decision): """ For any classifier decision, calling the function multiple times should produce the same result (consistency property). """ badge1 = VerificationUIComponents.get_classifier_decision_badge(decision) badge2 = VerificationUIComponents.get_classifier_decision_badge(decision) assert badge1 == badge2 class TestConfidenceFormatting: """ Property 9: Confidence is Formatted as Percentage **Validates: Requirements 2.4** For any confidence score (0.0-1.0), the system should display it as a percentage (e.g., "92% confident") where percentage = confidence * 100. """ @given(confidence=st.floats(min_value=0.0, max_value=1.0)) @settings(max_examples=100) def test_confidence_formatted_as_percentage(self, confidence): """ **Feature: verification-mode, Property 9: Confidence is Formatted as Percentage** For any confidence score, the formatted string should contain: - A percentage number - The word "confident" - The percentage should equal confidence * 100 (rounded) """ result = VerificationUIComponents.format_confidence_percentage(confidence) # Verify format contains "confident" assert "confident" in result.lower() # Verify format contains percentage sign assert "%" in result # Extract percentage - format is like "šŸŽÆ **85%** confident" # Find the number before the % sign import re match = re.search(r'(\d+)%', result) assert match is not None, f"Could not find percentage in: {result}" percentage = int(match.group(1)) expected_percentage = int(round(confidence * 100)) assert percentage == expected_percentage @given(confidence=st.floats(min_value=0.0, max_value=1.0)) @settings(max_examples=100) def test_confidence_percentage_is_valid_number(self, confidence): """ For any confidence score, the extracted percentage should be a valid integer between 0 and 100. """ result = VerificationUIComponents.format_confidence_percentage(confidence) # Extract percentage using regex - format is like "šŸŽÆ **85%** confident" import re match = re.search(r'(\d+)%', result) assert match is not None, f"Could not find percentage in: {result}" percentage = int(match.group(1)) # Verify it's in valid range assert 0 <= percentage <= 100 @given(confidence=st.floats(min_value=0.0, max_value=1.0)) @settings(max_examples=100) def test_confidence_formatting_is_consistent(self, confidence): """ For any confidence score, calling the function multiple times should produce the same result (consistency property). """ result1 = VerificationUIComponents.format_confidence_percentage(confidence) result2 = VerificationUIComponents.format_confidence_percentage(confidence) assert result1 == result2 class TestIndicatorsDisplay: """ Property 10: Indicators are Displayed as Bullet Points **Validates: Requirements 2.5** For any list of indicators, the system should display them as bullet points with each indicator on a separate line. """ @given(indicators=st.lists( st.text( alphabet=st.characters(blacklist_categories=("Cc", "Cs"), blacklist_characters="\n•,"), min_size=1 ), min_size=1, max_size=5 # Limited to 5 since implementation shows max 5 indicators )) @settings(max_examples=100) def test_indicators_displayed_correctly(self, indicators): """ **Feature: verification-mode, Property 10: Indicators are Displayed** For any list of indicators, each indicator should be displayed in the result. The implementation uses comma-separated format with "Detected:" prefix. """ result = VerificationUIComponents.format_indicators_as_bullets(indicators) # Verify each indicator is in the result for indicator in indicators: assert indicator in result # Verify the result has the Detected prefix assert "Detected" in result @given(indicators=st.lists( st.text( alphabet=st.characters(blacklist_categories=("Cc", "Cs"), blacklist_characters="\n•,"), min_size=1 ), min_size=1, max_size=5 )) @settings(max_examples=100) def test_indicators_format_is_consistent(self, indicators): """ For any list of indicators, calling the function multiple times should produce the same result (consistency property). """ result1 = VerificationUIComponents.format_indicators_as_bullets(indicators) result2 = VerificationUIComponents.format_indicators_as_bullets(indicators) assert result1 == result2 @given(indicators=st.lists( st.text( alphabet=st.characters(blacklist_categories=("Cc", "Cs"), blacklist_characters="\n•,"), min_size=1 ), min_size=1, max_size=5 )) @settings(max_examples=100) def test_indicators_all_present_in_output(self, indicators): """ For any list of indicators, all indicators should be present in the output. """ result = VerificationUIComponents.format_indicators_as_bullets(indicators) # Verify all indicators are present for indicator in indicators: assert indicator in result @given(indicators=st.lists(st.text(min_size=1), min_size=0, max_size=0)) @settings(max_examples=10) def test_empty_indicators_list_handled(self, indicators): """ For an empty indicators list, the system should display a message indicating no indicators were detected. """ result = VerificationUIComponents.format_indicators_as_bullets(indicators) # Should contain a message about no specific indicators assert "No specific indicators" in result or "no indicators" in result.lower()