helpers

package
v0.0.1-alpha.30 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 5, 2026 License: MIT Imports: 28 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var GoldenHeaders = []string{"Content-Type"}

GoldenHeaders lists the only response headers a golden fixture ever captures or compares. Request-ID headers (x-amzn-requestid, x-amz-request-id) are deliberately excluded — they are unique per request by design (see internal/middleware/requestid.go) and would make every golden fail on every run even with a mock clock.

Functions

func Assert

func Assert(t *testing.T) *assert.Assertions

Assert returns a testify Assertions value that calls t.Fail on failure.

func AssertHeader

func AssertHeader(t *testing.T, resp *http.Response, header, value string)

AssertHeader fails the test if the named header is not present or doesn't match value. Pass an empty value string to just assert presence.

func AssertJSONError

func AssertJSONError(t *testing.T, resp *http.Response, expectedCode string)

AssertJSONError decodes the response as a JSON error and checks the error code.

func AssertQueryXMLError

func AssertQueryXMLError(t *testing.T, resp *http.Response, expectedCode string)

AssertQueryXMLError decodes an AWS Query ErrorResponse and checks its error code.

func AssertRequestID

func AssertRequestID(t *testing.T, resp *http.Response)

AssertRequestID fails the test if x-amzn-requestid or x-amz-request-id is missing.

func AssertStatus

func AssertStatus(t *testing.T, resp *http.Response, expected int)

AssertStatus fails the test if the response status code doesn't match expected.

func AssertXMLError

func AssertXMLError(t *testing.T, resp *http.Response, expectedCode string)

AssertXMLError decodes the response as an XML error and checks the error code.

func DecodeJSON

func DecodeJSON(t *testing.T, resp *http.Response, v any)

DecodeJSON decodes the response body into v and fails the test on error.

func DecodeXML

func DecodeXML(t *testing.T, resp *http.Response, v any)

DecodeXML decodes the response body into v and fails the test on error.

func Eventually

func Eventually(t *testing.T, timeout, interval time.Duration, fn func() bool, msg string)

Eventually retries fn every interval until it returns true or timeout elapses. Calls t.Fatal with msg if the deadline is reached without success.

func GoldenTest

func GoldenTest(t *testing.T, dir, action string, resp *http.Response, normalize NormalizeFunc)

GoldenTest asserts (or, under -record, writes) a wire-byte golden fixture for one AWS operation response.

dir is the golden directory for the service, conventionally "goldens" (relative to the test package — Go runs tests with the package directory as the working directory) so fixtures land in tests/integration/<service>/goldens/. action is the AWS operation name and doubles as the fixture's filename (<action>.json).

