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