TERANODE Testing Framework Technical Reference
Framework Components
Implementation files: test/utils/testenv.go, test/utils/arrange.go
TeranodeTestEnv Structure
type TeranodeTestEnv struct {
TConfig tconfig.TConfig // Test configuration
Context context.Context // Test context
Compose tc.ComposeStack // Docker compose stack
ComposeSharedStorage tstore.TStoreClient // Shared storage client
Nodes []TeranodeTestClient // Array of test nodes
LegacyNodes []SVNodeTestClient // Array of legacy nodes
Logger ulogger.Logger // Framework logger
Cancel context.CancelFunc // Context cancellation
Daemon daemon.Daemon // Daemon instance
}
TeranodeTestClient Structure
type TeranodeTestClient struct {
Name string // Node identifier
SettingsContext string // Configuration context
BlockchainClient bc.ClientI // Blockchain service client
BlockassemblyClient ba.Client // Block assembly client
DistributorClient distributor.Distributor // Distribution service client
ClientBlockstore *bhttp.HTTPStore // HTTP client for block storage
ClientSubtreestore *bhttp.HTTPStore // HTTP client for subtree storage
UtxoStore *utxostore.Store // UTXO storage
CoinbaseClient *stubs.CoinbaseClient // Coinbase service client stub
AssetURL string // Asset service URL
RPCURL string // RPC service URL
IPAddress string // Node IP address
SVNodeIPAddress string // Legacy node IP address
Settings *settings.Settings // Node settings
BlockChainDB bcs.Store // Blockchain storage
}
SVNodeTestClient Structure
type SVNodeTestClient struct {
Name string // Node identifier
IPAddress string // Node IP address
}
Framework Setup and Usage
Creating a Test Environment
The test environment is created using the NewTeraNodeTestEnv function, which accepts a test configuration:
func NewTeraNodeTestEnv(c tconfig.TConfig) *TeranodeTestEnv {
logger := ulogger.New("e2eTestRun", ulogger.WithLevel(c.Suite.LogLevel))
ctx, cancel := context.WithCancel(context.Background())
return &TeranodeTestEnv{
TConfig: c,
Context: ctx,
Logger: logger,
Cancel: cancel,
}
}
Setting Up Docker Nodes
The SetupDockerNodes method initializes the Docker Compose environment with the provided settings:
func (t *TeranodeTestEnv) SetupDockerNodes() error {
// Set up Docker Compose environment with provided settings
// Create test directory for test-specific data
// Configure environment settings including TEST_ID
// Set up shared storage client for local docker compose
// Initialize teranode and legacy node configurations
}
Initializing Node Clients
The InitializeTeranodeTestClients method sets up all the necessary client connections for the nodes:
func (t *TeranodeTestEnv) InitializeTeranodeTestClients() error {
// Set up blob stores for block and subtree data
// Retrieve IP addresses for containers
// Set up RPC and Asset service URLs
// Initialize blockchain, blockassembly and distributor clients
// Configure UTXO stores and other required connections
// Connect to necessary services for testing
}
Using the Framework in Tests
A typical test using this framework would follow this pattern:
func TestSomeFunctionality(t *testing.T) {
// Create test configuration
config := tconfig.NewTConfig("testID")
// Create test environment
env := utils.NewTeraNodeTestEnv(config)
// Set up Docker nodes
err := env.SetupDockerNodes()
require.NoError(t, err)
// Initialize node clients
err = env.InitializeTeranodeTestClients()
require.NoError(t, err)
// Run tests using the environment
// Access node clients via env.Nodes[i]
// Clean up when done
env.Cancel()
}
TeranodeTestSuite Lifecycle
Defined in test/utils/arrange.go. The TeranodeTestSuite provides a testify suite wrapper with automatic setup and teardown.
type TeranodeTestSuite struct {
suite.Suite
TeranodeTestEnv *TeranodeTestEnv
TConfig tconfig.TConfig
}
Setup and Teardown
The suite automatically handles test environment lifecycle:
// SetupTest runs before each test
func (suite *TeranodeTestSuite) SetupTest() {
// Initialize test environment
// Set up Docker nodes if configured
// Initialize node clients
// Send initial RUN event to blockchain
// Wait for all nodes to be healthy
// Optionally generate initial blocks based on InitBlockHeight
}
// TearDownTest runs after each test
func (suite *TeranodeTestSuite) TearDownTest() {
// Stop Docker nodes
// Clean up resources
// Cancel context
}
Helper Functions
// setupLocalTeranodeTestEnv creates and configures the test environment (test/utils/arrange.go:167)
func setupLocalTeranodeTestEnv(cfg tconfig.TConfig) (*TeranodeTestEnv, error)
// teardownLocalTeranodeTestEnv cleans up the test environment (test/utils/arrange.go:176)
func teardownLocalTeranodeTestEnv(testEnv *TeranodeTestEnv) error
// SetupPostgresTestDaemon creates a test daemon with Postgres backend (test/utils/arrange.go:184)
func SetupPostgresTestDaemon(t *testing.T, ctx context.Context, containerName string) *daemon.TestDaemon
Test Categories and Tags
Core Test Categories
| Category | Tag | Description |
|---|---|---|
| TNA | tnatests | Node responsibilities and network communication |
| TNB | tnbtests | Transaction validation and processing |
| TND | tndtests | Block propagation through network |
| TNF | tnftests | Longest chain management |
| TNJ | tnjtests | Consensus rules compliance |
| TEC | tectests | Error handling and recovery scenarios |
Service Port Mappings
Note: The actual docker-compose.e2etest.yml uses dynamic port mapping. Ports are assigned by Docker and can be retrieved using GetMappedPort().
| Service | Internal Port/Range | Description |
|---|---|---|
| Health Check | 8000/tcp | Daemon health check endpoint |
| Services GRPC | 8081-8097/tcp | Range includes blockchain (8087), block assembly (8085), propagation (8084), etc. |
| Monitoring | 8099/tcp | Metrics/monitoring endpoint |
| Asset HTTP | 9091/tcp | Asset service HTTP API |
| P2P | 9905/tcp | Peer-to-peer network communication |
| RPC | 9292/tcp | JSON-RPC interface |
Configuration Reference
Environment Variables
# Core Settings
SETTINGS_CONTEXT # Configuration context for the test run
LOG_LEVEL # Logging verbosity (DEBUG, INFO, WARN, ERROR)
GITHUB_ACTIONS # CI environment indicator
# Node Settings
SETTINGS_CONTEXT_1 # Configuration for node 1
SETTINGS_CONTEXT_2 # Configuration for node 2
SETTINGS_CONTEXT_3 # Configuration for node 3
Docker Compose Configuration
File: test/docker-compose.e2etest.yml
Note: The actual compose file includes additional services not shown here: 3 Aerospike instances, Kafka/Redpanda console, 3 blob block stores, 3 blob subtree stores, and a test storage service.
# Simplified excerpt from docker-compose.e2etest.yml
services:
teranode1:
image: teranode
environment:
- SETTINGS_CONTEXT=${SETTINGS_CONTEXT_1}
ports:
- "10090:8090" # Health check port
- "10093:8093" # Coinbase service
- "10087:8087" # Blockchain service
- "10085:8085" # Block assembly
- "10091:8091" # Asset service
- "10092:8092" # RPC service
teranode2:
# Similar configuration with ports 12090, 12093, 12087, etc.
teranode3:
# Similar configuration with ports 14090, 14093, 14087, etc.
Utility Methods
Node Management Methods
// StartNode starts a specific TeraNode by name
func (t *TeranodeTestEnv) StartNode(nodeName string) error {
// Start a specific node in the Docker Compose environment
// Wait for the node to be healthy
}
// StopNode stops a specific TeraNode by name
func (t *TeranodeTestEnv) StopNode(nodeName string) error {
// Stop a specific node in the Docker Compose environment
}
// RestartDockerNodes restarts the Docker Compose services
func (t *TeranodeTestEnv) RestartDockerNodes(envSettings map[string]string) error {
// Stop the current Docker Compose environment
// Re-initialize with potentially new settings
// Restart the services with the updated configuration
}
// StopDockerNodes stops the Docker Compose services and removes volumes
func (t *TeranodeTestEnv) StopDockerNodes() error {
// Stop all services and clean up resources
}
Client Setup Methods
// Sets up HTTP stores for blocks and subtrees
func (t *TeranodeTestEnv) setupBlobStores() error {
// Create HTTP clients for blob stores
// Configure block and subtree storage access
}
// Sets up blockchain client for a node
func (t *TeranodeTestEnv) setupBlockchainClient(node *TeranodeTestClient) error {
// Initialize gRPC connection to blockchain service
// Create and configure blockchain client
}
// Sets up block assembly client for a node
func (t *TeranodeTestEnv) setupBlockassemblyClient(node *TeranodeTestClient) error {
// Initialize gRPC connection to block assembly service
// Create and configure block assembly client
}
// Sets up distributor client for a node
func (t *TeranodeTestEnv) setupDistributorClient(node *TeranodeTestClient) error {
// Initialize gRPC connection to distributor service
// Create and configure distributor client
}
// GetMappedPort retrieves the mapped port for a service running in Docker Compose
func (t *TeranodeTestEnv) GetMappedPort(nodeName string, port nat.Port) (nat.Port, error) {
// Find the exposed port mapping for a container service
}
Transaction Utilities
// CreateAndSendTx creates and sends a transaction
func (n *TeranodeTestClient) CreateAndSendTx(t *testing.T, ctx context.Context, parentTx *bt.Tx) (*bt.Tx, error) {
// Create a new transaction spending outputs from the parent transaction
// Sign the transaction with test keys
// Submit the transaction to the node
// Return the created transaction
}
// CreateAndSendTxs creates and sends a chain of transactions
func (n *TeranodeTestClient) CreateAndSendTxs(t *testing.T, ctx context.Context, parentTx *bt.Tx, count int) ([]*bt.Tx, []*chainhash.Hash, error) {
// Create a series of chained transactions
// Each transaction spends outputs from the previous one
// Submit all transactions to the node
// Return the created transactions and their hashes
}
// CreateAndSendTxsConcurrently creates and sends transactions concurrently
func (n *TeranodeTestClient) CreateAndSendTxsConcurrently(t *testing.T, ctx context.Context, parentTx *bt.Tx, count int) ([]*bt.Tx, []*chainhash.Hash, error) {
// Create multiple transactions concurrently
// Use goroutines to parallelize transaction creation and submission
// Wait for all transactions to complete
// Return the created transactions and their hashes
}
API Reference
Framework Methods
// Core Framework Methods
func NewTeraNodeTestEnv(c tconfig.TConfig) *TeranodeTestEnv
func (t *TeranodeTestEnv) SetupDockerNodes() error
func (t *TeranodeTestEnv) InitializeTeranodeTestClients() error
func (t *TeranodeTestEnv) StopDockerNodes() error
func (t *TeranodeTestEnv) RestartDockerNodes(envSettings map[string]string) error
func (t *TeranodeTestEnv) StartNode(nodeName string) error
func (t *TeranodeTestEnv) StopNode(nodeName string) error
func (t *TeranodeTestEnv) GetMappedPort(nodeName string, port nat.Port) (nat.Port, error)
func (t *TeranodeTestEnv) GetContainerIPAddress(node *TeranodeTestClient) error
func (t *TeranodeTestEnv) GetLegacyContainerIPAddress(node *SVNodeTestClient) error
// Transaction Utility Methods
func (n *TeranodeTestClient) CreateAndSendTx(t *testing.T, ctx context.Context, parentTx *bt.Tx) (*bt.Tx, error)
func (n *TeranodeTestClient) CreateAndSendTxs(t *testing.T, ctx context.Context, parentTx *bt.Tx, count int) ([]*bt.Tx, []*chainhash.Hash, error)
func (n *TeranodeTestClient) CreateAndSendTxsConcurrently(t *testing.T, ctx context.Context, parentTx *bt.Tx, count int) ([]*bt.Tx, []*chainhash.Hash, error)
Client Interfaces
Blockchain Client (bc.ClientI)
Defined in services/blockchain/Interface.go. The blockchain client provides access to blockchain state and operations.
// Key methods from bc.ClientI
type ClientI interface {
// Health check returns (statusCode, message, error)
Health(ctx context.Context, checkLiveness bool) (int, string, error)
// Block operations
AddBlock(ctx context.Context, block *model.Block, peerID string, opts ...options.StoreBlockOption) error
GetBlock(ctx context.Context, blockHash *chainhash.Hash) (*model.Block, error)
GetBlockByHeight(ctx context.Context, height uint32) (*model.Block, error)
// FSM state management
Run(ctx context.Context, source string) error
Idle(ctx context.Context) error
GetFSMCurrentState(ctx context.Context) (*FSMStateType, error)
WaitForFSMtoTransitionToGivenState(ctx context.Context, state FSMStateType) error
// Subscription for blockchain events
Subscribe(ctx context.Context, source string) (chan *blockchain_api.Notification, error)
// ... many additional methods for headers, state, mining, etc.
}
Block Assembly Client (ba.Client)
Defined in services/blockassembly/client.go. The block assembly client manages transaction assembly for block candidates.
// Key methods from ba.Client
type Client struct {
// ... fields ...
}
func (s *Client) Health(ctx context.Context, checkLiveness bool) (int, string, error)
func (s *Client) Store(ctx context.Context, hash *chainhash.Hash, fee, size uint64, txInpoints subtree.TxInpoints) (bool, error)
func (s *Client) RemoveTx(ctx context.Context, hash *chainhash.Hash) error
func (s *Client) GetMiningCandidate(ctx context.Context, includeSubtreeHashes ...bool) (*model.MiningCandidate, error)
func (s *Client) SubmitMiningSolution(ctx context.Context, solution *model.MiningSolution) error
func (s *Client) BlockAssemblyAPIClient() blockassembly_api.BlockAssemblyAPIClient
Coinbase Client (Test Stub)
Defined in test/utils/stubs/coinbase_client.go. The coinbase client is a test stub that provides mock coinbase transaction functionality.
type CoinbaseClient struct {
balance uint64
}
func NewCoinbaseClient() *CoinbaseClient
func (c *CoinbaseClient) GetBalance(ctx context.Context) (uint64, uint64, error)
func (c *CoinbaseClient) RequestFunds(ctx context.Context, address string, wait bool) (*bt.Tx, error)
func (c *CoinbaseClient) SetMalformedUTXOConfig(ctx context.Context, percentage int32, malformationType MalformationType) error
Error Types
// Common error categories
errors.NewConfigurationError(format string, args ...interface{}) error
errors.NewServiceError(format string, args ...interface{}) error
errors.NewValidationError(format string, args ...interface{}) error
Directory Structure
test/
├── aerospike/ # Aerospike database tests
├── chaos/ # Chaos engineering tests
├── config/ # Test configuration files
├── consensus/ # Consensus mechanism tests
├── e2e/ # End-to-end integration tests
├── fsm/ # Finite State Machine tests
├── longtest/ # Long-running performance tests
├── nodeHelpers/ # Node helper utilities
├── postgres/ # PostgreSQL database tests
├── rpc/ # RPC service tests
├── scripts/ # Test automation scripts
├── sequentialtest/ # Sequential test execution
├── tec/ # Error case tests
├── testcontainers/ # Docker container test utilities
├── tna/ # Node responsibility tests
├── tnb/ # Transaction validation tests
├── tnd/ # Block propagation tests
├── tnf/ # Longest chain management tests
├── tnj/ # Consensus rules compliance tests
├── txregistry/ # Transaction registry tests
└── utils/ # Utility functions and test framework