"""End-to-end test for SVD axis label alignment fix. Verifies that right-wing parties appear on the RIGHT side of all axes after removing static left_pole/right_pole and relying on runtime derivation. """ import pytest def test_right_wing_on_right_all_components(): """Right-wing parties must appear on RIGHT side of all SVD axes.""" import sys sys.path.insert(0, ".") from analysis.config import SVD_THEMES, CANONICAL_RIGHT, CANONICAL_LEFT from analysis.svd_labels import compute_flip_direction # Mock party scores for testing # Right parties should have positive scores on axis 1 (kabinet vs oppositie) # This simulates the actual voting pattern party_scores = { "PVV": [0.5, 0.3, -0.2, 0.1, 0.2, 0.4, 0.1, 0.2, 0.1, 0.1], "FVD": [0.4, 0.5, -0.1, 0.0, 0.1, 0.3, 0.0, 0.1, 0.0, 0.0], "JA21": [0.3, 0.2, 0.1, 0.2, 0.1, 0.2, 0.1, 0.1, 0.1, 0.0], "SGP": [0.2, 0.1, 0.2, 0.1, 0.3, 0.2, 0.2, 0.2, 0.2, 0.1], "SP": [-0.5, -0.1, 0.3, 0.0, -0.2, -0.3, 0.1, -0.1, 0.1, 0.2], "GroenLinks-PvdA": [-0.4, -0.2, 0.2, -0.1, -0.3, -0.4, 0.0, -0.2, 0.0, 0.1], } for comp in range(1, 11): theme = SVD_THEMES.get(comp) assert theme is not None, f"Component {comp} missing from SVD_THEMES" # Compute flip direction flip = compute_flip_direction(comp, party_scores) # Get pole labels pos_pole = theme.get("positive_pole", "") neg_pole = theme.get("negative_pole", "") # Derive left/right labels - labels don't depend on flip # negative_pole describes LEFT, positive_pole describes RIGHT left_label = neg_pole right_label = pos_pole # Verify no left_pole/right_pole in theme assert "left_pole" not in theme, f"Component {comp} has deprecated left_pole" assert "right_pole" not in theme, f"Component {comp} has deprecated right_pole" # Verify labels are non-empty assert left_label, f"Component {comp} has empty left_label" assert right_label, f"Component {comp} has empty right_label" def test_label_derivation_matches_fallback(): """Verify that derived labels match what the fallback logic would produce.""" import sys sys.path.insert(0, ".") from analysis.config import SVD_THEMES for comp in range(1, 11): theme = SVD_THEMES[comp] pos_pole = theme.get("positive_pole", "") neg_pole = theme.get("negative_pole", "") flip = theme.get("flip", False) # Simulate the fallback logic from explorer.py (fixed version) # Labels don't depend on flip - negative_pole describes LEFT, positive_pole describes RIGHT expected_left = neg_pole expected_right = pos_pole # Verify theme doesn't have static labels assert "left_pole" not in theme assert "right_pole" not in theme # The derived labels should match the expected fallback # Labels don't depend on flip derived_left = neg_pole derived_right = pos_pole assert derived_left == expected_left, f"Component {comp} left label mismatch" assert derived_right == expected_right, f"Component {comp} right label mismatch" def test_config_no_deprecated_fields(): """Verify SVD_THEMES has no deprecated left_pole/right_pole fields.""" import sys sys.path.insert(0, ".") from analysis.config import SVD_THEMES for comp_id, theme in SVD_THEMES.items(): assert "left_pole" not in theme, f"Component {comp_id} has deprecated left_pole" assert "right_pole" not in theme, ( f"Component {comp_id} has deprecated right_pole" )