Skip to content

feat: [E2E] Layer 1 — stack interfaces, types, compose override, Makefile targets#186

Open
sadiq1971 wants to merge 1 commit intofeat/e2e-initfrom
feat/e2e-layer1-interfaces
Open

feat: [E2E] Layer 1 — stack interfaces, types, compose override, Makefile targets#186
sadiq1971 wants to merge 1 commit intofeat/e2e-initfrom
feat/e2e-layer1-interfaces

Conversation

@sadiq1971
Copy link
Copy Markdown
Member

Summary

  • tests/e2e/devstack/stack/interfaces.goAnvil, Canton, APIServer, Relayer, Indexer, Postgres interfaces; all test and DSL code depends only on these, never on concrete implementations (//go:build e2e)
  • tests/e2e/devstack/stack/types.goServiceManifest, Account, AnvilAccount0/1, request/response/row types, and all indexer paginated page types
  • tests/e2e/docker-compose.e2e.yaml — thin include wrapper over the root docker-compose.yaml; single entry point for docker compose up --wait in E2E runs
  • Makefiletest-e2e, test-e2e-api, test-e2e-bridge, test-e2e-indexer targets (stubbed with "not yet implemented" until later layers land)

Note on compose port overrides

The include directive does not support overriding services from the included file. Since the root docker-compose.yaml already exposes all ports with fixed bindings (8545:8545, 5011:5011, etc.) and the e2e-deploy volume was already wired in #177, no service overrides are needed — docker compose port resolves correctly against the fixed mappings.

Acceptance Criteria

  • go build -tags e2e ./tests/e2e/devstack/stack/... compiles cleanly
  • docker compose -f tests/e2e/docker-compose.e2e.yaml config succeeds
  • make test-e2e-api, make test-e2e-bridge, make test-e2e-indexer each print "not yet implemented"

Closes #178

…file targets (#178)

- Add tests/e2e/devstack/stack/interfaces.go: Anvil, Canton, APIServer,
  Relayer, Indexer, Postgres interfaces (build tag: e2e)
- Add tests/e2e/devstack/stack/types.go: ServiceManifest, Account,
  AnvilAccount0/1, all request/response/row types, indexer page types
- Add tests/e2e/docker-compose.e2e.yaml: thin include wrapper over root
  docker-compose.yaml; single entry point for E2E test runs
- Extend Makefile with test-e2e, test-e2e-api, test-e2e-bridge,
  test-e2e-indexer targets (stub: "not yet implemented")
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request establishes the infrastructure for end-to-end testing by introducing new Makefile targets, a dedicated Docker Compose configuration, and Go interfaces and types representing the various components of the stack. The reviewer provided several constructive suggestions to improve the robustness and clarity of the test code, including renaming the generic Postgres interface to APIDatabase, expanding the ServiceManifest to include connection strings for all databases in the stack, utilizing constants for event types instead of magic strings, and defining preconfigured test accounts as values to prevent accidental mutation during tests.

Comment on lines +108 to +115
type Postgres interface {
// DSN returns the postgres connection string.
DSN() string
// WhitelistAddress adds an EVM address to the whitelist table.
WhitelistAddress(ctx context.Context, evmAddress string) error
// GetUserByEVMAddress returns a user row or nil.
GetUserByEVMAddress(ctx context.Context, evmAddress string) (*UserRow, error)
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The Postgres interface name is too generic given that its methods (WhitelistAddress, GetUserByEVMAddress) are specifically tailored to the API server's user schema. Since the stack includes multiple databases (Relayer, API Server, Indexer), this interface should be renamed to something more specific like APIDatabase to avoid confusion and allow for other database-specific interfaces to be added later.

Suggested change
type Postgres interface {
// DSN returns the postgres connection string.
DSN() string
// WhitelistAddress adds an EVM address to the whitelist table.
WhitelistAddress(ctx context.Context, evmAddress string) error
// GetUserByEVMAddress returns a user row or nil.
GetUserByEVMAddress(ctx context.Context, evmAddress string) (*UserRow, error)
}
type APIDatabase interface {
// DSN returns the postgres connection string.
DSN() string
// WhitelistAddress adds an EVM address to the whitelist table.
WhitelistAddress(ctx context.Context, evmAddress string) error
// GetUserByEVMAddress returns a user row or nil.
GetUserByEVMAddress(ctx context.Context, evmAddress string) (*UserRow, error)
}

RelayerHTTP string // "http://localhost:8080"
IndexerHTTP string // "http://localhost:8082"
OAuthHTTP string // "http://localhost:8088"
PostgresDSN string // "postgres://postgres:p@ssw0rd@localhost:5432/erc20_api"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The ServiceManifest currently only provides a single PostgresDSN which points to the erc20_api database. However, the docker-compose.yaml defines three distinct databases: relayer, erc20_api, and canton_indexer. E2E tests may need to verify state across all components. Consider adding DSN fields for the Relayer and Indexer databases as well.

Suggested change
PostgresDSN string // "postgres://postgres:p@ssw0rd@localhost:5432/erc20_api"
APIDatabaseDSN string // "postgres://postgres:p@ssw0rd@localhost:5432/erc20_api"
RelayerDatabaseDSN string // "postgres://postgres:p@ssw0rd@localhost:5432/relayer"
IndexerDatabaseDSN string // "postgres://postgres:p@ssw0rd@localhost:5432/canton_indexer"

Comment on lines +53 to +59
// IndexerEvent mirrors indexer.ParsedEvent — a decoded TokenTransferEvent from the ledger.
type IndexerEvent struct {
ContractID string `json:"contract_id"`
TxID string `json:"tx_id"`
InstrumentAdmin string `json:"instrument_admin"`
InstrumentID string `json:"instrument_id"`
EventType string `json:"event_type"` // "MINT" | "BURN" | "TRANSFER"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using magic strings for EventType in tests can lead to typos and maintenance issues. It is recommended to define these as constants.

Suggested change
// IndexerEvent mirrors indexer.ParsedEvent — a decoded TokenTransferEvent from the ledger.
type IndexerEvent struct {
ContractID string `json:"contract_id"`
TxID string `json:"tx_id"`
InstrumentAdmin string `json:"instrument_admin"`
InstrumentID string `json:"instrument_id"`
EventType string `json:"event_type"` // "MINT" | "BURN" | "TRANSFER"
const (
EventTypeMint = "MINT"
EventTypeBurn = "BURN"
EventTypeTransfer = "TRANSFER"
)
// IndexerEvent mirrors indexer.ParsedEvent — a decoded TokenTransferEvent from the ledger.
type IndexerEvent struct {
ContractID string `json:"contract_id"`
TxID string `json:"tx_id"`
InstrumentAdmin string `json:"instrument_admin"`
InstrumentID string `json:"instrument_id"`
EventType string `json:"event_type"` // "MINT" | "BURN" | "TRANSFER"

Comment on lines +92 to +102
// Preconfigured Anvil test accounts (deterministic from mnemonic).
var (
AnvilAccount0 = &Account{
Address: common.HexToAddress("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"),
PrivateKey: "ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80",
}
AnvilAccount1 = &Account{
Address: common.HexToAddress("0x70997970C51812dc3A010C7d01b50e0d17dc79C8"),
PrivateKey: "59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d",
}
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Exporting preconfigured test accounts as global pointers allows any test to accidentally mutate their fields (e.g., changing the PrivateKey), which can lead to flaky tests and hard-to-debug side effects. It is safer to define them as values or provide them via a function that returns a fresh copy.

Suggested change
// Preconfigured Anvil test accounts (deterministic from mnemonic).
var (
AnvilAccount0 = &Account{
Address: common.HexToAddress("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"),
PrivateKey: "ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80",
}
AnvilAccount1 = &Account{
Address: common.HexToAddress("0x70997970C51812dc3A010C7d01b50e0d17dc79C8"),
PrivateKey: "59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d",
}
)
// Preconfigured Anvil test accounts (deterministic from mnemonic).
var (
AnvilAccount0 = Account{
Address: common.HexToAddress("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"),
PrivateKey: "ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80",
}
AnvilAccount1 = Account{
Address: common.HexToAddress("0x70997970C51812dc3A010C7d01b50e0d17dc79C8"),
PrivateKey: "59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d",
}
)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant