Documentation
¶
Overview ¶
Package celeritytest runs an application's handlers in a test, without a runtime, a network or a cloud account.
A handler is dispatched through the application's own pipeline, so the layers, guards and context a deployment would apply are the ones a test exercises:
func TestCreatingAnOrder(t *testing.T) {
res := celeritytest.Resources()
harness := celeritytest.New(t, orders.App(celerity.WithResourceProvider(res)))
harness.POST(t, "/orders", celeritytest.JSONBody(order{Total: 10})).
AssertStatus(t, http.StatusCreated)
}
The application is built by the application's own constructor, not rebuilt by the test.
There are two kinds of dependency and they are substituted in two different places. Firstly, what the application itself depends on, a payment gateway or a clock, arrives through the constructor the application wrote, as an argument or an option of its own. Secondly, what Celerity provides, the resources a blueprint declares, is substituted here: one celerity.WithResourceProvider redirects all of them at once, and nothing else about the application changes.
Every dispatch takes the test it belongs to rather than the one the application was built with, so a harness built once in a parent test is used from subtests that each have their own.
Resources ¶
A handler reaching a bucket, a queue, a topic or a data store is given a working one held in memory, so a write followed by a read answers with what was written. A cache is a recording stub, since reimplementing a surface that large for in-memory testing is a large undertaking and you can get all the information you need from call assertions on a stub.
A SQL database has neither. What a handler has to be right about is the statement it sends, and only an engine can judge that, so a database a test did not supply refuses the call and says where to get one. Provider.WithDatabase takes the engine a suite brought up, which in a development session is the one celerity dev already runs.
Live resources ¶
Live is for the integration cases, resources served by what a `celerity dev test` session brought up, reached through the provider the build linked rather than through anything here. A handler then runs against a real engine, a real object store and a real cache, while still being dispatched in process through the application's own pipeline, which is a smaller and faster test than one driving the runtime container.
The two mix per resource. Asking for a double by name before the application takes its handles substitutes it for that one resource, so a suite can exercise a real database and keep the queue in memory.
Index ¶
- type App
- func (a *App) Consume(t testing.TB, name string, bodies ...[]byte) *handler.BatchResult
- func (a *App) ConsumeJSON(t testing.TB, name string, messages ...any) *handler.BatchResult
- func (a *App) DELETE(t testing.TB, path string, opts ...RequestOption) *Response
- func (a *App) Do(t testing.TB, method, path string, opts ...RequestOption) *Response
- func (a *App) GET(t testing.TB, path string, opts ...RequestOption) *Response
- func (a *App) Invoke(t testing.TB, name string, input any, out any)
- func (a *App) PATCH(t testing.TB, path string, opts ...RequestOption) *Response
- func (a *App) POST(t testing.TB, path string, opts ...RequestOption) *Response
- func (a *App) PUT(t testing.TB, path string, opts ...RequestOption) *Response
- func (a *App) Resources() *Provider
- func (a *App) Schedule(t testing.TB, name string, input ...[]byte)
- func (a *App) Send(t testing.TB, route string, body []byte, opts ...WebSocketMessage)
- type Bucket
- func (b *Bucket) Contents(key string) ([]byte, bool)
- func (b *Bucket) Copy(ctx context.Context, sourceKey string, dest bucket.Destination, ...) (bucket.CopyResult, error)
- func (b *Bucket) Delete(ctx context.Context, key string, opts ...bucket.DeleteOption) error
- func (b *Bucket) DeleteMany(ctx context.Context, refs []bucket.ObjectRef) (bucket.DeleteManyResult, error)
- func (b *Bucket) Exists(ctx context.Context, key string, opts ...bucket.ExistsOption) (bool, error)
- func (b *Bucket) Get(ctx context.Context, key string, opts ...bucket.GetOption) (*bucket.Object, error)
- func (b *Bucket) Info(ctx context.Context, key string, opts ...bucket.InfoOption) (bucket.ObjectInfo, error)
- func (b *Bucket) Keys() []string
- func (b *Bucket) Len() int
- func (b *Bucket) List(ctx context.Context, prefix string, opts ...bucket.ListOption) ([]bucket.ObjectInfo, bucket.Cursor, error)
- func (b *Bucket) Put(ctx context.Context, key string, body io.Reader, opts ...bucket.PutOption) (bucket.PutResult, error)
- func (b *Bucket) SignedURL(ctx context.Context, key string, action bucket.SignAction, ...) (bucket.SignedURL, error)
- func (b *Bucket) Versions(ctx context.Context, prefix string, opts ...bucket.ListOption) ([]bucket.ObjectVersion, bucket.Cursor, error)
- type CacheStub
- func (s *CacheStub) Append(ctx context.Context, key string, value string) (int64, error)
- func (s *CacheStub) Called(name string) bool
- func (s *CacheStub) Calls() []string
- func (s *CacheStub) Connection(ctx context.Context) (cache.Connection, error)
- func (s *CacheStub) Decrement(ctx context.Context, key string, delta int64) (int64, error)
- func (s *CacheStub) Delete(ctx context.Context, key string) (bool, error)
- func (s *CacheStub) DeleteMany(ctx context.Context, keys []string) (int64, error)
- func (s *CacheStub) Exists(ctx context.Context, key string) (bool, error)
- func (s *CacheStub) Expire(ctx context.Context, key string, ttl time.Duration) (bool, error)
- func (s *CacheStub) Get(ctx context.Context, key string) (string, error)
- func (s *CacheStub) GetMany(ctx context.Context, keys []string) (map[string]string, error)
- func (s *CacheStub) GetSet(ctx context.Context, key string, value string) (string, error)
- func (s *CacheStub) HashDelete(ctx context.Context, key string, fields []string) (int64, error)
- func (s *CacheStub) HashExists(ctx context.Context, key string, field string) (bool, error)
- func (s *CacheStub) HashGet(ctx context.Context, key string, field string) (string, error)
- func (s *CacheStub) HashGetAll(ctx context.Context, key string) (map[string]string, error)
- func (s *CacheStub) HashIncrement(ctx context.Context, key string, field string, delta int64) (int64, error)
- func (s *CacheStub) HashKeys(ctx context.Context, key string) ([]string, error)
- func (s *CacheStub) HashLen(ctx context.Context, key string) (int64, error)
- func (s *CacheStub) HashSet(ctx context.Context, key string, fields map[string]string) error
- func (s *CacheStub) Increment(ctx context.Context, key string, delta int64) (int64, error)
- func (s *CacheStub) IncrementFloat(ctx context.Context, key string, delta float64) (float64, error)
- func (s *CacheStub) ListIndex(ctx context.Context, key string, index int64) (string, error)
- func (s *CacheStub) ListLen(ctx context.Context, key string) (int64, error)
- func (s *CacheStub) ListPop(ctx context.Context, key string, count int64, opts ...cache.EndOption) ([]string, error)
- func (s *CacheStub) ListPush(ctx context.Context, key string, values []string, opts ...cache.EndOption) (int64, error)
- func (s *CacheStub) ListRange(ctx context.Context, key string, start int64, stop int64) ([]string, error)
- func (s *CacheStub) ListTrim(ctx context.Context, key string, start int64, stop int64) error
- func (s *CacheStub) Persist(ctx context.Context, key string) (bool, error)
- func (s *CacheStub) Rename(ctx context.Context, key string, newKey string) error
- func (s *CacheStub) Scan(ctx context.Context, opts ...cache.ScanOption) iter.Seq2[string, error]
- func (s *CacheStub) Set(ctx context.Context, key string, value string, opts ...cache.SetOption) (bool, error)
- func (s *CacheStub) SetAdd(ctx context.Context, key string, members []string) (int64, error)
- func (s *CacheStub) SetDiff(ctx context.Context, keys []string) ([]string, error)
- func (s *CacheStub) SetIntersect(ctx context.Context, keys []string) ([]string, error)
- func (s *CacheStub) SetIsMember(ctx context.Context, key string, member string) (bool, error)
- func (s *CacheStub) SetLen(ctx context.Context, key string) (int64, error)
- func (s *CacheStub) SetMany(ctx context.Context, values map[string]string) error
- func (s *CacheStub) SetMembers(ctx context.Context, key string) ([]string, error)
- func (s *CacheStub) SetRemove(ctx context.Context, key string, members []string) (int64, error)
- func (s *CacheStub) SetUnion(ctx context.Context, keys []string) ([]string, error)
- func (s *CacheStub) SortedSetAdd(ctx context.Context, key string, members []cache.SortedSetMember) (int64, error)
- func (s *CacheStub) SortedSetCountByScore(ctx context.Context, key string, scores cache.ScoreRange) (int64, error)
- func (s *CacheStub) SortedSetIncrement(ctx context.Context, key string, member string, delta float64) (float64, error)
- func (s *CacheStub) SortedSetLen(ctx context.Context, key string) (int64, error)
- func (s *CacheStub) SortedSetRange(ctx context.Context, key string, start int64, stop int64, ...) ([]cache.SortedSetMember, error)
- func (s *CacheStub) SortedSetRangeByScore(ctx context.Context, key string, scores cache.ScoreRange, ...) ([]cache.SortedSetMember, error)
- func (s *CacheStub) SortedSetRank(ctx context.Context, key string, member string, opts ...cache.RangeOption) (int64, error)
- func (s *CacheStub) SortedSetRemove(ctx context.Context, key string, members []string) (int64, error)
- func (s *CacheStub) SortedSetRemoveByRank(ctx context.Context, key string, start int64, stop int64) (int64, error)
- func (s *CacheStub) SortedSetRemoveByScore(ctx context.Context, key string, scores cache.ScoreRange) (int64, error)
- func (s *CacheStub) SortedSetScore(ctx context.Context, key string, member string) (float64, error)
- func (s *CacheStub) TTL(ctx context.Context, key string) (time.Duration, bool, error)
- func (s *CacheStub) Transaction(ctx context.Context, queue func(cache.Tx)) ([]any, error)
- func (s *CacheStub) Type(ctx context.Context, key string) (cache.KeyType, error)
- type Datastore
- func (d *Datastore) Atomically(ctx context.Context, partition string, ops []datastore.AtomicOp) error
- func (d *Datastore) BatchGet(ctx context.Context, keys []datastore.Key, out any) ([]datastore.Key, error)
- func (d *Datastore) BatchWrite(ctx context.Context, ops []datastore.BatchOp) ([]datastore.BatchOp, error)
- func (d *Datastore) Delete(ctx context.Context, key datastore.Key, opts ...datastore.WriteOption) error
- func (d *Datastore) Get(ctx context.Context, key datastore.Key, out any) (datastore.Revision, error)
- func (d *Datastore) Len() int
- func (d *Datastore) Put(ctx context.Context, key datastore.Key, item any, ...) (datastore.Revision, error)
- func (d *Datastore) Query(ctx context.Context, q datastore.Query, out any) (datastore.Cursor, error)
- func (d *Datastore) Scan(ctx context.Context, s datastore.Scan, out any) (datastore.Cursor, error)
- func (d *Datastore) Seed(key datastore.Key, item any) error
- func (d *Datastore) Update(ctx context.Context, key datastore.Key, updates []datastore.Update, ...) (datastore.Revision, error)
- type Option
- type Provider
- func (p *Provider) Bucket(ref resources.Ref) (bucket.Store, error)
- func (p *Provider) BucketNamed(name string) *Bucket
- func (p *Provider) Cache(ref resources.Ref) (cache.Client, error)
- func (p *Provider) CacheNamed(name string) *CacheStub
- func (p *Provider) Close(ctx context.Context) error
- func (p *Provider) Datastore(ref resources.Ref) (datastore.Client, error)
- func (p *Provider) DatastoreNamed(name string) *Datastore
- func (p *Provider) Name() string
- func (p *Provider) Queue(ref resources.Ref) (queue.Client, error)
- func (p *Provider) QueueNamed(name string) *Queue
- func (p *Provider) SQLDatabase(ref resources.Ref) (sqldb.Client, error)
- func (p *Provider) Topic(ref resources.Ref) (topic.Client, error)
- func (p *Provider) TopicNamed(name string) *Topic
- func (p *Provider) WithDatabase(name string, client sqldb.Client) *Provider
- type PublishedMessage
- type Queue
- type RecordedSpan
- type RequestOption
- type Response
- type SentMessage
- type Topic
- func (t *Topic) Len() int
- func (t *Topic) Publish(ctx context.Context, body []byte, opts ...topic.SendOption) (string, error)
- func (t *Topic) PublishBatch(ctx context.Context, entries []topic.BatchEntry) (topic.BatchResult, error)
- func (t *Topic) Published() []PublishedMessage
- func (t *Topic) Reset()
- type Tracer
- func (r *Tracer) AssertSpan(t testing.TB, name string) RecordedSpan
- func (r *Tracer) Names() []string
- func (r *Tracer) Reset()
- func (r *Tracer) Span(name string) (RecordedSpan, bool)
- func (r *Tracer) Spans() []RecordedSpan
- func (r *Tracer) Start(ctx context.Context, name string, attrs ...telemetry.Attr) (context.Context, telemetry.Span)
- type WebSocketMessage
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type App ¶
type App struct {
// contains filtered or unexported fields
}
App dispatches events to an application's handlers.
func New ¶
New returns an application ready to dispatch to, failing the test where registration did not succeed.
Registration errors are collected rather than returned, so an application that named a resource no provider can build, or registered two handlers under one tag, reports it here rather than on the first dispatch.
func NewWithResources ¶
NewWithResources returns an application whose handlers reach the test doubles in p, and the doubles themselves for a test to read afterwards.
The provider has to be given to celerity.New as well, since handles are taken while the application is being built:
res := celeritytest.Resources()
harness := celeritytest.NewWithResources(
t, orders.App(celerity.WithResourceProvider(res)), res)
func (*App) Consume ¶
Consume delivers a batch of messages to the consumer handler registered under a blueprint name, and reports which records it asked to be delivered again.
The bodies are delivered as one batch, which is how a consumer is called: a handler that fails one record of several is the case worth testing, and it cannot be reached a message at a time.
func (*App) ConsumeJSON ¶
ConsumeJSON delivers a batch whose bodies are the encodings of the values given, which is how a message is usually carried.
func (*App) Do ¶
Do dispatches a request of any method to the handler registered for the path.
The path is the one a client would send rather than the route a handler was registered under, so the parameters in it are bound the way a runtime binds them. A path no handler is registered for fails the test, since a request that reached nothing is a mistake in the test rather than a 404 worth asserting on.
func (*App) Invoke ¶
Invoke calls a handler directly by its blueprint name and decodes its answer into out, which may be nil where the answer is not wanted.
func (*App) Resources ¶
Resources returns the test doubles an application reaches through celerity.WithResourceProvider.
type Bucket ¶
type Bucket struct {
// contains filtered or unexported fields
}
Bucket is an object store held in memory.
Versioning is always on, which is the shape that can answer both kinds of test. A bucket without it is one whose keys each hold a single version, and the reads a handler makes are the same either way.
func (*Bucket) Contents ¶
Contents returns what a key currently holds, and whether it holds anything.
func (*Bucket) Copy ¶
func (b *Bucket) Copy( ctx context.Context, sourceKey string, dest bucket.Destination, opts ...bucket.CopyOption, ) (bucket.CopyResult, error)
func (*Bucket) DeleteMany ¶
func (*Bucket) Info ¶
func (b *Bucket) Info( ctx context.Context, key string, opts ...bucket.InfoOption, ) (bucket.ObjectInfo, error)
func (*Bucket) Len ¶
Len reports how many keys hold a current object, for a test asserting on what a handler left behind.
type CacheStub ¶
type CacheStub struct {
// AppendFunc answers a call to Append.
AppendFunc func(ctx context.Context, key string, value string) (int64, error)
// ConnectionFunc answers a call to Connection.
ConnectionFunc func(ctx context.Context) (cache.Connection, error)
// DecrementFunc answers a call to Decrement.
DecrementFunc func(ctx context.Context, key string, delta int64) (int64, error)
// DeleteFunc answers a call to Delete.
DeleteFunc func(ctx context.Context, key string) (bool, error)
// DeleteManyFunc answers a call to DeleteMany.
DeleteManyFunc func(ctx context.Context, keys []string) (int64, error)
// ExistsFunc answers a call to Exists.
ExistsFunc func(ctx context.Context, key string) (bool, error)
// ExpireFunc answers a call to Expire.
ExpireFunc func(ctx context.Context, key string, ttl time.Duration) (bool, error)
// GetFunc answers a call to Get.
GetFunc func(ctx context.Context, key string) (string, error)
// GetManyFunc answers a call to GetMany.
GetManyFunc func(ctx context.Context, keys []string) (map[string]string, error)
// GetSetFunc answers a call to GetSet.
GetSetFunc func(ctx context.Context, key string, value string) (string, error)
// HashDeleteFunc answers a call to HashDelete.
HashDeleteFunc func(ctx context.Context, key string, fields []string) (int64, error)
// HashExistsFunc answers a call to HashExists.
HashExistsFunc func(ctx context.Context, key string, field string) (bool, error)
// HashGetFunc answers a call to HashGet.
HashGetFunc func(ctx context.Context, key string, field string) (string, error)
// HashGetAllFunc answers a call to HashGetAll.
HashGetAllFunc func(ctx context.Context, key string) (map[string]string, error)
// HashIncrementFunc answers a call to HashIncrement.
HashIncrementFunc func(ctx context.Context, key string, field string, delta int64) (int64, error)
// HashKeysFunc answers a call to HashKeys.
HashKeysFunc func(ctx context.Context, key string) ([]string, error)
// HashLenFunc answers a call to HashLen.
HashLenFunc func(ctx context.Context, key string) (int64, error)
// HashSetFunc answers a call to HashSet.
HashSetFunc func(ctx context.Context, key string, fields map[string]string) error
// IncrementFunc answers a call to Increment.
IncrementFunc func(ctx context.Context, key string, delta int64) (int64, error)
// IncrementFloatFunc answers a call to IncrementFloat.
IncrementFloatFunc func(ctx context.Context, key string, delta float64) (float64, error)
// ListIndexFunc answers a call to ListIndex.
ListIndexFunc func(ctx context.Context, key string, index int64) (string, error)
// ListLenFunc answers a call to ListLen.
ListLenFunc func(ctx context.Context, key string) (int64, error)
// ListPopFunc answers a call to ListPop.
ListPopFunc func(ctx context.Context, key string, count int64, opts ...cache.EndOption) ([]string, error)
// ListPushFunc answers a call to ListPush.
ListPushFunc func(ctx context.Context, key string, values []string, opts ...cache.EndOption) (int64, error)
// ListRangeFunc answers a call to ListRange.
ListRangeFunc func(ctx context.Context, key string, start int64, stop int64) ([]string, error)
// ListTrimFunc answers a call to ListTrim.
ListTrimFunc func(ctx context.Context, key string, start int64, stop int64) error
// PersistFunc answers a call to Persist.
PersistFunc func(ctx context.Context, key string) (bool, error)
// RenameFunc answers a call to Rename.
RenameFunc func(ctx context.Context, key string, newKey string) error
// ScanFunc answers a call to Scan.
ScanFunc func(ctx context.Context, opts ...cache.ScanOption) iter.Seq2[string, error]
// SetFunc answers a call to Set.
SetFunc func(ctx context.Context, key string, value string, opts ...cache.SetOption) (bool, error)
// SetAddFunc answers a call to SetAdd.
SetAddFunc func(ctx context.Context, key string, members []string) (int64, error)
// SetDiffFunc answers a call to SetDiff.
SetDiffFunc func(ctx context.Context, keys []string) ([]string, error)
// SetIntersectFunc answers a call to SetIntersect.
SetIntersectFunc func(ctx context.Context, keys []string) ([]string, error)
// SetIsMemberFunc answers a call to SetIsMember.
SetIsMemberFunc func(ctx context.Context, key string, member string) (bool, error)
// SetLenFunc answers a call to SetLen.
SetLenFunc func(ctx context.Context, key string) (int64, error)
// SetManyFunc answers a call to SetMany.
SetManyFunc func(ctx context.Context, values map[string]string) error
// SetMembersFunc answers a call to SetMembers.
SetMembersFunc func(ctx context.Context, key string) ([]string, error)
// SetRemoveFunc answers a call to SetRemove.
SetRemoveFunc func(ctx context.Context, key string, members []string) (int64, error)
// SetUnionFunc answers a call to SetUnion.
SetUnionFunc func(ctx context.Context, keys []string) ([]string, error)
// SortedSetAddFunc answers a call to SortedSetAdd.
SortedSetAddFunc func(ctx context.Context, key string, members []cache.SortedSetMember) (int64, error)
// SortedSetCountByScoreFunc answers a call to SortedSetCountByScore.
SortedSetCountByScoreFunc func(ctx context.Context, key string, scores cache.ScoreRange) (int64, error)
// SortedSetIncrementFunc answers a call to SortedSetIncrement.
SortedSetIncrementFunc func(ctx context.Context, key string, member string, delta float64) (float64, error)
// SortedSetLenFunc answers a call to SortedSetLen.
SortedSetLenFunc func(ctx context.Context, key string) (int64, error)
// SortedSetRangeFunc answers a call to SortedSetRange.
SortedSetRangeFunc func(ctx context.Context, key string, start int64, stop int64, opts ...cache.RangeOption) ([]cache.SortedSetMember, error)
// SortedSetRangeByScoreFunc answers a call to SortedSetRangeByScore.
SortedSetRangeByScoreFunc func(ctx context.Context, key string, scores cache.ScoreRange, opts ...cache.RangeOption) ([]cache.SortedSetMember, error)
// SortedSetRankFunc answers a call to SortedSetRank.
SortedSetRankFunc func(ctx context.Context, key string, member string, opts ...cache.RangeOption) (int64, error)
// SortedSetRemoveFunc answers a call to SortedSetRemove.
SortedSetRemoveFunc func(ctx context.Context, key string, members []string) (int64, error)
// SortedSetRemoveByRankFunc answers a call to SortedSetRemoveByRank.
SortedSetRemoveByRankFunc func(ctx context.Context, key string, start int64, stop int64) (int64, error)
// SortedSetRemoveByScoreFunc answers a call to SortedSetRemoveByScore.
SortedSetRemoveByScoreFunc func(ctx context.Context, key string, scores cache.ScoreRange) (int64, error)
// SortedSetScoreFunc answers a call to SortedSetScore.
SortedSetScoreFunc func(ctx context.Context, key string, member string) (float64, error)
// TTLFunc answers a call to TTL.
TTLFunc func(ctx context.Context, key string) (time.Duration, bool, error)
// TransactionFunc answers a call to Transaction.
TransactionFunc func(ctx context.Context, queue func(cache.Tx)) ([]any, error)
// TypeFunc answers a call to Type.
TypeFunc func(ctx context.Context, key string) (cache.KeyType, error)
// contains filtered or unexported fields
}
CacheStub records what a handler asked a cache for and answers what the test arranged.
This is a stub rather than a fully functional cache. The contract is fifty-three operations across seven groups, and reimplementing sorted sets, hashes and expiry faithfully enough to be worth trusting is a large undertaking for a test utility. A test that needs real behaviour is better pointed at a real Redis, which resources/redis already reaches.
A call the test did not arrange answers with an error naming it, rather than a zero value that turns up as a puzzle somewhere else.
func NewCacheStub ¶
NewCacheStub returns a stub answering nothing until a test arranges it.
func (*CacheStub) Connection ¶
func (*CacheStub) DeleteMany ¶
func (*CacheStub) HashDelete ¶
func (*CacheStub) HashExists ¶
func (*CacheStub) HashGetAll ¶
func (*CacheStub) HashIncrement ¶
func (*CacheStub) IncrementFloat ¶
func (*CacheStub) SetIntersect ¶
func (*CacheStub) SetIsMember ¶
func (*CacheStub) SetMembers ¶
func (*CacheStub) SortedSetAdd ¶
func (*CacheStub) SortedSetCountByScore ¶
func (*CacheStub) SortedSetIncrement ¶
func (*CacheStub) SortedSetLen ¶
func (*CacheStub) SortedSetRange ¶
func (*CacheStub) SortedSetRangeByScore ¶
func (s *CacheStub) SortedSetRangeByScore(ctx context.Context, key string, scores cache.ScoreRange, opts ...cache.RangeOption) ([]cache.SortedSetMember, error)
func (*CacheStub) SortedSetRank ¶
func (*CacheStub) SortedSetRemove ¶
func (*CacheStub) SortedSetRemoveByRank ¶
func (*CacheStub) SortedSetRemoveByScore ¶
func (*CacheStub) SortedSetScore ¶
func (*CacheStub) Transaction ¶
type Datastore ¶
type Datastore struct {
// contains filtered or unexported fields
}
Datastore is a document store held in memory.
Items are kept as the JSON they marshal to, which is how a real store keeps them as far as a handler is concerned: what goes in comes back out through the same encoding, so a field a type does not declare is not invented here and one it declares badly fails here too.
func NewDatastore ¶
NewDatastore returns an empty data store.
func (*Datastore) Atomically ¶
func (*Datastore) BatchWrite ¶
type Option ¶
type Option func(*App)
Option configures a test application.
func WithDeadline ¶
WithDeadline sets how long after an event's timestamp its deadline falls.
The deadline is applied to the handler's context the way a runtime applies one, so a handler that checks for cancellation can be tested against it.
func WithTraceContext ¶
WithTraceContext gives every dispatch the W3C trace context an event would have arrived with.
type Provider ¶
type Provider struct {
// contains filtered or unexported fields
}
Provider hands an application test doubles in place of deployed resources.
It is given to the application rather than registered, since a test says what its handlers reach rather than inferring it from an environment:
res := celeritytest.Resources() app := orders.App(celerity.WithResourceProvider(res))
A resource is made the first time a handle is taken for it and kept under that name, so the handle a handler closed over and the one a test reads afterwards are the same object.
func Live ¶
Live returns resources backed by the services a `celerity dev test` session brought up, rather than by doubles.
What serves them is the provider the test's build linked, found the way a deployed application finds it, so a handler under test resolves its resources through the same code a real session runs. Which means the test's build has to link them, as a session does:
import ( _ "github.com/newstack-cloud/celerity-go-sdk/resources/aws" _ "github.com/newstack-cloud/celerity-go-sdk/resources/local" _ "github.com/newstack-cloud/celerity-go-sdk/resources/local/redis" )
A double can still be substituted per resource, by asking for one before the application takes its handles: Provider.QueueNamed and the rest make a double under that name and the live provider is not asked for it. So a suite can exercise a real database and keep the queue in memory.
Releases what the provider holds when the test ends, since a live provider holds pools and sockets rather than maps.
func LiveWith ¶
LiveWith is Live against a provider named explicitly rather than the one the build linked.
For a suite that builds its own provider, and for testing the harness itself.
func (*Provider) Bucket ¶
Bucket returns the bucket an application took a handle for, making it on first use.
func (*Provider) BucketNamed ¶
BucketNamed returns the bucket a blueprint calls name, making it where no handle has been taken for it yet so that a test can arrange its contents before the application reads them.
Under live resources this is also how a double is substituted for one of them, and has to be called before the application takes its handles.
func (*Provider) CacheNamed ¶
CacheNamed returns the cache stub a blueprint calls name.
func (*Provider) Close ¶
Close gives back what the resources hold.
Nothing, where they are doubles as they are maps in this process. Present even then so that a test exercising shutdown finds the same resources.Closer a real provider offers. Under live resources it releases the live provider's pools, once however many times it is called.
func (*Provider) DatastoreNamed ¶
DatastoreNamed returns the data store a blueprint calls name.
func (*Provider) QueueNamed ¶
QueueNamed returns the queue a blueprint calls name.
func (*Provider) SQLDatabase ¶
SQLDatabase returns the database a test supplied with Provider.WithDatabase, and otherwise one that refuses every call and says why.
func (*Provider) TopicNamed ¶
TopicNamed returns the topic a blueprint calls name.
func (*Provider) WithDatabase ¶
WithDatabase hands the test's own database to the handlers that take a handle for the blueprint resource called name.
There is no in-memory database to arrange, so this takes a real one: the engine a development session brings up, or whatever a suite starts for itself. A name no test supplied is refused rather than recorded, so a handler that reaches a database the suite did not arrange says so.
type PublishedMessage ¶
type PublishedMessage struct {
// ID is what the topic called it, which is what the publish returned.
ID string
Body []byte
// Options are what the publish asked for, resolved.
Options topic.SendOptions
}
PublishedMessage is one message a handler published to a Topic.
func (PublishedMessage) Text ¶
func (m PublishedMessage) Text() string
Text is the body as a string, for the ordinary case of asserting on one.
type Queue ¶
type Queue struct {
// Refuse fails the next send, for a test exercising what a handler does
// when a queue will not take a message. Cleared once it has fired.
Refuse error
// contains filtered or unexported fields
}
Queue is a point-to-point queue held in memory.
Nothing consumes from it: what a test asserts on is what a handler sent, so the messages are kept rather than delivered.
func (*Queue) Reset ¶
func (q *Queue) Reset()
Reset forgets what was sent, for a test reusing one queue across cases.
func (*Queue) SendBatch ¶
func (q *Queue) SendBatch( ctx context.Context, entries []queue.BatchEntry, ) (queue.BatchResult, error)
func (*Queue) Sent ¶
func (q *Queue) Sent() []SentMessage
Sent returns the messages a handler sent, in order.
type RecordedSpan ¶
type RecordedSpan struct {
Name string
// TraceID and SpanID are what a log written during the span carries, so a
// test can assert that a record and a span name the same trace.
TraceID string
SpanID string
// Attrs are what the span recorded, in the order they were added.
Attrs []telemetry.Attr
// Err is what failed inside the span, and nil where nothing did.
Err error
// Parent is the name of the span this one opened inside, and empty for the
// outermost. What a test usually wants to know about a resource span is
// that it sits under the dispatch.
Parent string
// Ended reports whether the span was closed, which every span has to be.
Ended bool
}
RecordedSpan is one span a handler produced.
type RequestOption ¶
type RequestOption func(*celerityhandler.Request)
RequestOption configures a request before it is dispatched.
func Body ¶
func Body(body []byte) RequestOption
Body sends raw bytes, leaving the content type to the caller.
func Header ¶
func Header(name, value string) RequestOption
Header adds a request header. Names are lowercased, as a handler sees them.
func JSONBody ¶
func JSONBody(v any) RequestOption
JSONBody encodes a value as the request body and declares it as JSON.
A value that cannot be encoded is sent as no body at all, which the handler will reject: a test that got here has a fault in what it is sending rather than in what it is testing, and App.Do reports it.
func SourceIP ¶
func SourceIP(ip string) RequestOption
SourceIP sets the address the request appears to come from, which a guard or a rate limiter may read.
type Response ¶
type Response struct {
// Result is the whole answer, for an assertion the helpers do not cover.
// Nil where the handler failed before producing one.
Result *celerityhandler.Result
// Handler is the blueprint name of the handler that answered, named in
// failures so that a test says which one disappointed it.
Handler string
// contains filtered or unexported fields
}
Response is what an HTTP handler answered.
func (*Response) AssertStatus ¶
AssertStatus fails the test unless the handler answered with want.
The body is reported on a mismatch, since the usual cause is an error the handler described there.
func (*Response) Decode ¶
Decode reads a JSON response body into out, failing the test where it is not the shape asked for.
func (*Response) Error ¶
Error is the failure the handler reported, and nil where it answered normally.
A handler that returns an error does not answer with a status: turning one into a response is the runtime's job rather than the handler's, so this is what a test asserting on a failure reads.
type SentMessage ¶
type SentMessage struct {
// ID is what the queue called it, which is what the send returned.
ID string
Body []byte
// Options are what the send asked for, resolved.
Options queue.SendOptions
}
SentMessage is one message a handler put on a Queue.
func (SentMessage) Text ¶
func (m SentMessage) Text() string
Text is the body as a string, for the ordinary case of asserting on one.
type Topic ¶
type Topic struct {
// Refuse fails the next publish, for a test exercising what a handler does
// when a topic will not take a message. Cleared once it has fired.
Refuse error
// contains filtered or unexported fields
}
Topic is publish-subscribe held in memory.
Nothing subscribes to it: what a test asserts on is what a handler published, so the messages are kept rather than delivered.
func (*Topic) PublishBatch ¶
func (t *Topic) PublishBatch( ctx context.Context, entries []topic.BatchEntry, ) (topic.BatchResult, error)
func (*Topic) Published ¶
func (t *Topic) Published() []PublishedMessage
Published returns the messages a handler published, in order.
type Tracer ¶
type Tracer struct {
// contains filtered or unexported fields
}
Tracer records the spans a handler produced rather than exporting them.
What it is for is telemetry the application wrote: a span a handler opened with telemetry.Traced, the attributes it set on it, the error it recorded. Those are the application's own behaviour, and a test asserting them is testing the handler.
tracer := celeritytest.NewTracer(t)
harness.POST(t, "/orders", celeritytest.JSONBody(order{ID: "o-1", Total: 10}))
span := tracer.AssertSpan(t, "orders.price_quote")
total, _ := span.Attr("order.total")
Asserting the SDK's own span names is not what it is for. Those belong to the SDK and are the same in every language Celerity supports, so a test naming one asserts this package's behaviour rather than the application's, and fails when a name here changes without the application having changed at all.
Only one recorder can be installed at a time, because the tracer seam is process-wide. See NewTracer.
func NewTracer ¶
NewTracer installs a recording tracer and removes it when the test ends.
Fails the test where a recorder is already installed. The tracer is installed process-wide rather than carried per dispatch, so two recorders at once means the second collects the first's spans as well as its own and both tests read something other than what they produced. A test calling this therefore cannot call t.Parallel.
func (*Tracer) AssertSpan ¶
func (r *Tracer) AssertSpan(t testing.TB, name string) RecordedSpan
AssertSpan returns the first span recorded under a name, failing the test where nothing was recorded under it.
What was recorded is reported on failure, since a span that is missing is usually one recorded under a different name rather than one never opened.
func (*Tracer) Names ¶
Names returns the names of the spans recorded, which is what most assertions are about.
func (*Tracer) Reset ¶
func (r *Tracer) Reset()
Reset forgets the spans recorded so far, for a test making more than one dispatch and asserting on each.
func (*Tracer) Span ¶
func (r *Tracer) Span(name string) (RecordedSpan, bool)
Span returns the first span recorded under a name, and whether there was one.
func (*Tracer) Spans ¶
func (r *Tracer) Spans() []RecordedSpan
Spans returns the spans recorded, in the order they were started.
type WebSocketMessage ¶
type WebSocketMessage func(*handler.WebSocketMessage)
WebSocketMessage configures a message before it is delivered.
func Binary ¶
func Binary() WebSocketMessage
Binary delivers the message as a binary frame rather than a text one.
func From ¶
func From(connectionID string) WebSocketMessage
From sets the connection a message arrives on, which is what a handler answers back to.