Skip to content

Assert on events in tests

MemorySink keeps every event in a Vec you can inspect. It is available without any Cargo feature, so telemetry assertions do not drag the remote sink stack into your test build.

Capture what your code records

use std::sync::Arc;
use rtb_telemetry::{CollectionPolicy, MemorySink, TelemetryContext};

#[tokio::test]
async fn records_the_command_invocation() {
    let sink = Arc::new(MemorySink::new());
    let telemetry = TelemetryContext::builder()
        .tool("mytool")
        .tool_version("1.0.0")
        .salt("mytool-test-salt")
        .sink(sink.clone())
        .policy(CollectionPolicy::Enabled)
        .build();

    run_my_command(&telemetry).await.unwrap();

    let events = sink.snapshot();
    assert_eq!(events.len(), 1);
    assert_eq!(events[0].name, "command.invoke");
    assert_eq!(events[0].attrs.get("outcome").map(String::as_str), Some("ok"));
}

Clone the Arc into the builder and keep the original: cloning shares the backing vector, so the handle you kept sees everything the context recorded. snapshot() returns events in emit order; len() and is_empty() are there for the common assertions.

Prove that opting out really collects nothing

The test worth having is the negative one:

#[tokio::test]
async fn disabled_policy_records_nothing() {
    let sink = Arc::new(MemorySink::new());
    let telemetry = TelemetryContext::builder()
        .tool("mytool")
        .tool_version("1.0.0")
        // no salt needed — a Disabled context never derives an identity
        .sink(sink.clone())
        .build();               // policy defaults to Disabled

    run_my_command(&telemetry).await.unwrap();

    assert!(sink.is_empty());
}

Leaving .policy(…) off is deliberate here: it asserts the default behaviour, so the test fails if someone ever changes which way the default points.

Use a distinct salt in tests

Any string works — nothing validates it — but use a test-specific one. A test that reuses your production salt writes your real machine identity into test output and CI logs.

Do not assert redaction against MemorySink

MemorySink stores the event exactly as the caller built it, without calling redacted(). That is the right behaviour for asserting what your code passed, and the wrong tool for proving a secret was scrubbed. For that, either assert on event.redacted() directly or write through a FileSink into a tempfile::tempdir() and read the line back.

Testing an HTTP sink without a network

HttpSink refuses non-HTTPS endpoints unless allow_insecure_endpoint is set, which is what that flag is for: point it at a wiremock server on http://127.0.0.1:… in tests and leave the flag off everywhere else.

Beware a machine with no machine ID in CI

An Enabled context derives a machine ID at build(). On a host where that cannot be read — a stripped container image is the usual culprit — every derivation returns a different random value, so a test asserting that two derivations match will fail there and pass on your laptop. Seed a stable /etc/machine-id in the CI image, as this repository's own pipeline does, or do not assert on identity stability.