Catalog: exthost.ui-delivery leaves

exthost.ui-delivery leaves for the host proofs that carry it; the
catalog goes from 92 to 91.

- forgeext ui, in forgeext's tests/install_test.py (23156a4): an
  installed package's page comes back byte for byte as the html string,
  with bytes its length; an id that is not installed and a package the
  operator disabled are each refused in words.
- forgectrl's relay of the page and the settings, in tests/extpkg_test.c
  on forgectrl's build.yml: the page served, a disabled package's page
  404, the settings read, a value over its bound 400.
The install-time mismatch between the ui capability and a page stays
with forgeext's own tests.

It has no glob of its own, so no HOST_TESTED row. exthost.sender-keep-out
stays: forgeext carries a package's claim on the Grbl sender to the
machine with the token it reads only from /run/forgefirm/ext-host.token,
so run_test.py does not prove it, and suite/extsender.py is unchanged.

test_setup_suite.py passes (31 tests). The coverage lint on the tree
manifest finds no uncovered path and no host-tested row problem across
the 91 tests; its one finding is setup.check-lens's empty src/lensfind.*
entry. pyflakes reports nothing new in exthost.py. The full forgetest
suite was not run.

Fingerprints: exthost.py's shared text moves, so 12 tests move: the 8
left in exthost.py, and exthost.page-call, exthost.catalog,
exthost.wizard and exthost.sender-keep-out in the modules that read it. The pending pin bump
from c274f19 already owes 11 of them; exthost.platform is the one it
adds, as before. releases/ and TEST-AUDIT.md are unchanged.
This commit is contained in:
ScottW514
2026-10-14 12:41:15 -04:00
parent b447ae77f1
commit b5f355cb61
-102
View File
@@ -1634,108 +1634,6 @@ def motion_jog(ctx):
_as_found(ctx, fc, prior, raw, dir_mode, found_tree)
@test("exthost.ui-delivery", title="A package's interface is served, and only when it has one",
subsystem="exthost", kind="auto", hardware="api", est_min=4,
covers=[("forgeext", "src/install.*"), ("forgeext", "src/main.c"), ("forgectrl", "src/extpkg.*"),
("forgectrl", "src/main.c")],
requires=["exthost.service"],
description="A package that asks for ui must ship ui/index.html, and one that ships the file must "
"ask: the install refuses each of those the other way round, in words. With both, GET "
"/ext/ui hands the page back as a JSON string, byte for byte what the package shipped, "
"and a package that is not installed is refused, as is one the operator disabled - "
"disabling a package is the way out of everything it does, its interface included. "
"The page is a string in JSON and never "
"markup this daemon composed. Nothing runs and nothing moves: the machine is only asked "
"for files. The frame the panel builds around the page is a browser's to judge, and that "
"is exthost.ui-frame-isolation's, which is a browser harness and not this catalog.")
def ui_delivery(ctx):
import shutil
import tempfile
fc = ctx.forgectrl
ev = ctx.evidence
ctx.check(fc.wait_idle(timeout=30, abort=ctx.aborted), "machine not idle: settings are locked")
prior = fc.settings().get("ext_enabled") or ""
raw = read_file(record_path())
found_tree = _tree(EXT_ROOT)
dir_mode = os.stat(os.path.dirname(EXT_ROOT)).st_mode & 0o7777
st, body, hdrs = request(fc.base, "GET", "/advisories/extensions", headers={"Host": fc.host_header()})
etag = hdrs.get("etag")
work = tempfile.mkdtemp(prefix="forgetest-ffx.")
owner_key = os.path.join(EXT_ROOT, "keys", REF_KEY + ".pub")
try:
archive, pub = _pack_reference(work, lan_ip(), more_caps=["ui"])
shutil.copy(pub, owner_key)
os.chmod(owner_key, 0o644)
# Asking for an interface without shipping one, and shipping one
# without asking: each refused, and neither installed.
# Each archive is packed in a directory of its own: the packer
# makes a signing key there, and fwup will not remake one over an
# existing file.
for caps, want_ui, why_, words in ((["ui"], False, "asks_without_the_file", "ui/index.html"),
([], True, "ships_without_asking", "does not ask for ui")):
wn = tempfile.mkdtemp(prefix="forgetest-ffxn.")
try:
arch, _k = _pack_reference(wn, lan_ip(), more_caps=caps, ui=want_ui)
r = _forgeext("inspect", arch)
ev[why_] = r.get("error")
ctx.check(r.get("ok") is False and words in (r.get("error") or ""),
"%s -> %s", why_.replace("_", " "), r)
finally:
shutil.rmtree(wn, ignore_errors=True)
r = _forgeext("install", archive, "--consent-community")
ctx.check(r.get("ok") is True, "the install of a package with an interface -> %s", r.get("error"))
st, reply = fc.post("/settings", data={"ext_enabled": "1", "advisory": etag, "phrase": SAFETY_PHRASE})
ctx.check(st == 200, "ext_enabled=1 -> %s %r", st, reply)
st_, doc = fc.get("/ext/ui", params={"id": REF_ID})
ev["ui"] = {"status": st_, "bytes": (doc or {}).get("bytes")}
ctx.log("GET /ext/ui -> %s, %s bytes", st_, (doc or {}).get("bytes"))
ctx.check(st_ == 200 and isinstance(doc, dict) and doc.get("ok"), "GET /ext/ui -> %s %s", st_, doc)
ctx.check(doc.get("html") == REF_UI, "the page served is not the one the package shipped")
ctx.check(doc.get("bytes") == len(REF_UI), "the length does not match the page: %s", doc.get("bytes"))
st_, why = fc.get("/ext/ui", params={"id": "org.forgetest.nothere"})
ev["ui_absent"] = [st_, why]
# The host's refusal in its words, and 404: a 502 here is a relay
# that took the host's refusal for no answer.
ctx.check(st_ == 404 and "not installed" in json.dumps(why), "a package that is not installed -> %s %s", st_, why)
# Disabled, it has no page. Disabling a package is the operator's
# way out of everything it does, and the interface is part of
# that: the page reaches the machine through the panel's bridge,
# and a door that is shut is shut.
r = _forgeext("disable", REF_ID)
ctx.check(r.get("ok") is True, "disable -> %s", r.get("error"))
st_, why = fc.get("/ext/ui", params={"id": REF_ID})
ev["ui_disabled"] = [st_, why]
ctx.check(st_ >= 400 and "disabled" in json.dumps(why), "a disabled package's page -> %s %s", st_, why)
r = _forgeext("enable", REF_ID)
ctx.check(r.get("ok") is True, "enable -> %s", r.get("error"))
st_, doc = fc.get("/ext/ui", params={"id": REF_ID})
ctx.check(st_ == 200 and (doc or {}).get("ok"), "enabled again, its page is served -> %s", st_)
# Its settings come through the same relay, the page's way to them
# through the bridge. A value the host refuses is 400 in the host's
# words, and what is stored does not move.
st_, doc = fc.get("/ext/settings", params={"id": REF_ID})
ev["settings"] = [st_, (doc or {}).get("settings")]
ctx.check(st_ == 200 and (doc or {}).get("ok") and (doc.get("settings") or {}).get("threshold") == 40,
"GET /ext/settings -> %s %s", st_, doc)
st_, why = fc.post("/ext/settings", data={"id": REF_ID, "set": json.dumps({"threshold": 101})})
ev["settings_refused"] = [st_, why]
ctx.log("POST /ext/settings, a threshold over its bound -> %s %s", st_, why)
ctx.check(st_ == 400 and "at most 100" in json.dumps(why), "a value the host refuses -> %s %s", st_, why)
st_, doc = fc.get("/ext/settings", params={"id": REF_ID})
ctx.check(st_ == 200 and ((doc or {}).get("settings") or {}).get("threshold") == 40,
"a refused value moved the stored one: %s %s", st_, doc)
finally:
_put_back(ctx, fc, work, prior, etag, raw, dir_mode)
_as_found(ctx, fc, prior, raw, dir_mode, found_tree)
@test("exthost.motion-job", title="A package runs a program, under every gate a sender is under",
subsystem="exthost", kind="auto", hardware="takeover", mode="grbl", est_min=9,
covers=[("forgeext", "src/api.*"), ("forgeext", "src/machine.*"), ("forgeext", "src/run.*"),