resp is the *http.Response already produced by calling the operation against a helpers.TestServer (typically via the service's existing <svc>Call test helper) — GoldenTest does not perform the HTTP round trip itself, it only captures/compares the result. The caller retains ownership of resp and should not also call resp.Body.Close() before passing it in; GoldenTest reads and closes the body.

normalize, if non-nil, is applied to the raw body bytes before recording and before comparison, so both sides of the comparison go through the same redaction.

Usage:

resp := ssmCall(t, srv, "DescribeParameters", nil)
helpers.GoldenTest(t, "goldens", "DescribeParameters", resp, nil)

func NewHTTPBackend

func NewHTTPBackend(t *testing.T, handler http.HandlerFunc) *httptest.Server

NewHTTPBackend starts a throwaway HTTP server with the given handler. The server is closed automatically when the test ends.

func PaginationContractTest

func PaginationContractTest[T any](
	t *testing.T,
	wantIDs []string,
	idOf func(T) string,
	fetch PageFetcher[T],
	probe InvalidTokenProbe,
	opts PaginationContractOptions,
) []T

PaginationContractTest walks every page of a paginated operation (via fetch, starting from an empty token) until NextToken comes back empty, then asserts the pagination-plan's contract in one place:

  • exactly-once (+ order when known): if wantIDs is non-nil, the concatenated item IDs across every page must match it exactly, in sequence — any duplicate, dropped, or reordered item fails here. If wantIDs is nil (the operation's IDs are server-generated and its exact response order can't be predicted without duplicating the store's internals — e.g. random UUIDs), only exactly-once (no duplicate ID anywhere in the walk) is checked; callers that need an order assertion in that case should inspect the returned items themselves (e.g. checking a documented monotonic field).
  • terminal condition: the walk reaches an empty NextToken within opts.MaxPages; a NextToken that repeats without changing also fails immediately rather than spinning.
  • invalid-token error: probe (when non-nil) is called once with a garbled/out-of-range token, and its result must match opts.WantInvalidTokenStatus / WantInvalidTokenErrorCode — i.e. the service must return the documented AWS error instead of silently restarting the walk from page 1 (docs/plans/pagination-plan.md, H1/G3).

idOf extracts a comparable identifier from each item T (e.g. an event ID, a distribution ID, a parameter name+version composite) for the exactly-once/order comparison. When non-nil, wantIDs must already be in the operation's documented response order (e.g. reverse-chronological for DescribeStackEvents) — PaginationContractTest does not reorder anything.

Returns every item collected across the walk, in response order, so callers can run additional operation-specific assertions afterward.

func PullOrSkip

func PullOrSkip(t *testing.T, dc *docker.Client, image string)

PullOrSkip fetches image so a test can use it, skipping the test when the registry is unreachable and failing it when the registry answers with a refusal. Pulling up front also warms the daemon's cache for the emulator, whose own pull then resolves locally.

func ReadBody

func ReadBody(t *testing.T, resp *http.Response) string

ReadBody reads and returns the response body as a string.

func RegistryUnreachable

func RegistryUnreachable(err error) bool

RegistryUnreachable reports whether err is the registry being unavailable — a transport failure or a pull-rate rejection — rather than the registry answering that the image cannot be had.

Deliberately conservative: an error that does not match one of these stays a failure, because the cost of wrongly skipping is silent lost coverage while the cost of wrongly failing is one visibly red run.

func Require

func Require(t *testing.T) *require.Assertions

Require returns a testify Assertions value that calls t.FailNow on failure.

Types

type InvalidTokenProbe

type InvalidTokenProbe func(t *testing.T) (statusCode int, errorCode string)

InvalidTokenProbe drives one request against the same operation using a deliberately garbled/out-of-range continuation token and reports the HTTP status code and AWS error code/type the service returned. Implementations should NOT call t.Fatal/t.Error themselves — PaginationContractTest compares the returned values against the expected ones so failures report with full context (both the wanted and the observed response).

type MockStore

type MockStore struct {

	// Recorded calls — inspect these in tests to assert behaviour.
	GetCalls    []StoreCall
	SetCalls    []StoreCall
	DeleteCalls []StoreCall
	ListCalls   []StoreCall

	// Errors to inject — set these to simulate failures.
	GetError    error
	SetError    error
	DeleteError error
	ListError   error
	ScanError   error
	// contains filtered or unexported fields
}

MockStore is a hand-written test double for state.Store. Use it in unit tests that need to isolate a component from real storage.

It records all calls so tests can assert on what was called, in what order, with what arguments.

Example:

mock := helpers.NewMockStore()
mock.SetData("s3:buckets", "my-bucket", `{"name":"my-bucket"}`)
// ... inject mock into component under test ...
if len(mock.SetCalls) != 1 { t.Error("expected one Set call") }

func NewMockStore

func NewMockStore() *MockStore

NewMockStore returns an initialised MockStore.

func (*MockStore) Close

func (m *MockStore) Close() error

func (*MockStore) Delete

func (m *MockStore) Delete(_ context.Context, namespace, key string) error

func (*MockStore) Get

func (m *MockStore) Get(_ context.Context, namespace, key string) (string, bool, error)

func (*MockStore) List

func (m *MockStore) List(_ context.Context, namespace, prefix string) ([]string, error)

func (*MockStore) ListNamespaces

func (m *MockStore) ListNamespaces(_ context.Context) ([]string, error)

func (*MockStore) Reset

func (m *MockStore) Reset()

Reset clears all stored data and recorded calls.

func (*MockStore) Scan

func (m *MockStore) Scan(_ context.Context, namespace, prefix string) ([]state.KV, error)

Scan returns key-value pairs matching the prefix (implements state.Store).

func (*MockStore) ScanPage

func (m *MockStore) ScanPage(ctx context.Context, namespace, prefix, startAfter string, limit int) ([]state.KV, string, error)

func (*MockStore) Set

func (m *MockStore) Set(_ context.Context, namespace, key, value string) error

func (*MockStore) SetData

func (m *MockStore) SetData(namespace, key, value string)

SetData pre-populates the mock with a value, without recording a SetCall. Use this in the Given section of tests to set up initial state.

type NormalizeFunc

type NormalizeFunc func(body []byte) []byte

NormalizeFunc redacts expected non-determinism (e.g. an httptest-assigned host:port embedded in a response body) from raw response bytes before the golden comparison. Most operations need none — pass nil. Each service defines its own normalize functions, named and justified next to the golden test that uses them (docs/plans/wire-byte-goldens.md §4.4).

type Option

type Option func(*serverOptions)

Option is a functional option for configuring the test server. Use the With* constructors rather than crafting values directly.

func WithAccountID

func WithAccountID(id string) Option

WithAccountID overrides the fake AWS account ID used in ARNs.

func WithDataDir

func WithDataDir(dir string) Option

WithDataDir sets the data directory for on-disk state (e.g. S3 body files). If not set, a temporary directory is used automatically.

func WithDebug

func WithDebug(enabled bool) Option

WithDebug enables the /_debug/* endpoint namespace on the test server.

func WithEC2VPCStrategy

func WithEC2VPCStrategy(strategy string) Option

WithEC2VPCStrategy sets the VPC network strategy used by the EC2 service. Valid values: "shared" (default), "strict", "remapped". See docs/plans/ec2-vpc-network-strategies.md for details.

func WithEKSMode

func WithEKSMode(mode config.EKSMode) Option

WithEKSMode sets the EKS service mode used by the test server.

func WithEnforceAPIGatewayThrottle

func WithEnforceAPIGatewayThrottle(enabled bool) Option

WithEnforceAPIGatewayThrottle enables opt-in rejection of API Gateway requests that exceed their usage plan's throttle or quota limits. Usage is measured either way; this only decides whether an over-limit request is answered 429 instead of being served.

func WithEnforceIAM

func WithEnforceIAM(enabled bool) Option

WithEnforceIAM enables opt-in IAM authorization enforcement middleware.

func WithHostname

func WithHostname(hostname string) Option

WithHostname sets the external hostname used in client-facing URLs.

func WithInitRunner

func WithInitRunner(r *inithooks.Runner) Option

WithInitRunner injects an init hook runner into the test server so the /_overcast/init status endpoint reports its state.

func WithLambdaDocker

func WithLambdaDocker() Option

WithLambdaDocker enables Docker-backed Lambda execution on the test server. By default, test servers skip the Docker probe entirely (stub runtime only) to avoid 1000+ unnecessary Docker daemon round-trips across the test suite. Use this option for tests that invoke real Lambda container runtimes.

TODO(perf): For Approach B — share a single Docker client, RuntimeAPI server, and InstancePool across all test servers in a package via a package-level sync.Once. This would enable warm-container reuse across tests and further reduce Docker daemon pressure. Wire shared runtime via a new lambda.WithSharedRuntime(...) service option instead of each server probing independently.

func WithLambdaHotReload

func WithLambdaHotReload() Option

WithLambdaHotReload enables bind-mount-based Lambda hot reload. Functions must still opt in via the overcast:hot-reload-path tag.

func WithLogger

func WithLogger(logger *zap.Logger) Option

WithLogger routes the server's logs to the supplied zap.Logger instead of discarding them. Pair it with zaptest/observer to assert on a diagnostic the emulator emits but cannot surface in a response — AWS wire formats are fixed, so a log line is sometimes the only place a divergence can be reported.

core, logs := observer.New(zap.WarnLevel)
srv := helpers.NewTestServer(t, helpers.WithLogger(zap.New(core)))

func WithMockClock

func WithMockClock() Option

WithMockClock injects a manually-controlled clock into all services on the test server. Access srv.Clock to advance time without real sleeps:

srv := helpers.NewTestServer(t, helpers.WithMockClock())
srv.Clock.Add(35 * time.Second) // visibility timeout expires instantly

func WithRegion

func WithRegion(region string) Option

WithRegion overrides the AWS region reported in ARNs and responses.

func WithSMTPMock

func WithSMTPMock() Option

WithSMTPMock enables the built-in SMTP capture server on a random port. Emails delivered to SNS email/email-json subscribers are captured and accessible via GET /_overcast/inbox/messages on the test server.

func WithServiceStates

func WithServiceStates(states map[string]config.StateBackend) Option

WithServiceStates sets per-service storage backend overrides.

func WithServiceSubset

func WithServiceSubset(services ...string) Option

WithServiceSubset registers only the named services on the test server.

This is not a general-purpose knob and should not be reached for to "focus" a test: every service is always on in real runs, so a test that narrows the set is exercising a shape no user ever gets. It exists for the router tests that cannot be written any other way — proving no modeled operation falls through to S3's broad bucket/object routes requires a server where nothing else is registered to claim the path first. See config.TestOnlyServiceSubset.

func WithSigV4Validate

func WithSigV4Validate(enabled bool) Option

WithSigV4Validate enables or disables SigV4 signature validation for the test server. When enabled, unsigned requests and requests with invalid signatures are rejected with a 403. Default is false.

func WithStore

func WithStore(s state.Store) Option

WithStore injects a specific Store implementation (e.g. SQLiteStore). By default the server uses an in-memory store.

func WithTLS

func WithTLS() Option

WithTLS marks the server as TLS-enabled for the purposes of config (cfg.TLSEnabled()), without actually serving HTTPS.

Handlers that must not advertise an https:// URL the emulator cannot answer — CloudFront's ViewerProtocolPolicy is the case this exists for — branch on cfg.TLSEnabled(), which only checks that both paths are set. Serving real TLS would mean generating a certificate and re-dialling every helper in this package for one boolean, so this sets the paths and nothing else.

type PageFetcher

type PageFetcher[T any] func(t *testing.T, token string) (items []T, nextToken string)

PageFetcher fetches a single page of a paginated List/Describe operation. token is "" for the first page. Implementations should call t.Fatal on transport/decode failures (they run inside PaginationContractTest's walk, so t.Helper() + t.Fatal produces a useful failure line). nextToken must be "" when there are no more pages — the walk stops there.

type PaginationContractOptions

type PaginationContractOptions struct {
	// WantInvalidTokenStatus is the HTTP status code expected when Probe is
	// invoked with a garbage/out-of-range token (e.g. http.StatusBadRequest).
	WantInvalidTokenStatus int
	// WantInvalidTokenErrorCode is the AWS error code/type expected in the
	// invalid-token response (e.g. "ValidationError" for CloudFormation,
	// "InvalidArgument" for CloudFront, "InvalidNextToken" for SSM).
	WantInvalidTokenErrorCode string
	// MaxPages bounds the walk so an operation whose NextToken never
	// terminates (a G1-class bug) fails the test instead of looping
	// forever. Defaults to 1000 pages.
	MaxPages int
}

PaginationContractOptions configures PaginationContractTest.

type StoreCall

type StoreCall struct {
	Namespace string
	Key       string
	Value     string // only set for Set calls
	Prefix    string // only set for List calls
}

StoreCall records a single call to a store method.

type TestServer

type TestServer struct {
	*httptest.Server
	// Store is exposed so tests can inspect or pre-populate state directly
	// when needed. Prefer HTTP setup helpers (createBucket, createQueue etc.)
	// over direct store access wherever possible.
	Store  *state.MemoryStore
	Config *config.Config
	// Clock is the mock clock injected into all services on this server.
	// It is only set when WithMockClock() is passed to NewTestServer;
	// for real-clock servers it is nil.
	// Use Clock.Add(d) to advance time without any real sleep.
	Clock *clock.Mock
}

TestServer wraps httptest.Server with a pre-configured emulator instance. Each test receives a fresh server with empty in-memory state — isolation is guaranteed without any setup/teardown ceremony.

func NewTestServer

func NewTestServer(t *testing.T, opts ...Option) *TestServer

NewTestServer creates a started test server with sensible defaults. The server is automatically closed when the test ends via t.Cleanup.

Example — basic usage:

srv := helpers.NewTestServer(t)

Example — with options:

srv := helpers.NewTestServer(t,
    helpers.WithRegion("eu-west-1"),
    helpers.WithMockClock(),
)

Example — advancing time in a test:

srv := helpers.NewTestServer(t, helpers.WithMockClock())
srv.Clock.Add(35 * time.Second) // instant — no real sleep

func (*TestServer) ExternalBase

func (ts *TestServer) ExternalBase() string

ExternalBase returns the base URL this server embeds in client-facing responses: the configured hostname (OVERCAST_HOSTNAME — "localhost" by default, see defaultTestConfig) on the port httptest actually bound.

Assert against this, not Server.URL, whenever a test checks a resource URL a service handed back. Server.URL is the dial address (127.0.0.1), which is deliberately NOT what clients are told: an IP base matches no virtual-host rule, so a test pinned to it silently exercises a host shape no real client ever sends. That is exactly how the S3/host-route addressing collision survived — the Lambda function-URL round-trip test minted "{urlId}.lambda-url.us-east-1.127.0.0.1:PORT" and never hit the bug. See docs/plans/host-routing-precedence.md.

func (*TestServer) Reset

func (ts *TestServer) Reset()

Reset wipes all state on the server. Useful when a test wants to verify behaviour starting from a clean slate mid-test without creating a new server.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL