exthost.panel-install: the key goes in through the panel, and the box holds the button

The test added the owner's key by copying the file. It now adds it the way
an operator does: POST /ext/key with the machine's button held. Without the
button it is 409 and no file lands; a name with a space, a name that is a
path, and no key at all are 400; a key that is no key is 409 from the host;
with the button held it is added and listed with its id. Removed again, the
same archive reads unverified, and removing a key that is not there is 409.

The bench actuator's press was 200 ms, the firmware's default, and a
request that must reach the machine while the button is down often missed
it: the unverified install took three presses on one run and all ten on
another. ctx.act() now takes `ms`, the fixture client passes it to the box
(the firmware clamps it into 20 to 500), and the two button-held steps ask
for 500. Both landed on the first or second press afterward.

Proven. The unit suite: 451 tests, 0 undefined names (both stub fixtures
take the new argument, and the fixture test pins that the box is asked for
the longest press). On the bench reference, image 20260921161446 with the
cross-built forgectrl and extension host mounted over the image's:
exthost.panel-install PASS, exthost.package-routes PASS,
exthost.hold-pause-tier PASS. Against the image's own daemons the install
test FAILS, as it should.

Acceptance. exthost.panel-install covers forgectrl's src/extpkg.*,
src/main.c, src/auth.* and forgeext's src/main.c, src/install.*,
src/pkg.*; the runner and the fixture client are harness, outside the
suite and outside every fingerprint.
This commit is contained in:
ScottW514
2026-09-21 13:11:29 -04:00
parent 963bded9b3
commit eb0a1e7838
5 changed files with 66 additions and 18 deletions
+6 -2
View File
@@ -207,6 +207,10 @@ class ClientTests(unittest.TestCase):
f.act("interlock", "open")
f.act("button", "press")
self.assertEqual(self.dev.calls[-1], ("POST", "/button", {}))
time.sleep(0.15)
# a check that must reach the machine while the button is down asks for the longest press
f.act("button", "press", ms=500)
self.assertEqual(self.dev.calls[-1], ("POST", "/button", {"ms": 500}))
time.sleep(0.15) # the fake's pulse ends
self.assertEqual(sorted(fx.Fixture.energized(f.status())), ["interlock", "lid"])
f.release()
@@ -330,10 +334,10 @@ class StubFixture:
def covers(self, channel):
return channel in self.channels and (channel != "button" or self.button_enabled)
def act(self, channel, state):
def act(self, channel, state, ms=None):
if self.fail:
raise fx.FixtureError("the box is off")
self.acts.append((channel, state))
self.acts.append((channel, state) if ms is None else (channel, state, ms))
if state == "open":
self.held.append(channel)
if self.fc is not None and channel in ("lid", "interlock"):
+2 -2
View File
@@ -141,8 +141,8 @@ class ActTests(unittest.TestCase):
def covers(self, channel):
return channel == "lid"
def act(self, channel, state):
self.done.append((channel, state))
def act(self, channel, state, ms=None):
self.done.append((channel, state) if ms is None else (channel, state, ms))
self_outer.lid(state != "open")
self_outer = self