 9bdcbe0447
			
		
	
	9bdcbe0447
	
	
	
		
			
			Major integrations and fixes: - Added BACKBEAT SDK integration for P2P operation timing - Implemented beat-aware status tracking for distributed operations - Added Docker secrets support for secure license management - Resolved KACHING license validation via HTTPS/TLS - Updated docker-compose configuration for clean stack deployment - Disabled rollback policies to prevent deployment failures - Added license credential storage (CHORUS-DEV-MULTI-001) Technical improvements: - BACKBEAT P2P operation tracking with phase management - Enhanced configuration system with file-based secrets - Improved error handling for license validation - Clean separation of KACHING and CHORUS deployment stacks 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			387 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			387 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package temporal
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"testing"
 | |
| 	"time"
 | |
| 
 | |
| 	"chorus/pkg/ucxl"
 | |
| 	slurpContext "chorus/pkg/slurp/context"
 | |
| )
 | |
| 
 | |
| func TestDecisionNavigator_NavigateDecisionHops(t *testing.T) {
 | |
| 	storage := newMockStorage()
 | |
| 	graph := NewTemporalGraph(storage).(*temporalGraphImpl)
 | |
| 	navigator := NewDecisionNavigator(graph)
 | |
| 	ctx := context.Background()
 | |
| 	
 | |
| 	// Create a chain of versions
 | |
| 	address := createTestAddress("test/component")
 | |
| 	initialContext := createTestContext("test/component", []string{"go"})
 | |
| 	
 | |
| 	_, err := graph.CreateInitialContext(ctx, address, initialContext, "test_creator")
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("Failed to create initial context: %v", err)
 | |
| 	}
 | |
| 	
 | |
| 	// Create 3 more versions
 | |
| 	for i := 2; i <= 4; i++ {
 | |
| 		updatedContext := createTestContext("test/component", []string{"go", fmt.Sprintf("tech%d", i)})
 | |
| 		decision := createTestDecision(fmt.Sprintf("dec-%03d", i), "test_maker", "Update", ImpactLocal)
 | |
| 		
 | |
| 		_, err := graph.EvolveContext(ctx, address, updatedContext, ReasonCodeChange, decision)
 | |
| 		if err != nil {
 | |
| 			t.Fatalf("Failed to evolve context to version %d: %v", i, err)
 | |
| 		}
 | |
| 	}
 | |
| 	
 | |
| 	// Test forward navigation from version 1
 | |
| 	v1, err := graph.GetVersionAtDecision(ctx, address, 1)
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("Failed to get version 1: %v", err)
 | |
| 	}
 | |
| 	
 | |
| 	// Navigate 2 hops forward from version 1
 | |
| 	result, err := navigator.NavigateDecisionHops(ctx, address, 2, NavigationForward)
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("Failed to navigate forward: %v", err)
 | |
| 	}
 | |
| 	
 | |
| 	if result.Version != 3 {
 | |
| 		t.Errorf("Expected to navigate to version 3, got version %d", result.Version)
 | |
| 	}
 | |
| 	
 | |
| 	// Test backward navigation from version 4
 | |
| 	result2, err := navigator.NavigateDecisionHops(ctx, address, 2, NavigationBackward)
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("Failed to navigate backward: %v", err)
 | |
| 	}
 | |
| 	
 | |
| 	if result2.Version != 2 {
 | |
| 		t.Errorf("Expected to navigate to version 2, got version %d", result2.Version)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestDecisionNavigator_GetDecisionTimeline(t *testing.T) {
 | |
| 	storage := newMockStorage()
 | |
| 	graph := NewTemporalGraph(storage).(*temporalGraphImpl)
 | |
| 	navigator := NewDecisionNavigator(graph)
 | |
| 	ctx := context.Background()
 | |
| 	
 | |
| 	// Create main context with evolution
 | |
| 	address := createTestAddress("test/main")
 | |
| 	initialContext := createTestContext("test/main", []string{"go"})
 | |
| 	
 | |
| 	_, err := graph.CreateInitialContext(ctx, address, initialContext, "test_creator")
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("Failed to create initial context: %v", err)
 | |
| 	}
 | |
| 	
 | |
| 	// Evolve main context
 | |
| 	for i := 2; i <= 3; i++ {
 | |
| 		updatedContext := createTestContext("test/main", []string{"go", fmt.Sprintf("feature%d", i)})
 | |
| 		decision := createTestDecision(fmt.Sprintf("main-dec-%03d", i), fmt.Sprintf("dev%d", i), "Add feature", ImpactModule)
 | |
| 		
 | |
| 		_, err := graph.EvolveContext(ctx, address, updatedContext, ReasonCodeChange, decision)
 | |
| 		if err != nil {
 | |
| 			t.Fatalf("Failed to evolve main context to version %d: %v", i, err)
 | |
| 		}
 | |
| 	}
 | |
| 	
 | |
| 	// Create related context
 | |
| 	relatedAddr := createTestAddress("test/related")
 | |
| 	relatedContext := createTestContext("test/related", []string{"go"})
 | |
| 	
 | |
| 	_, err = graph.CreateInitialContext(ctx, relatedAddr, relatedContext, "test_creator")
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("Failed to create related context: %v", err)
 | |
| 	}
 | |
