Files
bzzz/pkg/slurp/temporal/navigator_test.go
anthonyrawlins d96c931a29 Resolve import cycles and migrate to chorus.services module path
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>
2025-08-17 10:04:25 +10:00

387 lines
12 KiB
Go

package temporal
import (
"context"
"testing"
"time"
"chorus.services/bzzz/pkg/ucxl"
slurpContext "chorus.services/bzzz/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)
}
}
}