tempo

package module
v0.3.1 Latest Latest
Warning

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

Go to latest
Published: Oct 17, 2025 License: MIT Imports: 19 Imported by: 0

README

Tempo

go.dev Go Report Card codecov

Tempo is a Go library for running Go-like tests in a distributed manner using Temporal.

Usage

Add the dependency to your go.mod file:

go get github.com/cito-oss/tempo

[!TIP] For more examples, check the _example directory.

Then create your first test app:

package main

import (
	"context"

	"github.com/cito-oss/tempo"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"go.temporal.io/sdk/client"
	"go.temporal.io/sdk/worker"
)

func Greeter(ctx context.Context, name string) (string, error) {
	if name == "" {
		name = "World"
	}

	return "Hello " + name, nil
}

func SayHello(t *tempo.T, name string) {
	// use t.Go for workflow.Go
	// use t.WaitGroup for workflow.NewWaitGroup
	// use t.BufferedChannel for workflow.NewBufferedChannel

	t.Run("must greet :name", func(t *tempo.T) {
		var greetings string

		err := t.Task(Greeter, name, &greetings)
		require.NoError(t, err)

		assert.Equal(t, "Hello "+name, greetings)
	})

	t.Run("must greet John Doe", func(t *tempo.T) {
		var greetings string

		err := t.Task(Greeter, "John Doe", &greetings)
		require.NoError(t, err)

		assert.Equal(t, "Hello John Doe", greetings)
	})

	t.Run("must greet World", func(t *tempo.T) {
		var greetings string

		err := t.Task(Greeter, "World", &greetings)
		require.NoError(t, err)

		assert.Equal(t, "Hello World", greetings)
	})
}

func main() {
	cli, err := client.Dial(client.Options{})
	if err != nil {
		panic(err)
	}
	defer cli.Close()

	queue := "default"

	// WORKER
	myworker := worker.New(cli, queue, worker.Options{})

	tempo.Worker(myworker, tempo.Registry{
		Tests: []tempo.Test{
			tempo.NewTestWithInput(SayHello),
		},
		Tasks: []tempo.Task{
			Greeter,
		},
	})

	// non-blocking call
	err = myworker.Start()
	if err != nil {
		panic(err)
	}
	defer myworker.Stop()
	// /WORKER

	// RUNNER
	myrunner := tempo.NewRunner(cli, queue,
		tempo.NewPlan(SayHello, "Tempo"),
	)

	myrunner.SetReporting(true) // enable Allure reporting
	myrunner.SetLimit(1)        // limit number of concurrent tests

	// blocking call
	err = myrunner.Run("v1.0.0")
	if err != nil {
		panic(err)
	}
	// /RUNNER
}

Now run it and check your Temporal:

Temporal Screenshot

Reporting

If you are using the runner, enabled it with:

	myrunner.SetReporting(true)

or if you are invoking tests manually:

	opts := []tempo.Option{
		tempo.WithReporting(true),
	}

	err := tempo.Run(cli, queue, id, plan, nil, opts...)

Then, run your tests. Once they are completed, you will notice that an allure-results folder has been created. You can now generate and view the report using the following commands:

allure generate allure-results --clean -o allure-report
allure open allure-report

Allure Report Screenshot 1 Allure Report Screenshot 2

[!IMPORTANT] Please note that reporting is handled by the runner. So, if you have separate applications for the runner and worker, the allure-results folder will be created where the runner is being executed.

Roadmap

  • Reporting
  • Slack integration
  • Support to Temporal Signals
  • Ability to Skip test cases

Documentation

Index

Constants

View Source
const TestFailedErrorType = "TestFailedError"

Variables

View Source
var (
	ErrSetLimitWithGoroutines = errors.New("set limit with goroutines")
	ErrWorkflowExecute        = errors.New("workflow execute")
	ErrWorkflowResult         = errors.New("workflow result")
	ErrTaskExecute            = errors.New("task execute")
	ErrTaskResult             = errors.New("task result")
	ErrPlanRun                = errors.New("plan run")
)
View Source
var DefaultActivityOptions = workflow.ActivityOptions{
	StartToCloseTimeout: time.Minute,
	RetryPolicy: &temporal.RetryPolicy{
		MaximumAttempts: 1,
	},
}

Functions

func NewTestFailedError

func NewTestFailedError(msgs []string) error

func Run

func Run(cli client.Client, queue string, id string, plan Plan, output any, opts ...Option) error

func Worker

func Worker(w worker.Worker, reg Registry)

Types

type Channel added in v0.3.0

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

Channel is a wrapper for Temporal Channel

func (*Channel) Close added in v0.3.0

