Documentation
¶
Index ¶
- Variables
- func Assert(t *testing.T) *assert.Assertions
- func AssertHeader(t *testing.T, resp *http.Response, header, value string)
- func AssertJSONError(t *testing.T, resp *http.Response, expectedCode string)
- func AssertQueryXMLError(t *testing.T, resp *http.Response, expectedCode string)
- func AssertRequestID(t *testing.T, resp *http.Response)
- func AssertStatus(t *testing.T, resp *http.Response, expected int)
- func AssertXMLError(t *testing.T, resp *http.Response, expectedCode string)
- func DecodeJSON(t *testing.T, resp *http.Response, v any)
- func DecodeXML(t *testing.T, resp *http.Response, v any)
- func Eventually(t *testing.T, timeout, interval time.Duration, fn func() bool, msg string)
- func GoldenTest(t *testing.T, dir, action string, resp *http.Response, normalize NormalizeFunc)
- func NewHTTPBackend(t *testing.T, handler http.HandlerFunc) *httptest.Server
- func PaginationContractTest[T any](t *testing.T, wantIDs []string, idOf func(T) string, fetch PageFetcher[T], ...) []T
- func PullOrSkip(t *testing.T, dc *docker.Client, image string)
- func ReadBody(t *testing.T, resp *http.Response) string
- func RegistryUnreachable(err error) bool
- func Require(t *testing.T) *require.Assertions
- type InvalidTokenProbe
- type MockStore
- func (m *MockStore) Close() error
- func (m *MockStore) Delete(_ context.Context, namespace, key string) error
- func (m *MockStore) Get(_ context.Context, namespace, key string) (string, bool, error)
- func (m *MockStore) List(_ context.Context, namespace, prefix string) ([]string, error)
- func (m *MockStore) ListNamespaces(_ context.Context) ([]string, error)
- func (m *MockStore) Reset()
- func (m *MockStore) Scan(_ context.Context, namespace, prefix string) ([]state.KV, error)
- func (m *MockStore) ScanPage(ctx context.Context, namespace, prefix, startAfter string, limit int) ([]state.KV, string, error)
- func (m *MockStore) Set(_ context.Context, namespace, key, value string) error
- func (m *MockStore) SetData(namespace, key, value string)
- type NormalizeFunc
- type Option
- func WithAccountID(id string) Option
- func WithDataDir(dir string) Option
- func WithDebug(enabled bool) Option
- func WithEC2VPCStrategy(strategy string) Option
- func WithEKSMode(mode config.EKSMode) Option
- func WithEnforceAPIGatewayThrottle(enabled bool) Option
- func WithEnforceIAM(enabled bool) Option
- func WithHostname(hostname string) Option
- func WithInitRunner(r *inithooks.Runner) Option
- func WithLambdaDocker() Option
- func WithLambdaHotReload() Option
- func WithLogger(logger *zap.Logger) Option
- func WithMockClock() Option
- func WithRegion(region string) Option
- func WithSMTPMock() Option
- func WithServiceStates(states map[string]config.StateBackend) Option
- func WithServiceSubset(services ...string) Option
- func WithSigV4Validate(enabled bool) Option
- func WithStore(s state.Store) Option
- func WithTLS() Option
- type PageFetcher
- type PaginationContractOptions
- type StoreCall
- type TestServer
Constants ¶
This section is empty.
Variables ¶
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 ¶
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 ¶
AssertJSONError decodes the response as a JSON error and checks the error code.
func AssertQueryXMLError ¶
AssertQueryXMLError decodes an AWS Query ErrorResponse and checks its error code.
func AssertRequestID ¶
AssertRequestID fails the test if x-amzn-requestid or x-amz-request-id is missing.
func AssertStatus ¶
AssertStatus fails the test if the response status code doesn't match expected.
func AssertXMLError ¶
AssertXMLError decodes the response as an XML error and checks the error code.
func DecodeJSON ¶
DecodeJSON decodes the response body into v and fails the test on error.
func Eventually ¶
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 ¶
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 ¶
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 ¶
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 RegistryUnreachable ¶
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.
Types ¶
type InvalidTokenProbe ¶
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 (*MockStore) ListNamespaces ¶
func (*MockStore) Reset ¶
func (m *MockStore) Reset()
Reset clears all stored data and recorded calls.
type NormalizeFunc ¶
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 ¶
WithAccountID overrides the fake AWS account ID used in ARNs.
func WithDataDir ¶
WithDataDir sets the data directory for on-disk state (e.g. S3 body files). If not set, a temporary directory is used automatically.
func WithEC2VPCStrategy ¶
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 ¶
WithEKSMode sets the EKS service mode used by the test server.
func WithEnforceAPIGatewayThrottle ¶
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 ¶
WithEnforceIAM enables opt-in IAM authorization enforcement middleware.
func WithHostname ¶
WithHostname sets the external hostname used in client-facing URLs.
func WithInitRunner ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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.