This comprehensive refactoring addresses critical architectural issues: IMPORT CYCLE RESOLUTION: • pkg/crypto ↔ pkg/slurp/roles: Created pkg/security/access_levels.go • pkg/ucxl → pkg/dht: Created pkg/storage/interfaces.go • pkg/slurp/leader → pkg/election → pkg/slurp/storage: Moved types to pkg/election/interfaces.go MODULE PATH MIGRATION: • Changed from github.com/anthonyrawlins/bzzz to chorus.services/bzzz • Updated all import statements across 115+ files • Maintains compatibility while removing personal GitHub account dependency TYPE SYSTEM IMPROVEMENTS: • Resolved duplicate type declarations in crypto package • Added missing type definitions (RoleStatus, TimeRestrictions, KeyStatus, KeyRotationResult) • Proper interface segregation to prevent future cycles ARCHITECTURAL BENEFITS: • Build now progresses past structural issues to normal dependency resolution • Cleaner separation of concerns between packages • Eliminates circular dependencies that prevented compilation • Establishes foundation for scalable codebase growth 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
244 lines
6.2 KiB
Go
244 lines
6.2 KiB
Go
package integration_test
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
"chorus.services/bzzz/pkg/config"
|
|
"chorus.services/bzzz/pkg/election"
|
|
)
|
|
|
|
func TestElectionIntegration_ElectionLogic(t *testing.T) {
|
|
// Test election management lifecycle
|
|
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
|
defer cancel()
|
|
|
|
cfg := &config.Config{
|
|
Agent: config.AgentConfig{
|
|
ID: "test-node",
|
|
},
|
|
Security: config.SecurityConfig{
|
|
ElectionConfig: config.ElectionConfig{
|
|
Enabled: true,
|
|
HeartbeatTimeout: 5 * time.Second,
|
|
ElectionTimeout: 10 * time.Second,
|
|
},
|
|
},
|
|
}
|
|
|
|
// Create a minimal election manager without full P2P (pass nils for deps we don't need)
|
|
em := election.NewElectionManager(ctx, cfg, nil, nil, "test-node")
|
|
if em == nil {
|
|
t.Fatal("Expected NewElectionManager to return non-nil manager")
|
|
}
|
|
|
|
// Test election states
|
|
initialState := em.GetElectionState()
|
|
if initialState != election.StateIdle {
|
|
t.Errorf("Expected initial state to be StateIdle, got %v", initialState)
|
|
}
|
|
|
|
// Test admin status methods
|
|
currentAdmin := em.GetCurrentAdmin()
|
|
if currentAdmin != "" {
|
|
t.Logf("Current admin: %s", currentAdmin)
|
|
}
|
|
|
|
isAdmin := em.IsCurrentAdmin()
|
|
t.Logf("Is current admin: %t", isAdmin)
|
|
|
|
// Test trigger election (this is the real available method)
|
|
em.TriggerElection(election.TriggerManual)
|
|
|
|
// Test state after trigger
|
|
newState := em.GetElectionState()
|
|
t.Logf("State after trigger: %v", newState)
|
|
|
|
t.Log("Election integration test completed successfully")
|
|
}
|
|
|
|
func TestElectionIntegration_AdminFailover(t *testing.T) {
|
|
// Test admin failover scenarios using election triggers
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
cfg := &config.Config{
|
|
Agent: config.AgentConfig{
|
|
ID: "failover-test-node",
|
|
},
|
|
Security: config.SecurityConfig{
|
|
ElectionConfig: config.ElectionConfig{
|
|
Enabled: true,
|
|
HeartbeatTimeout: 3 * time.Second,
|
|
ElectionTimeout: 6 * time.Second,
|
|
},
|
|
},
|
|
}
|
|
|
|
em := election.NewElectionManager(ctx, cfg, nil, nil, "failover-test-node")
|
|
|
|
// Test initial state
|
|
initialState := em.GetElectionState()
|
|
t.Logf("Initial state: %v", initialState)
|
|
|
|
// Test heartbeat timeout trigger (simulates admin failure)
|
|
em.TriggerElection(election.TriggerHeartbeatTimeout)
|
|
|
|
// Allow some time for state change
|
|
time.Sleep(100 * time.Millisecond)
|
|
|
|
afterFailureState := em.GetElectionState()
|
|
t.Logf("State after heartbeat timeout: %v", afterFailureState)
|
|
|
|
// Test split brain scenario
|
|
em.TriggerElection(election.TriggerSplitBrain)
|
|
|
|
time.Sleep(100 * time.Millisecond)
|
|
|
|
splitBrainState := em.GetElectionState()
|
|
t.Logf("State after split brain trigger: %v", splitBrainState)
|
|
|
|
// Test quorum restoration
|
|
em.TriggerElection(election.TriggerQuorumRestored)
|
|
|
|
time.Sleep(100 * time.Millisecond)
|
|
|
|
finalState := em.GetElectionState()
|
|
t.Logf("State after quorum restored: %v", finalState)
|
|
|
|
t.Log("Failover integration test completed")
|
|
}
|
|
|
|
func TestElectionIntegration_ConcurrentElections(t *testing.T) {
|
|
// Test concurrent election triggers
|
|
ctx, cancel := context.WithTimeout(context.Background(), 8*time.Second)
|
|
defer cancel()
|
|
|
|
cfg1 := &config.Config{
|
|
Agent: config.AgentConfig{
|
|
ID: "concurrent-node-1",
|
|
},
|
|
Security: config.SecurityConfig{
|
|
ElectionConfig: config.ElectionConfig{
|
|
Enabled: true,
|
|
HeartbeatTimeout: 4 * time.Second,
|
|
ElectionTimeout: 8 * time.Second,
|
|
},
|
|
},
|
|
}
|
|
|
|
cfg2 := &config.Config{
|
|
Agent: config.AgentConfig{
|
|
ID: "concurrent-node-2",
|
|
},
|
|
Security: config.SecurityConfig{
|
|
ElectionConfig: config.ElectionConfig{
|
|
Enabled: true,
|
|
HeartbeatTimeout: 4 * time.Second,
|
|
ElectionTimeout: 8 * time.Second,
|
|
},
|
|
},
|
|
}
|
|
|
|
em1 := election.NewElectionManager(ctx, cfg1, nil, nil, "concurrent-node-1")
|
|
em2 := election.NewElectionManager(ctx, cfg2, nil, nil, "concurrent-node-2")
|
|
|
|
// Trigger elections concurrently
|
|
go func() {
|
|
em1.TriggerElection(election.TriggerManual)
|
|
}()
|
|
|
|
go func() {
|
|
em2.TriggerElection(election.TriggerManual)
|
|
}()
|
|
|
|
// Wait for processing
|
|
time.Sleep(200 * time.Millisecond)
|
|
|
|
// Check states
|
|
state1 := em1.GetElectionState()
|
|
state2 := em2.GetElectionState()
|
|
|
|
t.Logf("Node 1 state: %v", state1)
|
|
t.Logf("Node 2 state: %v", state2)
|
|
|
|
// Both should be handling elections
|
|
if state1 == election.StateIdle && state2 == election.StateIdle {
|
|
t.Error("Expected at least one election manager to be in non-idle state")
|
|
}
|
|
|
|
t.Log("Concurrent elections test completed")
|
|
}
|
|
|
|
func TestElectionIntegration_ElectionCallbacks(t *testing.T) {
|
|
// Test election callback system
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
|
|
cfg := &config.Config{
|
|
Agent: config.AgentConfig{
|
|
ID: "callback-test-node",
|
|
},
|
|
Security: config.SecurityConfig{
|
|
ElectionConfig: config.ElectionConfig{
|
|
Enabled: true,
|
|
HeartbeatTimeout: 5 * time.Second,
|
|
ElectionTimeout: 10 * time.Second,
|
|
},
|
|
},
|
|
}
|
|
|
|
em := election.NewElectionManager(ctx, cfg, nil, nil, "callback-test-node")
|
|
|
|
// Track callback invocations
|
|
var adminChangedCalled bool
|
|
var electionCompleteCalled bool
|
|
var oldAdmin, newAdmin, winner string
|
|
|
|
// Set up callbacks
|
|
em.SetCallbacks(
|
|
func(old, new string) {
|
|
adminChangedCalled = true
|
|
oldAdmin = old
|
|
newAdmin = new
|
|
t.Logf("Admin changed callback: %s -> %s", old, new)
|
|
},
|
|
func(w string) {
|
|
electionCompleteCalled = true
|
|
winner = w
|
|
t.Logf("Election complete callback: winner %s", w)
|
|
},
|
|
)
|
|
|
|
// Trigger election
|
|
em.TriggerElection(election.TriggerManual)
|
|
|
|
// Give time for potential callback execution
|
|
time.Sleep(200 * time.Millisecond)
|
|
|
|
// Check state changes
|
|
currentState := em.GetElectionState()
|
|
t.Logf("Current election state: %v", currentState)
|
|
|
|
isAdmin := em.IsCurrentAdmin()
|
|
t.Logf("Is current admin: %t", isAdmin)
|
|
|
|
currentAdminID := em.GetCurrentAdmin()
|
|
t.Logf("Current admin ID: %s", currentAdminID)
|
|
|
|
// Log callback results
|
|
t.Logf("Admin changed callback called: %t", adminChangedCalled)
|
|
t.Logf("Election complete callback called: %t", electionCompleteCalled)
|
|
|
|
if adminChangedCalled {
|
|
t.Logf("Admin change: %s -> %s", oldAdmin, newAdmin)
|
|
}
|
|
|
|
if electionCompleteCalled {
|
|
t.Logf("Election winner: %s", winner)
|
|
}
|
|
|
|
t.Log("Election callback integration test completed")
|
|
} |