You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
motief/tests/test_svd_axis_alignment.py

97 lines
3.6 KiB

"""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"
)