Files
node/chains/manager_test.go
T

273 lines
6.7 KiB
Go

// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package chains
import (
"sync"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/luxfi/constants"
"github.com/luxfi/ids"
"github.com/luxfi/log"
"github.com/luxfi/metric"
"github.com/luxfi/node/nets"
"github.com/luxfi/node/vms"
"github.com/luxfi/vm"
)
// TestNew tests creating a new manager
func TestNew(t *testing.T) {
require := require.New(t)
config := &ManagerConfig{
SkipBootstrap: true,
EnableAutomining: true,
Log: log.NewNoOpLogger(),
Metrics: metric.NewMultiGatherer(),
VMManager: vms.NewManager(),
ChainDataDir: t.TempDir(),
}
m, err := New(config)
require.NoError(err)
require.NotNil(m)
// Cast to implementation to check internal state
mImpl := m.(*manager)
require.True(mImpl.SkipBootstrap)
require.True(mImpl.EnableAutomining)
require.NotNil(mImpl.chains)
require.NotNil(mImpl.chainsQueue)
}
// TestSkipBootstrapTracker tests that skip bootstrap mode uses correct tracker
func TestSkipBootstrapTracker(t *testing.T) {
require := require.New(t)
// Create a mock tracker for testing
config := &ManagerConfig{
SkipBootstrap: true,
EnableAutomining: true,
Log: log.NewNoOpLogger(),
Metrics: metric.NewMultiGatherer(),
VMManager: vms.NewManager(),
ChainDataDir: t.TempDir(),
// Tracker configuration not required for basic manager testing
}
m, err := New(config)
require.NoError(err)
require.NotNil(m)
// Verify skip bootstrap mode is enabled
mImpl := m.(*manager)
require.True(mImpl.SkipBootstrap)
// Test that manager can handle bootstrap status queries
// even when skip bootstrap is enabled
testChainID := ids.GenerateTestID()
isBootstrapped := m.IsBootstrapped(testChainID)
// When skip bootstrap is enabled, chains should be considered
// bootstrapped by default, but this specific chain doesn't exist
// so it returns false
require.False(isBootstrapped)
}
// TestQueueChainCreation tests queuing chain creation
func TestQueueChainCreation(t *testing.T) {
require := require.New(t)
// Create chains with primary network config
chainConfigs := map[ids.ID]nets.Config{
constants.PrimaryNetworkID: {},
}
chains, err := NewNets(ids.GenerateTestNodeID(), chainConfigs)
require.NoError(err)
config := &ManagerConfig{
Log: log.NewNoOpLogger(),
Metrics: metric.NewMultiGatherer(),
VMManager: vms.NewManager(),
ChainDataDir: t.TempDir(),
Nets: chains,
}
m, err := New(config)
require.NoError(err)
mImpl := m.(*manager)
// Create test chain parameters
chainID := ids.GenerateTestID()
netID := ids.GenerateTestID()
chainParams := ChainParameters{
ID: chainID,
ChainID: netID,
VMID: ids.GenerateTestID(),
}
// Queue the chain
m.QueueChainCreation(chainParams)
// Check that the chain was queued
queuedParams, ok := mImpl.chainsQueue.PopLeft()
require.True(ok)
require.Equal(chainParams.ID, queuedParams.ID)
require.Equal(chainParams.ChainID, queuedParams.ChainID)
require.Equal(chainParams.VMID, queuedParams.VMID)
}
// TestLookup tests chain alias lookup
func TestLookup(t *testing.T) {
require := require.New(t)
config := &ManagerConfig{
Log: log.NewNoOpLogger(),
Metrics: metric.NewMultiGatherer(),
VMManager: vms.NewManager(),
ChainDataDir: t.TempDir(),
}
m, err := New(config)
require.NoError(err)
// Create a test chain ID and alias
chainID := ids.GenerateTestID()
alias := "test-chain"
// Add the alias
require.NoError(m.Alias(chainID, alias))
// Lookup by alias
lookedUpID, err := m.Lookup(alias)
require.NoError(err)
require.Equal(chainID, lookedUpID)
// According to the comment in manager.go, the string representation of a chain's ID
// is also considered to be an alias of the chain. So we need to add it explicitly.
require.NoError(m.Alias(chainID, chainID.String()))
// Now lookup by ID string should work
lookedUpID, err = m.Lookup(chainID.String())
require.NoError(err)
require.Equal(chainID, lookedUpID)
}
// TestIsBootstrapped tests checking if a chain is bootstrapped
func TestIsBootstrapped(t *testing.T) {
require := require.New(t)
config := &ManagerConfig{
Log: log.NewNoOpLogger(),
Metrics: metric.NewMultiGatherer(),
VMManager: vms.NewManager(),
ChainDataDir: t.TempDir(),
}
m, err := New(config)
require.NoError(err)
// Test non-existent chain
chainID := ids.GenerateTestID()
require.False(m.IsBootstrapped(chainID))
}
// TestToEngineChannelFlow verifies the toEngine channel notification flow
// This tests the goroutine that reads from toEngine and triggers block building
func TestToEngineChannelFlow(t *testing.T) {
require := require.New(t)
// Create toEngine channel (same as what manager creates)
toEngine := make(chan vm.Message, 1)
defer close(toEngine)
// Track block builds
var buildCalls int
var mu sync.Mutex
// Simulate the goroutine that reads from toEngine
done := make(chan struct{})
go func() {
defer close(done)
for msg := range toEngine {
if msg.Type == 0 { // PendingTxs
mu.Lock()
buildCalls++
mu.Unlock()
}
}
}()
// Send PendingTxs notification
toEngine <- vm.Message{Type: 0} // PendingTxs = 0
// Give goroutine time to process
time.Sleep(10 * time.Millisecond)
mu.Lock()
count := buildCalls
mu.Unlock()
require.Equal(1, count, "Expected 1 build call after PendingTxs notification")
// Send multiple notifications
for i := 0; i < 5; i++ {
toEngine <- vm.Message{Type: 0}
}
time.Sleep(50 * time.Millisecond)
mu.Lock()
count = buildCalls
mu.Unlock()
require.Equal(6, count, "Expected 6 total build calls")
}
// TestToEngineMessageTypes verifies different message types are handled correctly
func TestToEngineMessageTypes(t *testing.T) {
require := require.New(t)
toEngine := make(chan vm.Message, 10)
defer close(toEngine)
var pendingTxsCalls int
var otherCalls int
var mu sync.Mutex
done := make(chan struct{})
go func() {
defer close(done)
for msg := range toEngine {
mu.Lock()
if msg.Type == 0 { // PendingTxs
pendingTxsCalls++
} else {
otherCalls++
}
mu.Unlock()
}
}()
// Send different message types
toEngine <- vm.Message{Type: 0} // PendingTxs - should trigger build
toEngine <- vm.Message{Type: 1} // StateSyncDone - should NOT trigger build
toEngine <- vm.Message{Type: 0} // PendingTxs - should trigger build
toEngine <- vm.Message{Type: 2} // Unknown - should NOT trigger build
time.Sleep(50 * time.Millisecond)
mu.Lock()
pendingCount := pendingTxsCalls
otherCount := otherCalls
mu.Unlock()
require.Equal(2, pendingCount, "Expected 2 PendingTxs messages")
require.Equal(2, otherCount, "Expected 2 other messages")
}