1"""Unit tests for the pre-index run summary, and the contract that opening a run is cheap.
3The point of this module is that selecting a run must not trigger a parquet export, so
4the tests assert the absence of work as much as the presence of numbers.
6Run: cd webviewer && .venv/bin/python -m pytest backend/test_summary.py -q
12from fastapi.testclient
import TestClient
14from .
import config, export_cache, summary
20 """A fake run laid out the way elodin-db lays one out: a numeric directory per
21 component, each holding data/index/metadata/schema, plus non-component siblings."""
22 monkeypatch.setattr(config,
"ELODIN_DIR", tmp_path)
23 monkeypatch.setattr(config,
"CACHE_DIR", tmp_path /
"_cache")
24 run = tmp_path /
"daq_20260811_120000"
26 for i, age
in enumerate([30.0, 20.0, 0.0]):
27 d = run / str(1000 + i)
29 (d /
"data").write_bytes(b
"x")
30 os.utime(d /
"data", (1_000_000 - age, 1_000_000 - age))
31 (run /
"db_state").write_bytes(b
"")
32 (run /
"msgs").mkdir()
37 s = summary.summarize(
"daq_20260811_120000")
38 assert s[
"n_components"] == 3
42 s = summary.summarize(
"daq_20260811_120000")
43 assert s[
"duration_s"] == pytest.approx(30.0)
45 assert s[
"duration_approx"]
is True
49 s = summary.summarize(
"daq_20260811_120000")
50 assert s[
"size_bytes"]
and s[
"size_bytes"] > 0
51 assert s[
"cached"]
is False
57 export_cache,
"ensure_exported",
58 lambda *a, **k: pytest.fail(
"summarize() must never trigger an export"),
60 summary.summarize(
"daq_20260811_120000")
61 assert not (config.CACHE_DIR /
"daq_20260811_120000").exists()
67 assert summary.summarize(
"daq_20260811_120000")[
"has_config"]
is False
68 (run_dir.parent /
"daq_20260811_120000.toml").write_text(
"[network]\n")
69 assert summary.summarize(
"daq_20260811_120000")[
"has_config"]
is True
73 monkeypatch.setattr(config,
"ELODIN_DIR", tmp_path)
74 monkeypatch.setattr(config,
"CACHE_DIR", tmp_path /
"_cache")
75 (tmp_path /
"daq_20260811_120000").mkdir()
76 s = summary.summarize(
"daq_20260811_120000")
77 assert s[
"n_components"] == 0
and s[
"duration_s"]
is None
84 """GET /components used to export on demand, which made merely clicking a run in the
85 list cost tens of seconds. It now refuses; POST /index is the way to pay that."""
87 export_cache,
"ensure_exported",
88 lambda *a, **k: pytest.fail(
"GET /components must not trigger an export"),
90 r = TestClient(app).get(
"/api/runs/daq_20260811_120000/components")
91 assert r.status_code == 409
92 assert "not indexed" in r.json()[
"detail"]
96 client = TestClient(app)
98 assert client.get(
"/api/runs/not-a-run/summary").status_code
in (400, 404)
99 assert client.get(
"/api/runs/daq_19990101_000000/summary").status_code == 404
test_reports_component_count_ignoring_non_component_entries(run_dir)
test_reports_size_and_cached_state(run_dir)
test_summarize_does_not_export(run_dir, monkeypatch)
test_components_refuses_an_unindexed_run_rather_than_exporting(run_dir, monkeypatch)
test_duration_is_the_write_span_and_is_flagged_approximate(run_dir)
run_dir(tmp_path, monkeypatch)
test_summary_route_rejects_a_bad_run_id_and_a_missing_run(run_dir)
test_reports_whether_a_config_snapshot_exists(run_dir)
test_a_run_with_no_components_still_summarizes(tmp_path, monkeypatch)