File size: 3,435 Bytes
3ab07bd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78c571c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
"""TDD for spaceutil.plot.build_umap_figure."""

from __future__ import annotations


def test_returns_plotly_figure(synthetic_cards):
    import plotly.graph_objects as go

    from spaceutil.plot import build_umap_figure

    fig = build_umap_figure(synthetic_cards)
    assert isinstance(fig, go.Figure)


def test_base_trace_only_without_highlights(synthetic_cards):
    from spaceutil.plot import build_umap_figure

    fig = build_umap_figure(synthetic_cards, highlight_indices=None)
    assert len(fig.data) == 1


def test_overlay_trace_added_for_highlights(synthetic_cards):
    from spaceutil.plot import build_umap_figure

    fig = build_umap_figure(synthetic_cards, highlight_indices=[0, 2, 5])
    assert len(fig.data) == 2


def test_empty_highlights_treated_as_none(synthetic_cards):
    from spaceutil.plot import build_umap_figure

    fig = build_umap_figure(synthetic_cards, highlight_indices=[])
    assert len(fig.data) == 1


def test_base_trace_customdata_carries_corpus_index(synthetic_cards):
    """Click events need to round-trip the corpus index from customdata,
    so we can resolve any clicked point back to a corpus row regardless
    of which trace it landed on."""
    from spaceutil.plot import build_umap_figure

    fig = build_umap_figure(synthetic_cards)
    base = fig.data[0]
    assert base.customdata is not None
    assert len(base.customdata) == len(synthetic_cards)
    first_row = base.customdata[0]
    # customdata rows may be array-like; first element must be the index.
    assert int(first_row[0]) == 0
    assert int(base.customdata[5][0]) == 5


def test_overlay_trace_customdata_length_matches_highlights(synthetic_cards):
    from spaceutil.plot import build_umap_figure

    indices = [1, 3, 7, 11]
    fig = build_umap_figure(synthetic_cards, highlight_indices=indices)
    overlay = fig.data[1]
    assert overlay.customdata is not None
    assert len(overlay.customdata) == len(indices)


def test_hover_text_includes_card_name(synthetic_cards):
    from spaceutil.plot import build_umap_figure

    fig = build_umap_figure(synthetic_cards)
    base = fig.data[0]
    assert base.text is not None
    assert len(base.text) == len(synthetic_cards)
    assert synthetic_cards[0]["name"] in base.text[0]


def test_cost_curve_figure_with_hits():
    import plotly.graph_objects as go

    from spaceutil.plot import build_cost_curve_figure
    from spaceutil.synergy import SynergyHit

    hits = [
        SynergyHit(1, "A", "A", 0.9, 0.0, 0.9, False, "Character", ["Red"], 3, "OP01"),
        SynergyHit(2, "B", "B", 0.8, 0.0, 0.8, False, "Character", ["Red"], 3, "OP01"),
        SynergyHit(3, "C", "C", 0.7, 0.0, 0.7, False, "Event", ["Red"], 7, "OP01"),
    ]
    fig = build_cost_curve_figure(hits)
    assert isinstance(fig, go.Figure)
    assert len(fig.data) == 1
    bar = fig.data[0]
    # Two distinct cost buckets: 3 (count 2) and 7 (count 1)
    assert list(bar.x) == ["3", "7"]
    assert list(bar.y) == [2, 1]


def test_cost_curve_figure_empty_shows_placeholder():
    import plotly.graph_objects as go

    from spaceutil.plot import build_cost_curve_figure

    fig = build_cost_curve_figure([])
    assert isinstance(fig, go.Figure)
    # Empty case has no bar trace
    assert len(fig.data) == 0
    # But carries an annotation
    assert fig.layout.annotations is not None
    assert any("pick a leader" in a.text.lower() for a in fig.layout.annotations)