Here is a workflow shift worth adopting: the QA is the test. flua
gives every QA an exit code — a runtime error exits 1, exit(N)
exits N — so a QA can assert its own behavior and report success or failure
to whatever runs it. No test framework, no extra files.
--%%name:my_qa_test
local failures = 0
local function assertEq(actual, expected, msg)
if tostring(actual) == tostring(expected) then
print("PASS: " .. msg .. " (" .. tostring(actual) .. ")")
else
failures = failures + 1
print("FAIL: " .. msg .. " — expected " .. tostring(expected) .. ", got " .. tostring(actual))
end
end
function QuickApp:onInit()
local device = api.get("/devices/" .. self.id) -- the sim sees the QA
assertEq(device.id, self.id, "device visible in the sim")
self:updateProperty("value", 50)
assertEq(self.properties.value, 50, "updateProperty round trip")
print("failures:", failures)
exit(failures > 0 and 1 or 0)
end
Run it (F5 works too — offline is the default, so no flags are needed):
flua --api local my_qa.lua echo $? # 0 = everything passed, non-zero = look at the FAIL lines
This is the exact pattern of selfTest.lua in the examples
directory (flua --examples). Every change you make can be
verified in one command — your own mini test suite, living inside the QA.
Two extensions that make this even more useful:
flua --tool setup has already
installed agent skills into your project — including
quickapp-test, which teaches the agent this exact
verify-loop: run offline, check the exit code, drive the sim over HTTP
when interactions are needed. Tell your agent "change the QA to do X and
verify it" and it can actually run and check the result.require("testlib") — files
next to the QA load automatically, and only --%%file files
get packaged into the .fqa, so your test scaffolding never reaches the
controller.Next post: when the tests pass — the one-command path from laptop to HC3.
flua: https://github.com/jangabrielsson/flua · agents: USAGE.md + the quickapp-test skill