Files
Claude f9863700e6 Remove module ui.js support: unused by any module or the live app
Drops the registry lookup, manifest field, /modules route, ui.js loading in panels.js, MODULE_RULES lint, services.js, their tests and docs.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EymCrVCefq4m9Uv6MAkzz5
2026-10-17 13:59:18 +00:00

305 lines
12 KiB
Python

"""Scanning a real tree of modules, most of them deliberately broken.
The fixtures under fixtures/fixture_modules are the point: one good module and
five ways of being wrong, so "a broken module is absent, not fatal" is a thing
the suite demonstrates rather than a claim in a comment.
"""
from pathlib import Path
import pytest
from core import registry as registry_mod
from core.relations import order
from core.traits import missing_for, split
FIXTURES = Path(__file__).parent / "fixtures" / "fixture_modules"
PACKAGE = "core.tests.fixtures.fixture_modules"
@pytest.fixture(scope="module")
def scanned():
return registry_mod.scan(FIXTURES, package=PACKAGE)
def test_the_good_module_loads(scanned):
assert "audio_clock" in scanned.specs
spec = scanned.specs["audio_clock"]
assert spec.title == "Audio clock"
assert spec.stage == "conditioning"
assert spec.category == "guidance"
assert spec.status == "proven"
def test_a_module_that_raises_on_import_is_absent_not_fatal(scanned):
# The scan completed and returned other modules, which is the guarantee.
assert "explodes" not in scanned.specs
assert any("raises" in where for where, _ in scanned.failed)
assert scanned.specs, "one bad module did not take the rest down with it"
@pytest.mark.parametrize("bad_id", ["bad_ui", "bad_label", "no_default"])
def test_an_invalid_declaration_never_reaches_the_manifest(scanned, bad_id):
assert bad_id not in scanned.specs
def test_every_failure_says_why(scanned):
# A module that is silently missing is indistinguishable from one that was
# never installed; the log line is what makes it debuggable.
assert scanned.failed
for where, why in scanned.failed:
assert where and why, f"failure recorded with no reason: {where!r} {why!r}"
def test_defaults_are_available_without_any_ui(scanned):
assert scanned.specs["audio_clock"].defaults() == {
"enabled": True, "strength": 0.65, "mode": "beat",
}
def test_a_duplicate_id_is_refused_rather_than_decided_by_import_order():
reg = registry_mod.Registry()
from core.schema import validate
a = validate({"id": "x", "title": "A", "mount": "m"}, source="one")
b = validate({"id": "x", "title": "B", "mount": "m"}, source="two")
reg.add(a)
reg.add(b)
assert reg.specs["x"].title == "A"
assert any("duplicate id" in why for _, why in reg.failed)
# --- ordering --------------------------------------------------------------
def test_relations_decide_the_order(scanned):
ordered, rejected = order([scanned.specs["audio_clock"], scanned.specs["momentum"]])
ids = [s.id for s in ordered]
assert ids.index("audio_clock") < ids.index("momentum")
assert rejected == []
def test_before_is_the_same_edge_from_the_other_end():
from core.schema import validate
a = validate({"id": "a", "title": "A", "mount": "m", "before": ["b"]})
b = validate({"id": "b", "title": "B", "mount": "m"})
ordered, _ = order([b, a])
assert [s.id for s in ordered] == ["a", "b"]
def test_a_relation_naming_an_absent_module_is_not_an_error():
# Modules come and go; "after audio_clock" has nothing to wait for when
# audio_clock was never installed.
from core.schema import validate
solo = validate({"id": "solo", "title": "S", "mount": "m", "after": ["not_installed"]})
ordered, rejected = order([solo])
assert [s.id for s in ordered] == ["solo"]
assert rejected == []
def test_a_cycle_is_dropped_rather_than_ordered_arbitrarily(scanned):
# An arbitrary order is one that works until it does not, and by then nobody
# remembers it was never real.
pair = [scanned.specs["cycle_first"], scanned.specs["cycle_second"]]
ordered, rejected = order(pair)
assert ordered == []
assert {spec.id for spec, _ in rejected} == {"cycle_first", "cycle_second"}
assert all("cycle" in why for _, why in rejected)
def test_a_cycle_does_not_take_the_healthy_modules_with_it(scanned):
everything = list(scanned.specs.values())
ordered, rejected = order(everything)
assert "audio_clock" in [s.id for s in ordered]
assert {s.id for s, _ in rejected} == {"cycle_first", "cycle_second"}
def test_stages_run_in_declared_order():
from core.schema import validate
late = validate({"id": "late", "title": "L", "mount": "m", "stage": "post"})
early = validate({"id": "early", "title": "E", "mount": "m", "stage": "load"})
ordered, _ = order([late, early])
assert [s.id for s in ordered] == ["early", "late"]
def test_ordering_is_stable_without_any_relations():
from core.schema import validate
specs = [validate({"id": i, "title": i, "mount": "m"}) for i in ("c", "a", "b")]
assert [s.id for s in order(specs)[0]] == ["a", "b", "c"]
# --- traits ----------------------------------------------------------------
def test_a_module_is_kept_only_when_the_model_has_what_it_needs(scanned):
specs = list(scanned.specs.values())
ok, no = split(specs, ["audio_stream", "flow_match"])
assert "audio_clock" in [s.id for s in ok]
assert "needs_trait" in [s.id for s in no], "cfg_free was not on offer"
def test_a_module_with_no_requirements_fits_anything(scanned):
ok, _ = split([scanned.specs["momentum"]], [])
assert [s.id for s in ok] == ["momentum"]
def test_the_missing_traits_are_nameable(scanned):
assert missing_for(scanned.specs["needs_trait"], ["audio_stream"]) == ["cfg_free"]
def test_traits_are_names_the_model_offers_not_model_names(scanned):
# A module listing model names has to be edited whenever a model ships, and
# the one nobody edits is the one that silently stops appearing.
for spec in scanned.specs.values():
for trait in spec.requires:
assert trait.islower() and " " not in trait
# --- ordering: what a bad relation must not take with it -------------------
def test_a_cycle_does_not_drop_the_modules_that_merely_depend_on_it():
# A module saying "after B" is not itself circular. Once B is gone its
# relation has nothing to wait for -- exactly like a relation naming a
# module that was never installed.
from core.schema import validate
b = validate({"id": "b", "title": "B", "mount": "m", "after": ["c"]})
c = validate({"id": "c", "title": "C", "mount": "m", "after": ["b"]})
a = validate({"id": "a", "title": "A", "mount": "m", "after": ["b"]})
d = validate({"id": "d", "title": "D", "mount": "m"})
ordered, rejected = order([a, b, c, d])
assert {s.id for s in ordered} == {"a", "d"}
assert {s.id for s, _ in rejected} == {"b", "c"}
def test_a_longer_cycle_is_found_whole():
from core.schema import validate
ring = [
validate({"id": "x", "title": "X", "mount": "m", "after": ["z"]}),
validate({"id": "y", "title": "Y", "mount": "m", "after": ["x"]}),
validate({"id": "z", "title": "Z", "mount": "m", "after": ["y"]}),
]
healthy = validate({"id": "ok", "title": "OK", "mount": "m", "after": ["x"]})
ordered, rejected = order(ring + [healthy])
assert [s.id for s in ordered] == ["ok"]
assert {s.id for s, _ in rejected} == {"x", "y", "z"}
def test_a_relation_may_not_overturn_the_stage_order():
# Stage is the coarse order. Honouring "load after conditioning" would put a
# whole stage out of sequence to satisfy one module's relation.
from core.schema import validate
late = validate({"id": "late", "title": "L", "mount": "m", "stage": "post"})
early = validate({"id": "early", "title": "E", "mount": "m", "stage": "load",
"after": ["mid"]})
mid = validate({"id": "mid", "title": "M", "mount": "m", "stage": "conditioning"})
ordered, rejected = order([late, early, mid])
ids = [s.id for s in ordered]
assert rejected == [], "the module is kept; only its impossible relation is dropped"
assert ids == ["early", "mid", "late"], f"stages out of order: {ids}"
def test_before_may_not_overturn_the_stage_order_either():
from core.schema import validate
early = validate({"id": "early", "title": "E", "mount": "m", "stage": "load"})
late = validate({"id": "late", "title": "L", "mount": "m", "stage": "post",
"before": ["early"]})
ordered, _ = order([late, early])
assert [s.id for s in ordered] == ["early", "late"]
def test_a_relation_within_a_stage_is_still_honoured():
from core.schema import validate
first = validate({"id": "first", "title": "F", "mount": "m", "stage": "latent"})
second = validate({"id": "second", "title": "S", "mount": "m", "stage": "latent",
"after": ["first"]})
ordered, _ = order([second, first])
assert [s.id for s in ordered] == ["first", "second"]
def test_a_scan_under_a_redirected_modules_dir_does_not_outlive_the_test(monkeypatch, tmp_path):
"""The cache is session-wide, so a test that redirects MODULES_DIR and lets
a scan happen leaves every later test looking at its temp directory.
monkeypatch.setattr on the cached registry is what puts it back; this asserts
that it does, because the failure it prevents shows up in other files and
looks like anything but a scan.
"""
from core import config, registry as registry_mod
real = registry_mod.current()
assert real.specs, "this test needs a real installation to compare against"
def redirected():
monkeypatch.setattr(config, "MODULES_DIR", tmp_path / "nothing-here")
monkeypatch.setattr(registry_mod, "_current", registry_mod._current, raising=False)
registry_mod.current(rescan=True)
return registry_mod.current().specs
assert redirected() == {}, "the redirect did not take effect"
monkeypatch.undo()
assert registry_mod.current().specs, "the empty scan outlived its test"
def test_a_provider_that_raises_is_skipped_and_said_once():
"""One broken provider must not take the question down, and must not stay silent or repeat on every call."""
from core import log
from core.schema import validate
log._reset()
reg = registry_mod.Registry()
reg.add(validate({"id": "bad", "title": "B", "mount": "m", "provides": {"cap": lambda: 1 / 0}}, source="a"))
reg.add(validate({"id": "good", "title": "G", "mount": "m", "provides": {"cap": lambda: "ok"}}, source="b"))
assert reg.ask("cap") == "ok" and reg.ask("cap") == "ok"
said = [r for r in log.history() if "failed while answering" in r["message"]]
assert len(said) == 1 and "ZeroDivisionError" in said[0]["message"]
def test_a_provider_that_fails_in_a_new_way_is_said_again():
from core import log
from core.schema import validate
log._reset()
calls = iter([ValueError("a"), MemoryError("b")])
def flaky():
raise next(calls)
reg = registry_mod.Registry()
reg.add(validate({"id": "flaky", "title": "F", "mount": "m", "provides": {"cap": flaky}}, source="a"))
reg.ask("cap"); reg.ask("cap")
said = [r["message"] for r in log.history() if "failed while answering" in r["message"]]
assert len(said) == 2 and "ValueError" in said[0] and "MemoryError" in said[1]
def test_the_same_kind_of_failure_with_new_text_every_call_is_said_once():
"""The spam guard: a provider asked on every sampling step must not fill the log."""
from core import log
from core.schema import validate
log._reset()
n = iter(range(100))
def noisy():
raise ValueError(f"row {next(n)}")
reg = registry_mod.Registry()
reg.add(validate({"id": "noisy", "title": "N", "mount": "m", "provides": {"cap": noisy}}, source="a"))
for _ in range(30):
reg.ask("cap")
assert len([r for r in log.history() if "failed while answering" in r["message"]]) == 1
def test_a_duplicate_id_is_said_in_the_log_not_only_in_settings():
from core import log
from core.schema import validate
log._reset()
reg = registry_mod.Registry()
reg.add(validate({"id": "dup", "title": "A", "mount": "m"}, source="one"))
reg.add(validate({"id": "dup", "title": "B", "mount": "m"}, source="two"))
assert any("duplicate id" in r["message"] for r in log.history())
def test_route_providers_that_raise_are_said_by_name_once_each():
from core import log, routes
log._reset()
a = type("S", (), {"id": "mod_a"})()
b = type("S", (), {"id": "mod_b"})()
for s in (a, b, a, b):
routes._provider_failed("settings_sink", KeyError("x"), s)
said = [r for r in log.history() if "failed while answering" in r["message"]]
assert sorted(r["source"] for r in said) == ["settings_sink (mod_a)", "settings_sink (mod_b)"]