stest

package
v0.5.0 Latest Latest
Warning

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

Go to latest
Published: Nov 30, 2023 License: MIT Imports: 7 Imported by: 0

README

How tests work?

Capataz' supervision system was built with observability as a prime directive, given this, the library emit events for every goroutine that starts, stops, terminates and fails. To make sure this notification is accurate, all the test infrastructure is built around making sure that the events are triggered in the expected order when an specific set of children are used in a supervision system.

Each test is composed by a few components:

Child builders

The different known children builders are:

  • WaitDoneWorker
  • FailStartWorker
  • FailOnSignalWorker
  • NeverTerminateWorker

All of them always return a ChildSpec with some expected behavior. In some cases a child builder returns a second value that allows you to control when the child really "starts" executing. This is important because we want to build a scenario that we can assert without relying on time delays, and ensuring the behavior triggers when things are setup; this is a good approach to have reliable test-suite.

ObserveSupervisor call

Once we have defined a tree to test, we use the ObserveSupervisor function to start the supervisor tree. This function will collect in memory all the events that are emitted by the notification system embedded in the library.

Notes about the callback parameter

The function receives a callback that gets executed once the supervision tree is up; this is the best spot to start the controlled execution of children that you created in the supervision tree (start failing children, etc).

The first parameter of the callback is an EventManager record, this has an Iterator method that returns a record that has functions (WaitTill, TakeTill, etc.) that allow you to wait for certain events to happen (using an assertion predicate). This approach is necessary to avoid race-conditions, you want to make sure an event happens before triggering a new one concurrently.

Finally, this function returns two values, the events that got triggered, and if the supervisor failed with an error. You can then use the assertion functions (described below) to check that the events are the ones you expect.

Assertion functions

The different known assert functions are:

  • AssertExactMatch
  • AssertPartialMatch

These functions receive a list of predicate functions as parameters, they allow you to assert that the returned collected events match all the criteria that you are expecting.

Some of the predicate methods are:

  • SupervisorStarted
  • WorkerStarted
  • SupervisorStartFailed
  • WorkerStartFailed
  • SupervisorFailed
  • WorkerFailed
  • SupervisorTerminated
  • WorkerTerminated

Check the test implementation to see how they are used.

A note about event order in assertions

Some may find counter-intuitive the order of the collected events. The supervision tree start procedure will spawn goroutines for the leafs of the tree first. Every leave of the tree correspond to a Worker goroutine, and every branch corresponds to a supervisor that tracks errors on its children.

A supervisor is considered in a running state when all its children goroutines are spawned. You may be wondering, what happens to errors triggered by goroutines while a supervisor is bootstraping? Each worker goroutine will block trying to send an error to it's supervisor monitor; once the supervisor is in a running state, the errors are handled as expected.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var AssertExactMatch = smtest.AssertExactMatch[cap.Event]

AssertExactMatch is an assertion that checks the input slice of EventP predicate match 1 to 1 with a given list of supervision system events.

View Source
var NewEventManager = smtest.NewEventManager[cap.Event]

NewEventManager returns an EventManager instance that can be used to wait for events to happen on the observed supervision system

Functions

func CompleteOnSignalWorker

func CompleteOnSignalWorker(
	totalCompleteCount int32,
	name string,
	opts ...cap.WorkerOpt,
) (cap.Node, func())

CompleteOnSignalWorker creates a cap.Node that runs a goroutine that will complete at at as soon as the returned start signal is called.

func FailOnSignalDynSubtree

func FailOnSignalDynSubtree(
	totalErrCount int32,
	name string,
	spawnerOpts []cap.Opt,
	workerOpts []cap.WorkerOpt,
	children ...cap.Node,
) (cap.Node, func(bool))

FailOnSignalDynSubtree creates a cap.Node that runs a dynamic subtree that will fail at least the given number of times as soon as the returned start signal is called. Once this number of times has been reached, it waits until the given `context.Done` channel indicates a supervisor termination.

