 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>
		
			
				
	
	
		
			459 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			459 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package ucxi
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"fmt"
 | |
| 	"testing"
 | |
| 	"time"
 | |
| 
 | |
| 	"chorus.services/bzzz/pkg/ucxl"
 | |
| )
 | |
| 
 | |
| func TestNewBasicAddressResolver(t *testing.T) {
 | |
| 	nodeID := "test-node-123"
 | |
| 	resolver := NewBasicAddressResolver(nodeID)
 | |
| 	
 | |
| 	if resolver == nil {
 | |
| 		t.Error("NewBasicAddressResolver should not return nil")
 | |
| 	}
 | |
| 	
 | |
| 	if resolver.nodeID != nodeID {
 | |
| 		t.Errorf("Node ID = %s, want %s", resolver.nodeID, nodeID)
 | |
| 	}
 | |
| 	
 | |
| 	if resolver.registry == nil {
 | |
| 		t.Error("Registry should be initialized")
 | |
| 	}
 | |
| 	
 | |
| 	if resolver.defaultTTL == 0 {
 | |
| 		t.Error("Default TTL should be set")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestResolverAnnounceAndResolve(t *testing.T) {
 | |
| 	resolver := NewBasicAddressResolver("test-node")
 | |
| 	ctx := context.Background()
 | |
| 	
 | |
| 	addr, err := ucxl.Parse("ucxl://agent1:developer@project1:task1/*^")
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("Failed to parse address: %v", err)
 | |
| 	}
 | |
| 	
 | |
| 	content := &Content{
 | |
| 		Data:        []byte("test content"),
 | |
| 		ContentType: "text/plain",
 | |
| 		Metadata:    map[string]string{"version": "1.0"},
 | |
| 		CreatedAt:   time.Now(),
 | |
| 	}
 | |
| 	
 | |
| 	// Test announce
 | |
| 	err = resolver.Announce(ctx, addr, content)
 | |
| 	if err != nil {
 | |
| 		t.Errorf("Announce failed: %v", err)
 | |
| 	}
 | |
| 	
 | |
| 	// Test resolve
 | |
| 	resolved, err := resolver.Resolve(ctx, addr)
 | |
| 	if err != nil {
 | |
| 		t.Errorf("Resolve failed: %v", err)
 | |
| 	}
 | |
| 	
 | |
| 	if resolved == nil {
 | |
| 		t.Error("Resolved content should not be nil")
 | |
| 	}
 | |
| 	
 | |
| 	if string(resolved.Content.Data) != "test content" {
 | |
| 		t.Errorf("Content data = %s, want 'test content'", string(resolved.Content.Data))
 | |
| 	}
 | |
| 	
 | |
| 	if resolved.Source != "test-node" {
 | |
| 		t.Errorf("Source = %s, want 'test-node'", resolved.Source)
 | |
| 	}
 | |
| 	
 | |
| 	if resolved.Address.String() != addr.String() {
 | |
| 		t.Errorf("Address mismatch: got %s, want %s", resolved.Address.String(), addr.String())
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestResolverTTLExpiration(t *testing.T) {
 | |
| 	resolver := NewBasicAddressResolver("test-node")
 | |
| 	resolver.SetDefaultTTL(50 * time.Millisecond) // Very short TTL for testing
 | |
| 	
 | |
| 	ctx := context.Background()
 | |
| 	addr, _ := ucxl.Parse("ucxl://agent1:developer@project1:task1/*^")
 | |
| 	content := &Content{Data: []byte("test")}
 | |
| 	
 | |
| 	// Announce content
 | |
| 	resolver.Announce(ctx, addr, content)
 | |
| 	
 | |
| 	// Should resolve immediately
 | |
| 	resolved, err := resolver.Resolve(ctx, addr)
 | |
| 	if err != nil {
 | |
| 		t.Errorf("Immediate resolve failed: %v", err)
 | |
| 	}
 | |
| 	if resolved == nil {
 | |
| 		t.Error("Content should be found immediately after announce")
 | |
| 	}
 | |
| 	
 | |
| 	// Wait for TTL expiration
 | |
| 	time.Sleep(100 * time.Millisecond)
 | |
| 	
 | |
| 	// Should fail to resolve after TTL expiration
 | |
| 	resolved, err = resolver.Resolve(ctx, addr)
 | |
| 	if err == nil {
 | |
| 		t.Error("Resolve should fail after TTL expiration")
 | |
| 	}
 | |
| 	if resolved != nil {
 | |
| 		t.Error("Resolved content should be nil after TTL expiration")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestResolverWildcardMatching(t *testing.T) {
 | |
| 	resolver := NewBasicAddressResolver("test-node")
 | |
| 	ctx := context.Background()
 | |
| 	
 | |
| 	// Announce content with wildcard address
 | |
| 	wildcardAddr, _ := ucxl.Parse("ucxl://any:any@project1:task1/*^")
 | |
| 	content := &Content{Data: []byte("wildcard content")}
 | |
| 	resolver.Announce(ctx, wildcardAddr, content)
 | |
| 	
 | |
| 	// Try to resolve with specific address
 | |
| 	specificAddr, _ := ucxl.Parse("ucxl://agent1:developer@project1:task1/*^")
 | |
| 	resolved, err := resolver.Resolve(ctx, specificAddr)
 | |
| 	
 | |
| 	if err != nil {
 | |
| 		t.Errorf("Wildcard resolve failed: %v", err)
 | |
| 	}
 | |
| 	
 | |
| 	if resolved == nil {
 | |
| 		t.Error("Should resolve specific address against wildcard pattern")
 | |
| 	}
 | |
| 	
 | |
| 	if string(resolved.Content.Data) != "wildcard content" {
 | |
| 		t.Error("Should return wildcard content")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestResolverDiscover(t *testing.T) {
 | |
| 	resolver := NewBasicAddressResolver("test-node")
 | |
| 	ctx := context.Background()
 | |
| 	
 | |
| 	// Announce several pieces of content
 | |
| 	addresses := []string{
 | |
| 		"ucxl://agent1:developer@project1:task1/*^",
 | |
| 		"ucxl://agent2:developer@project1:task2/*^",
 | |
| 		"ucxl://agent1:tester@project2:task1/*^",
 | |
| 		"ucxl://agent3:admin@project1:task3/*^",
 | |
| 	}
 | |
| 	
 | |
| 	for i, addrStr := range addresses {
 | |
| 		addr, _ := ucxl.Parse(addrStr)
 | |
| 		content := &Content{Data: []byte(fmt.Sprintf("content-%d", i))}
 | |
| 		resolver.Announce(ctx, addr, content)
 | |
| 	}
 | |
| 	
 | |
| 	tests := []struct {
 | |
| 		name           string
 | |
| 		pattern        string
 | |
| 		expectedCount  int
 | |
| 		minCount       int
 | |
| 	}{
 | |
| 		{
 | |
| 			name:        "find all project1 tasks",
 | |
| 			pattern:     "ucxl://any:any@project1:any/*^",
 | |
| 			minCount:    3, // Should match 3 project1 addresses
 | |
| 		},
 | |
| 		{
 | |
| 			name:        "find all developer roles",
 | |
| 			pattern:     "ucxl://any:developer@any:any/*^",
 | |
| 			minCount:    2, // Should match 2 developer addresses
 | |
| 		},
 | |
| 		{
 | |
| 			name:        "find specific address",
 | |
| 			pattern:     "ucxl://agent1:developer@project1:task1/*^",
 | |
| 			minCount:    1, // Should match exactly 1
 | |
| 		},
 | |
| 		{
 | |
| 			name:        "find non-existent pattern",
 | |
| 			pattern:     "ucxl://nonexistent:role@project:task/*^",
 | |
| 			minCount:    0, // Should match none
 | |
| 		},
 | |
| 	}
 | |
| 	
 | |
| 	for _, tt := range tests {
 | |
| 		t.Run(tt.name, func(t *testing.T) {
 | |
| 			pattern, _ := ucxl.Parse(tt.pattern)
 | |
| 			results, err := resolver.Discover(ctx, pattern)
 | |
| 			
 | |
| 			if err != nil {
 | |
| 				t.Errorf("Discover failed: %v", err)
 | |
| 			}
 | |
| 			
 | |
| 			if len(results) < tt.minCount {
 | |
| 				t.Errorf("Results count = %d, want at least %d", len(results), tt.minCount)
 | |
| 			}
 | |
| 			
 | |
| 			// Verify all results match the pattern
 | |
| 			for _, result := range results {
 | |
| 				if !result.Address.Matches(pattern) {
 | |
| 					t.Errorf("Result address %s does not match pattern %s", 
 | |
| 						result.Address.String(), pattern.String())
 | |
| 				}
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestResolverHooks(t *testing.T) {
 | |
| 	resolver := NewBasicAddressResolver("test-node")
 | |
| 	ctx := context.Background()
 | |
| 	
 | |
| 	var announceHookCalled bool
 | |
| 	var discoverHookCalled bool
 | |
| 	
 | |
| 	// Set announce hook
 | |
| 	resolver.SetAnnounceHook(func(ctx context.Context, addr *ucxl.Address, content *Content) error {
 | |
| 		announceHookCalled = true
 | |
| 		return nil
 | |
| 	})
 | |
| 	
 | |
| 	// Set discover hook
 | |
| 	resolver.SetDiscoverHook(func(ctx context.Context, pattern *ucxl.Address) ([]*ResolvedContent, error) {
 | |
| 		discoverHookCalled = true
 | |
| 		return []*ResolvedContent{}, nil
 | |
| 	})
 | |
| 	
 | |
| 	addr, _ := ucxl.Parse("ucxl://agent1:developer@project1:task1/*^")
 | |
| 	content := &Content{Data: []byte("test")}
 | |
| 	
 | |
| 	// Test announce hook
 | |
| 	resolver.Announce(ctx, addr, content)
 | |
| 	if !announceHookCalled {
 | |
| 		t.Error("Announce hook should be called")
 | |
| 	}
 | |
| 	
 | |
| 	// Test discover hook (when address not found locally)
 | |
| 	nonExistentAddr, _ := ucxl.Parse("ucxl://nonexistent:agent@project:task/*^")
 | |
| 	resolver.Discover(ctx, nonExistentAddr)
 | |
| 	if !discoverHookCalled {
 | |
| 		t.Error("Discover hook should be called")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestResolverCleanupExpired(t *testing.T) {
 | |
| 	resolver := NewBasicAddressResolver("test-node")
 | |
| 	resolver.SetDefaultTTL(50 * time.Millisecond) // Short TTL for testing
 | |
| 	
 | |
| 	ctx := context.Background()
 | |
| 	
 | |
| 	// Add several entries
 | |
| 	for i := 0; i < 5; i++ {
 | |
| 		addr, _ := ucxl.Parse(fmt.Sprintf("ucxl://agent%d:developer@project:task/*^", i))
 | |
| 		content := &Content{Data: []byte(fmt.Sprintf("content-%d", i))}
 | |
| 		resolver.Announce(ctx, addr, content)
 | |
| 	}
 | |
| 	
 | |
| 	// Wait for TTL expiration
 | |
| 	time.Sleep(100 * time.Millisecond)
 | |
| 	
 | |
| 	// Cleanup expired entries
 | |
| 	removed := resolver.CleanupExpired()
 | |
| 	if removed != 5 {
 | |
| 		t.Errorf("Cleanup removed %d entries, want 5", removed)
 | |
| 	}
 | |
| 	
 | |
| 	// Verify all entries are gone
 | |
| 	stats := resolver.GetRegistryStats()
 | |
| 	activeEntries := stats["active_entries"].(int)
 | |
| 	if activeEntries != 0 {
 | |
| 		t.Errorf("Active entries = %d, want 0 after cleanup", activeEntries)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestResolverGetRegistryStats(t *testing.T) {
 | |
| 	resolver := NewBasicAddressResolver("test-node-123")
 | |
| 	ctx := context.Background()
 | |
| 	
 | |
| 	// Initially should have no entries
 | |
| 	stats := resolver.GetRegistryStats()
 | |
| 	if stats["total_entries"].(int) != 0 {
 | |
| 		t.Error("Should start with 0 entries")
 | |
| 	}
 | |
| 	if stats["node_id"].(string) != "test-node-123" {
 | |
| 		t.Error("Node ID should match")
 | |
| 	}
 | |
| 	
 | |
| 	// Add some entries
 | |
| 	for i := 0; i < 3; i++ {
 | |
| 		addr, _ := ucxl.Parse(fmt.Sprintf("ucxl://agent%d:developer@project:task/*^", i))
 | |
| 		content := &Content{Data: []byte(fmt.Sprintf("content-%d", i))}
 | |
| 		resolver.Announce(ctx, addr, content)
 | |
| 	}
 | |
| 	
 | |
| 	stats = resolver.GetRegistryStats()
 | |
| 	if stats["total_entries"].(int) != 3 {
 | |
| 		t.Errorf("Total entries = %d, want 3", stats["total_entries"])
 | |
| 	}
 | |
| 	if stats["active_entries"].(int) != 3 {
 | |
| 		t.Errorf("Active entries = %d, want 3", stats["active_entries"])
 | |
| 	}
 | |
| 	if stats["expired_entries"].(int) != 0 {
 | |
| 		t.Errorf("Expired entries = %d, want 0", stats["expired_entries"])
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestResolverErrorCases(t *testing.T) {
 | |
| 	resolver := NewBasicAddressResolver("test-node")
 | |
| 	ctx := context.Background()
 | |
| 	
 | |
| 	// Test nil address in Resolve
 | |
| 	_, err := resolver.Resolve(ctx, nil)
 | |
| 	if err == nil {
 | |
| 		t.Error("Resolve with nil address should return error")
 | |
| 	}
 | |
| 	
 | |
| 	// Test nil address in Announce
 | |
| 	content := &Content{Data: []byte("test")}
 | |
| 	err = resolver.Announce(ctx, nil, content)
 | |
| 	if err == nil {
 | |
| 		t.Error("Announce with nil address should return error")
 | |
| 	}
 | |
| 	
 | |
| 	// Test nil content in Announce
 | |
| 	addr, _ := ucxl.Parse("ucxl://agent:role@project:task/*^")
 | |
| 	err = resolver.Announce(ctx, addr, nil)
 | |
| 	if err == nil {
 | |
| 		t.Error("Announce with nil content should return error")
 | |
| 	}
 | |
| 	
 | |
| 	// Test nil pattern in Discover
 | |
| 	_, err = resolver.Discover(ctx, nil)
 | |
| 	if err == nil {
 | |
| 		t.Error("Discover with nil pattern should return error")
 | |
| 	}
 | |
| 	
 | |
| 	// Test resolve non-existent address
 | |
| 	nonExistentAddr, _ := ucxl.Parse("ucxl://nonexistent:agent@project:task/*^")
 | |
| 	_, err = resolver.Resolve(ctx, nonExistentAddr)
 | |
| 	if err == nil {
 | |
| 		t.Error("Resolve non-existent address should return error")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestResolverSetDefaultTTL(t *testing.T) {
 | |
| 	resolver := NewBasicAddressResolver("test-node")
 | |
| 	
 | |
| 	newTTL := 10 * time.Minute
 | |
| 	resolver.SetDefaultTTL(newTTL)
 | |
| 	
 | |
| 	if resolver.defaultTTL != newTTL {
 | |
| 		t.Errorf("Default TTL = %v, want %v", resolver.defaultTTL, newTTL)
 | |
| 	}
 | |
| 	
 | |
| 	// Test that new content uses the new TTL
 | |
| 	ctx := context.Background()
 | |
| 	addr, _ := ucxl.Parse("ucxl://agent:role@project:task/*^")
 | |
| 	content := &Content{Data: []byte("test")}
 | |
| 	
 | |
| 	resolver.Announce(ctx, addr, content)
 | |
| 	resolved, _ := resolver.Resolve(ctx, addr)
 | |
| 	
 | |
| 	if resolved.TTL != newTTL {
 | |
| 		t.Errorf("Resolved content TTL = %v, want %v", resolved.TTL, newTTL)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Test concurrent access to resolver
 | |
| func TestResolverConcurrency(t *testing.T) {
 | |
| 	resolver := NewBasicAddressResolver("test-node")
 | |
| 	ctx := context.Background()
 | |
| 	
 | |
| 	// Run multiple goroutines that announce and resolve content
 | |
| 	done := make(chan bool, 10)
 | |
| 	
 | |
| 	for i := 0; i < 10; i++ {
 | |
| 		go func(id int) {
 | |
| 			defer func() { done <- true }()
 | |
| 			
 | |
| 			addr, _ := ucxl.Parse(fmt.Sprintf("ucxl://agent%d:developer@project:task/*^", id))
 | |
| 			content := &Content{Data: []byte(fmt.Sprintf("content-%d", id))}
 | |
| 			
 | |
| 			// Announce
 | |
| 			if err := resolver.Announce(ctx, addr, content); err != nil {
 | |
| 				t.Errorf("Goroutine %d announce failed: %v", id, err)
 | |
| 				return
 | |
| 			}
 | |
| 			
 | |
| 			// Resolve
 | |
| 			if _, err := resolver.Resolve(ctx, addr); err != nil {
 | |
| 				t.Errorf("Goroutine %d resolve failed: %v", id, err)
 | |
| 				return
 | |
| 			}
 | |
| 			
 | |
| 			// Discover
 | |
| 			pattern, _ := ucxl.Parse("ucxl://any:any@project:task/*^")
 | |
| 			if _, err := resolver.Discover(ctx, pattern); err != nil {
 | |
| 				t.Errorf("Goroutine %d discover failed: %v", id, err)
 | |
| 				return
 | |
| 			}
 | |
| 		}(i)
 | |
| 	}
 | |
| 	
 | |
| 	// Wait for all goroutines to complete
 | |
| 	for i := 0; i < 10; i++ {
 | |
| 		<-done
 | |
| 	}
 | |
| 	
 | |
| 	// Verify final state
 | |
| 	stats := resolver.GetRegistryStats()
 | |
| 	if stats["total_entries"].(int) != 10 {
 | |
| 		t.Errorf("Expected 10 total entries, got %d", stats["total_entries"])
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Benchmark tests
 | |
| func BenchmarkResolverAnnounce(b *testing.B) {
 | |
| 	resolver := NewBasicAddressResolver("test-node")
 | |
| 	ctx := context.Background()
 | |
| 	
 | |
| 	addr, _ := ucxl.Parse("ucxl://agent:developer@project:task/*^")
 | |
| 	content := &Content{Data: []byte("test content")}
 | |
| 	
 | |
| 	b.ResetTimer()
 | |
| 	for i := 0; i < b.N; i++ {
 | |
| 		resolver.Announce(ctx, addr, content)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func BenchmarkResolverResolve(b *testing.B) {
 | |
| 	resolver := NewBasicAddressResolver("test-node")
 | |
| 	ctx := context.Background()
 | |
| 	
 | |
| 	addr, _ := ucxl.Parse("ucxl://agent:developer@project:task/*^")
 | |
| 	content := &Content{Data: []byte("test content")}
 | |
| 	resolver.Announce(ctx, addr, content)
 | |
| 	
 | |
| 	b.ResetTimer()
 | |
| 	for i := 0; i < b.N; i++ {
 | |
| 		resolver.Resolve(ctx, addr)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func BenchmarkResolverDiscover(b *testing.B) {
 | |
| 	resolver := NewBasicAddressResolver("test-node")
 | |
| 	ctx := context.Background()
 | |
| 	
 | |
| 	// Setup test data
 | |
| 	for i := 0; i < 100; i++ {
 | |
| 		addr, _ := ucxl.Parse(fmt.Sprintf("ucxl://agent%d:developer@project:task/*^", i))
 | |
| 		content := &Content{Data: []byte(fmt.Sprintf("content-%d", i))}
 | |
| 		resolver.Announce(ctx, addr, content)
 | |
| 	}
 | |
| 	
 | |
| 	pattern, _ := ucxl.Parse("ucxl://any:developer@project:task/*^")
 | |
| 	
 | |
| 	b.ResetTimer()
 | |
| 	for i := 0; i < b.N; i++ {
 | |
| 		resolver.Discover(ctx, pattern)
 | |
| 	}
 | |
| } |