package service import ( "testing" "github.com/orchard9/rdev/internal/adapter/memory" "github.com/orchard9/rdev/internal/domain" ) func TestBuildProgressTracker_Start(t *testing.T) { streams := memory.NewStreamPublisher() tracker := NewBuildProgressTracker(streams) tracker.Start("task-1", "project-1") phase, percentage, ok := tracker.GetProgress("task-1") if !ok { t.Fatal("expected to find progress for task-1") } if phase != domain.BuildPhaseStarting { t.Errorf("got phase %q, want %q", phase, domain.BuildPhaseStarting) } if percentage < 0 || percentage > 100 { t.Errorf("got invalid percentage %f", percentage) } } func TestBuildProgressTracker_RecordToolUse(t *testing.T) { streams := memory.NewStreamPublisher() tracker := NewBuildProgressTracker(streams) tracker.Start("task-1", "project-1") // Simulate reading phase tracker.RecordToolUse("task-1", "Read") phase, _, _ := tracker.GetProgress("task-1") if phase != domain.BuildPhaseReading { t.Errorf("after Read tool, got phase %q, want %q", phase, domain.BuildPhaseReading) } // Simulate writing phase tracker.RecordToolUse("task-1", "Write") phase, _, _ = tracker.GetProgress("task-1") if phase != domain.BuildPhaseWriting { t.Errorf("after Write tool, got phase %q, want %q", phase, domain.BuildPhaseWriting) } // Simulate testing phase tracker.RecordToolUse("task-1", "Bash") phase, _, _ = tracker.GetProgress("task-1") if phase != domain.BuildPhaseTesting { t.Errorf("after Bash tool, got phase %q, want %q", phase, domain.BuildPhaseTesting) } } func TestBuildProgressTracker_Complete(t *testing.T) { streams := memory.NewStreamPublisher() tracker := NewBuildProgressTracker(streams) tracker.Start("task-1", "project-1") tracker.Complete("task-1", true) // After completion, task should be removed _, _, ok := tracker.GetProgress("task-1") if ok { t.Error("expected task to be removed after completion") } } func TestBuildProgressTracker_PercentageIncrease(t *testing.T) { streams := memory.NewStreamPublisher() tracker := NewBuildProgressTracker(streams) tracker.Start("task-1", "project-1") _, initialPercent, _ := tracker.GetProgress("task-1") // Record several tool uses for i := 0; i < 5; i++ { tracker.RecordToolUse("task-1", "Read") } _, newPercent, _ := tracker.GetProgress("task-1") if newPercent <= initialPercent { t.Errorf("expected percentage to increase from %f, got %f", initialPercent, newPercent) } } func TestBuildProgressTracker_NonexistentTask(t *testing.T) { streams := memory.NewStreamPublisher() tracker := NewBuildProgressTracker(streams) // These should not panic tracker.RecordToolUse("nonexistent", "Read") tracker.RecordOutput("nonexistent") tracker.Complete("nonexistent", true) _, _, ok := tracker.GetProgress("nonexistent") if ok { t.Error("expected not to find progress for nonexistent task") } } func TestBuildProgressTracker_EmitsEvents(t *testing.T) { streams := memory.NewStreamPublisher() tracker := NewBuildProgressTracker(streams) // Subscribe to events events, cleanup := streams.Subscribe("task-1") defer cleanup() // Start should emit a progress event tracker.Start("task-1", "project-1") select { case event := <-events: if event.Type != "build.progress" { t.Errorf("got event type %q, want build.progress", event.Type) } phase, ok := event.Data["phase"].(string) if !ok || phase != "starting" { t.Errorf("got phase %v, want 'starting'", event.Data["phase"]) } default: t.Error("expected progress event to be published") } }