func FailOnSignalWorker

func FailOnSignalWorker(
	totalErrCount int32,
	name string,
	opts ...cap.WorkerOpt,
) (cap.Node, func(bool))

FailOnSignalWorker creates a cap.Node that runs a goroutine that will fail at least the given number of times as soon as the returned start signal is called. Once this number of times has been reached, it waits until the given `context.Done` channel indicates a supervisor termination.

func FailStartWorker

func FailStartWorker(name string) cap.Node

FailStartWorker creates a `cap.Node` that runs a goroutine that fails on start

func FailTerminationWorker

func FailTerminationWorker(
	name string,
	err error,
	opts ...cap.WorkerOpt,
) cap.Node

FailTerminationWorker creates a `cap.Node` that runs a goroutine that blocks until the `context.Done` channel indicates a supervisor termination, then, it returns a given error

func NeverTerminateWorker

func NeverTerminateWorker(name string) cap.Node

NeverTerminateWorker creates a `cap.Node` that runs a goroutine that never stops when asked to, causing the goroutine to leak in the runtime

func ObserveDynSupervisor

func ObserveDynSupervisor(
	ctx0 context.Context,
	rootName string,
	childNodes []cap.Node,
	opts0 []cap.Opt,
	callback func(cap.DynSupervisor, EventManager),
) ([]cap.Event, []error)

ObserveDynSupervisor is an utility function that receives all the arguments required to build a DynSupervisor, and a callback that when executed will block until some point in the future (after we performed the side-effects we are testing). This function returns the list of events that happened in the monitored supervised tree, as well as any crash errors.

func ObserveSupervisor

func ObserveSupervisor(
	ctx context.Context,
	rootName string,
	buildNodes cap.BuildNodesFn,
	opts0 []cap.Opt,
	callback func(EventManager),
) ([]cap.Event, error)

ObserveSupervisor is an utility function that receives all the arguments required to build a SupervisorSpec, and a callback that when executed will block until some point in the future (after we performed the side-effects we are testing). This function returns the list of events that happened in the monitored supervised tree, as well as any crash errors.

func ObserveSupervisorWithNotifiers

func ObserveSupervisorWithNotifiers(
	ctx0 context.Context,
	rootName string,
	buildNodes cap.BuildNodesFn,
	opts0 []cap.Opt,
	notifiers []cap.EventNotifier,
	callback func(EventManager),
) ([]cap.Event, error)

ObserveSupervisorWithNotifiers is an utility function that receives all the arguments required to build a SupervisorSpec, and a callback that when executed will block until some point in the future (after we performed the side-effects we are testing). This function returns the list of events that happened in the monitored supervised tree, as well as any crash errors.

func PanicOnSignalWorker

func PanicOnSignalWorker(
	totalErrCount int32,
	name string,
	opts ...cap.WorkerOpt,
) (cap.Node, func(bool))

PanicOnSignalWorker creates a cap.Node that runs a goroutine that will panic at least the given number of times as soon as the returned start signal is called. Once this number of times has been reached, it waits until the given context.Done channel indicates a supervisor termination.

func PanicStartWorker added in v0.5.0

func PanicStartWorker(name string) cap.Node

PanicStartWorker creates a `cap.Node` that runs a goroutine that panics on start

func WaitDoneDynSubtree

func WaitDoneDynSubtree(
	name string,
	spawnerOpts []cap.Opt,
	workerOpts []cap.WorkerOpt,
	children ...cap.Node,
) cap.Node

WaitDoneDynSubtree creates a `cap.Node` that runs a goroutine with a spawner that will spawn the given children. It blocks until the `context.Done` channel indicates a supervisor termination

func WaitDoneWorker

func WaitDoneWorker(name string) cap.Node

WaitDoneWorker creates a `cap.Node` that runs a goroutine that blocks until the `context.Done` channel indicates a supervisor termination