| 	
 | |
| 	// Add influence relationship
 | |
| 	err = graph.AddInfluenceRelationship(ctx, address, relatedAddr)
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("Failed to add influence relationship: %v", err)
 | |
| 	}
 | |
| 	
 | |
| 	// Get decision timeline with related decisions
 | |
| 	timeline, err := navigator.GetDecisionTimeline(ctx, address, true, 5)
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("Failed to get decision timeline: %v", err)
 | |
| 	}
 | |
| 	
 | |
| 	if len(timeline.DecisionSequence) != 3 {
 | |
| 		t.Errorf("Expected 3 decisions in timeline, got %d", len(timeline.DecisionSequence))
 | |
| 	}
 | |
| 	
 | |
| 	// Check ordering
 | |
| 	for i, entry := range timeline.DecisionSequence {
 | |
| 		expectedVersion := i + 1
 | |
| 		if entry.Version != expectedVersion {
 | |
| 			t.Errorf("Expected version %d at index %d, got %d", expectedVersion, i, entry.Version)
 | |
| 		}
 | |
| 	}
 | |
| 	
 | |
| 	// Should have related decisions
 | |
| 	if len(timeline.RelatedDecisions) == 0 {
 | |
| 		t.Error("Expected to find related decisions")
 | |
| 	}
 | |
| 	
 | |
