Context
This issue wires the entire devstack together: System (Layer 4) holds all shims and provides the composed view of the running stack; DSL (Layer 5) provides high-level test operations; Presets (Layer 6) provide one-line test entry points (NewFullStack(t)).
After this issue, writing a new E2E test requires only:
sys := presets.NewFullStack(t)
sys.DSL.RegisterUser(t, sys.Accounts.User1)
Files to Create
All with //go:build e2e.
tests/e2e/devstack/system/system.go
package system
type System struct {
Anvil stack.Anvil
Canton stack.Canton
APIServer stack.APIServer
Relayer stack.Relayer
Indexer stack.Indexer
Postgres stack.Postgres
DSL *dsl.DSL
Manifest *stack.ServiceManifest
Accounts *Accounts
}
type Accounts struct {
User1 *stack.Account // AnvilAccount0
User2 *stack.Account // AnvilAccount1
}
// New wires all shims from the manifest and returns a ready System.
func New(t *testing.T, manifest *stack.ServiceManifest) *System
tests/e2e/devstack/dsl/dsl.go
High-level operations:
| Method |
Description |
RegisterUser(t, acc) |
Whitelist + EIP-191 sign + POST /register |
Deposit(t, acc, amount) |
Anvil.ApproveAndDeposit |
Withdraw(t, acc, amount, toEVMAddr) |
APIServer.Transfer to relayer withdrawal addr |
Transfer(t, from, to, amount, token) |
APIServer.Transfer between two Canton parties |
GetBalance(t, acc, token) |
APIServer.GetBalance |
MintDEMO(t, acc, amount) |
Direct Canton DAML command via APIServer |
WaitForCantonBalance(t, acc, token, expected) |
Poll with 30s timeout |
WaitForRelayerReady(t) |
Poll Relayer.IsReady with 60s timeout |
GetIndexerBalance(t, partyID, admin, id) |
Indexer.GetBalance |
WaitForIndexerBalance(t, partyID, admin, id, expected) |
Poll with 60s timeout |
GetIndexerTotalSupply(t, admin, id) |
Indexer.TotalSupply |
WaitForIndexerTotalSupply(t, admin, id, expected) |
Poll with 60s timeout |
GetIndexerEvent(t, contractID) |
Indexer.GetEvent |
WaitForIndexerEvent(t, partyID, eventType) |
Poll Indexer.ListPartyEvents until event appears, 60s timeout |
ListIndexerPartyEvents(t, partyID, eventType) |
Indexer.ListPartyEvents |
Polling pattern — all WaitFor* helpers use require.Eventually with the appropriate timeout (30s for Canton, 60s for Indexer) and a 500ms interval.
tests/e2e/devstack/dsl/helpers.go
func signEIP191(message, privateKey string) (string, error)
func balanceEquals(a, b string) bool // decimal string comparison
func addDecimal(a, b string) string
func subtractDecimal(a, b string) string
func toWei(decimal string) *big.Int
func fromWei(wei *big.Int) string
tests/e2e/devstack/presets/presets.go
// DoMain starts the compose stack and runs m.Run(). Called from TestMain.
func DoMain(m *testing.M, opts ...Option) int
// NewFullStack starts the full stack (anvil + canton + postgres + api-server + relayer + indexer).
// Registers t.Cleanup to stop the stack.
func NewFullStack(t *testing.T, opts ...Option) *system.System
// NewAPIStack starts only api-server + dependencies (no relayer, no indexer).
func NewAPIStack(t *testing.T, opts ...Option) *system.System
// NewIndexerStack starts api-server + relayer + indexer.
func NewIndexerStack(t *testing.T, opts ...Option) *system.System
tests/e2e/devstack/presets/options.go
type Option func(*config)
func WithComposeFile(path string) Option
func WithProjectName(name string) Option
func envOr(key, fallback string) string
Default compose file: tests/e2e/docker-compose.e2e.yaml (resolved relative to repo root via runtime.Caller).
Default project name: canton-e2e.
Acceptance Criteria
go build ./tests/e2e/devstack/... compiles cleanly with -tags e2e.
presets.DoMain + presets.NewFullStack work against a running stack (manual smoke test).
WaitForIndexerBalance uses 60s timeout; WaitForCantonBalance uses 30s timeout.
Dependencies
Size
M (1 day)
Context
This issue wires the entire devstack together: System (Layer 4) holds all shims and provides the composed view of the running stack; DSL (Layer 5) provides high-level test operations; Presets (Layer 6) provide one-line test entry points (
NewFullStack(t)).After this issue, writing a new E2E test requires only:
Files to Create
All with
//go:build e2e.tests/e2e/devstack/system/system.gotests/e2e/devstack/dsl/dsl.goHigh-level operations:
RegisterUser(t, acc)/registerDeposit(t, acc, amount)Anvil.ApproveAndDepositWithdraw(t, acc, amount, toEVMAddr)APIServer.Transferto relayer withdrawal addrTransfer(t, from, to, amount, token)APIServer.Transferbetween two Canton partiesGetBalance(t, acc, token)APIServer.GetBalanceMintDEMO(t, acc, amount)APIServerWaitForCantonBalance(t, acc, token, expected)WaitForRelayerReady(t)Relayer.IsReadywith 60s timeoutGetIndexerBalance(t, partyID, admin, id)Indexer.GetBalanceWaitForIndexerBalance(t, partyID, admin, id, expected)GetIndexerTotalSupply(t, admin, id)Indexer.TotalSupplyWaitForIndexerTotalSupply(t, admin, id, expected)GetIndexerEvent(t, contractID)Indexer.GetEventWaitForIndexerEvent(t, partyID, eventType)Indexer.ListPartyEventsuntil event appears, 60s timeoutListIndexerPartyEvents(t, partyID, eventType)Indexer.ListPartyEventsPolling pattern — all
WaitFor*helpers userequire.Eventuallywith the appropriate timeout (30s for Canton, 60s for Indexer) and a 500ms interval.tests/e2e/devstack/dsl/helpers.gotests/e2e/devstack/presets/presets.gotests/e2e/devstack/presets/options.goDefault compose file:
tests/e2e/docker-compose.e2e.yaml(resolved relative to repo root viaruntime.Caller).Default project name:
canton-e2e.Acceptance Criteria
go build ./tests/e2e/devstack/...compiles cleanly with-tags e2e.presets.DoMain+presets.NewFullStackwork against a running stack (manual smoke test).WaitForIndexerBalanceuses 60s timeout;WaitForCantonBalanceuses 30s timeout.Dependencies
[E2E] Devstack: Layer 1) — stack interfaces and types.[E2E] Devstack: Layer 3) —ComposeOrchestrator,ServiceDiscovery.[E2E] Devstack: Layer 2) — shim constructors.Size
M (1 day)