60 lines
2.8 KiB
Python
60 lines
2.8 KiB
Python
"""nav_sparkline renders a self-contained inline SVG polyline from a NAV series (no JS, no external libs)."""
|
|
from __future__ import annotations
|
|
|
|
from fxhnt.adapters.web.charts import dual_equity_curve, nav_sparkline
|
|
|
|
|
|
def test_sparkline_is_svg_with_points() -> None:
|
|
svg = nav_sparkline([1.0, 1.01, 1.005, 1.02], width=100, height=20)
|
|
assert svg.startswith("<svg") and svg.rstrip().endswith("</svg>")
|
|
assert "<polyline" in svg and "points=" in svg
|
|
|
|
|
|
def test_sparkline_empty_series_is_safe() -> None:
|
|
svg = nav_sparkline([], width=100, height=20)
|
|
assert svg.startswith("<svg") and "polyline" not in svg
|
|
|
|
|
|
def test_sparkline_flat_series_does_not_divide_by_zero() -> None:
|
|
svg = nav_sparkline([1.0, 1.0, 1.0], width=100, height=20)
|
|
assert "<polyline" in svg # flat line, no exception
|
|
|
|
|
|
def test_dual_equity_curve_renders_two_distinct_series() -> None:
|
|
"""Two equity series overlaid in one inline SVG: two polylines with distinct stroke colors + a legend."""
|
|
a = [100000.0, 101000.0, 99000.0, 103000.0]
|
|
b = [100000.0, 100500.0, 101000.0, 100200.0]
|
|
svg = dual_equity_curve(a, b, width=600, height=180,
|
|
color_a="#58a6ff", color_b="#d29922",
|
|
label_a="Binance", label_b="Bybit")
|
|
assert svg.startswith("<svg") and svg.rstrip().endswith("</svg>")
|
|
# exactly two data polylines
|
|
assert svg.count("<polyline") == 2
|
|
# both colors present (distinct series)
|
|
assert "#58a6ff" in svg and "#d29922" in svg
|
|
# tiny legend with both labels
|
|
assert "Binance" in svg and "Bybit" in svg
|
|
|
|
|
|
def test_dual_equity_curve_shares_one_scale() -> None:
|
|
"""Both series are scaled to the SAME min/max so shapes compare directly on one axis."""
|
|
a = [100.0, 200.0] # spans 100..200
|
|
b = [100.0, 100.0] # flat at the bottom of the shared scale
|
|
svg = dual_equity_curve(a, b, width=100, height=20)
|
|
assert svg.count("<polyline") == 2 # both drawn, no divide-by-zero
|
|
|
|
|
|
def test_dual_equity_curve_empty_is_safe() -> None:
|
|
svg = dual_equity_curve([], [], width=100, height=20)
|
|
assert svg.startswith("<svg") and "polyline" not in svg
|
|
|
|
|
|
def test_dual_equity_curve_is_relative_url_leak_clean() -> None:
|
|
"""No external network references baked into the SVG (no fetched hrefs/srcs). The SVG xmlns is the XML
|
|
namespace identifier, not a network load, so it's allowed; href/src/url() loaders are not."""
|
|
svg = dual_equity_curve([1.0, 2.0, 1.5], [1.0, 1.2, 1.4], width=120, height=40)
|
|
assert "href" not in svg and "src=" not in svg and "url(" not in svg
|
|
# the only http occurrence is the SVG namespace declaration, never an https fetch
|
|
assert "https://" not in svg
|
|
assert svg.count("http://") == 1 and 'xmlns="http://www.w3.org/2000/svg"' in svg
|