Files
forgefirm/forgetest
ScottW514 57ba3454b4 Clear a setting the only way the daemon accepts, everywhere it is cleared
An empty form field never reaches the request, so POST /settings with an
empty value in the body carries no key at all and the daemon answers 400,
"no known setting in request". Only the query-string form gets an empty
value through. Measured on the bench reference:

    POST /settings -d "lid_policy="   -> 400, the value unchanged
    POST /settings?lid_policy=        -> 200, the key cleared

Three places in the suite already knew this and say so in a comment; two
did not.

motion.lid-policy-hold restored with the form body, so on a machine whose
lid_policy has never been set the restore was refused and the test failed
its own check. It now uses the query-string form for the empty value, as
the others do. This is the same test as the commit before: that one
stopped it from writing the word "cancel" where the machine had nothing,
and this one lets it write the nothing.

cloud.mode-switch had the older shape of the same fault: it restored
homing_mode to the literal "none" where the machine had it unset. On the
bench reference the restore branch never ran, because that machine homes
through the cloud, so it has not fired yet; on a machine with homing_mode
unset it would have. It now restores exactly what it found.

The rest of the suite was audited for this and is clean: camera.key-read,
cooling.critical-tier, cooling.tec-drive, cooling.crash-watch-plumbing,
cooling.fire-watch-tiers, cooling.floor-and-warm-up, cloud.verdict-hold,
forgectrl.settings, logs.levels and motion.xy-microsteps all either guard
the empty value or go through the shared Restore helper, which has always
done it correctly. commission.cloud-header-capture uses that helper too.
2026-09-10 10:26:25 -04:00
..

forgetest - the ForgeFIRM release acceptance tool

The daemon behind http://<machine>:8090/ on the dev image: runs the acceptance catalog against the machine, keeps the append-only result log, decides which results still apply to the image that is running, exports the release artifact scripts/release.sh gates on, and serves the bench diagnostics page. The contract - catalog, campaigns, fingerprints, inheritance, the gate, the coverage rule - is the Acceptance page of the documentation site.

Run the host tests

cd forgetest
python3 -m unittest discover -s tests -v

Run the daemon on a workstation (against a mock or a manifest file)

FORGETEST_DATA=/tmp/ft FORGETEST_MANIFEST=../tree-manifest.json \
FORGECTRL_URL=http://<machine> python3 -m forgetest --port 8090

scripts/manifest-from-tree.py produces tree-manifest.json from the recipe pins; the coverage lint is python3 -m forgetest.coverage --manifest ....

Environment

Variable Default Purpose
FORGETEST_DATA /data/forgetest results.jsonl, bench.jsonl, token, export/
FORGETEST_MANIFEST /etc/forgefirm-manifest.json the image manifest
FORGETEST_PORT, FORGETEST_HOST 8090, 0.0.0.0 listener
FORGETEST_BENCH_DIR /usr/share/forgetest/bench the installed bench scripts
FORGETEST_BENCH_DATA <FORGETEST_DATA>/bench passed to bench tools: where they keep their data files (with GF_HOST=127.0.0.1 and the panel token in GF_TOKEN)
FORGETEST_MARKER /run/forgetest.active takeover marker
FORGECTRL_URL, FORGECTRL_TOKEN_FILE http://127.0.0.1, /data/forgefirm/panel.token forgectrl client (HTTP; the token authorizes writes from the board)
FORGECTRL_TLS_URL https://127.0.0.1 forgectrl over HTTPS (self-signed, unverified), for the login test
GF_SYSFS_ROOT /sys/glowforge/ kernel module sysfs
GRBL_HOST, GRBL_PORT 127.0.0.1, 23 Grbl TCP

Adding a test

Register it in the subsystem module under forgetest/suite/ with @test(...): id subsystem.name, kind, hardware, mode (the controller mode the test needs; the runner switches to it first), covers, requires, always, steps. The body gets a Context (log, check, fail, prompt, confirm, instruct, sleep, evidence, forgectrl, sysfs, grbl, takeover). Return normally for PASS, raise runner.Failed for FAIL. Then run the unit tests and the coverage lint.