Documentation
¶
Overview ¶
Package testutil — OTLP gRPC test receiver.
FakeOTLP is an in-process OTLP gRPC server for verifying telemetry export in tests. It accepts trace, metric, and log Export RPCs, records the received payloads, and exposes counts (and the raw payloads) for assertions.
Usage:
r := testutil.NewFakeOTLP(t)
t.Setenv("OTEL_EXPORTER_OTLP_ENDPOINT", "http://"+r.Addr())
shutdown, _, _ := observability.InitProvider(ctx, "svc", observability.ProviderConfig{...})
... emit spans/metrics/logs ...
_ = shutdown(ctx) // forces a final flush
assert.Equal(t, expected, r.SpanCount())
Always call shutdown before asserting counts — the OTel SDK batches exports and only drains on shutdown (or after the batch timeout).
NewFakeOTLPTLS is the TLS variant — it mints an ephemeral self-signed cert and exposes it as a PEM file via CertFile(t), which the test points the SDK at with OTEL_EXPORTER_OTLP_CERTIFICATE (the same custom-CA path a real operator uses for a private gateway). Each Export RPC's gRPC metadata is also captured (LastTraceHeaders / LastMetricHeaders / LastLogHeaders) for asserting auth-header propagation.
Index ¶
- Constants
- func AssertBodyContains(t *testing.T, rec *httptest.ResponseRecorder, expectedStatus int, ...)
- func AssertBodyEquals(t *testing.T, rec *httptest.ResponseRecorder, expectedStatus int, ...)
- func AssertJSONContains(t *testing.T, rec *httptest.ResponseRecorder, expectedStatus int, ...)
- func AssertJSONErrorResponse(t *testing.T, w *httptest.ResponseRecorder)
- func AssertJSONResponse(t *testing.T, rec *httptest.ResponseRecorder, expectedStatus int, expected any)
- func MakeExpiredJWT(t *testing.T, claims map[string]any) string
- func MakeJWT(t *testing.T, claims map[string]any) string
- func NewEmbeddedMQ(t testing.TB, maxBytes int64, tenants ...tenant.ID) *mq.EmbeddedNATS
- func NewSchemaConn(tables []*discovery.TableSchema) driver.Conn
- func NewTestSchemaRegistry(t testing.TB, tables []*discovery.TableSchema) *discovery.SchemaRegistry
- func RunRoleMatrix(t *testing.T, cases []RoleCase, ...)
- type CapturedRequest
- type FakeOTLP
- func (r *FakeOTLP) Addr() string
- func (r *FakeOTLP) CertFile(t *testing.T) string
- func (r *FakeOTLP) LastLogHeaders() metadata.MD
- func (r *FakeOTLP) LastMetricHeaders() metadata.MD
- func (r *FakeOTLP) LastTraceHeaders() metadata.MD
- func (r *FakeOTLP) LogCount() int
- func (r *FakeOTLP) LogCountAtLevel(minSeverity int32) int
- func (r *FakeOTLP) MetricCount() int
- func (r *FakeOTLP) Reset()
- func (r *FakeOTLP) SpanCount() int
- type MockCache
- type MockDeadLetterStats
- type MockDeduplicator
- func (m *MockDeduplicator) Close() error
- func (m *MockDeduplicator) Commit(_ context.Context, claims []dedupe.Claim, retention time.Duration) error
- func (m *MockDeduplicator) Committed(k dedupe.Key) bool
- func (m *MockDeduplicator) Hold(k dedupe.Key)
- func (m *MockDeduplicator) Pending(k dedupe.Key) bool
- func (m *MockDeduplicator) Release(_ context.Context, claims []dedupe.Claim) error
- func (m *MockDeduplicator) Reserve(_ context.Context, keys []dedupe.Key, _ time.Duration) ([]dedupe.Claim, error)
- func (m *MockDeduplicator) Retention(k dedupe.Key) time.Duration
- type MockMessage
- type MockPublisher
- func (m *MockPublisher) Close() error
- func (m *MockPublisher) DeadLetter(_ context.Context, msg *mq.Message, opts ...mq.PublishOpt) error
- func (m *MockPublisher) LastMessage() *PublishedMessage
- func (m *MockPublisher) Publish(_ context.Context, topic mq.Topic, data []byte, opts ...mq.PublishOpt) error
- func (m *MockPublisher) Published() []PublishedMessage
- type MockPurger
- type MockRoundTripper
- type MockSubscriber
- type PublishedMessage
- type PurgeCall
- type RoleCase
- type UTCRow
Constants ¶
const TestJWTSecret = "test-secret-at-least-32-chars-long!"
TestJWTSecret is a shared HMAC secret for test JWT generation.
const TestServerVersion = "24.8.1.1"
TestServerVersion is the ClickHouse version NewTestSchemaRegistry's mock connection reports, so a test can assert against ServerVersion() without hardcoding the same literal twice.
Variables ¶
This section is empty.
Functions ¶
func AssertBodyContains ¶
func AssertBodyContains(t *testing.T, rec *httptest.ResponseRecorder, expectedStatus int, expectedSubstring string)
AssertBodyContains checks that rec has the expected status code and that the response body contains the expected substring.
func AssertBodyEquals ¶
func AssertBodyEquals(t *testing.T, rec *httptest.ResponseRecorder, expectedStatus int, expectedBody string)
AssertBodyEquals checks that rec has the expected status code and that the response body equals the expected string.
func AssertJSONContains ¶
func AssertJSONContains(t *testing.T, rec *httptest.ResponseRecorder, expectedStatus int, expectedKeys map[string]any)
AssertJSONContains checks that rec has the expected status code and that the response body contains the expected key-value pairs.
func AssertJSONErrorResponse ¶
func AssertJSONErrorResponse(t *testing.T, w *httptest.ResponseRecorder)
assertJSONErrorResponse verifies that a recorded response is a well-formed JSON error body with the exact headers writeJSONError guarantees: Content-Type: application/json and X-Content-Type-Options: nosniff. Pinned strict so any handler that bypasses writeJSONError and emits a different Content-Type fails the assertion across every call site.
func AssertJSONResponse ¶
func AssertJSONResponse(t *testing.T, rec *httptest.ResponseRecorder, expectedStatus int, expected any)
AssertJSONResponse checks that rec has the expected status code and that the response body, decoded as JSON, matches expected (compared as Go values).
func MakeExpiredJWT ¶
MakeExpiredJWT creates a signed HS256 JWT that expired 1 hour ago.
func MakeJWT ¶
MakeJWT creates a signed HS256 JWT with the given claims merged with standard exp/iat.
func NewEmbeddedMQ ¶ added in v0.2.0
NewEmbeddedMQ starts the embedded broker over a storedir.New directory, closed by the test framework, with a queue open for each of tenants — tenant.Default when none is named — at maxBytes: a tenant has a queue once its budget is applied, as the wiring does for every tenant it serves.
func NewSchemaConn ¶ added in v0.2.0
func NewSchemaConn(tables []*discovery.TableSchema) driver.Conn
NewSchemaConn is the mock connection NewTestSchemaRegistry discovers from, for a test that builds the registry itself.
func NewTestSchemaRegistry ¶
func NewTestSchemaRegistry(t testing.TB, tables []*discovery.TableSchema) *discovery.SchemaRegistry
NewTestSchemaRegistry creates a SchemaRegistry pre-loaded with the given table schemas, without a real ClickHouse: a mock connection serves the schemas as system.columns rows (UTC as the server zone) and the registry is built by the real discovery path — NewSchemaRegistry + Refresh — so timestamp column specs are precomputed exactly as in production.
The registry holds schemas rebuilt from those rows (Name, Type, HasDefault, DefaultKind, DefaultExpression, Position, DDL; IsNullable derived from the type string), not the caller's structs.
func RunRoleMatrix ¶
func RunRoleMatrix(t *testing.T, cases []RoleCase, invoke func(t *testing.T, tc RoleCase) *httptest.ResponseRecorder)
RunRoleMatrix runs each case as a parallel subtest, building and invoking the handler through invoke and asserting the response matches WantForbidden. A forbidden case must return 403 with the standard JSON error envelope (`application/json`, `nosniff`, an `error` field); a permitted case must return neither 403 nor 404. invoke owns the resource/request wiring (and any panic recovery for handlers that reach a nil backend on the allowed path).
Types ¶
type CapturedRequest ¶
CapturedRequest is a frozen snapshot of an outgoing HTTP request.
type FakeOTLP ¶
type FakeOTLP struct {
// contains filtered or unexported fields
}
FakeOTLP is a single gRPC server that implements the trace, metric, and log Export RPCs and captures every received payload (and each request's gRPC metadata). Cleanup is registered on the *testing.T automatically.
func NewFakeOTLP ¶
NewFakeOTLP binds the receiver to 127.0.0.1 on a random port and starts serving plaintext gRPC. The server is stopped automatically when the test ends.
func NewFakeOTLPTLS ¶
NewFakeOTLPTLS is the TLS variant: mints an ephemeral self-signed cert (SAN 127.0.0.1) and exposes it as a PEM file via CertFile(t).
func (*FakeOTLP) Addr ¶
Addr returns the listener address (e.g. "127.0.0.1:42891") suitable for OTEL_EXPORTER_OTLP_ENDPOINT (prefix with http:// for plaintext or https:// for the TLS variant), typically set via t.Setenv before InitProvider.
func (*FakeOTLP) CertFile ¶
CertFile writes the receiver's self-signed certificate to a temp PEM file and returns its path, suitable for OTEL_EXPORTER_OTLP_CERTIFICATE. Only valid for a server constructed via NewFakeOTLPTLS.
func (*FakeOTLP) LastLogHeaders ¶
LastLogHeaders returns a copy of the gRPC metadata captured from the most recent log Export RPC, or nil if none.
func (*FakeOTLP) LastMetricHeaders ¶
LastMetricHeaders returns a copy of the gRPC metadata captured from the most recent metric Export RPC, or nil if none.
func (*FakeOTLP) LastTraceHeaders ¶
LastTraceHeaders returns a copy of the gRPC metadata captured from the most recent trace Export RPC, or nil if none.
func (*FakeOTLP) LogCount ¶
LogCount returns the total number of log records received across all RPCs.
func (*FakeOTLP) LogCountAtLevel ¶
LogCountAtLevel returns the count of log records whose severity number is >= minSeverity. OTel severity numbers: DEBUG=5, INFO=9, WARN=13, ERROR=17.
func (*FakeOTLP) MetricCount ¶
MetricCount returns the total number of metric records received, flattened across resource and scope groupings.
type MockCache ¶
type MockCache struct {
cache.Cache // Embed to satisfy remaining interface methods silently
InvNamespaces []cache.Namespace
// InvTenants records every InvalidateTenant, in order.
InvTenants []tenant.ID
InvErr error
// contains filtered or unexported fields
}
MockCache implements cache.Cache and records invalidated namespaces for testing.
func (*MockCache) GetNamespaces ¶
func (*MockCache) GetTenants ¶ added in v0.2.0
GetTenants returns the tenants InvalidateTenant was called for, in order.
func (*MockCache) Invalidate ¶
type MockDeadLetterStats ¶ added in v0.2.0
type MockDeadLetterStats struct {
Counts mq.DeadLetterCounts
Err error
Tenant tenant.ID
Table string
// contains filtered or unexported fields
}
MockDeadLetterStats implements mq.DeadLetterStats with a canned answer, recording the tenant and table each call asked about.
func (*MockDeadLetterStats) DeadLetterCounts ¶ added in v0.2.0
func (m *MockDeadLetterStats) DeadLetterCounts(_ context.Context, id tenant.ID, table string) (mq.DeadLetterCounts, error)
type MockDeduplicator ¶
type MockDeduplicator struct {
// Err, if set, fails Reserve — after ErrAfter calls have succeeded;
// CommitErr and ReleaseErr fail their phase.
Err error
ErrAfter int
CommitErr error
ReleaseErr error
Released []dedupe.Claim // every claim Release was given
// Calls to each phase, for tests that count round trips.
Reserves, Commits int
// contains filtered or unexported fields
}
MockDeduplicator implements dedupe.Deduplicator in memory, with per-phase error injection and a record of what was committed and released.
func NewMockDeduplicator ¶
func NewMockDeduplicator() *MockDeduplicator
func (*MockDeduplicator) Close ¶
func (m *MockDeduplicator) Close() error
func (*MockDeduplicator) Committed ¶ added in v0.2.0
func (m *MockDeduplicator) Committed(k dedupe.Key) bool
Committed reports whether k was committed.
func (*MockDeduplicator) Hold ¶ added in v0.2.0
func (m *MockDeduplicator) Hold(k dedupe.Key)
Hold claims k as another in-flight request would, so Reserve answers InFlight for it.
func (*MockDeduplicator) Pending ¶ added in v0.2.0
func (m *MockDeduplicator) Pending(k dedupe.Key) bool
Pending reports whether k is claimed and neither committed nor released.
type MockMessage ¶ added in v0.2.0
type MockMessage struct {
MsgData []byte
MsgTopic mq.Topic
// Configurable errors returned from the matching ack-family methods.
AckErr error
NakErr error
DoubleAckErr error
// State flags — atomic because production calls these from goroutines.
Acked atomic.Bool
Naked atomic.Bool
DoubleAcked atomic.Bool
// NakDelay is the delay of the last NakWithDelay (which also sets Naked).
NakDelay atomic.Int64
}
MockMessage builds mq.Messages whose ack-family callbacks flip the flags below, so a test can assert what the worker did with a message.
func (*MockMessage) Message ¶ added in v0.2.0
func (m *MockMessage) Message() *mq.Message
Message returns an mq.Message wired to this mock's flags. Every call returns a new Message sharing the same flags.
type MockPublisher ¶
type MockPublisher struct {
Messages []PublishedMessage
Err error // if set, Publish and DeadLetter return this error
// ErrAfter lets that many calls succeed before Err applies, to fail a
// batch part-way through.
ErrAfter int
// contains filtered or unexported fields
}
MockPublisher records all published messages — ingest publishes and dead-letter parkings alike — for test assertions.
func (*MockPublisher) Close ¶
func (m *MockPublisher) Close() error
func (*MockPublisher) DeadLetter ¶ added in v0.2.0
func (m *MockPublisher) DeadLetter(_ context.Context, msg *mq.Message, opts ...mq.PublishOpt) error
func (*MockPublisher) LastMessage ¶
func (m *MockPublisher) LastMessage() *PublishedMessage
LastMessage returns the most recently published message, or nil.
func (*MockPublisher) Publish ¶
func (m *MockPublisher) Publish(_ context.Context, topic mq.Topic, data []byte, opts ...mq.PublishOpt) error
func (*MockPublisher) Published ¶ added in v0.2.0
func (m *MockPublisher) Published() []PublishedMessage
Published returns a snapshot of the publish calls recorded so far.
type MockPurger ¶ added in v0.2.0
type MockPurger struct {
Purged bool // what PurgeAcked reports
Err error // if set, PurgeAcked returns this error
Calls []PurgeCall
// contains filtered or unexported fields
}
MockPurger implements mq.Purger and records every call.
type MockRoundTripper ¶
type MockRoundTripper struct {
Fn func(req *http.Request) (*http.Response, error)
// contains filtered or unexported fields
}
MockRoundTripper intercepts HTTP requests for the components that take a custom *http.Client (today: the ClickHouse inserter). Fn lets a test hand-roll a response; if Fn is nil, returns 200 OK.
Every request is captured (with body slurped + replaced so the handler can still read it) — useful for asserting request shape after the fact.
func (*MockRoundTripper) Captured ¶
func (m *MockRoundTripper) Captured() []CapturedRequest
func (*MockRoundTripper) Hits ¶
func (m *MockRoundTripper) Hits() int32
type MockSubscriber ¶
MockSubscriber implements mq.Subscriber for testing.
func (*MockSubscriber) Close ¶
func (m *MockSubscriber) Close() error
type PublishedMessage ¶
type PublishedMessage struct {
Topic mq.Topic
DeadLetter bool // parked via DeadLetter rather than published via Publish
Data []byte
Headers mq.Headers
}
PublishedMessage records a single Publish or DeadLetter call, with the headers the options set.
type RoleCase ¶
type RoleCase struct {
Name string
AllowedRoles []string
Role string // role placed in context when SetRole is true
SetRole bool // false → no role in context at all (e.g. no token / invalid token)
// WantForbidden is true when the request must be rejected with 403.
WantForbidden bool
}
RoleCase is one row of a per-resource role-authorization matrix: a resource restricted to AllowedRoles, observed under a given request role.
func StandardRoleMatrix ¶
func StandardRoleMatrix() []RoleCase
StandardRoleMatrix is the canonical set of (AllowedRoles, observed-role) cases every AllowedRoles-style gate must cover. Authorization is allowlist membership: the observed role must appear in AllowedRoles, the admin role (policy.AdminRole — "admin" by default, which is what these cases assume) always passes, and an empty/absent role matches nothing — so a resource with no AllowedRoles authorizes nobody but admin. The empty/absent-role rows are the ones that fail open when a gate only enforces its allowlist for a non-empty role — the class of bug behind #159. Keeping them in one shared place means a handler that takes AllowedRoles without running this matrix looks obviously under-tested in review.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package cachetest is the conformance suite every cache.Cache backend runs: what a hit, a miss and an invalidation mean, independent of where the entries and versions live.
|
Package cachetest is the conformance suite every cache.Cache backend runs: what a hit, a miss and an invalidation mean, independent of where the entries and versions live. |
|
Package logtest points slog's default logger somewhere a test can use.
|
Package logtest points slog's default logger somewhere a test can use. |
|
Package mutationtest holds the statements api.IsMutation is tested on, shared by its unit test and by the integration test that checks each one against ClickHouse's own parser.
|
Package mutationtest holds the statements api.IsMutation is tested on, shared by its unit test and by the integration test that checks each one against ClickHouse's own parser. |
|
Package storedir gives a test a directory for the embedded message broker's store.
|
Package storedir gives a test a directory for the embedded message broker's store. |