func (c *Channel) Close()

func (*Channel) Receive added in v0.3.0

func (c *Channel) Receive(valuePtr any) (more bool)

func (*Channel) Send added in v0.3.0

func (c *Channel) Send(v any)

type Option added in v0.2.0

type Option func(*runConfig)

func WithReportHandler added in v0.2.0

func WithReportHandler(fn func(report *allure.Result)) Option

func WithReporting added in v0.2.0

func WithReporting(enabled bool) Option

type Plan

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

func NewPlan

func NewPlan(fn any, input any) Plan

func (Plan) Input

func (p Plan) Input() any

func (Plan) Name

func (p Plan) Name() string

type Registry

type Registry struct {
	Tests []Test
	Tasks []Task
}

type Runner

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

func NewRunner

func NewRunner(client client.Client, queue string, plans ...Plan) *Runner

func (*Runner) Run

func (r *Runner) Run(prefix string) error

func (*Runner) SetLimit

func (r *Runner) SetLimit(limit int)

func (*Runner) SetReporting added in v0.2.0

func (r *Runner) SetReporting(enabled bool)

func (*Runner) SetResultHandler added in v0.2.0

func (r *Runner) SetResultHandler(fn func(result *allure.Result))

type T

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

func (*T) BufferedChannel added in v0.3.0

func (t *T) BufferedChannel(size int) *Channel

func (*T) Errorf

func (t *T) Errorf(format string, args ...any)

func (*T) FailNow

func (t *T) FailNow()

func (*T) Go

func (t *T) Go(fn func(t *T))

func (*T) Run

func (t *T) Run(name string, fn func(*T))

func (*T) RunAsChild

func (t *T) RunAsChild(fn any, input any, output any)

func (*T) SetActivityOptions

func (t *T) SetActivityOptions(options workflow.ActivityOptions)

func (*T) Task

func (t *T) Task(task any, input any, output any) error

func (*T) WaitGroup

func (t *T) WaitGroup() *WaitGroup

func (*T) Warnf

func (t *T) Warnf(format string, args ...any)

type Task

type Task any

type Test

type Test interface {
	Name() string
	Function() any
}

func NewTest

func NewTest(fn func(*T)) Test

NewTest wraps fn in a workflowWrapper, that can be passed to temporal Worker:

mytest := tempo.NewTest(func(*T))

w.RegisterWorkflowWithOptions(mytest.Function(), workflow.RegisterOptions{
	Name: mytest.Name(),
})

func NewTestWithInput

func NewTestWithInput[INPUT any](fn func(*T, INPUT)) Test

NewTestWithInput wraps fn in a workflowWrapper, that can be passed to temporal Worker:

mytest := tempo.NewTestWithInput(func(*T, string))

w.RegisterWorkflowWithOptions(mytest.Function(), workflow.RegisterOptions{
	Name: mytest.Name(),
})

func NewTestWithInputAndOutput

func NewTestWithInputAndOutput[INPUT any, OUTPUT any](fn func(*T, INPUT) OUTPUT) Test

NewTestWithInputAndOutput wraps fn in a workflowWrapper, that can be passed to temporal Worker:

mytest := tempo.NewTestWithInputAndOutput(func(*T, string) string)

w.RegisterWorkflowWithOptions(mytest.Function(), workflow.RegisterOptions{
	Name: mytest.Name(),
})

func NewTestWithOutput

func NewTestWithOutput[OUTPUT any](fn func(*T) OUTPUT) Test

NewTestWithOutput wraps fn in a workflowWrapper, that can be passed to temporal Worker:

mytest := tempo.NewTestWithOutput(func(*T) string)

w.RegisterWorkflowWithOptions(mytest.Function(), workflow.RegisterOptions{
	Name: mytest.Name(),
})

type TestFailedError

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

func (TestFailedError) Error

func (e TestFailedError) Error() string

type TestingT

type TestingT interface {
	Errorf(format string, args ...any)
	FailNow()
}

type WaitGroup

type WaitGroup struct {
	workflow.WaitGroup
	// contains filtered or unexported fields
}

func (*WaitGroup) Wait

func (wg *WaitGroup) Wait()

type Workflow added in v0.3.0

type Workflow[INPUT any, OUTPUT any] struct {
	// contains filtered or unexported fields
}

Workflow is a wrapper for Temporal Workflow

func (*Workflow[I, O]) Function added in v0.3.0

func (c *Workflow[I, O]) Function() any

Function returns the function that should be given to Temporal

func (*Workflow[I, O]) Name added in v0.3.0

func (c *Workflow[I, O]) Name() string

Name returns the name of the Workflow

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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