Automated Testing
Setting Up Test Environment
How to Set Up Your Local Test Environment
-
Prerequisites:
# Install required tools docker compose go 1.21 or higher make -
Initial setup:
# Clone and build git clone [repository] cd teranode docker compose build -
Verify setup:
# Run smoke tests make smoketest
How to Configure Test Settings
-
Create local settings:
touch settings_local.conf -
Add required settings for your specific testing activity. Common test contexts:
test- Standard testing environmentdocker- Docker-based testingdocker.ci- CI environment testing
Running Tests
How to Run Test Suites
-
Run all tests in a suite:
# Format cd /teranode/test/<suite-name> go test -v -tags test_<suite-code> # Examples # Run TNA tests cd /teranode/test/tna go test -v -tags test_tna # Run TNB tests cd /teranode/test/tnb go test -v -tags test_tnb # Run TEC tests cd /teranode/test/tec go test -v -tags test_tec -
Run specific test:
# Format go test -v -run "^<TestSuiteName>$/^<TestName>$" -tags <test-tag> # Example: Run specific TNA test go test -v -run "^TestTNA1TestSuite$/^TestBroadcastNewTxAllNodes$" -tags test_tna -
Run with different options:
# With timeout go test -v -tags test_tna -timeout 10m # With race detection go test -v -race -tags test_tna # With specific cache size go test -v -tags "test_tna,testtxmetacache"
How to Debug Failed Tests
-
Enable verbose logging:
# Set log level to DEBUG export LOG_LEVEL=DEBUG go test -v -tags test_tna -
Check container logs:
# View logs for specific node docker logs teranode1 docker logs teranode2 docker logs teranode3 -
Inspect test state:
# Check node health (verify actual ports in your settings) curl http://localhost:18090/api/v1/bestblockheader/json curl http://localhost:28090/api/v1/bestblockheader/json curl http://localhost:38090/api/v1/bestblockheader/json
Adding New Tests
How to Add a New Test Case
-
Create test file with proper structure:
//go:build test_tna || debug // How to run this test manually: // $ go test -v -run "^TestTNA5TestSuite$/^TestNewFeature$" -tags test_tna package tna import ( "testing" "time" helper "github.com/bsv-blockchain/teranode/test/utils" "github.com/bsv-blockchain/teranode/test/utils/tconfig" "github.com/stretchr/testify/suite" ) type TNA5TestSuite struct { helper.TeranodeTestSuite } func TestTNA5TestSuite(t *testing.T) { suite.Run(t, &TNA5TestSuite{ TeranodeTestSuite: helper.TeranodeTestSuite{ TConfig: tconfig.LoadTConfig( map[string]any{ tconfig.KeyTeranodeContexts: []string{ "docker.teranode1.test.tna5Test", "docker.teranode2.test.tna5Test", "docker.teranode3.test.tna5Test", }, }, ), }, }, ) } func (suite *TNA5TestSuite) TestNewFeature() { // Access test environment testEnv := suite.TeranodeTestEnv ctx := testEnv.Context t := suite.T() logger := testEnv.Logger // Access nodes node1 := testEnv.Nodes[0] blockchainClient := node1.BlockchainClient // Your test logic here t.Log("Starting test") // Example: Check node health health, err := blockchainClient.Health(ctx) if err != nil { t.Fatalf("Failed to get health: %v", err) } if !health.Ok { t.Error("Expected health to be OK") } t.Log("Test completed successfully") } -
Common test patterns:
// Create and send transaction tx, err := helper.CreateAndSendTx(ctx, node1) if err != nil { t.Fatalf("Failed to create transaction: %v", err) } // Mine a block blockHash, err := helper.MineBlock(ctx, node1.Settings, node1.BlockAssemblyClient, logger) if err != nil { t.Fatalf("Failed to mine block: %v", err) } // Wait for block height err = helper.WaitForNodeBlockHeight(ctx, blockchainClient, targetHeight, 30*time.Second) if err != nil { t.Fatalf("Failed to reach target height: %v", err) } // Wait for nodes to sync blockchainClients := []blockchain.ClientI{ testEnv.Nodes[0].BlockchainClient, testEnv.Nodes[1].BlockchainClient, testEnv.Nodes[2].BlockchainClient, } err = helper.WaitForNodesToSync(ctx, blockchainClients, 30*time.Second) if err != nil { t.Fatalf("Nodes failed to sync: %v", err) }
How to Add Custom Test Configuration
-
Use TConfig system to customize settings:
func TestCustomTestSuite(t *testing.T) { suite.Run(t, &CustomTestSuite{ TeranodeTestSuite: helper.TeranodeTestSuite{ TConfig: tconfig.LoadTConfig( map[string]any{ tconfig.KeyTeranodeContexts: []string{ "docker.teranode1.test.customTest", }, tconfig.KeyComposeFiles: []string{ "docker-compose.e2etest.yml", "docker-compose.custom.override.yml", }, }, ), }, }) } -
Create custom docker compose override:
# docker-compose.custom.override.yml services: teranode1: environment: - CUSTOM_SETTING=value
Working with Multiple Nodes
How to Test Node Communication
-
Setup multiple nodes:
// Ensure all nodes are running for i, node := range testEnv.Nodes { health, err := node.BlockchainClient.Health(ctx) if err != nil { t.Errorf("Node %d health check failed: %v", i, err) } if !health.Ok { t.Errorf("Node %d is not healthy", i) } } -
Test transaction propagation:
// Send transaction to node 0 tx, err := helper.CreateAndSendTx(ctx, testEnv.Nodes[0]) if err != nil { t.Fatalf("Failed to create transaction: %v", err) } // Allow time for propagation time.Sleep(2 * time.Second) // Verify on other nodes block, err := testEnv.Nodes[1].BlockchainClient.GetBestBlockHeader(ctx) if err != nil { t.Fatalf("Failed to get block from node 1: %v", err) }
How to Test Node Failure Scenarios
-
Stop specific containers:
# Stop a specific node container docker stop teranode2 -
Restart containers:
# Restart the node docker start teranode2 -
Test resilience in code:
// Send transaction before node failure tx1, _ := helper.CreateAndSendTx(ctx, testEnv.Nodes[0]) // Simulate node failure by stopping docker container // (typically done manually or via docker SDK) // Verify other nodes continue working tx2, err := helper.CreateAndSendTx(ctx, testEnv.Nodes[1]) if err != nil { t.Fatalf("Remaining nodes should continue working: %v", err) }
Cleanup and Maintenance
How to Clean Test Environment
-
Remove test data:
# Clean data directory rm -rf data/test/* -
Remove containers:
# Remove all test containers docker ps -a -q --filter label=com.docker.compose.project=e2e | xargs docker rm -f -
Full cleanup in test code:
// TearDown is handled automatically by TeranodeTestSuite // But you can add custom cleanup in TearDownTest: func (suite *CustomTestSuite) TearDownTest() { // Custom cleanup here suite.TeranodeTestSuite.TearDownTest() // Call parent teardown }
How to Handle Common Issues
-
Port conflicts:
# Check port usage lsof -i :8087 lsof -i :8090 lsof -i :9292 -
Resource cleanup:
# Force remove hanging containers docker ps -a -q --filter label=com.docker.compose.project=e2e | xargs docker rm -f # Remove volumes docker volume prune -f -
Data persistence issues:
# Reset all data (use sudo if needed) rm -rf ./data/test # Or if permission issues: sudo rm -rf ./data/test
Available Test Tags
Teranode uses build tags to control which tests run:
test_tna- TNA test suitetest_tnb- TNB test suitetest_tec- TEC test suitetest_tnd- TND test suitetest_tnf- TNF test suitetest_tnj- TNJ test suitetest_smoke- Smoke teststest_functional- Functional teststesttxmetacache- Use small transaction metadata cachelargetxmetacache- Use production-sized cacheaerospike- Tests requiring Aerospikedebug- Enable debug mode for tests