func WorkerFailedWith

func WorkerFailedWith(name, errMsg string) smtest.EventP[cap.Event]

WorkerFailedWith is a predicate to assert an event represents a process that failed

func WorkerStartFailed

func WorkerStartFailed(name string) smtest.EventP[cap.Event]

WorkerStartFailed is a predicate to assert an event represents a process that failed on start

func WorkerStarted

func WorkerStarted(name string) smtest.EventP[cap.Event]

WorkerStarted is a predicate to assert an event represents a process that got started

Types

type AndP

type AndP = smtest.AndP[cap.Event]

AndP is a predicate that builds the conjunction of a group EventP predicates (e.g. join EventP predicates with &&)

type ErrorMsgP

type ErrorMsgP struct {
	// contains filtered or unexported fields
}

ErrorMsgP is a predicate that asserts the message of an error is the one specified

func (ErrorMsgP) Call

func (p ErrorMsgP) Call(ev cap.Event) bool

Call will try and verify that the event has an error attribute with the specified message

func (ErrorMsgP) String

func (p ErrorMsgP) String() string

type EventManager

type EventManager = smtest.EventManager[cap.Event]

EventManager provides an API that allows to block a goroutine for particular events in a test system

type EventP

type EventP = smtest.EventP[cap.Event]

EventP represents a predicate function that allows us to assert properties of an Event signaled by the supervision system

func ProcessName

func ProcessName(name string) EventP

ProcessName is a predicate to assert an event was triggered by the given runtime process name

func SupervisorFailed

func SupervisorFailed(name string) EventP

SupervisorFailed is a predicate to assert an event represents a process that failed

func SupervisorStartFailed

func SupervisorStartFailed(name string) EventP

SupervisorStartFailed is a predicate to assert an event represents a process that failed on start

func SupervisorStarted

func SupervisorStarted(name string) EventP

SupervisorStarted is a predicate to assert an event represents a process that got started

func SupervisorTerminated

func SupervisorTerminated(name string) EventP

SupervisorTerminated is a predicate to assert an event represents a process that got stopped by its parent supervisor

func WorkerCompleted

func WorkerCompleted(name string) EventP

WorkerCompleted is a predicate to assert an event represents a worker process that got completed

func WorkerFailed

func WorkerFailed(name string) EventP

WorkerFailed is a predicate to assert an event represents a process that failed

func WorkerTerminated

func WorkerTerminated(name string) EventP

WorkerTerminated is a predicate to assert an event represents a process that got stopped by its parent supervisor

type EventTagP

type EventTagP struct {
	// contains filtered or unexported fields
}

EventTagP is a predicate that asserts the `cap.EventTag` of a given `capataz.Event` matches an expected `cap.EventTag`

func (EventTagP) Call

func (p EventTagP) Call(ev cap.Event) bool

Call will execute predicate that checks tag name of event

func (EventTagP) String

func (p EventTagP) String() string

type ProcessNameP

type ProcessNameP struct {
	// contains filtered or unexported fields
}

ProcessNameP is a predicate that asserts the name of the `Child` that triggered the event matches the expected name

func (ProcessNameP) Call

func (p ProcessNameP) Call(ev cap.Event) bool

Call will execute predicate that checks the name of the process that triggered the event

func (ProcessNameP) String

func (p ProcessNameP) String() string

type ProcessNodeTagP

type ProcessNodeTagP struct {
	// contains filtered or unexported fields
}

ProcessNodeTagP is a predicate that asserts the ChildTag of the `Child` that triggered the event matches the expect ChildTag

func (ProcessNodeTagP) Call

func (p ProcessNodeTagP) Call(ev cap.Event) bool

Call will execute predicate that checks the ChildTag of the process that triggered the event matches the expected ChildTag

func (ProcessNodeTagP) String

func (p ProcessNodeTagP) String() string

Jump to

Keyboard shortcuts

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