Dagger
Search

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.0

Entrypoint

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
NameTypeDefault ValueDescription
reportString !-

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.

exitCodeString !"0"

the suite’s REAL exit status, as the wrapper wrote it (echo $? > /tmp/test-exit). Anything other than “0” with zero parsed failures folds to failed = 1: a compile error emits no test events and would otherwise report a plausible, green zero. Defaults to “0”, which parses the report as given and applies no fold — pass the real value in a gate.

Example
dagger -m github.com/wildbitca/daggerverse/testing@65b66de365f75ca17cb1fe647ffb753c88d4f56f call \
 flutter-machine --report string --exit-code string
func (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
NameTypeDefault ValueDescription
reportFile !-

the report file, e.g. container.file("/tmp/test-report.json")

exitCodeString !"0"

see flutterMachine

Example
dagger -m github.com/wildbitca/daggerverse/testing@65b66de365f75ca17cb1fe647ffb753c88d4f56f call \
 flutter-machine-file --report file:path --exit-code string
func (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
NameTypeDefault ValueDescription
reportString !-

the raw JSON. An empty or malformed report yields zeroed metrics rather than an error, on purpose: a runner that died before writing --outputFile leaves nothing to parse, and exitCode is what turns that into a reported failure.

exitCodeString !"0"

the suite’s REAL exit status, as the wrapper wrote it (echo $? > /tmp/vitest-exit). Anything other than “0” with zero parsed failures folds to failed = 1. Defaults to “0”, which applies no fold — pass the real value in a gate.

Example
dagger -m github.com/wildbitca/daggerverse/testing@65b66de365f75ca17cb1fe647ffb753c88d4f56f call \
 vitest-json --report string --exit-code string
func (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
NameTypeDefault ValueDescription
reportFile !-

the report file, e.g. container.file("/tmp/vitest.json")

exitCodeString !"0"

see vitestJson

Example
dagger -m github.com/wildbitca/daggerverse/testing@65b66de365f75ca17cb1fe647ffb753c88d4f56f call \
 vitest-json-file --report file:path --exit-code string
func (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)
}