 d96c931a29
			
		
	
	d96c931a29
	
	
	
		
			
			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")
 | |
| } |