End-to-End Testing
Unit tests (go test ./...) cover DevKit’s Go logic. End-to-end (E2E)
tests cover the parts that only a real invocation exercises: the DPM
component surface, shelling out to dpm/daml, Docker-backed LocalNet
lifecycle, and the artifacts DevKit produces on disk.
The repository has two E2E layers:
| Layer | Location | Framework | Scope |
|---|---|---|---|
| Milestone 1 (LocalNet lifecycle) | scripts/e2e/ (run-all.sh + per-test m1-*.sh) |
hand-rolled shell harness (scripts/e2e/lib.sh) |
up/down/status/logs/snapshot/restore, multi-instance, Docker |
dpm localnet component |
e2e-tests/ |
bats-core | the DPM component path: dpm localnet …, nested dpm build, DAR build/upload |
New E2E tests use bats-core. The Milestone 1 shell suite predates that decision and will migrate to bats over time; until then the two layers coexist.
Why bats-core
Section titled “Why bats-core”The dpm localnet suite was originally a bespoke shell harness with its
own pass/fail/skip counters, result table, and aggregate-vs-
standalone dispatch. bats-core replaces all of that boilerplate with a
maintained TAP-compliant runner, so tests carry only their own logic:
@testblocks, one assertion group each, each in its own subprocess.setup/teardown(per test) andsetup_file/teardown_file(once per file) hooks.- The
runhelper plus$status/$output/$lines, and thebats-assertmatchers (assert_success,refute_output --partial, …). - Native
skip "reason"for graceful skips (e.g.dpmnot installed).
Layout
Section titled “Layout”e2e-tests/ bats/ bats-core runner (git submodule) test_helper/ bats-support/ assertion support library (git submodule) bats-assert/ assertion matchers (git submodule) dpm.bash DevKit-specific helpers dpm-dar-001.bats one test file per test ID daml-test-contracts/ shared Daml fixturesbats-core and its helper libraries are vendored as pinned git submodules, so a checkout resolves the exact tested versions with no network install at run time (consistent with how the rest of the toolchain is pinned):
| Submodule | Path | Pin |
|---|---|---|
| bats-core | e2e-tests/bats |
v1.13.0 |
| bats-support | e2e-tests/test_helper/bats-support |
v0.3.0 |
| bats-assert | e2e-tests/test_helper/bats-assert |
v2.2.4 |
The DevKit-specific glue lives in e2e-tests/test_helper/dpm.bash:
dpm_available— succeeds when thedpmCLI is onPATH; tests use it toskipgracefully rather than hard-fail.dpm_build_component— assembles a local file-based DevKit component (bin/canton-devkit+ a renderedcomponent.yaml) from the binary built in this run. Resolving the component from a local{name, path}reference keeps the suite hermetic — no OCI registry, no TLS — and exercises this binary, not a released one.dpm_make_project— scaffolds a minimal, compilable Daml project that installs the local component.
Running locally
Section titled “Running locally”Prerequisites:
- Go (see
go.mod) — the suite buildsbin/canton-devkit. - The
dpmCLI onPATH. If it is missing the suite skips rather than fails. - macOS or Linux (the
dpm localnetsuite does not require Docker; the Milestone 1 suite does).
Run the whole dpm localnet suite:
make e2e-dpmmake e2e-dpm initializes the bats submodules on demand (so a fresh
clone just works) and runs every e2e-tests/*.bats file. To run one
file, or pass bats flags, invoke the vendored runner directly:
BATS_LIB_PATH="$PWD/e2e-tests/test_helper" \ e2e-tests/bats/bin/bats e2e-tests/dpm-dar-001.batsUseful overrides (environment variables):
| Variable | Effect |
|---|---|
DPM |
Path to the dpm CLI (default dpm). |
CDK_BIN |
Use a prebuilt canton-devkit binary instead of building one. |
DPM_SKIP_BUILD |
Skip the in-suite make build (CI builds once, up front). |
Scratch (built components, scaffolded projects) is written under the
repo’s gitignored .tmp/, never /tmp.
Writing a test
Section titled “Writing a test”Each test file is e2e-tests/<TEST-ID>.bats, where the test ID follows
the milestone convention (DPM-DAR-001, DPM-DAR-002, …). A minimal
file:
#!/usr/bin/env bats
setup_file() { bats_load_library bats-support bats_load_library bats-assert load 'test_helper/dpm'
dpm_available || skip "dpm not found on PATH (DPM=${DPM:-dpm})"
# Build + assemble the local component once for the file. if [ -z "${DPM_SKIP_BUILD:-}" ] && [ -z "${CDK_BIN:-}" ]; then make -C "$DPM_REPO_ROOT" build >&2 fi COMPONENT_DIR="$(dpm_build_component)" export COMPONENT_DIR}
setup() { bats_load_library bats-support bats_load_library bats-assert load 'test_helper/dpm'
dpm_available || skip "dpm not found on PATH (DPM=${DPM:-dpm})" PROJECT_DIR="$(dpm_make_project "$COMPONENT_DIR")"}
teardown() { [ -n "${PROJECT_DIR:-}" ] && rm -rf "$PROJECT_DIR"}
@test "DPM-DAR-002: <describe the behaviour>" { cd "$PROJECT_DIR" run "$DPM" localnet dar build-upload --build-only assert_success refute_output --partial "file exists"}Guidelines:
- Put reusable DevKit logic in
dpm.bash, not in individual tests. - Prefer
run+assert_*/refute_*over hand-rolledout=$(...); [ ... ]checks — the failure output is far clearer. - Skip (don’t fail) when a required tool is absent, so contributors
without
dpmand unrelated CI jobs stay green. - Keep tests hermetic: build the component locally, scaffold into
.tmp/, and clean up inteardown.
Continuous integration
Section titled “Continuous integration”The dpm localnet suite runs in
.github/workflows/e2e-test-dpm-localnet.yml
on the self-hosted Linux runner, one job per test ID (a failed case can
be re-run without replaying the suite). Checkout uses
submodules: recursive so the pinned bats submodules are present. The
per-test composite action
.github/actions/e2e-dpm-test
builds the binary once, installs a SHA-256-verified dpm, and runs one
.bats file — passing the built binary via CDK_BIN rather than a
cross-job artifact (all jobs share one runner).
Triggers:
- schedule — nightly.
- workflow_dispatch — manual, ad-hoc validation.
- pull_request — only when the PR carries the
run-e2elabel.
The Milestone 1 suite runs analogously from
.github/workflows/e2e-test-devkit-functions.yml.
Updating pinned versions
Section titled “Updating pinned versions”To move a vendored library to a new release, update the submodule and commit the new gitlink:
cd e2e-tests/batsgit fetch --tagsgit checkout v1.13.1 # the desired tagcd ../..git add e2e-tests/batsgit commit -m "test(e2e): bump bats-core to v1.13.1"The recorded submodule commit is the pin; CI and make e2e-dpm resolve
exactly that revision.