package doctor import ( "context" "os" "os/exec" "path/filepath" "testing" "github.com/steveyegge/beads/internal/git" "github.com/steveyegge/beads/internal/storage/sqlite" ) func TestCheckDaemonStatus(t *testing.T) { t.Run("no beads directory", func(t *testing.T) { tmpDir := t.TempDir() check := CheckDaemonStatus(tmpDir, "1.0.0") // Should return OK when no .beads directory (daemon not needed) if check.Status != StatusOK { t.Errorf("Status = %q, want %q", check.Status, StatusOK) } }) t.Run("beads directory exists", func(t *testing.T) { tmpDir := t.TempDir() beadsDir := filepath.Join(tmpDir, ".beads") if err := os.Mkdir(beadsDir, 0755); err != nil { t.Fatal(err) } check := CheckDaemonStatus(tmpDir, "1.0.0") // Should check daemon status - may be OK or warning depending on daemon state if check.Name != "Daemon Health" { t.Errorf("Name = %q, want %q", check.Name, "Daemon Health") } }) } func TestCheckGitSyncSetup(t *testing.T) { t.Run("not in git repository", func(t *testing.T) { tmpDir := t.TempDir() oldDir, err := os.Getwd() if err != nil { t.Fatal(err) } defer func() { _ = os.Chdir(oldDir) git.ResetCaches() }() if err := os.Chdir(tmpDir); err != nil { t.Fatal(err) } git.ResetCaches() check := CheckGitSyncSetup(tmpDir) if check.Status != StatusWarning { t.Errorf("Status = %q, want %q", check.Status, StatusWarning) } if check.Name != "Git Sync Setup" { t.Errorf("Name = %q, want %q", check.Name, "Git Sync Setup") } if check.Fix == "" { t.Error("Expected Fix to contain instructions") } }) t.Run("in git repository without sync-branch", func(t *testing.T) { tmpDir := t.TempDir() oldDir, err := os.Getwd() if err != nil { t.Fatal(err) } defer func() { _ = os.Chdir(oldDir) git.ResetCaches() }() // Initialize git repo cmd := exec.Command("git", "init", "--initial-branch=main") cmd.Dir = tmpDir if err := cmd.Run(); err != nil { t.Fatalf("Failed to init git repo: %v", err) } if err := os.Chdir(tmpDir); err != nil { t.Fatal(err) } git.ResetCaches() check := CheckGitSyncSetup(tmpDir) if check.Status != StatusOK { t.Errorf("Status = %q, want %q", check.Status, StatusOK) } if check.Name != "Git Sync Setup" { t.Errorf("Name = %q, want %q", check.Name, "Git Sync Setup") } // Should mention sync-branch not configured if check.Detail == "" { t.Error("Expected Detail to contain sync-branch hint") } }) } func TestCheckDaemonAutoSync(t *testing.T) { t.Run("no daemon socket", func(t *testing.T) { tmpDir := t.TempDir() beadsDir := filepath.Join(tmpDir, ".beads") if err := os.Mkdir(beadsDir, 0755); err != nil { t.Fatal(err) } check := CheckDaemonAutoSync(tmpDir) if check.Status != StatusOK { t.Errorf("Status = %q, want %q", check.Status, StatusOK) } if check.Message != "Daemon not running (will use defaults on next start)" { t.Errorf("Message = %q, want 'Daemon not running...'", check.Message) } }) t.Run("no sync-branch configured", func(t *testing.T) { tmpDir := t.TempDir() beadsDir := filepath.Join(tmpDir, ".beads") if err := os.Mkdir(beadsDir, 0755); err != nil { t.Fatal(err) } // Create database without sync-branch config dbPath := filepath.Join(beadsDir, "beads.db") ctx := context.Background() store, err := sqlite.New(ctx, dbPath) if err != nil { t.Fatal(err) } defer func() { _ = store.Close() }() // Create a fake socket file to simulate daemon running socketPath := filepath.Join(beadsDir, "bd.sock") if err := os.WriteFile(socketPath, []byte{}, 0600); err != nil { t.Fatal(err) } check := CheckDaemonAutoSync(tmpDir) // Should return OK because no sync-branch means auto-sync not applicable if check.Status != StatusOK { t.Errorf("Status = %q, want %q", check.Status, StatusOK) } if check.Message != "No sync-branch configured (auto-sync not applicable)" { t.Errorf("Message = %q, want 'No sync-branch...'", check.Message) } }) t.Run("sync-branch configured but cannot connect", func(t *testing.T) { tmpDir := t.TempDir() beadsDir := filepath.Join(tmpDir, ".beads") if err := os.Mkdir(beadsDir, 0755); err != nil { t.Fatal(err) } // Create database with sync-branch config dbPath := filepath.Join(beadsDir, "beads.db") ctx := context.Background() store, err := sqlite.New(ctx, dbPath) if err != nil { t.Fatal(err) } if err := store.SetConfig(ctx, "sync.branch", "beads-sync"); err != nil { t.Fatal(err) } _ = store.Close() // Create a fake socket file (not a real daemon) socketPath := filepath.Join(beadsDir, "bd.sock") if err := os.WriteFile(socketPath, []byte{}, 0600); err != nil { t.Fatal(err) } check := CheckDaemonAutoSync(tmpDir) // Should return warning because can't connect to fake socket if check.Status != StatusWarning { t.Errorf("Status = %q, want %q", check.Status, StatusWarning) } }) }