parent
69208e0bf6
commit
24796f97d3
@ -0,0 +1,102 @@ |
|||||||
|
"""Integration test: full trajectory pipeline produces non-empty plot.""" |
||||||
|
|
||||||
|
import pytest |
||||||
|
|
||||||
|
from explorer import load_positions, load_party_map, select_trajectory_plot_data |
||||||
|
from explorer_helpers import compute_party_centroids |
||||||
|
|
||||||
|
|
||||||
|
def test_trajectory_pipeline_produces_traces(): |
||||||
|
"""Regression: trajectories must produce colored traces, not empty charts.""" |
||||||
|
db_path = "data/motions.db" |
||||||
|
window_size = "annual" |
||||||
|
|
||||||
|
# Stage 1: load positions |
||||||
|
positions_by_window, _ = load_positions(db_path, window_size) |
||||||
|
assert len(positions_by_window) > 0, "Expected at least one window" |
||||||
|
total_mps = sum(len(v) for v in positions_by_window.values()) |
||||||
|
assert total_mps > 0, "Expected MPs in windows" |
||||||
|
|
||||||
|
# Stage 2: load party map |
||||||
|
party_map = load_party_map(db_path) |
||||||
|
assert len(party_map) > 0, "Expected party map entries" |
||||||
|
|
||||||
|
# Stage 3: compute centroids |
||||||
|
windows = list(positions_by_window.keys()) |
||||||
|
centroids, mp_positions = compute_party_centroids( |
||||||
|
positions_by_window, party_map, windows |
||||||
|
) |
||||||
|
assert len(centroids) > 0, "Expected at least one party centroid" |
||||||
|
|
||||||
|
# Stage 4: select trajectory plot data (default party selection) |
||||||
|
# Use the same defaults as build_trajectories_tab: CDA, D66, VVD if available |
||||||
|
default_parties = [p for p in ["CDA", "D66", "VVD"] if p in centroids] |
||||||
|
if not default_parties: |
||||||
|
default_parties = list(centroids.keys())[:3] |
||||||
|
|
||||||
|
fig, trace_count, banner = select_trajectory_plot_data( |
||||||
|
positions_by_window, |
||||||
|
party_map, |
||||||
|
windows, |
||||||
|
selected_parties=default_parties, |
||||||
|
smooth_alpha=0.35, |
||||||
|
) |
||||||
|
|
||||||
|
# Assertions |
||||||
|
assert trace_count > 0, ( |
||||||
|
f"Expected traces but got trace_count={trace_count}, banner={banner}" |
||||||
|
) |
||||||
|
assert banner is None, f"Expected no fallback banner but got: {banner}" |
||||||
|
assert len(fig.data) == trace_count, ( |
||||||
|
f"fig.data ({len(fig.data)}) should equal trace_count ({trace_count})" |
||||||
|
) |
||||||
|
|
||||||
|
# Verify traces have real coordinates (not all NaN) |
||||||
|
for trace in fig.data: |
||||||
|
assert len(trace.x) > 0, f"Trace {trace.name} has no x values" |
||||||
|
assert len(trace.y) > 0, f"Trace {trace.name} has no y values" |
||||||
|
# At least some values should be real (not NaN) |
||||||
|
import math |
||||||
|
|
||||||
|
real_x = sum( |
||||||
|
1 for v in trace.x if not (v is None or (isinstance(v, float) and v != v)) |
||||||
|
) # v != v is True only for NaN |
||||||
|
real_y = sum( |
||||||
|
1 for v in trace.y if not (v is None or (isinstance(v, float) and v != v)) |
||||||
|
) |
||||||
|
assert real_x > 0, f"Trace {trace.name} has all NaN x values" |
||||||
|
assert real_y > 0, f"Trace {trace.name} has all NaN y values" |
||||||
|
|
||||||
|
|
||||||
|
def test_trajectory_helper_skips_second_loop(): |
||||||
|
"""Regression: when select_trajectory_plot_data succeeds, build_trajectories_tab |
||||||
|
should NOT add duplicate traces via the fallback loop. |
||||||
|
|
||||||
|
This test verifies that the helper produces clean output without relying on |
||||||
|
the second loop in build_trajectories_tab. |
||||||
|
""" |
||||||
|
db_path = "data/motions.db" |
||||||
|
window_size = "annual" |
||||||
|
|
||||||
|
positions_by_window, _ = load_positions(db_path, window_size) |
||||||
|
party_map = load_party_map(db_path) |
||||||
|
windows = list(positions_by_window.keys()) |
||||||
|
centroids, _ = compute_party_centroids(positions_by_window, party_map, windows) |
||||||
|
|
||||||
|
# Use 6 parties like the app's multiselect |
||||||
|
selected = list(centroids.keys())[:6] |
||||||
|
|
||||||
|
fig, trace_count, banner = select_trajectory_plot_data( |
||||||
|
positions_by_window, |
||||||
|
party_map, |
||||||
|
windows, |
||||||
|
selected_parties=selected, |
||||||
|
smooth_alpha=0.35, |
||||||
|
) |
||||||
|
|
||||||
|
# Should produce exactly the number of selected parties (or fewer if some have all-NaN) |
||||||
|
assert trace_count <= len(selected), ( |
||||||
|
f"trace_count ({trace_count}) should not exceed selected ({len(selected)})" |
||||||
|
) |
||||||
|
assert banner is None, "No fallback should be needed with valid data" |
||||||
|
assert len(fig.data) == trace_count |
||||||
Loading…
Reference in new issue