| 	if timeline.AnalysisMetadata == nil {
 | |
| 		t.Error("Expected analysis metadata")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestDecisionNavigator_FindStaleContexts(t *testing.T) {
 | |
| 	storage := newMockStorage()
 | |
| 	graph := NewTemporalGraph(storage).(*temporalGraphImpl)
 | |
| 	navigator := NewDecisionNavigator(graph)
 | |
| 	ctx := context.Background()
 | |
| 	
 | |
| 	// Create contexts with different staleness levels
 | |
| 	addresses := make([]ucxl.Address, 3)
 | |
| 	
 | |
| 	for i := 0; i < 3; i++ {
 | |
| 		addresses[i] = createTestAddress(fmt.Sprintf("test/component%d", i))
 | |
| 		context := createTestContext(fmt.Sprintf("test/component%d", i), []string{"go"})
 | |
| 		
 | |
| 		_, err := graph.CreateInitialContext(ctx, addresses[i], context, "test_creator")
 | |
| 		if err != nil {
 | |
| 			t.Fatalf("Failed to create context %d: %v", i, err)
 | |
| 		}
 | |
| 	}
 | |
| 	
 | |
| 	// Manually set staleness scores for testing
 | |
| 	graph.mu.Lock()
 | |
| 	for _, nodes := range graph.addressToNodes {
 | |
| 		for j, node := range nodes {
 | |
| 			// Set different staleness levels
 | |
| 			node.Staleness = float64(j+1) * 0.3
 | |
| 		}
 | |
| 	}
 | |
| 	graph.mu.Unlock()
 | |
| 	
 | |
| 	// Find stale contexts with threshold 0.5
 | |
| 	staleContexts, err := navigator.FindStaleContexts(ctx, 0.5)
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("Failed to find stale contexts: %v", err)
 | |
| 	}
 | |
| 	
 | |
| 	// Should find contexts with staleness >= 0.5
 | |
| 	expectedStale := 0
 | |
| 	graph.mu.RLock()
 | |
| 	for _, nodes := range graph.addressToNodes {
 | |
| 		for _, node := range nodes {
 | |
| 			if node.Staleness >= 0.5 {
 | |
| 				expectedStale++
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	graph.mu.RUnlock()
 | |
| 	
 | |
| 	if len(staleContexts) != expectedStale {
 | |
| 		t.Errorf("Expected %d stale contexts, got %d", expectedStale, len(staleContexts))
 | |
| 	}
 | |
| 	
 | |
| 	// Results should be sorted by staleness score (highest first)
 | |
| 	for i := 1; i < len(staleContexts); i++ {
 | |
| 		if staleContexts[i-1].StalenessScore < staleContexts[i].StalenessScore {
 | |
| 			t.Error("Results should be sorted by staleness score in descending order")
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestDecisionNavigator_BookmarkManagement(t *testing.T) {
 | |
| 	storage := newMockStorage()
 | |
| 	graph := NewTemporalGraph(storage).(*temporalGraphImpl)
 | |
| 	navigator := NewDecisionNavigator(graph)
 | |
| 	ctx := context.Background()
 | |
| 	
 | |
| 	// Create context with multiple versions
 | |
| 	address := createTestAddress("test/component")
 | |
| 	initialContext := createTestContext("test/component", []string{"go"})
 | |
| 	
 | |
| 	_, err := graph.CreateInitialContext(ctx, address, initialContext, "test_creator")
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("Failed to create initial context: %v", err)
 | |
| 	}
 | |
| 	
 | |
| 	// Create more versions
 | |
| 	for i := 2; i <= 5; i++ {
 | |
| 		updatedContext := createTestContext("test/component", []string{"go", fmt.Sprintf("feature%d", i)})
 | |
| 		decision := createTestDecision(fmt.Sprintf("dec-%03d", i), "test_maker", "Update", ImpactLocal)
 | |
| 		
 | |
| 		_, err := graph.EvolveContext(ctx, address, updatedContext, ReasonCodeChange, decision)
 | |
| 		if err != nil {
 | |
| 			t.Fatalf("Failed to evolve context to version %d: %v", i, err)
 | |
| 		}
 | |
| 	}
 | |
| 	
 | |
| 	// Create bookmarks
 | |
| 	bookmarkNames := []string{"Initial Release", "Major Feature", "Bug Fix", "Performance Improvement"}
 | |
| 	for i, name := range bookmarkNames {
 | |
| 		err := navigator.BookmarkDecision(ctx, address, i+1, name)
 | |
| 		if err != nil {
 | |
| 			t.Fatalf("Failed to create bookmark %s: %v", name, err)
 | |
| 		}
 | |
| 	}
 | |
| 	
 | |
| 	// List bookmarks
 | |
| 	bookmarks, err := navigator.ListBookmarks(ctx)
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("Failed to list bookmarks: %v", err)
 | |
| 	}
 | |
| 	
 | |
| 	if len(bookmarks) != len(bookmarkNames) {
 | |
| 		t.Errorf("Expected %d bookmarks, got %d", len(bookmarkNames), len(bookmarks))
 | |
| 	}
 | |
| 	
 | |
| 	// Verify bookmark details
 | |
| 	for _, bookmark := range bookmarks {
 | |
| 		if bookmark.Address.String() != address.String() {
 | |
| 			t.Errorf("Expected bookmark address %s, got %s", address.String(), bookmark.Address.String())
 | |
| 		}
 | |
| 		
 | |
| 		if bookmark.DecisionHop < 1 || bookmark.DecisionHop > 4 {
 | |
| 			t.Errorf("Expected decision hop between 1-4, got %d", bookmark.DecisionHop)
 | |
| 		}
 | |
| 		
 | |
| 		if bookmark.Metadata == nil {
 | |
| 			t.Error("Expected bookmark metadata")
 | |
| 		}
 | |
| 	}
 | |
| 	
 | |
| 	// Bookmarks should be sorted by creation time (newest first)
 | |
| 	for i := 1; i < len(bookmarks); i++ {
 | |
| 		if bookmarks[i-1].CreatedAt.Before(bookmarks[i].CreatedAt) {
 | |
| 			t.Error("Bookmarks should be sorted by creation time (newest first)")
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestDecisionNavigator_ValidationAndErrorHandling(t *testing.T) {
 | |
| 	storage := newMockStorage()
 | |
| 	graph := NewTemporalGraph(storage).(*temporalGraphImpl)
 | |
| 	navigator := NewDecisionNavigator(graph)
 | |
| 	ctx := context.Background()
 | |
| 	
 | |
| 	// Test: Navigate decision hops on non-existent address
 | |
| 	nonExistentAddr := createTestAddress("non/existent")
 | |
| 	_, err := navigator.NavigateDecisionHops(ctx, nonExistentAddr, 1, NavigationForward)
 | |
| 	if err == nil {
 | |
| 		t.Error("Expected error when navigating on non-existent address")
 | |
| 	}
 | |
| 	
 | |
| 	// Test: Create bookmark for non-existent decision
 | |
| 	err = navigator.BookmarkDecision(ctx, nonExistentAddr, 1, "Test Bookmark")
 | |
| 	if err == nil {
 | |
| 		t.Error("Expected error when bookmarking non-existent decision")
 | |
| 	}
 | |
| 	
 | |
| 	// Create valid context for path validation tests
 | |
| 	address := createTestAddress("test/component")
 | |
| 	initialContext := createTestContext("test/component", []string{"go"})
 | |
| 	
 | |
| 	_, err = graph.CreateInitialContext(ctx, address, initialContext, "test_creator")
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("Failed to create initial context: %v", err)
 | |
| 	}
 | |
| 	
 | |
| 	// Test: Validate empty decision path
 | |
| 	err = navigator.ValidateDecisionPath(ctx, []*DecisionStep{})
 | |
| 	if err == nil {
 | |
| 		t.Error("Expected error when validating empty decision path")
 | |
| 	}
 | |
| 	
 | |
| 	// Test: Validate path with nil temporal node
 | |
| 	invalidPath := []*DecisionStep{
 | |
| 		{
 | |
| 			Address:      address,
 | |
| 			TemporalNode: nil,
 | |
| 			HopDistance:  0,
 | |
| 			Relationship: "test",
 | |
| 		},
 | |
| 	}
 | |
| 	
 | |
| 	err = navigator.ValidateDecisionPath(ctx, invalidPath)
 | |
| 	if err == nil {
 | |
| 		t.Error("Expected error when validating path with nil temporal node")
 | |
| 	}
 | |
| 	
 | |
| 	// Test: Get navigation history for non-existent session
 | |
| 	_, err = navigator.GetNavigationHistory(ctx, "non-existent-session")
 | |
| 	if err == nil {
 | |
| 		t.Error("Expected error when getting history for non-existent session")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func BenchmarkDecisionNavigator_GetDecisionTimeline(b *testing.B) {
 | |
| 	storage := newMockStorage()
 | |
| 	graph := NewTemporalGraph(storage).(*temporalGraphImpl)
 | |
| 	navigator := NewDecisionNavigator(graph)
 | |
| 	ctx := context.Background()
 | |
| 	
 | |
| 	// Setup: Create context with many versions
 | |
| 	address := createTestAddress("test/component")
 | |
| 	initialContext := createTestContext("test/component", []string{"go"})
 | |
| 	
 | |
| 	_, err := graph.CreateInitialContext(ctx, address, initialContext, "test_creator")
 | |
| 	if err != nil {
 | |
| 		b.Fatalf("Failed to create initial context: %v", err)
 | |
| 	}
 | |
| 	
 | |
| 	// Create 100 versions
 | |
| 	for i := 2; i <= 100; i++ {
 | |
| 		updatedContext := createTestContext("test/component", []string{"go", fmt.Sprintf("feature%d", i)})
 | |
| 		decision := createTestDecision(fmt.Sprintf("dec-%03d", i), "test_maker", "Update", ImpactLocal)
 | |
| 		
 | |
| 		_, err := graph.EvolveContext(ctx, address, updatedContext, ReasonCodeChange, decision)
 | |
| 		if err != nil {
 | |
| 			b.Fatalf("Failed to evolve context to version %d: %v", i, err)
 | |
| 		}
 | |
| 	}
 | |
| 	
 | |
| 	b.ResetTimer()
 | |
| 	
 | |
| 	for i := 0; i < b.N; i++ {
 | |
| 		_, err := navigator.GetDecisionTimeline(ctx, address, true, 10)
 | |
| 		if err != nil {
 | |
| 			b.Fatalf("Failed to get decision timeline: %v", err)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func BenchmarkDecisionNavigator_FindStaleContexts(b *testing.B) {
 | |
| 	storage := newMockStorage()
 | |
| 	graph := NewTemporalGraph(storage).(*temporalGraphImpl)
 | |
| 	navigator := NewDecisionNavigator(graph)
 | |
| 	ctx := context.Background()
 | |
| 	
 | |
| 	// Setup: Create many contexts
 | |
| 	for i := 0; i < 1000; i++ {
 | |
| 		address := createTestAddress(fmt.Sprintf("test/component%d", i))
 | |
| 		context := createTestContext(fmt.Sprintf("test/component%d", i), []string{"go"})
 | |
| 		
 | |
| 		_, err := graph.CreateInitialContext(ctx, address, context, "test_creator")
 | |
| 		if err != nil {
 | |
| 			b.Fatalf("Failed to create context %d: %v", i, err)
 | |
| 		}
 | |
| 	}
 | |
| 	
 | |
| 	// Set random staleness scores
 | |
| 	graph.mu.Lock()
 | |
| 	for _, nodes := range graph.addressToNodes {
 | |
| 		for _, node := range nodes {
 | |
| 			node.Staleness = 0.3 + (float64(node.Version)*0.1) // Varying staleness
 | |
| 		}
 | |
| 	}
 | |
| 	graph.mu.Unlock()
 | |
| 	
 | |
| 	b.ResetTimer()
 | |
| 	
 | |
| 	for i := 0; i < b.N; i++ {
 | |
| 		_, err := navigator.FindStaleContexts(ctx, 0.5)
 | |
| 		if err != nil {
 | |
| 			b.Fatalf("Failed to find stale contexts: %v", err)
 | |
| 		}
 | |
| 	}
 | |
| } |