-
Notifications
You must be signed in to change notification settings - Fork 0
Extending Testing Lua Plugins
Prev: Lua Plugin Cookbook | Up: Extending ReqPack | Next: Building Registry Entries
rqp test-plugin runs hermetic plugin conformance cases without touching real package managers.
Use it when you want to verify wrapper behavior such as:
- commands executed through
context.exec.run(...)orreqpack.exec.run(...) - emitted events and payloads
- reported query results
- artifact registration
- success/failure behavior
It is intentionally narrower than full end-to-end testing. It does not validate real package-manager integration.
rqp test-plugin --plugin <path-or-id> --preset core [--report <file.json>]
rqp test-plugin --plugin <path-or-id> --case <file.lua> [--case <file.lua> ...]
rqp test-plugin --plugin <path-or-id> --cases <directory> [--report <file.json>]Options:
-
--plugin <value>: plugin script path, plugin bundle directory, or plugin id underconfig.registry.pluginDirectory -
--preset <name>: add preset cases from<plugin>/.reqpack-test/<name>/ -
--case <file.lua>: add one case file -
--cases <directory>: add all*.luafiles in directory -
--report <file.json>: write JSON summary report
Known preset today:
core
Case collection behavior:
- all paths are normalized to absolute paths
- duplicates are removed
- final case list is sorted
--plugin is resolved in this order:
- direct script path
- plugin directory containing
run.lua - installed plugin id under
config.registry.pluginDirectory/<id>/run.lua
If directory is passed and run.lua is missing, command fails.
Supported test actions:
installremoveupdatelistsearchoutdatedinfo
Local install case is represented as:
request.action = "install"- plus non-empty
request.localPath
That dispatches to plugin.installLocal(context, localPath).
Not supported by current test runner:
pack- proxy resolution as top-level action
- arbitrary executor/orchestrator flows outside single plugin call
Case file must return Lua table.
Top-level sections:
namerequestfakeExecexpect
Minimal passing install case:
return {
name = "install success",
request = {
action = "install",
system = "demo",
packages = {
{ name = "curl", version = "8.0" }
}
},
fakeExec = {
{
match = "fake-pm install curl",
exitCode = 0,
stdout = "ok\n",
stderr = "",
success = true,
}
},
expect = {
success = true,
commands = { "fake-pm install curl" },
stdout = { "ok\n" },
events = { "installed", "success" },
eventPayloads = {
installed = "{name=curl}",
success = "ok",
},
}
}Recognized request fields:
request = {
action = "install",
system = "demo",
flags = { "flag-a" },
prompt = "curl",
localPath = "./artifact.rpm",
packages = {
{
action = "install",
system = "demo",
name = "curl",
version = "8.0",
sourcePath = "/tmp/curl.rpm",
localTarget = true,
flags = { "dep-flag" },
}
}
}Rules:
-
request.actionis required -
request.systemshould normally be set -
request.promptis used forsearchandinfo -
request.localPathswitches install case toinstallLocal -
request.packagesentries map to normalPackagefields
Action notes:
-
listandoutdatedignoreprompt -
infotreats call as successful when returned record has non-empty displayable content -
searchpassesrequest.promptdirectly into pluginsearch(context, prompt)
fakeExec is ordered array of substring-match rules.
Each entry:
{
match = "fake-pm install curl",
exitCode = 0,
stdout = "ok\n",
stderr = "",
success = true,
}Behavior:
- first rule whose
matchstring appears inside executed command wins - if
successis omitted, runner derives it fromexitCode == 0 - if no rule matches command, fake exec returns failure with
exitCode = 127and stderrno fakeExec rule matched command: ...
This affects both:
context.exec.run(...)reqpack.exec.run(...)
Supported expectation keys:
expect = {
success = true,
commands = { "cmd" },
stdout = { "line\n" },
stderr = { "boom\n" },
artifacts = { "/tmp/out.txt" },
events = { "installed", "success" },
eventPayloads = {
installed = "{name=curl}",
},
resultCount = 1,
resultName = "curl",
resultVersion = "8.0",
}Expectation semantics:
-
commands,stdout,stderr,artifacts, andeventsuse exact ordered comparison when provided -
eventPayloadschecks first event with same name -
resultCount,resultName, andresultVersioninspect first query result, or singleinfo()result - omitted expectation fields are not checked
--report writes JSON with:
pluginpluginPathpassedfailedcases[]
Each case entry contains:
namesuccessmessagecommands[]stdout[]stderr[]events[]artifacts[]-
eventRecords[]withnameandpayload
Preset cases live inside plugin bundle:
<plugin>/
.reqpack-test/
core/
list.lua
search.lua
--preset core fails if directory is missing or contains no *.lua files.
Good preset candidates:
- one happy-path
list - one happy-path
search - one mutating case
- one failure case
- command construction
- parsing logic
- emitted event names
- event payload formatting
- query result mapping
- artifact registration
- wrapper thinness and deterministic behavior
- real package-manager smoke tests
- privileged execution tests
- network/auth tests against real repositories
- full orchestrator flows with planning, graph edges, and transaction recovery
Keep both:
- hermetic
test-plugincases for fast regression coverage - a few real host smoke tests for final confidence
- keep
matchstrings specific enough that only one fake command fits - prefer deterministic stdout fixtures over complex free-form output
- add at least one failure case
- use preset
corefor baseline contract coverage - test emitted payload text explicitly when parser logic matters
Prev: Lua Plugin Cookbook | Up: Extending ReqPack | Next: Building Registry Entries
- User Guide
- Getting Started
- Command Reference
- Configuration
- Configuration Reference
- Security, Audit, and SBOM
- Output and Report Formats
- Remote Mode
- Remote Protocol Reference
- Using Native
rqpPackages - Troubleshooting