Portal¶
Your end-to-end tests pass. But do you see what they do?
Stove Portal is a local observability dashboard for your e2e test runs.
- Captures everything — HTTP calls, Kafka messages, database queries, gRPC calls, distributed traces, system snapshots
- Real-time web UI — updates live via SSE as your tests execute
- Single binary — receives events via gRPC, persists in SQLite, serves an embedded SPA
- Persistent — browse test runs after they complete, across sessions
Unlike Reporting (console output on failure) and Tracing (span collection for assertions), Portal gives you a persistent, browsable view of your test runs — including successful ones.
Install the CLI¶
Options:
Download the binary for your platform from GitHub Releases:
| Platform | Archive |
|---|---|
| macOS arm64 | stove-<version>-darwin-arm64.tar.gz |
| macOS amd64 | stove-<version>-darwin-amd64.tar.gz |
| Linux amd64 | stove-<version>-linux-amd64.tar.gz |
Each archive includes a .sha256 checksum file.
The CLI is a single binary with no runtime dependencies. It embeds the web UI, so there's nothing else to install.
Quick Start¶
1. Start the portal
You'll see:
Stove CLI v0.23.0
HTTP server: http://localhost:4040
gRPC server: http://localhost:4041
Database: ~/.stove-portal.db
2. Add the dependency
3. Register in your Stove config
Stove()
.with {
portal { PortalSystemOptions(appName = "product-api") }
tracing { enableSpanReceiver() } // recommended: enables distributed trace capture
// ... other systems
}.run()
4. Run your tests and open the dashboard
Navigate to http://localhost:4040. The UI updates in real time as tests execute.
What Gets Captured¶
Once portal {} is registered, Stove automatically captures everything — no code changes to your tests:
| Event | Data |
|---|---|
| Run lifecycle | Start/end timestamps, app name, active systems, pass/fail counts |
| Test lifecycle | Test name, spec name, duration, status, error messages |
| Entries | Every http {}, kafka {}, postgresql {} assertion — system, action, input/output, expected/actual, trace ID |
| Spans | Distributed traces via OpenTelemetry — operation, service, duration, attributes, exceptions |
| Snapshots | System state at test boundaries — database contents, Kafka offsets, WireMock stubs |
The Dashboard¶
The embedded SPA provides four views for each test:
Timeline¶
Chronological list of every action the test performed. Each entry shows timestamp, system badge (color-coded), action name, and pass/fail indicator. Click any entry to expand full detail: input, output, expected vs. actual, error, metadata.
Recognized systems: HTTP, Kafka, PostgreSQL, MongoDB, Couchbase, Redis, Elasticsearch, WireMock, gRPC, MySQL, MSSQL, Cassandra.
Trace¶
Distributed trace tree built from OpenTelemetry spans. Spans are linked to tests via two mechanisms:
- Entry-based: spans sharing a
trace_idwith a test entry - Attribute-based: spans containing
x-stove-test-idin their attributes
The tree shows operation name, service, duration, status, relevant attributes (http.*, db.*, messaging.*, rpc.*), and exception details with stack traces.
Combine with Tracing
Portal's trace view is the visual counterpart to the Tracing component's console output. Enable both for the best experience: Tracing gives you assertion DSL and failure reports in the terminal, Portal gives you a browsable trace tree in the browser.
Snapshots¶
Grid of system state cards captured at test boundaries. Each card shows the system name with a color-coded icon and a summary of the captured state.
Kafka Explorer¶
Dedicated view filtering Kafka-specific entries. Shows consumed/published/failed message counts with expandable JSON payloads.
Configuration¶
PortalSystemOptions¶
PortalSystemOptions(
appName = "product-api", // required: identifies the application under test
cliHost = "localhost", // where the stove CLI is running
cliPort = 4041 // gRPC port of the stove CLI
)
| Parameter | Type | Default | Description |
|---|---|---|---|
appName |
String |
(required) | Application name for grouping test runs |
cliHost |
String |
"localhost" |
Hostname where stove CLI is running |
cliPort |
Int |
4041 |
gRPC port where stove CLI is listening |
CLI Options¶
stove [OPTIONS]
Options:
--port <PORT> HTTP port for the web UI and REST API [default: 4040]
--grpc-port <PORT> gRPC port for receiving events [default: 4041]
--db <PATH> Path to SQLite database file [default: ~/.stove-portal.db]
--clear Clear all stored data and exit
--fresh-start Back up and recreate the database, then start normally
-h, --help Print help
-V, --version Print version
# Run on custom ports
stove --port 8080 --grpc-port 8081
# Use a project-specific database
stove --db ./my-project-portal.db
# Reset all data (exits after clearing)
stove --clear
# Drop and recreate the database (backs up first, then starts servers)
stove --fresh-start
Fault Tolerance¶
The portal emitter is designed to never break your tests:
- Non-blocking event queue (capacity: 512)
- Auto-disables after 5 consecutive gRPC failures
- 3-second drain timeout on shutdown
- If the portal CLI is not running, tests continue normally with zero overhead
This means you can add portal {} to your config permanently. When the CLI is running, you get the dashboard. When it's not, nothing changes.
REST API¶
The portal exposes a REST API at /api/v1 for programmatic access:
| Method | Path | Description |
|---|---|---|
| GET | /apps |
List applications with latest run info |
| GET | /runs?app={name} |
List runs, optionally filtered by app |
| GET | /runs/{run_id} |
Get a specific run |
| GET | /runs/{run_id}/tests |
List tests in a run |
| GET | /runs/{run_id}/tests/{test_id}/entries |
List entries for a test |
| GET | /runs/{run_id}/tests/{test_id}/spans |
List spans linked to a test |
| GET | /runs/{run_id}/tests/{test_id}/snapshots |
List snapshots for a test |
| GET | /traces/{trace_id} |
Get all spans in a trace |
| GET | /events/stream |
SSE stream for real-time events |
SSE Events¶
The /events/stream endpoint delivers server-sent events with JSON payloads:
Event types: run_started, run_ended, test_started, test_ended, entry_recorded, span_recorded, snapshot.
Complete Example¶
class StoveConfig : AbstractProjectConfig() {
override val extensions = listOf(StoveKotestExtension())
override suspend fun beforeProject() =
Stove()
.with {
portal { PortalSystemOptions(appName = "spring-example") }
tracing { enableSpanReceiver() }
httpClient {
HttpClientSystemOptions(baseUrl = "http://localhost:$appPort")
}
postgresql {
PostgresqlOptions(databaseName = "stove", configureExposedConfiguration = { cfg ->
listOf("spring.datasource.url=${cfg.jdbcUrl}")
})
}
kafka {
KafkaSystemOptions(configureExposedConfiguration = {
listOf("kafka.bootstrapServers=${it.bootstrapServers}")
})
}
springBoot(runner = { params -> run(params) { addTestSystemDependencies() } })
}.run()
override suspend fun afterProject() = Stove.stop()
}
Then write tests as usual — the portal captures everything automatically:
test("should create order and publish event") {
stove {
http {
postAndExpectBodilessResponse("/orders", body = CreateOrderRequest(orderId).some()) {
it.status shouldBe 201
}
}
kafka {
shouldBePublished<OrderCreatedEvent> {
actual.orderId == orderId
}
}
postgresql {
shouldQuery<Order>("SELECT * FROM orders WHERE id = '$orderId'") {
it.first().status shouldBe "CREATED"
}
}
}
}
Open http://localhost:4040 to see every HTTP request, Kafka message, database query, and distributed trace — in real time.
How It Relates to Reporting and Tracing¶
Portal, Reporting, and Tracing are complementary:
| Feature | Reporting | Tracing | Portal |
|---|---|---|---|
| When | On test failure | On test failure | Always (real-time) |
| Where | Console/CI output | Console/CI output | Browser UI |
| What | Test actions + assertions | Application call chain | Everything + history |
| Persistence | None (ephemeral) | None (ephemeral) | SQLite (across runs) |
Use all three together for the best experience:
- Reporting gives you immediate feedback in the terminal when something breaks
- Tracing gives you the execution trace and assertion DSL in your test code
- Portal gives you a browsable, persistent view of all test runs — successful and failed
Troubleshooting¶
Portal UI shows "Waiting for test events..."¶
- Verify the
stoveCLI is running:stove --version - Check that gRPC ports match: CLI default is
4041, Kotlin default is4041 - Look for connection errors in the CLI's terminal output
Tests run fine but nothing appears in Portal¶
- Ensure
portal {}is registered in your Stove config - Verify
stove-portalis in your test dependencies - Check that the CLI started before running tests
Portal works locally but not in CI¶
Portal is designed for local development. In CI, use Reporting and Tracing for failure diagnostics — they output to the console and don't require a running server.
Data from previous runs clutters the UI¶
This wipes the SQLite database and exits. Start the CLI again for a clean slate.
Database schema is corrupted or migrations fail¶
This backs up the existing database (printing the backup path), deletes it, and recreates a fresh one with all migrations applied. The servers start normally after — no need to run stove again.