Skip to content

dagnatstest

import "github.com/danmestas/dagnats/dagnatstest"

Test helpers for DagNats workflows. Starts an embedded NATS server with all required streams and KV buckets in a single call, ready for workflow testing.

Key Functions

FunctionDescription
Server(t)Starts an embedded NATS server with JetStream and all DagNats infrastructure, returns a ready *nats.Conn
NewHarness(t)Starts NATS, an orchestrator and an API service, and builds an unstarted worker. Returns a *Harness
(*Harness).Handle(t, taskType, fn)Registers a handler on the harness worker
HandleTypedOn[I, O](h, t, taskType, fn)Registers a typed handler (generic, so not a method)
(*Harness).Start(t)Starts the worker and stops it when the test ends
(*Harness).RegisterAndRun(t, def, input, timeout)Registers a definition, runs it, and waits for a terminal status
RunAndWait(t, svc, workflow, input, timeout)Starts a run and blocks until it reaches a terminal status
WaitForStatus(t, svc, runID, timeout, statuses...)Polls a run until it reaches one of the target statuses
Workflow(t, svc, builder)Builds and registers a workflow, failing the test on error
Worker[I, O](t, nc, taskType, fn)Starts a worker with one typed handler, stopped at test end
MockTaskContextA worker.TaskContext for unit-testing handlers without NATS

What Server(t) Provides

A single call to Server(t) starts an embedded NATS server with JetStream enabled, creates the streams and KV buckets DagNats needs, returns a connected *nats.Conn, and shuts the server down with t.Cleanup().

RunAndWait

Starts a workflow run and blocks until it reaches any terminal status (Completed, Failed, Cancelled, Compensated, CompensateFailed). Returns the final dag.WorkflowRun snapshot. Fatals the test if the run does not finish within the given timeout. svc is the harness’s h.Svc.

run := dagnatstest.RunAndWait(t, h.Svc, "my-workflow", input, 10*time.Second)
if run.Status != dag.RunStatusCompleted {
    t.Fatalf("status = %s", run.Status)
}

WaitForStatus

Polls the run every 25ms until it reaches one of the given statuses. Returns the matching snapshot, or fatals the test on timeout.

run := dagnatstest.WaitForStatus(t, h.Svc, runID, 5*time.Second,
    dag.RunStatusCompleted, dag.RunStatusFailed,
)

Usage

func TestMyWorkflow(t *testing.T) {
    h := dagnatstest.NewHarness(t)
    h.Handle(t, "process", func(ctx worker.TaskContext) error {
        return ctx.Complete([]byte(`{"ok": true}`))
    })
    h.Start(t)

    wf := dag.NewWorkflow("my-workflow")
    wf.Task("only", "process").WithTimeout(10 * time.Second)
    def, err := wf.Build()
    if err != nil {
        t.Fatal(err)
    }

    run := h.RegisterAndRun(t, def, nil, 10*time.Second)
    if run.Status != dag.RunStatusCompleted {
        t.Fatalf("status = %s", run.Status)
    }
}

Design Notes

Each test gets its own embedded NATS server. Servers are not shared between tests, preventing cross-test interference. The harness fields Engine and Svc have internal types, so use their methods but do not name the types.