"""Tests for jarvis.tool_summary storage and limit parsing.""" import math import time from concurrent.futures import ThreadPoolExecutor from jarvis.tool_summary import ToolSummaryStore, ToolSummary, record_summary, list_summaries def _summary(name: str) -> ToolSummary: return ToolSummary( name=name, status="ok", duration_ms=12.0, detail=None, effect=None, risk=None, timestamp=time.time(), ) def test_list_limit_accepts_numeric_strings(): store = ToolSummaryStore() store.add(_summary("a")) store.add(_summary("b")) items = store.list("1") assert len(items) == 1 assert items[0]["name"] == "b" def test_list_limit_handles_non_finite_values(): store = ToolSummaryStore() store.add(_summary("a")) items_nan = store.list(math.nan) items_inf = store.list(math.inf) assert len(items_nan) == 1 assert len(items_inf) == 1 def test_list_limit_rejects_bool_and_fractional_values(): store = ToolSummaryStore() store.add(_summary("a")) store.add(_summary("b")) items_bool = store.list(True) items_fractional = store.list(1.8) assert len(items_bool) == 2 assert len(items_fractional) == 2 def test_record_summary_clamps_non_finite_start_time(): record_summary("nan_case", "ok", math.nan) payload = list_summaries(1)[0] assert payload["name"] == "nan_case" assert payload["duration_ms"] == 0.0 def test_store_add_and_list_are_thread_safe(): store = ToolSummaryStore(maxlen=500) def _add_item(idx: int) -> None: store.add(_summary(f"item_{idx}")) with ThreadPoolExecutor(max_workers=8) as ex: list(ex.map(_add_item, range(120))) items = store.list(200) assert len(items) == 120