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