temporalio / features

Behavior and history compatibility testing for Temporal SDKs
14 stars 17 forks source link

Temporal Features

This repository contains snippets for many Temporal features written in different Temporal SDK languages. This also contains a runner and language-specific harnesses to confirm feature behavior across versions.

These features serve several purposes:

Building

With latest Go installed, run:

go build -o temporal-features # or temporal-features.exe on Windows

Running

Prerequisites:

Command:

temporal-features run --lang LANG [--version VERSION] [PATTERN...]

Note, go run . can be used in place of go build + temporal-features to save on the build step.

LANG can be go, java, ts, php, py, or cs. VERSION is per SDK and if left off, uses the latest version set for the language in this repository.

PATTERN must match either the features relative directory or the relative directory + /feature.<ext> via Go path match rules which notably does not include recursive depth matching. If PATTERN arguments are not present, the default is to run all features.

Several other options are available, some of which are described below. Run temporal-features run --help to see all options.

Preparing

By default when using run and a version, a temporary directory is created with a temporary project, the project is built, and then the features are run. To separate the steps, the prepare command can be used to prebuild the project in a directory. Then run can use the --prepared-dir to reference that directory.

The command to prepare is:

temporal-features prepare --lang LANG --version VERSION --dir DIR

The version is required and the directory is a simple string name of a not-yet-existing directory to be created directly beneath this SDK features directory. That same directory value can then be provided as --prepared-dir to run. When using a prepared directory on run, a version cannot be specified.

Building docker images

The CLI supports building docker images from prepared features.

There are 2 types of image builds supported, by SDK version or by git repository ref as shown below:

./temporal-features build-image --lang go --repo-ref master

The built image will be tagged with features:go-master

./temporal-features build-image --lang go --version v1.13.1

The built image will be tagged with features:go-1.13.1

External Server and Namespace

By default, a CLI Dev Server is dynamically started at runtime to handle all feature runs and a namespace is dynamically generated. To not start the embedded server, use --server to specify the address of a server to use. Similarly, to not use a dynamic namespace (that may not be registered on the external server), use --namespace.

History Checking

History files are present at features/<path/to/feature>/history/history.<lang>.<version>.json. By default, three history checks are performed:

  1. Specific languages use their replayers to replay the just-executed workflow's history to confirm it works.
  2. Specific languages use their replayers to replay all history files with versions <= the current version to confirm it works.
  3. The primary runner scrubs the history of the just-executed workflow of all execution-dependent values. Then it compares the exact events to all similarly-scrubbed history files.

Currently there are not ways for features to opt out of specific history checks. To opt out of all history checking for a specific run, use --no-history-check.

Writing Features

Developers can write workflows and activities that represent a SDK "feature". These are organized into directories under the features/ directory.

In addition to code for the feature, there are configuration settings that can be set in .config.json. The possible settings are:

There are also files in the history/ subdirectory which contain history files used during run. See the "History Checking" and "Generating History" sections for more info.

Best Practices

Generating History

To generate history, run the same test (see the "Running" section) for the version to generate at, but use the --generate-history option. When generating history, only one test can be specified and the version of the SDK must be specified. Any existing history for that feature, language, and version will be overwritten.

History generation should only be needed when first developing a feature or when a version intentionally introduces an incompatibility. Otherwise, history files should remain checked in and not regenerated.

Usage within CI

The repo defines GitHub workflows which are designed to allow running the SDK features suites against changes to an SDK, or against changes to server. The former is accomplished by syncing the SDK repo and using it as a path-version when running the suites. The latter is accomplished by building the changes to server into a docker image, and using that docker image for the server when running the suites.

Publishing docker images of the features runner/suites is also supported. It may be run manually, but is also triggered by default on each push to main.

The dynamic configuration file located at dockerfiles/dynamicconfig/docker.yaml defines the dynamic configuration settings needed for features to run, and should be used as part of or all of the dynamic config settings for any external server not using the basic docker-compose setup.

TODO