Files
ledgrab/server/tests/core/test_demo_seed.py
T
alexei.dolgolyov 6de61b965e feat(value-sources): add sandboxed-Jinja template combinator
A new `template` value source evaluates a hardened, sandboxed Jinja
expression over the live values of other value sources — the system's
first float combinator.

Backend:
- Shared engine (utils/template_expr.py): ImmutableSandboxedEnvironment with
  filters/tests and auto-injected globals stripped; only min/max/abs/round/
  clamp exposed; rejects **, string/collection-literal repetition, attribute
  access and non-global calls; NaN/inf-safe result coercion.
- TemplateValueSource model + TemplateValueStream runtime: compile-once,
  primitives-only eval context, raw[name] exposure, eval_interval throttle,
  ref-counted input acquire/release, rename-safe hot-update.
- Validation: unbound-variable + reserved-name rejection, reference
  cycle/depth guards (depth-only at create, full cycle at update), runtime
  acquire() depth backstop, and delete referential-integrity.
- API: Create/Update/Response schemas + discriminated unions, _RESPONSE_MAP,
  and an advisory POST /value-sources/validate-template endpoint.
- Demo seed: a static source plus a template combinator example.

Frontend:
- Editor modal section: repeatable inputs list (EntitySelect rows), a
  zero-dependency Jinja syntax highlighter, a hints/reference panel, and a
  debounced live validator that gates Save (stale-response-safe).
- Graph editor: read-only template node with one edge per input.
- i18n (en/ru/zh), icon, and card rendering.

Tests: engine, stream, factory/cycle, validate endpoint, and demo seed.
2026-06-01 18:53:56 +03:00

51 lines
1.7 KiB
Python

"""Demo-seed regression tests (value sources, incl. the template combinator)."""
from ledgrab.core.demo_seed import seed_demo_data
from ledgrab.storage.database import Database
from ledgrab.storage.value_source import StaticValueSource, TemplateValueSource
from ledgrab.storage.value_source_store import ValueSourceStore
def _seed(tmp_path):
db = Database(tmp_path / "demo.db")
seed_demo_data(db)
return db
def test_demo_seeds_template_value_source(tmp_path):
db = _seed(tmp_path)
try:
store = ValueSourceStore(db)
by_id = {s.id: s for s in store.get_all_sources()}
base = by_id["vs_demo0001"]
boost = by_id["vs_demo0002"]
assert isinstance(base, StaticValueSource)
assert isinstance(boost, TemplateValueSource)
assert boost.template == "clamp(level * 1.5)"
assert boost.inputs == [{"name": "level", "value_source_id": "vs_demo0001"}]
# The reference graph is intact and consistent.
assert store.get_transitive_dependencies("vs_demo0002") == {"vs_demo0001"}
assert store.find_referencing_sources("vs_demo0001") == [boost.name]
finally:
db.close()
def test_demo_template_evaluates_through_manager(tmp_path):
"""The seeded template must actually evaluate over its seeded input."""
from ledgrab.core.processing.value_stream import ValueStreamManager
db = _seed(tmp_path)
try:
store = ValueSourceStore(db)
vsm = ValueStreamManager(value_source_store=store)
stream = vsm.acquire("vs_demo0002")
try:
# base level 0.5 -> clamp(0.5 * 1.5) = 0.75
assert abs(stream.get_value() - 0.75) < 1e-6
finally:
vsm.release("vs_demo0002")
finally:
db.close()