Documentation
¶
Overview ¶
Package testkit provides deterministic, fluent test harnesses for both nexssp actions and standard Go http.Handler implementations.
Layout of this package:
- suite.go — Suite lifecycle: New, NewWithHandler, NewE2E, global headers/cookies, context bridge.
- request.go — Request builder and every With* method; Suite's HTTP verb methods (GET, POST, …).
- response.go — Response and every fluent assertion.
- traverse.go — dot-path resolution used by HasField and ExpectArrayLen.
- chaos_bridge.go — WithChaos, the only bridge from this package into testkit/chaos.
- async.go — WaitForJSON.
- bench.go — BenchHTTP.
- load.go — Suite.LoadTest.
- load_bg.go — Suite.StartBackgroundLoad.
- sse.go, sse_wait.go — Server-Sent Events capture and waits.
- smoke.go — RunSmokeTests.
Generic helpers (Eventually, GoldenJSON, Trace, Fake, Script, Recorder, Simulate, AssertContracts, BenchAction, xctx test contexts, xerr assertions) live in github.com/nexssp/kernel/xtest.
Subpackages (imported on demand only):
- chaos — stochastic latency/error/panic injection.
- rpc — JSON-RPC 2.0 test client over net.Pipe.
- examples — runnable, self-contained demonstrations of the API.
Index ¶
- Constants
- func BenchHTTP(b *testing.B, s *Suite, method, path string, payload any)
- func RunSmokeTests(t *testing.T, actions []action.AnyAction)
- func WaitForJSON[T any](t testing.TB, timeout, interval time.Duration, ...) T
- func WithChaos(actions []action.AnyAction, cfg chaos.Config) []action.AnyAction
- type FakeClock
- type LoadConfig
- type LoadResult
- type Request
- func (r *Request) Do() *Response
- func (r *Request) DoContext(ctx context.Context) (*Response, error)
- func (r *Request) DoE() (*Response, error)
- func (r *Request) WithBearerToken(token string) *Request
- func (r *Request) WithContext(ctx context.Context) *Request
- func (r *Request) WithCookie(name, value string) *Request
- func (r *Request) WithForm(data map[string]string) *Request
- func (r *Request) WithHeader(k, v string) *Request
- func (r *Request) WithJSON(v any) *Request
- func (r *Request) WithMultipartFile(fieldName, filename string, content []byte) *Request
- func (r *Request) WithQueries(params map[string]string) *Request
- func (r *Request) WithQuery(key, value string) *Request
- func (r *Request) WithTenant(tenantID string) *Request
- type Response
- func (c *Response) Body() []byte
- func (c *Response) BodyString() string
- func (c *Response) ContainsString(substr string) *Response
- func (c *Response) ExpectArrayLen(path string, n int) *Response
- func (c *Response) ExpectBadRequest() *Response
- func (c *Response) ExpectCreated() *Response
- func (c *Response) ExpectErrorKind(kind xerr.Kind, message string) *Response
- func (c *Response) ExpectForbidden() *Response
- func (c *Response) ExpectHeader(key, value string) *Response
- func (c *Response) ExpectNotFound() *Response
- func (c *Response) ExpectOK() *Response
- func (c *Response) ExpectStatus(code int) *Response
- func (c *Response) ExpectSuccess() *Response
- func (c *Response) ExpectUnauthorized() *Response
- func (c *Response) HasField(path string, expected any) *Response
- func (c *Response) Header(k string) string
- func (c *Response) Into(v any) *Response
- func (c *Response) Status() int
- type SSEEvent
- type StreamCapture
- func (sc *StreamCapture) Close()
- func (sc *StreamCapture) Endpoint(t testing.TB, timeout time.Duration) string
- func (sc *StreamCapture) WaitFor(t testing.TB, eventName string, timeout time.Duration) SSEEvent
- func (sc *StreamCapture) WaitForData(t testing.TB, substr string, timeout time.Duration) SSEEvent
- func (sc *StreamCapture) WaitForSSE(t testing.TB, timeout time.Duration, match func(SSEEvent) bool) SSEEvent
- func (sc *StreamCapture) WaitForSSEData(t testing.TB, substr string, timeout time.Duration) SSEEvent
- type Suite
- func (s *Suite) DELETE(path string, body ...any) *Request
- func (s *Suite) GET(path string, body ...any) *Request
- func (s *Suite) ListenSSE(path string) *StreamCapture
- func (s *Suite) ListenSSEWithRequest(t testing.TB, req *http.Request) *StreamCapture
- func (s *Suite) LoadTest(t *testing.T, cfg LoadConfig) LoadResult
- func (s *Suite) PATCH(path string, body ...any) *Request
- func (s *Suite) POST(path string, body ...any) *Request
- func (s *Suite) PUT(path string, body ...any) *Request
- func (s *Suite) Request(method, path string) *Request
- func (s *Suite) ResetHeaders() *Suite
- func (s *Suite) StartBackgroundLoad(cfg LoadConfig) func()
- func (s *Suite) WithCookie(name, value string) *Suite
- func (s *Suite) WithGlobalBearerToken(token string) *Suite
- func (s *Suite) WithGlobalHeader(key, value string) *Suite
Constants ¶
const (
HeaderTenantID = "X-Tenant-Id"
)
Variables ¶
This section is empty.
Functions ¶
func BenchHTTP ¶
BenchHTTP benchmarks the complete HTTP stack over in-memory sockets. For pure action execution without transport, use xtest.BenchAction.
func WaitForJSON ¶ added in v0.3.0
func WaitForJSON[T any]( t testing.TB, timeout, interval time.Duration, request func() (*Response, error), ready func(T) bool, ) T
WaitForJSON polls request until the response body decodes as T and satisfies ready, or until timeout elapses. It returns the last successfully-decoded value.
All polling and timeout semantics are delegated to kernel/xtest.EventuallyEveryE, so HTTP polling behaves exactly like every other Eventually assertion in the kernel. This function adds only the decode-then-check step.
- nil ready accepts any decodable body.
- timeout <= 0 performs a single synchronous attempt (matching EventuallyEveryE's contract).
- a non-positive interval is normalized by the kernel poll loop.
On timeout the test is failed via t, and the last observed error (transport error, JSON error, or errNotReady) is included in the failure message.
func WithChaos ¶
WithChaos wraps provided actions with chaos fault injection.
This is the only function in package testkit that references testkit/chaos. Keeping it in its own file makes the dependency boundary explicit: any reader of the package can see at a glance that chaos is opt-in, not part of the core Suite.
Types ¶
type FakeClock ¶ added in v0.4.0
type FakeClock struct {
// contains filtered or unexported fields
}
FakeClock is a deterministic replacement for time.Now / time.After / time.NewTicker. Install it into a context with WithClock, and code that reads from ctx can be driven forward in tests with Advance.
This is what lets a test verify "the retry waited 200ms" without a real 200ms sleep.
func NewFakeClock ¶ added in v0.4.0
NewFakeClock returns a clock frozen at the given instant.
func (*FakeClock) Advance ¶ added in v0.4.0
Advance moves the clock forward, waking every waiter whose deadline has passed. Waiters are woken in deadline order.
type LoadConfig ¶
type LoadResult ¶
type Request ¶
type Request struct {
// contains filtered or unexported fields
}
func (*Request) DoContext ¶
DoContext executes the HTTP request with the provided context and returns an error without calling t.Fatalf (goroutine safe).
func (*Request) WithBearerToken ¶
func (*Request) WithCookie ¶
func (*Request) WithHeader ¶
func (*Request) WithMultipartFile ¶
func (*Request) WithTenant ¶
type Response ¶
type Response struct {
// contains filtered or unexported fields
}
func (*Response) BodyString ¶
func (*Response) ContainsString ¶
func (*Response) ExpectBadRequest ¶
func (*Response) ExpectCreated ¶
func (*Response) ExpectErrorKind ¶
func (*Response) ExpectForbidden ¶
func (*Response) ExpectHeader ¶
func (*Response) ExpectNotFound ¶
func (*Response) ExpectStatus ¶
func (*Response) ExpectSuccess ¶
func (*Response) ExpectUnauthorized ¶
type StreamCapture ¶
type StreamCapture struct {
// contains filtered or unexported fields
}
func (*StreamCapture) Close ¶
func (sc *StreamCapture) Close()
func (*StreamCapture) Endpoint ¶ added in v0.2.0
Endpoint conveniently extracts an MCP SSE endpoint event.
func (*StreamCapture) WaitForData ¶ added in v0.2.0
func (*StreamCapture) WaitForSSE ¶ added in v0.3.0
func (sc *StreamCapture) WaitForSSE(t testing.TB, timeout time.Duration, match func(SSEEvent) bool) SSEEvent
WaitForSSE waits for the first buffered SSE event accepted by match. Events that do not match are discarded, just like StreamCapture.WaitFor and WaitForData. A nil match predicate is rejected to avoid an accidental match-all wait; use WaitFor with an empty event name when matching any event.
func (*StreamCapture) WaitForSSEData ¶ added in v0.3.0
func (sc *StreamCapture) WaitForSSEData(t testing.TB, substr string, timeout time.Duration) SSEEvent
WaitForSSEData waits for an SSE event whose data contains substr. It is the predicate equivalent of StreamCapture.WaitForData and is useful when a test needs the event name as well as a data substring.
type Suite ¶
type Suite struct {
T testing.TB
Server *httptest.Server
// contains filtered or unexported fields
}
Suite wraps an in-memory or live HTTP server with fluent test assertions.
func NewWithHandler ¶
NewWithHandler initializes a test suite against ANY standard Go http.Handler.
func (*Suite) ListenSSE ¶
func (s *Suite) ListenSSE(path string) *StreamCapture
ListenSSE connects to an SSE endpoint using GET.
func (*Suite) ListenSSEWithRequest ¶ added in v0.2.0
ListenSSEWithRequest connects to an SSE endpoint using a custom HTTP request. This supports POST + SSE patterns such as MCP Streamable HTTP and A2A.
func (*Suite) LoadTest ¶
func (s *Suite) LoadTest(t *testing.T, cfg LoadConfig) LoadResult
LoadTest executes in-process stress tests, properly propagating cancellation to in-flight requests.
func (*Suite) ResetHeaders ¶
func (*Suite) StartBackgroundLoad ¶
func (s *Suite) StartBackgroundLoad(cfg LoadConfig) func()
StartBackgroundLoad generates background load, correctly checking for URL/request errors.