netsleuth/tests/test_cli_formats.py
Andrew Pimenov 245533f904 add Prometheus/CSV export, auto-diff vs previous run, and --trend
New metrics.py: collect_metrics() does the per-section extraction a flat
dict walk can't (latency needs label/host/method, TLS label/host, path hops
target/ttl/ip/asn), producing a flat Metric list that render_prometheus()
and render_csv() each format in about 15 lines. Simpler than treating them
as two separate renderers. FORMAT_EXTENSIONS gained "prom"/"csv" as the
single wiring point both parse_formats() and Output._normalize_formats()
already key off of.

New history.py finds a previous JSON report for the same ASN by reusing
the exact filename convention exporter.report_filename() already writes,
so the lookup key can't drift from what's on disk. Wired into diagnose()
before write_report() - a compact vs-previous-run summary
(compare.py::render_diff_brief(), new) prints automatically whenever this
run writes JSON and an earlier one exists for the same network; silent on
every other case (first run, prior run wrote no JSON prints one note
instead of guessing). New --no-auto-diff escapes it per run,
history.auto_diff in config disables it entirely.

New trend.py aggregates N saved JSON reports into per-label sparkline
series via --trend N - read-only, and it doesn't touch the network to find
"this network's" history either: history.latest_key() just reads it off
the newest report filename already on disk.

Growing FORMAT_EXTENSIONS to 5 entries broke three pre-existing tests that
hardcoded the old 3-format set; fixed in this commit.

Live-tested: prom/csv artifacts render correctly, auto-diff fired on a
second run, --trend 3 produced readable output, --compare unaffected.

Part of plans/zesty-zooming-wilkinson.md (gitignored).
2026-08-12 22:10:21 +03:00

85 lines
2.8 KiB
Python

from __future__ import annotations
import pytest
import typer
from netsleuth.cli import format_siblings, parse_formats
DEFAULT = frozenset({"md"})
def test_no_flags_returns_the_default():
assert parse_formats([], ru=False, json_flag=False, default=DEFAULT) == DEFAULT
def test_json_shortcut_selects_json_only():
assert parse_formats([], ru=False, json_flag=True, default=DEFAULT) == frozenset({"json"})
def test_ru_shortcut_selects_ru_md_only():
assert parse_formats([], ru=True, json_flag=False, default=DEFAULT) == frozenset({"ru-md"})
def test_json_and_format_md_stack():
assert parse_formats(["md"], ru=False, json_flag=True, default=DEFAULT) == frozenset({"md", "json"})
def test_repeated_format_flags_union():
assert parse_formats(["md", "json"], ru=False, json_flag=False, default=DEFAULT) == frozenset(
{"md", "json"}
)
def test_comma_separated_single_flag():
assert parse_formats(["md,json"], ru=False, json_flag=False, default=DEFAULT) == frozenset(
{"md", "json"}
)
def test_all_expands_to_every_known_format():
assert parse_formats(["all"], ru=False, json_flag=False, default=DEFAULT) == frozenset(
{"md", "ru-md", "json", "prom", "csv"}
)
def test_none_alone_means_write_nothing():
assert parse_formats(["none"], ru=False, json_flag=False, default=DEFAULT) == frozenset()
def test_none_combined_with_another_format_is_an_error():
with pytest.raises(typer.BadParameter):
parse_formats(["none,md"], ru=False, json_flag=False, default=DEFAULT)
def test_unknown_format_is_an_error():
with pytest.raises(typer.BadParameter):
parse_formats(["pdf"], ru=False, json_flag=False, default=DEFAULT)
def test_case_and_whitespace_are_normalized():
assert parse_formats([" MD ", "JSON"], ru=False, json_flag=False, default=DEFAULT) == frozenset(
{"md", "json"}
)
def test_order_of_flags_never_changes_the_result():
a = parse_formats(["json", "md"], ru=False, json_flag=False, default=DEFAULT)
b = parse_formats(["md", "json"], ru=False, json_flag=False, default=DEFAULT)
assert a == b
def test_format_siblings_cross_link_when_both_languages_selected():
result = format_siblings(frozenset({"md", "ru-md"}), "report.md", "report.ru.md")
assert result == ("report.ru.md", "report.md")
def test_format_siblings_are_none_when_only_english_selected():
assert format_siblings(frozenset({"md"}), "report.md", "report.ru.md") == (None, None)
def test_format_siblings_are_none_when_only_russian_selected():
assert format_siblings(frozenset({"ru-md"}), "report.md", "report.ru.md") == (None, None)
def test_format_siblings_are_none_when_neither_markdown_selected():
assert format_siblings(frozenset({"json"}), "report.md", "report.ru.md") == (None, None)