 b207f32d9e
			
		
	
	b207f32d9e
	
	
	
		
			
			- Add complete UCXL address parser with BNF grammar validation - Implement temporal navigation system with bounds checking - Create UCXI HTTP server with REST-like operations - Add comprehensive test suite with 87 passing tests - Integrate with existing BZZZ architecture (opt-in via config) - Support semantic addressing with wildcards and version control Core Features: - UCXL address format: ucxl://agent:role@project:task/temporal/path - Temporal segments: *^, ~~N, ^^N, *~, *~N with navigation logic - UCXI endpoints: GET/PUT/POST/DELETE/ANNOUNCE operations - Production-ready with error handling and graceful shutdown 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			456 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			456 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package protocol
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"testing"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/libp2p/go-libp2p/core/peer"
 | |
| 	"github.com/libp2p/go-libp2p/core/peerstore"
 | |
| 	"github.com/libp2p/go-libp2p/core/test"
 | |
| )
 | |
| 
 | |
| func TestNewResolver(t *testing.T) {
 | |
| 	// Create a mock peerstore
 | |
| 	mockPeerstore := &mockPeerstore{}
 | |
| 	
 | |
| 	resolver := NewResolver(mockPeerstore)
 | |
| 	
 | |
| 	if resolver == nil {
 | |
| 		t.Fatal("resolver is nil")
 | |
| 	}
 | |
| 	
 | |
| 	if resolver.peerstore != mockPeerstore {
 | |
| 		t.Error("peerstore not set correctly")
 | |
| 	}
 | |
| 	
 | |
| 	if resolver.defaultStrategy != StrategyBestMatch {
 | |
| 		t.Errorf("expected default strategy %v, got %v", StrategyBestMatch, resolver.defaultStrategy)
 | |
| 	}
 | |
| 	
 | |
| 	if resolver.maxPeersPerResult != 5 {
 | |
| 		t.Errorf("expected max peers per result 5, got %d", resolver.maxPeersPerResult)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestResolverWithOptions(t *testing.T) {
 | |
| 	mockPeerstore := &mockPeerstore{}
 | |
| 	
 | |
| 	resolver := NewResolver(mockPeerstore,
 | |
| 		WithCacheTTL(10*time.Minute),
 | |
| 		WithDefaultStrategy(StrategyPriority),
 | |
| 		WithMaxPeersPerResult(10),
 | |
| 	)
 | |
| 	
 | |
| 	if resolver.cacheTTL != 10*time.Minute {
 | |
| 		t.Errorf("expected cache TTL 10m, got %v", resolver.cacheTTL)
 | |
| 	}
 | |
| 	
 | |
| 	if resolver.defaultStrategy != StrategyPriority {
 | |
| 		t.Errorf("expected strategy %v, got %v", StrategyPriority, resolver.defaultStrategy)
 | |
| 	}
 | |
| 	
 | |
| 	if resolver.maxPeersPerResult != 10 {
 | |
| 		t.Errorf("expected max peers 10, got %d", resolver.maxPeersPerResult)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestRegisterPeer(t *testing.T) {
 | |
| 	resolver := NewResolver(&mockPeerstore{})
 | |
| 	
 | |
| 	peerID := test.RandPeerIDFatal(t)
 | |
| 	capability := &PeerCapability{
 | |
| 		Agent:          "claude",
 | |
| 		Role:           "frontend",
 | |
| 		Capabilities:   []string{"react", "javascript"},
 | |
| 		Models:         []string{"claude-3"},
 | |
| 		Specialization: "frontend",
 | |
| 		Status:         "ready",
 | |
| 		Metadata:       make(map[string]string),
 | |
| 	}
 | |
| 	
 | |
| 	resolver.RegisterPeer(peerID, capability)
 | |
| 	
 | |
| 	// Verify peer was registered
 | |
| 	caps := resolver.GetPeerCapabilities()
 | |
| 	if len(caps) != 1 {
 | |
| 		t.Errorf("expected 1 peer, got %d", len(caps))
 | |
| 	}
 | |
| 	
 | |
| 	registeredCap, exists := caps[peerID]
 | |
| 	if !exists {
 | |
| 		t.Error("peer not found in capabilities")
 | |
| 	}
 | |
| 	
 | |
| 	if registeredCap.Agent != capability.Agent {
 | |
| 		t.Errorf("expected agent %s, got %s", capability.Agent, registeredCap.Agent)
 | |
| 	}
 | |
| 	
 | |
| 	if registeredCap.PeerID != peerID {
 | |
| 		t.Error("peer ID not set correctly")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestUnregisterPeer(t *testing.T) {
 | |
| 	resolver := NewResolver(&mockPeerstore{})
 | |
| 	
 | |
| 	peerID := test.RandPeerIDFatal(t)
 | |
| 	capability := &PeerCapability{
 | |
| 		Agent: "claude",
 | |
| 		Role:  "frontend",
 | |
| 	}
 | |
| 	
 | |
| 	// Register then unregister
 | |
| 	resolver.RegisterPeer(peerID, capability)
 | |
| 	resolver.UnregisterPeer(peerID)
 | |
| 	
 | |
| 	caps := resolver.GetPeerCapabilities()
 | |
| 	if len(caps) != 0 {
 | |
| 		t.Errorf("expected 0 peers after unregister, got %d", len(caps))
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestUpdatePeerStatus(t *testing.T) {
 | |
| 	resolver := NewResolver(&mockPeerstore{})
 | |
| 	
 | |
| 	peerID := test.RandPeerIDFatal(t)
 | |
| 	capability := &PeerCapability{
 | |
| 		Agent:  "claude",
 | |
| 		Role:   "frontend",
 | |
| 		Status: "ready",
 | |
| 	}
 | |
| 	
 | |
| 	resolver.RegisterPeer(peerID, capability)
 | |
| 	resolver.UpdatePeerStatus(peerID, "busy")
 | |
| 	
 | |
| 	caps := resolver.GetPeerCapabilities()
 | |
| 	updatedCap := caps[peerID]
 | |
| 	
 | |
| 	if updatedCap.Status != "busy" {
 | |
| 		t.Errorf("expected status 'busy', got '%s'", updatedCap.Status)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestResolveURI(t *testing.T) {
 | |
| 	resolver := NewResolver(&mockPeerstore{})
 | |
| 	
 | |
| 	// Register some test peers
 | |
| 	peerID1 := test.RandPeerIDFatal(t)
 | |
| 	peerID2 := test.RandPeerIDFatal(t)
 | |
| 	
 | |
| 	resolver.RegisterPeer(peerID1, &PeerCapability{
 | |
| 		Agent:        "claude",
 | |
| 		Role:         "frontend",
 | |
| 		Capabilities: []string{"react", "javascript"},
 | |
| 		Status:       "ready",
 | |
| 		Metadata:     map[string]string{"project": "chorus"},
 | |
| 	})
 | |
| 	
 | |
| 	resolver.RegisterPeer(peerID2, &PeerCapability{
 | |
| 		Agent:        "claude",
 | |
| 		Role:         "backend",
 | |
| 		Capabilities: []string{"go", "api"},
 | |
| 		Status:       "ready",
 | |
| 		Metadata:     map[string]string{"project": "chorus"},
 | |
| 	})
 | |
| 	
 | |
| 	// Test exact match
 | |
| 	uri, err := ParseBzzzURI("bzzz://claude:frontend@chorus:react")
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("failed to parse URI: %v", err)
 | |
| 	}
 | |
| 	
 | |
| 	ctx := context.Background()
 | |
| 	result, err := resolver.Resolve(ctx, uri)
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("failed to resolve URI: %v", err)
 | |
| 	}
 | |
| 	
 | |
| 	if len(result.Peers) != 1 {
 | |
| 		t.Errorf("expected 1 peer in result, got %d", len(result.Peers))
 | |
| 	}
 | |
| 	
 | |
| 	if result.Peers[0].PeerID != peerID1 {
 | |
| 		t.Error("wrong peer returned")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestResolveURIWithWildcards(t *testing.T) {
 | |
| 	resolver := NewResolver(&mockPeerstore{})
 | |
| 	
 | |
| 	peerID1 := test.RandPeerIDFatal(t)
 | |
| 	peerID2 := test.RandPeerIDFatal(t)
 | |
| 	
 | |
| 	resolver.RegisterPeer(peerID1, &PeerCapability{
 | |
| 		Agent:        "claude",
 | |
| 		Role:         "frontend",
 | |
| 		Capabilities: []string{"react"},
 | |
| 		Status:       "ready",
 | |
| 	})
 | |
| 	
 | |
| 	resolver.RegisterPeer(peerID2, &PeerCapability{
 | |
| 		Agent:        "claude", 
 | |
| 		Role:         "backend",
 | |
| 		Capabilities: []string{"go"},
 | |
| 		Status:       "ready",
 | |
| 	})
 | |
| 	
 | |
| 	// Test wildcard match
 | |
| 	uri, err := ParseBzzzURI("bzzz://claude:*@*:*")
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("failed to parse URI: %v", err)
 | |
| 	}
 | |
| 	
 | |
| 	ctx := context.Background()
 | |
| 	result, err := resolver.Resolve(ctx, uri)
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("failed to resolve URI: %v", err)
 | |
| 	}
 | |
| 	
 | |
| 	if len(result.Peers) != 2 {
 | |
| 		t.Errorf("expected 2 peers in result, got %d", len(result.Peers))
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestResolveURIWithOfflinePeers(t *testing.T) {
 | |
| 	resolver := NewResolver(&mockPeerstore{})
 | |
| 	
 | |
| 	peerID := test.RandPeerIDFatal(t)
 | |
| 	
 | |
| 	resolver.RegisterPeer(peerID, &PeerCapability{
 | |
| 		Agent:  "claude",
 | |
| 		Role:   "frontend",
 | |
| 		Status: "offline", // This peer should be filtered out
 | |
| 	})
 | |
| 	
 | |
| 	uri, err := ParseBzzzURI("bzzz://claude:frontend@*:*")
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("failed to parse URI: %v", err)
 | |
| 	}
 | |
| 	
 | |
| 	ctx := context.Background()
 | |
| 	result, err := resolver.Resolve(ctx, uri)
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("failed to resolve URI: %v", err)
 | |
| 	}
 | |
| 	
 | |
| 	if len(result.Peers) != 0 {
 | |
| 		t.Errorf("expected 0 peers (offline filtered), got %d", len(result.Peers))
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestResolveString(t *testing.T) {
 | |
| 	resolver := NewResolver(&mockPeerstore{})
 | |
| 	
 | |
| 	peerID := test.RandPeerIDFatal(t)
 | |
| 	resolver.RegisterPeer(peerID, &PeerCapability{
 | |
| 		Agent:  "claude",
 | |
| 		Role:   "frontend",
 | |
| 		Status: "ready",
 | |
| 	})
 | |
| 	
 | |
| 	ctx := context.Background()
 | |
| 	result, err := resolver.ResolveString(ctx, "bzzz://claude:frontend@*:*")
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("failed to resolve string: %v", err)
 | |
| 	}
 | |
| 	
 | |
| 	if len(result.Peers) != 1 {
 | |
| 		t.Errorf("expected 1 peer, got %d", len(result.Peers))
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestResolverCaching(t *testing.T) {
 | |
| 	resolver := NewResolver(&mockPeerstore{}, WithCacheTTL(1*time.Second))
 | |
| 	
 | |
| 	peerID := test.RandPeerIDFatal(t)
 | |
| 	resolver.RegisterPeer(peerID, &PeerCapability{
 | |
| 		Agent:  "claude",
 | |
| 		Role:   "frontend",
 | |
| 		Status: "ready",
 | |
| 	})
 | |
| 	
 | |
| 	ctx := context.Background()
 | |
| 	uri := "bzzz://claude:frontend@*:*"
 | |
| 	
 | |
| 	// First resolution should hit the resolver
 | |
| 	result1, err := resolver.ResolveString(ctx, uri)
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("failed to resolve: %v", err)
 | |
| 	}
 | |
| 	
 | |
| 	// Second resolution should hit the cache
 | |
| 	result2, err := resolver.ResolveString(ctx, uri)
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("failed to resolve: %v", err)
 | |
| 	}
 | |
| 	
 | |
| 	// Results should be identical (from cache)
 | |
| 	if result1.ResolvedAt != result2.ResolvedAt {
 | |
| 		// This is expected behavior - cache should return same timestamp
 | |
| 	}
 | |
| 	
 | |
| 	// Wait for cache to expire
 | |
| 	time.Sleep(2 * time.Second)
 | |
| 	
 | |
| 	// Third resolution should miss cache and create new result
 | |
| 	result3, err := resolver.ResolveString(ctx, uri)
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("failed to resolve: %v", err)
 | |
| 	}
 | |
| 	
 | |
| 	if result3.ResolvedAt.Before(result1.ResolvedAt.Add(1 * time.Second)) {
 | |
| 		t.Error("cache should have expired and created new result")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestResolutionStrategies(t *testing.T) {
 | |
| 	resolver := NewResolver(&mockPeerstore{})
 | |
| 	
 | |
| 	// Register peers with different priorities
 | |
| 	peerID1 := test.RandPeerIDFatal(t)
 | |
| 	peerID2 := test.RandPeerIDFatal(t)
 | |
| 	
 | |
| 	resolver.RegisterPeer(peerID1, &PeerCapability{
 | |
| 		Agent:  "claude",
 | |
| 		Role:   "frontend",
 | |
| 		Status: "ready",
 | |
| 	})
 | |
| 	
 | |
| 	resolver.RegisterPeer(peerID2, &PeerCapability{
 | |
| 		Agent:  "claude",
 | |
| 		Role:   "frontend", 
 | |
| 		Status: "busy",
 | |
| 	})
 | |
| 	
 | |
| 	ctx := context.Background()
 | |
| 	uri, _ := ParseBzzzURI("bzzz://claude:frontend@*:*")
 | |
| 	
 | |
| 	// Test different strategies
 | |
| 	strategies := []ResolutionStrategy{
 | |
| 		StrategyBestMatch,
 | |
| 		StrategyPriority,
 | |
| 		StrategyLoadBalance,
 | |
| 		StrategyExact,
 | |
| 	}
 | |
| 	
 | |
| 	for _, strategy := range strategies {
 | |
| 		result, err := resolver.Resolve(ctx, uri, strategy)
 | |
| 		if err != nil {
 | |
| 			t.Errorf("failed to resolve with strategy %s: %v", strategy, err)
 | |
| 		}
 | |
| 		
 | |
| 		if len(result.Peers) == 0 {
 | |
| 			t.Errorf("no peers found with strategy %s", strategy)
 | |
| 		}
 | |
| 		
 | |
| 		if result.Strategy != string(strategy) {
 | |
| 			t.Errorf("strategy not recorded correctly: expected %s, got %s", strategy, result.Strategy)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestPeerMatching(t *testing.T) {
 | |
| 	resolver := NewResolver(&mockPeerstore{})
 | |
| 	
 | |
| 	capability := &PeerCapability{
 | |
| 		Agent:        "claude",
 | |
| 		Role:         "frontend",
 | |
| 		Capabilities: []string{"react", "javascript"},
 | |
| 		Status:       "ready",
 | |
| 		Metadata:     map[string]string{"project": "chorus"},
 | |
| 	}
 | |
| 	
 | |
| 	tests := []struct {
 | |
| 		name     string
 | |
| 		uri      *BzzzURI
 | |
| 		expected bool
 | |
| 	}{
 | |
| 		{
 | |
| 			name:     "exact match",
 | |
| 			uri:      &BzzzURI{Agent: "claude", Role: "frontend", Project: "chorus", Task: "react"},
 | |
| 			expected: true,
 | |
| 		},
 | |
| 		{
 | |
| 			name:     "wildcard agent",
 | |
| 			uri:      &BzzzURI{Agent: "*", Role: "frontend", Project: "chorus", Task: "react"},
 | |
| 			expected: true,
 | |
| 		},
 | |
| 		{
 | |
| 			name:     "capability match",
 | |
| 			uri:      &BzzzURI{Agent: "claude", Role: "frontend", Project: "*", Task: "javascript"},
 | |
| 			expected: true,
 | |
| 		},
 | |
| 		{
 | |
| 			name:     "no match - wrong agent",
 | |
| 			uri:      &BzzzURI{Agent: "gpt", Role: "frontend", Project: "chorus", Task: "react"},
 | |
| 			expected: false,
 | |
| 		},
 | |
| 		{
 | |
| 			name:     "no match - wrong role",
 | |
| 			uri:      &BzzzURI{Agent: "claude", Role: "backend", Project: "chorus", Task: "react"},
 | |
| 			expected: false,
 | |
| 		},
 | |
| 	}
 | |
| 	
 | |
| 	for _, tt := range tests {
 | |
| 		t.Run(tt.name, func(t *testing.T) {
 | |
| 			result := resolver.peerMatches(capability, tt.uri)
 | |
| 			if result != tt.expected {
 | |
| 				t.Errorf("expected %v, got %v", tt.expected, result)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestGetPeerCapability(t *testing.T) {
 | |
| 	resolver := NewResolver(&mockPeerstore{})
 | |
| 	
 | |
| 	peerID := test.RandPeerIDFatal(t)
 | |
| 	capability := &PeerCapability{
 | |
| 		Agent: "claude",
 | |
| 		Role:  "frontend",
 | |
| 	}
 | |
| 	
 | |
| 	// Test before registration
 | |
| 	_, exists := resolver.GetPeerCapability(peerID)
 | |
| 	if exists {
 | |
| 		t.Error("peer should not exist before registration")
 | |
| 	}
 | |
| 	
 | |
| 	// Register and test
 | |
| 	resolver.RegisterPeer(peerID, capability)
 | |
| 	
 | |
| 	retrieved, exists := resolver.GetPeerCapability(peerID)
 | |
| 	if !exists {
 | |
| 		t.Error("peer should exist after registration")
 | |
| 	}
 | |
| 	
 | |
| 	if retrieved.Agent != capability.Agent {
 | |
| 		t.Errorf("expected agent %s, got %s", capability.Agent, retrieved.Agent)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Mock peerstore implementation for testing
 | |
| type mockPeerstore struct{}
 | |
| 
 | |
| func (m *mockPeerstore) PeerInfo(peer.ID) peer.AddrInfo                    { return peer.AddrInfo{} }
 | |
| func (m *mockPeerstore) Peers() peer.IDSlice                               { return nil }
 | |
| func (m *mockPeerstore) Addrs(peer.ID) []peerstore.Multiaddr              { return nil }
 | |
| func (m *mockPeerstore) AddrStream(context.Context, peer.ID) <-chan peerstore.Multiaddr { return nil }
 | |
| func (m *mockPeerstore) SetAddr(peer.ID, peerstore.Multiaddr, time.Duration) {}
 | |
| func (m *mockPeerstore) SetAddrs(peer.ID, []peerstore.Multiaddr, time.Duration) {}
 | |
| func (m *mockPeerstore) UpdateAddrs(peer.ID, time.Duration, time.Duration) {}
 | |
| func (m *mockPeerstore) ClearAddrs(peer.ID) {}
 | |
| func (m *mockPeerstore) PeersWithAddrs() peer.IDSlice { return nil }
 | |
| func (m *mockPeerstore) PubKey(peer.ID) peerstore.PubKey { return nil }
 | |
| func (m *mockPeerstore) SetPubKey(peer.ID, peerstore.PubKey) error { return nil }
 | |
| func (m *mockPeerstore) PrivKey(peer.ID) peerstore.PrivKey { return nil }
 | |
| func (m *mockPeerstore) SetPrivKey(peer.ID, peerstore.PrivKey) error { return nil }
 | |
| func (m *mockPeerstore) Get(peer.ID, string) (interface{}, error) { return nil, nil }
 | |
| func (m *mockPeerstore) Put(peer.ID, string, interface{}) error { return nil }
 | |
| func (m *mockPeerstore) GetProtocols(peer.ID) ([]peerstore.Protocol, error) { return nil, nil }
 | |
| func (m *mockPeerstore) SetProtocols(peer.ID, ...peerstore.Protocol) error { return nil }
 | |
| func (m *mockPeerstore) SupportsProtocols(peer.ID, ...peerstore.Protocol) ([]peerstore.Protocol, error) { return nil, nil }
 | |
| func (m *mockPeerstore) RemovePeer(peer.ID) {}
 | |
| func (m *mockPeerstore) Close() error { return nil } |