Documentation
¶
Overview ¶
Package testhelper is the Go family's TestHelper bundle.
Every other library in the family that ships a TestHelper ships it as its own `testhelper` sub-package, which leaves a consumer's SIT suite importing seven of them by hand and, worse, guessing which one owns a given fake. This package is the one import that answers that: it re-exports the sibling helpers under sibling-qualified names, so `ProblemAssertError`, `AuthNewFakeIDP`, and `PresetStartPostgres` say where they come from without a single import alias.
It bundles the seven helpers that EXIST. The core-utils sibling deliberately ships none — pure deterministic functions have no seam to fake — so there is nothing to re-export and no empty facade pretending otherwise.
The sibling packages remain importable directly. A consumer that wants the full surface of one helper should import it; this bundle carries the fakes, constructors, and assertion entry points a SIT suite reaches for, not a mirror of every symbol.
Its own glue ¶
On top of the bundle it ships what only the harness can:
- StartStack — Testcontainers glue that boots the four frozen infra presets together and emits the configuration blocks that address them. This is the DB-adapter integration tier (G1); telemetry infrastructure is never spun up, there is no fake OTLP collector, and real export is proven at SIT against the Garden preview environment instead.
- NewScriptedDriver and EchoEntrypoint — drivers that let a consumer test its own journeys without a system under test.
- AssertStep, AssertReport, and friends — the assertions a harness consumer would otherwise rewrite in every suite.
Everything here is proven by the meta tier, including the assertions themselves: each one is shown to pass on known-good input and fail on known-bad, because an assertion helper that cannot fail is worse than none.
Index ¶
- Constants
- Variables
- func AssertHarnessProblem(t TestingT, err error, id string) problem.Problem
- func AssertNoHarnessProblem(t TestingT, err error)
- func AssertReport(t TestingT, actual e2e.Report, journey e2e.Journey)
- func AssertResult(t TestingT, actual e2e.Result, want e2e.Expectation)
- func AssertStep(t TestingT, step e2e.Step, actual e2e.Result)
- func CheckHarnessProblem(err error, id string) (problem.Problem, error)
- func CheckReport(actual e2e.Report, journey e2e.Journey) error
- func CheckResult(actual e2e.Result, want e2e.Expectation) error
- func CheckStep(step e2e.Step, actual e2e.Result) error
- func EchoEntrypoint(_ context.Context, invocation e2e.Invocation, stdout io.Writer, ...) (int, error)
- func NewEchoDriver(problems *e2e.Problems) (*e2e.InProcessDriver, error)
- func PresetRequireEntry[Entry any](t TestingT, block map[string]Entry, key string) Entry
- func PresetRequireStarted[Started any](t TestingT, started *Started, err error) *Started
- func RequireReport(t TestingT, report e2e.Report, err error) e2e.Report
- func SampleProblems() (*e2e.Problems, error)
- type APIFakeBackend
- type APIFakeBackendOptions
- type APIProblemOptions
- type APIRoute
- type AuthFakeIDP
- type AuthFakeIDPOptions
- type AuthFakeProvider
- type AuthFakeProviderOptions
- type InMemorySystem
- type InMemorySystemOptions
- type InMemoryTerminal
- type InMemoryVfs
- type InMemoryVfsOptions
- type OtelLoggerSink
- type OtelMetricsCollector
- type OtelTraceEmitter
- type PresetContainer
- type PresetDockerRuntime
- type PresetPostgresOptions
- type PresetRedisOptions
- type PresetRuntime
- type PresetStartedCache
- type PresetStartedKv
- type PresetStartedPostgres
- type PresetStartedStorage
- type PresetStorageOptions
- type ProblemOption
- type ScriptedDriver
- type ScriptedStep
- type Stack
- type StackOptions
- type TestingT
Examples ¶
Constants ¶
const ExitCodeVar = "E2E_ECHO_EXIT_CODE"
ExitCodeVar is the environment variable EchoEntrypoint reads its exit code from.
const PresetDefaultKey = presetth.DefaultKey
PresetDefaultKey is the connection key the preset helpers emit by default.
Variables ¶
var ( // ProblemExpectID expects a Problem whose type URI ends in the given id. ProblemExpectID = problemth.ExpectID // ProblemExpectType expects an exact Problem type URI. ProblemExpectType = problemth.ExpectType // ProblemExpectTitle expects a Problem title. ProblemExpectTitle = problemth.ExpectTitle // ProblemExpectStatus expects a Problem status code. ProblemExpectStatus = problemth.ExpectStatus // ProblemExpectDetail expects a Problem detail string. ProblemExpectDetail = problemth.ExpectDetail // ProblemExpectInstance expects a Problem instance reference. ProblemExpectInstance = problemth.ExpectInstance // ProblemExpectRecoverable expects a Problem's recoverable flag. ProblemExpectRecoverable = problemth.ExpectRecoverable // ProblemExpectData expects a Problem's `data` extension. ProblemExpectData = problemth.ExpectData // ProblemCheckError reports why an error is not the expected Problem. ProblemCheckError = problemth.CheckError // ProblemAssertError fails the test unless an error is the expected Problem. ProblemAssertError = problemth.AssertError // ProblemCheckProblem reports why a Problem envelope misses expectations. ProblemCheckProblem = problemth.CheckProblem // ProblemAssertProblem fails the test unless a Problem envelope matches. ProblemAssertProblem = problemth.AssertProblem // ProblemSampleErrorPortal is a ready error portal for fixtures. ProblemSampleErrorPortal = problemth.SampleErrorPortal // ProblemSample is a ready Problem envelope for fixtures. ProblemSample = problemth.SampleProblem )
var ( // NewInMemorySystem creates a deterministic clock and environment seam. NewInMemorySystem = interfacesth.NewInMemorySystem // NewInMemoryVfs creates an in-memory filesystem seam. NewInMemoryVfs = interfacesth.NewInMemoryVfs // NewInMemoryTerminal creates a scripted process-execution seam, which is // what lets the harness test its own compiled-artifact driver without ever // compiling an artifact. NewInMemoryTerminal = interfacesth.NewInMemoryTerminal )
var ( // ConfigRequireConfig fails the test unless a configuration loaded. ConfigRequireConfig = configth.RequireConfig // ConfigRequireLoadError fails the test unless loading failed. ConfigRequireLoadError = configth.RequireLoadError // ConfigRequireIssue fails the test unless a validation issue names a path. ConfigRequireIssue = configth.RequireIssue // ConfigStub builds a configuration straight from a raw map. ConfigStub = configth.StubConfig // ConfigStubApp builds a configuration carrying only an app block. ConfigStubApp = configth.StubApp // ConfigBaseSource wraps a base document as a YAML source. ConfigBaseSource = configth.BaseSource // ConfigOverlaySource wraps a landscape overlay as a YAML source. ConfigOverlaySource = configth.OverlaySource // ConfigEnvSource wraps environment variables as an env source. ConfigEnvSource = configth.EnvSource // ConfigInvalidSchemaBlock is a block that fails schema composition. ConfigInvalidSchemaBlock = configth.InvalidSchemaBlock )
var ( // NewOtelLoggerSink creates the in-memory logger-sink mock. NewOtelLoggerSink = otelth.NewInMemoryLoggerSink // NewOtelMetricsCollector creates the in-memory metrics-collector mock. NewOtelMetricsCollector = otelth.NewInMemoryMetricsCollector // NewOtelTraceEmitter creates the in-memory trace-emitter mock. NewOtelTraceEmitter = otelth.NewInMemoryTraceEmitter // OtelAssertLogRecords fails the test unless the emitted logs match. OtelAssertLogRecords = otelth.AssertLogRecords // OtelAssertMetricRecords fails the test unless the emitted metrics match. OtelAssertMetricRecords = otelth.AssertMetricRecords // OtelAssertTraceRecords fails the test unless the emitted traces match. OtelAssertTraceRecords = otelth.AssertTraceRecords // OtelAssertResourceAttributes fails the test unless the semconv and // `atomi.*` resource attributes match. OtelAssertResourceAttributes = otelth.AssertResourceAttributes // OtelSampleConfig is a ready otel configuration block. OtelSampleConfig = otelth.SampleConfig // OtelSampleIdentity is a ready otel application identity. OtelSampleIdentity = otelth.SampleIdentity )
var ( // AuthNewFakeIDP creates a fake IdP with real JWKS material. AuthNewFakeIDP = authth.NewFakeIDP // AuthNewFakeProvider creates a scripted token provider. AuthNewFakeProvider = authth.NewFakeProvider // AuthNewMemoryTokenStore creates an in-memory access-token store. AuthNewMemoryTokenStore = authth.NewMemoryTokenStore // AuthNewMemoryRefreshStore creates an in-memory rotating-refresh store. AuthNewMemoryRefreshStore = authth.NewMemoryRefreshStore // AuthAssertProblem fails the test unless an error is the expected auth // problem. AuthAssertProblem = authth.AssertAuthProblem // AuthAssertOwnershipDenied fails the test unless ownership was refused. AuthAssertOwnershipDenied = authth.AssertOwnershipDenied )
var ( // APINewFakeBackend creates a fake backend for the client tree. APINewFakeBackend = apith.NewFakeBackend // APINewFakeTree creates a fake multi-backend client tree. APINewFakeTree = apith.NewFakeTree // APINewFakeRetriever creates a fake per-backend token retriever. APINewFakeRetriever = apith.NewFakeRetriever // APICanned builds a canned Problem-envelope route. APICanned = apith.Canned // APIAssertProblem fails the test unless an error is the expected backend // problem. APIAssertProblem = apith.AssertProblem // APIAssertOutcome fails the test unless the 3-case classification matches. APIAssertOutcome = apith.AssertOutcome )
var ( // PresetStartPostgres boots a Postgres container and emits its block. PresetStartPostgres = presetth.StartPostgres // PresetStartCache boots a cache container and emits its block. PresetStartCache = presetth.StartCache // PresetStartKv boots a kv container and emits its block. PresetStartKv = presetth.StartKv // PresetStartStorage boots a storage container and emits its block. PresetStartStorage = presetth.StartStorage // PresetCreateBucket creates the bucket a storage entry addresses. PresetCreateBucket = presetth.CreateBucket // PresetFakePostgres is a Postgres block that addresses nothing, for tests // that need shape rather than a live dependency. PresetFakePostgres = presetth.FakePostgres // PresetFakeCache is a cache block that addresses nothing. PresetFakeCache = presetth.FakeCache // PresetFakeKv is a kv block that addresses nothing. PresetFakeKv = presetth.FakeKv // PresetFakeStorage is a storage block that addresses nothing. PresetFakeStorage = presetth.FakeStorage )
var ErrEmptyStack = errors.New("testhelper: a container stack needs at least one preset")
ErrEmptyStack reports a stack that was asked to boot nothing.
var ErrScriptExhausted = errors.New("testhelper: the scripted driver has no answer left")
ErrScriptExhausted reports a scripted driver asked for more invocations than it was given answers for.
It is an error rather than a zero result because a silent zero would let a journey pass on steps the script never anticipated.
Functions ¶
func AssertHarnessProblem ¶
AssertHarnessProblem fails the test unless err is the harness problem id names, and returns the envelope for further assertions.
Example ¶
package main
import (
"fmt"
"github.com/AtomiCloud/diene.go-e2e/lib/e2e"
"github.com/AtomiCloud/diene.go-e2e/testhelper"
)
// exampleT satisfies [testhelper.TestingT] outside a test binary, so the
// examples can show what a failing assertion says.
type exampleT struct{}
// Helper is a no-op outside a test binary.
func (exampleT) Helper() {}
// Fatalf prints instead of aborting.
func (exampleT) Fatalf(format string, args ...any) {
fmt.Printf(format+"\n", args...)
}
// exampleProblems builds the harness problem factory on the shared sample
// portal.
func exampleProblems() *e2e.Problems {
problems, err := testhelper.SampleProblems()
if err != nil {
panic(err)
}
return problems
}
func main() {
err := exampleProblems().Raise(e2e.ProblemJourneyEmpty, "nothing to run", nil)
envelope := testhelper.AssertHarnessProblem(exampleT{}, err, e2e.ProblemJourneyEmpty)
fmt.Println(envelope.Status)
}
Output: 422
func AssertNoHarnessProblem ¶
AssertNoHarnessProblem fails the test when err is set.
func AssertReport ¶
AssertReport fails the test unless actual describes a complete, passing run of journey.
Example ¶
package main
import (
"fmt"
"github.com/AtomiCloud/diene.go-e2e/lib/e2e"
"github.com/AtomiCloud/diene.go-e2e/testhelper"
)
// exampleT satisfies [testhelper.TestingT] outside a test binary, so the
// examples can show what a failing assertion says.
type exampleT struct{}
// Helper is a no-op outside a test binary.
func (exampleT) Helper() {}
// Fatalf prints instead of aborting.
func (exampleT) Fatalf(format string, args ...any) {
fmt.Printf(format+"\n", args...)
}
func main() {
journey := e2e.Journey{
Name: "seeds",
Steps: []e2e.Step{{
Name: "seed",
Invocation: e2e.Invocation{},
Expect: e2e.Expectation{ExitCode: 0},
}},
}
report := e2e.Report{Driver: "scripted", Journey: "seeds", Steps: []e2e.StepReport{
{Name: "seed", Result: e2e.Result{ExitCode: 0}},
}}
testhelper.AssertReport(exampleT{}, report, journey)
fmt.Println("report matches")
}
Output: report matches
func AssertResult ¶
func AssertResult(t TestingT, actual e2e.Result, want e2e.Expectation)
AssertResult fails the test unless actual satisfies want.
Example ¶
package main
import (
"fmt"
"github.com/AtomiCloud/diene.go-e2e/lib/e2e"
"github.com/AtomiCloud/diene.go-e2e/testhelper"
)
// exampleT satisfies [testhelper.TestingT] outside a test binary, so the
// examples can show what a failing assertion says.
type exampleT struct{}
// Helper is a no-op outside a test binary.
func (exampleT) Helper() {}
// Fatalf prints instead of aborting.
func (exampleT) Fatalf(format string, args ...any) {
fmt.Printf(format+"\n", args...)
}
func main() {
testhelper.AssertResult(exampleT{}, e2e.Result{ExitCode: 0, Stdout: "ready"},
e2e.Expectation{ExitCode: 0, StdoutContains: []string{"ready"}})
testhelper.AssertResult(exampleT{}, e2e.Result{ExitCode: 1},
e2e.Expectation{ExitCode: 0})
}
Output: unexpected result: step "result": exit code 1 is not 0
func AssertStep ¶
AssertStep fails the test unless a step's observed result meets its expectation.
func CheckHarnessProblem ¶
CheckHarnessProblem reports why err is not the harness problem id names.
func CheckReport ¶
CheckReport reports why a report does not describe the journey it should.
It proves the report covers every step of journey, in order, which is the assertion that catches a driver that silently skipped one — the failure mode a per-step assertion structurally cannot see.
func CheckResult ¶
func CheckResult(actual e2e.Result, want e2e.Expectation) error
CheckResult reports why a result misses want, or nil when it matches.
Only the fields want actually sets are compared: a zero expected exit code with no output fragments asserts a clean run and nothing more, so a caller never has to restate output it does not care about.
func EchoEntrypoint ¶
func EchoEntrypoint(_ context.Context, invocation e2e.Invocation, stdout io.Writer, stderr io.Writer) (int, error)
EchoEntrypoint is an e2e.Entrypoint that writes back what it was given.
It is the in-process half of a parity fixture: a compiled artifact that echoes its arguments and environment behaves identically, so a consumer can prove its parity wiring works before wiring in a real service.
The exit code is the value of the ExitCodeVar environment variable, defaulting to zero, which is what lets one entrypoint drive both the passing and the failing halves of a journey suite.
func NewEchoDriver ¶
func NewEchoDriver(problems *e2e.Problems) (*e2e.InProcessDriver, error)
NewEchoDriver creates an in-process driver over EchoEntrypoint.
Example ¶
package main
import (
"context"
"fmt"
"github.com/AtomiCloud/diene.go-e2e/lib/e2e"
"github.com/AtomiCloud/diene.go-e2e/testhelper"
)
// exampleProblems builds the harness problem factory on the shared sample
// portal.
func exampleProblems() *e2e.Problems {
problems, err := testhelper.SampleProblems()
if err != nil {
panic(err)
}
return problems
}
func main() {
driver, err := testhelper.NewEchoDriver(exampleProblems())
if err != nil {
panic(err)
}
result, err := driver.Run(context.Background(), e2e.Invocation{
Args: []string{"seed"},
Env: map[string]string{"MARKER": "one"},
})
if err != nil {
panic(err)
}
fmt.Print(result.Stdout)
}
Output: args: seed env: MARKER=one
func PresetRequireEntry ¶
PresetRequireEntry fails the test unless a keyed preset block carries key.
It is a wrapper rather than an alias because Go cannot bind a generic function to a variable, and a bundle that silently dropped the generic helpers would send consumers back to the sibling import it exists to replace.
func PresetRequireStarted ¶
PresetRequireStarted fails the test unless a preset container started.
func RequireReport ¶
RequireReport fails the test unless a journey run succeeded, and returns its report.
func SampleProblems ¶
SampleProblems builds a harness problem factory on the shared sample error portal, so a consumer's first test needs no portal of its own.
Example ¶
package main
import (
"fmt"
"github.com/AtomiCloud/diene.go-e2e/lib/e2e"
"github.com/AtomiCloud/diene.go-e2e/testhelper"
)
// exampleProblems builds the harness problem factory on the shared sample
// portal.
func exampleProblems() *e2e.Problems {
problems, err := testhelper.SampleProblems()
if err != nil {
panic(err)
}
return problems
}
func main() {
problems := exampleProblems()
fmt.Println(len(problems.Registry().Entries()))
}
Output: 10
Types ¶
type APIFakeBackend ¶
type APIFakeBackend = apith.FakeBackend
APIFakeBackend is the api-engine sibling's fake HTTP backend.
type APIFakeBackendOptions ¶
type APIFakeBackendOptions = apith.FakeBackendOptions
APIFakeBackendOptions configures an APIFakeBackend.
type APIProblemOptions ¶
type APIProblemOptions = apith.ProblemOptions
APIProblemOptions describes a canned Problem-envelope response.
type AuthFakeIDP ¶
AuthFakeIDP is the auth-engine sibling's fake identity provider.
type AuthFakeIDPOptions ¶
type AuthFakeIDPOptions = authth.FakeIDPOptions
AuthFakeIDPOptions configures an AuthFakeIDP.
type AuthFakeProvider ¶
type AuthFakeProvider = authth.FakeProvider
AuthFakeProvider is the auth-engine sibling's scripted token provider.
type AuthFakeProviderOptions ¶
type AuthFakeProviderOptions = authth.FakeProviderOptions
AuthFakeProviderOptions configures an AuthFakeProvider.
type InMemorySystem ¶
type InMemorySystem = interfacesth.InMemorySystem
InMemorySystem is the interfaces sibling's deterministic System seam.
type InMemorySystemOptions ¶
type InMemorySystemOptions = interfacesth.InMemorySystemOptions
InMemorySystemOptions configures an InMemorySystem.
type InMemoryTerminal ¶
type InMemoryTerminal = interfacesth.InMemoryTerminal
InMemoryTerminal is the interfaces sibling's scripted Terminal seam.
type InMemoryVfs ¶
type InMemoryVfs = interfacesth.InMemoryVfs
InMemoryVfs is the interfaces sibling's in-memory filesystem seam.
type InMemoryVfsOptions ¶
type InMemoryVfsOptions = interfacesth.InMemoryVfsOptions
InMemoryVfsOptions configures an InMemoryVfs.
type OtelLoggerSink ¶
type OtelLoggerSink = otelth.InMemoryLoggerSink
OtelLoggerSink is the otel sibling's in-memory LoggerSink mock.
type OtelMetricsCollector ¶
type OtelMetricsCollector = otelth.InMemoryMetricsCollector
OtelMetricsCollector is the otel sibling's in-memory MetricsCollector mock.
type OtelTraceEmitter ¶
type OtelTraceEmitter = otelth.InMemoryTraceEmitter
OtelTraceEmitter is the otel sibling's in-memory TraceEmitter mock.
type PresetContainer ¶
PresetContainer is one started container.
type PresetDockerRuntime ¶
type PresetDockerRuntime = presetth.DockerRuntime
PresetDockerRuntime is the real Docker-backed runtime.
type PresetPostgresOptions ¶
type PresetPostgresOptions = presetth.PostgresOptions
PresetPostgresOptions configures a Postgres preset container.
type PresetRedisOptions ¶
type PresetRedisOptions = presetth.RedisOptions
PresetRedisOptions configures a cache or kv preset container.
type PresetRuntime ¶
PresetRuntime is the container runtime the preset helpers start on.
type PresetStartedCache ¶
type PresetStartedCache = presetth.StartedCache
PresetStartedCache is a started cache preset and its config block.
type PresetStartedKv ¶
PresetStartedKv is a started kv preset and its config block.
type PresetStartedPostgres ¶
type PresetStartedPostgres = presetth.StartedPostgres
PresetStartedPostgres is a started Postgres preset and its config block.
type PresetStartedStorage ¶
type PresetStartedStorage = presetth.StartedStorage
PresetStartedStorage is a started storage preset and its config block.
type PresetStorageOptions ¶
type PresetStorageOptions = presetth.StorageOptions
PresetStorageOptions configures a storage preset container.
type ProblemOption ¶
ProblemOption is one expectation about a Problem envelope.
type ScriptedDriver ¶
type ScriptedDriver struct {
// contains filtered or unexported fields
}
ScriptedDriver answers invocations from a script and records what it was asked.
It is how a consumer tests its OWN journeys before a system under test exists, and how the harness tests the journey runner without a process. It is safe for concurrent use, because a parity run drives two drivers and a consumer will eventually run them in parallel.
func NewScriptedDriver ¶
func NewScriptedDriver(label string, steps ...ScriptedStep) *ScriptedDriver
NewScriptedDriver creates a driver that answers from steps in order.
Example ¶
package main
import (
"context"
"fmt"
"github.com/AtomiCloud/diene.go-e2e/lib/e2e"
"github.com/AtomiCloud/diene.go-e2e/testhelper"
)
func main() {
driver := testhelper.NewScriptedDriver(
"scripted",
testhelper.ScriptedStep{Result: e2e.Result{ExitCode: 0, Stdout: "seeded"}},
)
result, err := driver.Run(context.Background(), e2e.Invocation{Args: []string{"seed"}})
if err != nil {
panic(err)
}
fmt.Println(driver.Name(), result.Stdout, driver.Remaining())
fmt.Println(driver.Calls()[0].Args)
}
Output: scripted seeded 0 [seed]
Example (Exhausted) ¶
package main
import (
"context"
"fmt"
"github.com/AtomiCloud/diene.go-e2e/lib/e2e"
"github.com/AtomiCloud/diene.go-e2e/testhelper"
)
func main() {
// A script that runs out REFUSES rather than inventing a zero result, because
// a silent zero would let a journey pass on a step nobody anticipated.
driver := testhelper.NewScriptedDriver("scripted")
_, err := driver.Run(context.Background(), e2e.Invocation{})
fmt.Println(err)
}
Output: testhelper: the scripted driver has no answer left
func (*ScriptedDriver) Calls ¶
func (d *ScriptedDriver) Calls() []e2e.Invocation
Calls returns the invocations the driver was asked to run, in order.
Each one is an independent copy, so reading recorded history cannot rewrite it — a caller that inspects an argument slice and then reuses it would otherwise corrupt the record it just read.
func (*ScriptedDriver) Name ¶
func (d *ScriptedDriver) Name() string
Name identifies the driver in reports and parity failures.
func (*ScriptedDriver) Remaining ¶
func (d *ScriptedDriver) Remaining() int
Remaining reports how many scripted answers are unused, so a test can prove its journey consumed the whole script rather than stopping early.
func (*ScriptedDriver) Run ¶
func (d *ScriptedDriver) Run(_ context.Context, invocation e2e.Invocation) (e2e.Result, error)
Run records the invocation and returns the next scripted answer.
type ScriptedStep ¶
type ScriptedStep struct {
// Result is what the driver reports for this invocation.
Result e2e.Result
// Err, when set, is returned instead of the result: the invocation could
// not be carried out at all.
Err error
}
ScriptedStep is one canned answer a ScriptedDriver gives.
type Stack ¶
type Stack struct {
// Postgres is the started Postgres preset, or nil.
Postgres *PresetStartedPostgres
// Cache is the started cache preset, or nil.
Cache *PresetStartedCache
// Kv is the started kv preset, or nil.
Kv *PresetStartedKv
// Storage is the started storage preset, or nil.
Storage *PresetStartedStorage
}
Stack is a booted set of infra preset containers and the configuration blocks that address them.
This is the DB-adapter integration tier (G1) and nothing more. It boots DATA dependencies; it never boots telemetry infrastructure, and there is no fake OTLP collector here or anywhere else in this module — the otel interface mocks cover emission at the integration tier, and real export is a SIT concern against the Garden preview environment.
func RequireStack ¶
RequireStack fails the test unless a stack booted, and registers nothing: terminating it stays the caller's decision, because a suite usually shares one stack across many tests.
func StartStack ¶
func StartStack(ctx context.Context, options StackOptions) (*Stack, error)
StartStack boots the selected presets and returns them together.
A partial failure terminates whatever already started before returning, so a failed boot never leaks a container into the next test. Selecting nothing is a ErrEmptyStack rather than an empty success: a test that boots no dependency does not want this helper.
Example ¶
package main
import (
"context"
"fmt"
"github.com/AtomiCloud/diene.go-e2e/testhelper"
)
func main() {
// Nothing is booted by default: a suite pays only for the dependencies it
// actually needs.
_, err := testhelper.StartStack(context.Background(), testhelper.StackOptions{})
fmt.Println(err)
}
Output: testhelper: a container stack needs at least one preset
func (*Stack) Blocks ¶
Blocks renders the started presets as the configuration document fragment that addresses them.
This is the glue the whole helper exists for: a Testcontainers port is only known at run time, so the block a service loads has to be BUILT from the started containers rather than written down in a fixture.
type StackOptions ¶
type StackOptions struct {
// Key is the connection key every started preset is emitted under. Blank
// uses [PresetDefaultKey].
Key string
// Postgres boots the Postgres preset.
Postgres bool
// Cache boots the cache preset.
Cache bool
// Kv boots the kv preset.
Kv bool
// Storage boots the storage preset; the preset helper creates its bucket.
Storage bool
// Runtime overrides the container runtime. Nil uses the real Docker
// runtime.
Runtime PresetRuntime
}
StackOptions selects which infra presets a Stack boots.
Nothing is booted by default. An integration test that needs Postgres should pay for Postgres and nothing else, because a suite that boots four containers to test one adapter is a suite people stop running.
type TestingT ¶
type TestingT interface {
// Helper marks the caller as a test helper.
Helper()
// Fatalf reports a fatal failure.
Fatalf(format string, args ...any)
}
TestingT is the slice of *testing.T the harness assertions need.
It is an interface rather than *testing.T so the helpers can be proven to FAIL, which a helper that only ever ran against a real *testing.T could not be: the meta tier hands them a recorder and asserts on what they reported.