testing
── THE RULE THIS MODULE EXISTS TO ENFORCE ──────────────────────────────────Metrics come from the runner's OWN machine-readable reporter, never scraped
from the human-readable console output. A console format is presentation: it
changes with a runner upgrade, with `--verbose`, with terminal width, and it
changes without a version bump. A regex over it does not fail when it breaks —
it returns zero, and a card that says "0 tests" reads exactly like a card for a
lane that has no tests. That is the one kind of green this pipeline cannot tell
from success (contract §4.7: the floors are part of the gate, not a statistic).
So: `flutter test --machine` (NDJSON, one event per line) and
`vitest run --reporter=json` (a Jest-shaped object). Both are documented
protocols with stable field names.
── WHERE THIS CODE COMES FROM ──────────────────────────────────────────────
Ported verbatim in behaviour from `pacha/app` (`parseFlutterMachine`,
`parseFlutterFailures`, `firstFrameIn`) and from `pacha-api` + `pacha-site`
(`parseVitestJson`, byte-identical in both). Every measured behaviour those
carried is preserved and the measurement is recorded next to it; a couple of
them look like bugs until you read why, and deleting one costs a red run or,
worse, a green one.
── COMPLEX VALUES TRAVEL AS JSON STRINGS ───────────────────────────────────
Dagger's TypeScript SDK exposes structural types poorly across a module
boundary, and a shape mismatch there fails at call time with an unreadable
error. Everything non-scalar crosses as a JSON string. The consumer does
`JSON.parse` and hands the same string straight to `slack.render` and
`slack.breakdown`.
Installation
dagger install github.com/wildbitca/daggerverse/testing@v0.1.0Entrypoint
Return Type
Testing Example
dagger -m github.com/wildbitca/daggerverse/testing@65b66de365f75ca17cb1fe647ffb753c88d4f56f call \
func (m *MyModule) Example() *dagger.Testing {
return dag.
Testing()
}@function
def example() -> dagger.Testing:
return (
dag.testing()
)@func()
example(): Testing {
return dag
.testing()
}Types
Testing 🔗
flutterMachine() 🔗
Parse a flutter test --machine report into metrics plus failure detail.
Returns the JSON of a FlutterReport: the six TestMetrics fields that
slack.render and slack.breakdown consume, plus failures with the file,
line, kind and message of every red test. Extra keys are ignored by slack,
so the same string goes to both without stripping anything.
Return Type
String !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| report | String ! | - | the raw NDJSON, one event per line. An unparseable line is skipped, not fatal — a truncated tail on a killed run must not discard the events before it. |
| exitCode | String ! | "0" | the suite’s REAL exit status, as the wrapper wrote it
( |
Example
dagger -m github.com/wildbitca/daggerverse/testing@65b66de365f75ca17cb1fe647ffb753c88d4f56f call \
flutter-machine --report string --exit-code stringfunc (m *MyModule) Example(ctx context.Context, report string, exitCode string) string {
return dag.
Testing().
Fluttermachine(ctx, report, exitCode)
}@function
async def example(report: str, exitcode: str) -> str:
return await (
dag.testing()
.fluttermachine(report, exitcode)
)@func()
async example(report: string, exitCode: string): Promise<string> {
return dag
.testing()
.flutterMachine(report, exitCode)
}flutterMachineFile() 🔗
flutterMachine, reading the report from a File.
Not sugar: pacha/app collects 4576 tests and its NDJSON runs to several
megabytes. A File stays in the engine and is read once here, instead of
crossing the module boundary as a multi-megabyte GraphQL string argument that
the orchestrator also has to hold in memory.
Return Type
String !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| report | File ! | - | the report file, e.g. |
| exitCode | String ! | "0" | see |
Example
dagger -m github.com/wildbitca/daggerverse/testing@65b66de365f75ca17cb1fe647ffb753c88d4f56f call \
flutter-machine-file --report file:path --exit-code stringfunc (m *MyModule) Example(ctx context.Context, report *dagger.File, exitCode string) string {
return dag.
Testing().
Fluttermachinefile(ctx, report, exitCode)
}@function
async def example(report: dagger.File, exitcode: str) -> str:
return await (
dag.testing()
.fluttermachinefile(report, exitcode)
)@func()
async example(report: File, exitCode: string): Promise<string> {
return dag
.testing()
.flutterMachineFile(report, exitCode)
}vitestJson() 🔗
Parse a vitest run --reporter=json report into metrics.
Returns the JSON of a TestMetrics — exactly the shape slack.render and
slack.breakdown consume.
Return Type
String !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| report | String ! | - | the raw JSON. An empty or malformed report yields zeroed
metrics rather than an error, on purpose: a runner that died
before writing |
| exitCode | String ! | "0" | the suite’s REAL exit status, as the wrapper wrote it
( |
Example
dagger -m github.com/wildbitca/daggerverse/testing@65b66de365f75ca17cb1fe647ffb753c88d4f56f call \
vitest-json --report string --exit-code stringfunc (m *MyModule) Example(ctx context.Context, report string, exitCode string) string {
return dag.
Testing().
Vitestjson(ctx, report, exitCode)
}@function
async def example(report: str, exitcode: str) -> str:
return await (
dag.testing()
.vitestjson(report, exitcode)
)@func()
async example(report: string, exitCode: string): Promise<string> {
return dag
.testing()
.vitestJson(report, exitCode)
}vitestJsonFile() 🔗
vitestJson, reading the report from a File.
Return Type
String !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| report | File ! | - | the report file, e.g. |
| exitCode | String ! | "0" | see |
Example
dagger -m github.com/wildbitca/daggerverse/testing@65b66de365f75ca17cb1fe647ffb753c88d4f56f call \
vitest-json-file --report file:path --exit-code stringfunc (m *MyModule) Example(ctx context.Context, report *dagger.File, exitCode string) string {
return dag.
Testing().
Vitestjsonfile(ctx, report, exitCode)
}@function
async def example(report: dagger.File, exitcode: str) -> str:
return await (
dag.testing()
.vitestjsonfile(report, exitcode)
)@func()
async example(report: File, exitCode: string): Promise<string> {
return dag
.testing()
.vitestJsonFile(report, exitCode)
}