The decisions behind mix mob.smoke, kept free of I/O so each can be
tested against the shapes agent-device and Mob.Diag really return.
A replayed flow passing is the tool's opinion. The app's own counters say whether it held up: a store that lost its table or reset, events the listener could not deliver, or, on mob 0.9.7 or later, no receipts at all while a flow ran (the taps landed somewhere other than this app). So the verdict combines both. A flow whose health could not be read is reported as not checked: it does not fail the run, but the run does not claim the app held up either.
agent-device test --json reports "success": true even when scripts
fail; only data.failed and data.notRun say what happened.
The counters live in the app's BEAM and start from zero when it boots, and a
flow that begins with open --relaunch boots a new one. So health is read
around each flow, and a snapshot from a different BEAM (its OS pid changed)
is compared against zero rather than against the old BEAM's counters.
Summary
Types
A session's lease on a device.
agent-device refused to run ("success": false).
One failed script from agent-device test --json.
An RPC reply sorted by what it means for the check.
The parts of a suite report the verdict reads.
Everything mix mob.smoke learned about one device. health is present
when health was checked flow by flow: how many flows ran, and why each
flow whose snapshot before or after it did not read went unchecked.
Mob.Diag.health/0 and the BEAM's OS pid, read together.
Types
@type claim() :: MobDev.DeviceLeases.claim()
A session's lease on a device.
agent-device refused to run ("success": false).
@type failure() :: %{ file: String.t() | nil, status: String.t() | nil, attempts: non_neg_integer(), artifacts_dir: String.t() | nil, code: String.t() | nil, cause: String.t() | nil, message: String.t() | nil, hint: String.t() | nil }
One failed script from agent-device test --json.
@type finding() :: {:failure | :warning | :note, String.t()}
@type reply() :: {:ok, term()} | {:unreachable, String.t()} | {:unsupported, String.t()} | {:error, String.t()}
An RPC reply sorted by what it means for the check.
@type report() :: %{ total: non_neg_integer(), executed: non_neg_integer(), passed: non_neg_integer(), failed: non_neg_integer(), skipped: non_neg_integer(), not_run: non_neg_integer(), duration_ms: non_neg_integer(), failures: [failure()] }
The parts of a suite report the verdict reads.
@type result() :: %{ :device => String.t(), :outcome => outcome(), :findings => [finding()], :receipts_delta => integer() | nil, optional(:health) => %{flows: non_neg_integer(), unchecked: [String.t()]} }
Everything mix mob.smoke learned about one device. health is present
when health was checked flow by flow: how many flows ran, and why each
flow whose snapshot before or after it did not read went unchecked.
Mob.Diag.health/0 and the BEAM's OS pid, read together.