11package cmd
22
33import (
4+ "bytes"
45 "errors"
6+ "path/filepath"
57 "strings"
68 "testing"
9+
10+ "github.com/lugassawan/rimba/internal/config"
11+ "github.com/spf13/cobra"
712)
813
914// mergedWorktreeRunner returns a mockRunner that supports MergedBranches and ListWorktrees.
@@ -24,7 +29,7 @@ func mergedWorktreeRunner(mergedOut, worktreeOut string) *mockRunner {
2429
2530func TestFindMergedCandidatesFound (t * testing.T ) {
2631 worktreeOut := strings .Join ([]string {
27- "worktree /repo" ,
32+ wtRepo ,
2833 "HEAD abc123" ,
2934 "branch refs/heads/main" ,
3035 "" ,
@@ -52,7 +57,7 @@ func TestFindMergedCandidatesFound(t *testing.T) {
5257}
5358
5459func TestFindMergedCandidatesNone (t * testing.T ) {
55- r := mergedWorktreeRunner ("" , "worktree /repo \n HEAD abc \n branch refs/heads/main \n " )
60+ r := mergedWorktreeRunner ("" , wtRepo + headMainBlock )
5661 candidates , err := findMergedCandidates (r , branchMain )
5762 if err != nil {
5863 t .Fatalf ("findMergedCandidates: %v" , err )
@@ -120,7 +125,7 @@ func TestRemoveMergedWorktreesRemoveFails(t *testing.T) {
120125 cmd , buf := newTestCmd ()
121126 r := & mockRunner {
122127 run : func (args ... string ) (string , error ) {
123- if len (args ) >= 2 && args [0 ] == cmdWorktreeTest && args [1 ] == "remove" {
128+ if len (args ) >= 2 && args [0 ] == cmdWorktreeTest && args [1 ] == cmdRemove {
124129 return "" , errors .New ("locked" )
125130 }
126131 return "" , nil
@@ -181,3 +186,206 @@ func TestConfirmRemoval(t *testing.T) {
181186 })
182187 }
183188}
189+
190+ func newCleanPruneCmd () (* cobra.Command , * bytes.Buffer ) {
191+ cmd , buf := newTestCmd ()
192+ cmd .Flags ().Bool (flagDryRun , false , "" )
193+ cmd .Flags ().Bool (flagMerged , false , "" )
194+ return cmd , buf
195+ }
196+
197+ func TestCleanPruneSuccess (t * testing.T ) {
198+ cmd , buf := newCleanPruneCmd ()
199+ r := & mockRunner {
200+ run : func (args ... string ) (string , error ) {
201+ if len (args ) >= 1 && args [0 ] == cmdWorktreeTest {
202+ return "Removing worktrees/stale" , nil
203+ }
204+ return "" , nil
205+ },
206+ runInDir : noopRunInDir ,
207+ }
208+
209+ err := cleanPrune (cmd , r )
210+ if err != nil {
211+ t .Fatalf (fatalCleanPrune , err )
212+ }
213+ if ! strings .Contains (buf .String (), "Removing worktrees/stale" ) {
214+ t .Errorf ("output = %q, want prune output" , buf .String ())
215+ }
216+ }
217+
218+ func TestCleanPruneDryRunEmpty (t * testing.T ) {
219+ cmd , buf := newCleanPruneCmd ()
220+ _ = cmd .Flags ().Set (flagDryRun , "true" )
221+ r := & mockRunner {
222+ run : func (_ ... string ) (string , error ) { return "" , nil },
223+ runInDir : noopRunInDir ,
224+ }
225+
226+ err := cleanPrune (cmd , r )
227+ if err != nil {
228+ t .Fatalf (fatalCleanPrune , err )
229+ }
230+ if ! strings .Contains (buf .String (), "Nothing to prune" ) {
231+ t .Errorf ("output = %q, want 'Nothing to prune'" , buf .String ())
232+ }
233+ }
234+
235+ func TestCleanPruneNoDryRunEmpty (t * testing.T ) {
236+ cmd , buf := newCleanPruneCmd ()
237+ r := & mockRunner {
238+ run : func (_ ... string ) (string , error ) { return "" , nil },
239+ runInDir : noopRunInDir ,
240+ }
241+
242+ err := cleanPrune (cmd , r )
243+ if err != nil {
244+ t .Fatalf (fatalCleanPrune , err )
245+ }
246+ if ! strings .Contains (buf .String (), "Pruned stale worktree references" ) {
247+ t .Errorf ("output = %q, want 'Pruned stale worktree references'" , buf .String ())
248+ }
249+ }
250+
251+ func TestCleanPruneError (t * testing.T ) {
252+ cmd , _ := newCleanPruneCmd ()
253+ r := & mockRunner {
254+ run : func (args ... string ) (string , error ) {
255+ if len (args ) >= 1 && args [0 ] == cmdWorktreeTest {
256+ return "" , errGitFailed
257+ }
258+ return "" , nil
259+ },
260+ runInDir : noopRunInDir ,
261+ }
262+
263+ err := cleanPrune (cmd , r )
264+ if err == nil {
265+ t .Fatal ("expected error from prune failure" )
266+ }
267+ }
268+
269+ func cleanMergedTestRunner (t * testing.T , mergedOut , worktreeOut string ) * mockRunner {
270+ dir := t .TempDir ()
271+ cfg := & config.Config {DefaultSource : branchMain }
272+ _ = config .Save (filepath .Join (dir , config .FileName ), cfg )
273+
274+ return & mockRunner {
275+ run : func (args ... string ) (string , error ) {
276+ if len (args ) >= 2 && args [1 ] == cmdShowToplevel {
277+ return dir , nil
278+ }
279+ if len (args ) >= 1 && args [0 ] == cmdBranch {
280+ return mergedOut , nil
281+ }
282+ if len (args ) >= 1 && args [0 ] == cmdWorktreeTest {
283+ return worktreeOut , nil
284+ }
285+ if len (args ) >= 1 && args [0 ] == "fetch" {
286+ return "" , errors .New ("no remote" )
287+ }
288+ return "" , nil
289+ },
290+ runInDir : noopRunInDir ,
291+ }
292+ }
293+
294+ func newCleanMergedCmd () (* cobra.Command , * bytes.Buffer ) {
295+ cmd , buf := newTestCmd ()
296+ cmd .Flags ().Bool (flagDryRun , false , "" )
297+ cmd .Flags ().Bool (flagForce , false , "" )
298+ cmd .Flags ().Bool (flagMerged , false , "" )
299+ return cmd , buf
300+ }
301+
302+ func cleanMergedWorktreeOut () string {
303+ return strings .Join ([]string {
304+ wtRepo ,
305+ "HEAD abc123" ,
306+ "branch refs/heads/main" ,
307+ "" ,
308+ "worktree " + pathWtDone ,
309+ "HEAD def456" ,
310+ "branch refs/heads/" + branchDone ,
311+ "" ,
312+ }, "\n " )
313+ }
314+
315+ func TestCleanMergedNoCandidates (t * testing.T ) {
316+ worktreeOut := cleanMergedWorktreeOut ()
317+ cmd , buf := newCleanMergedCmd ()
318+ r := cleanMergedTestRunner (t , "" , worktreeOut )
319+
320+ err := cleanMerged (cmd , r )
321+ if err != nil {
322+ t .Fatalf (fatalCleanMerged , err )
323+ }
324+ if ! strings .Contains (buf .String (), "No merged worktrees found" ) {
325+ t .Errorf ("output = %q, want 'No merged worktrees found'" , buf .String ())
326+ }
327+ }
328+
329+ func TestCleanMergedDryRun (t * testing.T ) {
330+ worktreeOut := cleanMergedWorktreeOut ()
331+ cmd , buf := newCleanMergedCmd ()
332+ _ = cmd .Flags ().Set (flagDryRun , "true" )
333+ r := cleanMergedTestRunner (t , " " + branchDone , worktreeOut )
334+
335+ err := cleanMerged (cmd , r )
336+ if err != nil {
337+ t .Fatalf (fatalCleanMerged , err )
338+ }
339+ out := buf .String ()
340+ if ! strings .Contains (out , "Merged worktrees:" ) {
341+ t .Errorf ("output = %q, want 'Merged worktrees:'" , out )
342+ }
343+ if strings .Contains (out , "Cleaned" ) {
344+ t .Errorf ("dry-run should not show 'Cleaned'" )
345+ }
346+ }
347+
348+ func TestCleanMergedAbort (t * testing.T ) {
349+ worktreeOut := cleanMergedWorktreeOut ()
350+ cmd , buf := newCleanMergedCmd ()
351+ cmd .SetIn (strings .NewReader ("n\n " ))
352+ r := cleanMergedTestRunner (t , " " + branchDone , worktreeOut )
353+
354+ err := cleanMerged (cmd , r )
355+ if err != nil {
356+ t .Fatalf (fatalCleanMerged , err )
357+ }
358+ if ! strings .Contains (buf .String (), "Aborted" ) {
359+ t .Errorf ("output = %q, want 'Aborted'" , buf .String ())
360+ }
361+ }
362+
363+ func TestCleanMergedForce (t * testing.T ) {
364+ worktreeOut := cleanMergedWorktreeOut ()
365+ cmd , buf := newCleanMergedCmd ()
366+ _ = cmd .Flags ().Set (flagForce , "true" )
367+ r := cleanMergedTestRunner (t , " " + branchDone , worktreeOut )
368+
369+ err := cleanMerged (cmd , r )
370+ if err != nil {
371+ t .Fatalf (fatalCleanMerged , err )
372+ }
373+ out := buf .String ()
374+ if ! strings .Contains (out , "Cleaned" ) {
375+ t .Errorf ("output = %q, want 'Cleaned'" , out )
376+ }
377+ }
378+
379+ func TestCleanMergedResolveError (t * testing.T ) {
380+ cmd , _ := newCleanMergedCmd ()
381+ r := & mockRunner {
382+ run : func (_ ... string ) (string , error ) { return "" , errGitFailed },
383+ runInDir : noopRunInDir ,
384+ }
385+
386+ err := cleanMerged (cmd , r )
387+ if err == nil {
388+ t .Fatal ("expected error from resolveMainBranch failure" )
389+ }
390+ }
391+
0 commit comments