50 lines
1.5 KiB
Python
50 lines
1.5 KiB
Python
"""notebooks/code_families.py — structure and headless degradation."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import ast
|
|
import importlib.util
|
|
import re
|
|
from pathlib import Path
|
|
|
|
NB = Path(__file__).resolve().parents[2] / "notebooks" / "code_families.py"
|
|
|
|
|
|
def _load():
|
|
spec = importlib.util.spec_from_file_location("code_families_nb", NB)
|
|
mod = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(mod)
|
|
return mod
|
|
|
|
|
|
def test_notebook_is_a_marimo_app():
|
|
mod = _load()
|
|
assert mod.app.__class__.__name__ == "App"
|
|
|
|
|
|
def test_cells_are_anonymous_and_banners_present():
|
|
src = NB.read_text()
|
|
tree = ast.parse(src)
|
|
names = [n.name for n in tree.body if isinstance(n, ast.FunctionDef)]
|
|
assert names and set(names) == {"_"}
|
|
banners = re.findall(r"# ── (\d)\. ", src)
|
|
assert [int(b) for b in banners] == list(range(0, 9))
|
|
|
|
|
|
def test_headless_run_degrades_without_data(monkeypatch, tmp_path):
|
|
"""With no replica and no bib the guards render notes instead of raising."""
|
|
monkeypatch.setenv("STACK_DUCKDB_REPLICA", "1")
|
|
mod = _load()
|
|
import conf.connect as cc
|
|
|
|
monkeypatch.setattr(
|
|
cc,
|
|
"duckdb",
|
|
lambda *a, **k: (_ for _ in ()).throw(FileNotFoundError("no replica")),
|
|
)
|
|
monkeypatch.setattr(
|
|
cc, "bib", lambda *a, **k: (_ for _ in ()).throw(FileNotFoundError("no bib"))
|
|
)
|
|
result = mod.app.run() # marimo runs all cells; mo.stop cascades are fine
|
|
assert result is not None
|