1package volumes
2
3import (
4 "context"
5 "fmt"
6 "testing"
7
8 "github.com/stretchr/testify/assert"
9 "github.com/stretchr/testify/require"
10
11 outmocks "github.com/bnema/gordon/internal/boundaries/out/mocks"
12 "github.com/bnema/gordon/internal/domain"
13)
14
15func TestService_ListVolumes(t *testing.T) {
16 runtime := outmocks.NewMockContainerRuntime(t)
17
18 vols := []*domain.VolumeInfo{
19 {Name: "vol1", InUse: true, Containers: []string{"web"}},
20 {Name: "vol2", InUse: false},
21 }
22 runtime.EXPECT().ListVolumes(context.Background()).Return(vols, nil)
23
24 svc := NewService(runtime)
25 result, err := svc.ListVolumes(context.Background())
26 require.NoError(t, err)
27 assert.Len(t, result, 2)
28}
29
30func TestService_PruneVolumes_RemovesOrphaned(t *testing.T) {
31 runtime := outmocks.NewMockContainerRuntime(t)
32
33 vols := []*domain.VolumeInfo{
34 {Name: "vol-in-use", InUse: true, Containers: []string{"web"}, Labels: map[string]string{domain.LabelManaged: "true"}},
35 {Name: "vol-orphaned", InUse: false, Size: 1024, Labels: map[string]string{domain.LabelManaged: "true"}},
36 }
37 runtime.EXPECT().ListVolumes(context.Background()).Return(vols, nil)
38 runtime.EXPECT().RemoveVolume(context.Background(), "vol-orphaned", false).Return(nil)
39
40 svc := NewService(runtime)
41 report, removed, err := svc.PruneVolumes(context.Background(), false)
42 require.NoError(t, err)
43 assert.Equal(t, 1, report.VolumesRemoved)
44 assert.Equal(t, int64(1024), report.SpaceReclaimed)
45 assert.Len(t, removed, 1)
46 assert.Equal(t, "vol-orphaned", removed[0].Name)
47}
48
49func TestService_PruneVolumes_DryRunDoesNotRemove(t *testing.T) {
50 runtime := outmocks.NewMockContainerRuntime(t)
51
52 vols := []*domain.VolumeInfo{
53 {Name: "vol-orphaned", InUse: false, Size: 2048, Labels: map[string]string{domain.LabelManaged: "true"}},
54 }
55 runtime.EXPECT().ListVolumes(context.Background()).Return(vols, nil)
56
57 svc := NewService(runtime)
58 report, removed, err := svc.PruneVolumes(context.Background(), true)
59 require.NoError(t, err)
60 assert.Equal(t, 1, report.VolumesRemoved)
61 assert.Equal(t, int64(2048), report.SpaceReclaimed)
62 assert.Len(t, removed, 1)
63}
64
65func TestService_PruneVolumes_OnlyRemovesUnusedGordonManagedVolumes(t *testing.T) {
66 runtime := outmocks.NewMockContainerRuntime(t)
67
68 vols := []*domain.VolumeInfo{
69 {Name: "gordon-used", InUse: true, Size: 100, Labels: map[string]string{domain.LabelManaged: "true"}},
70 {Name: "gordon-unused", InUse: false, Size: 200, Labels: map[string]string{domain.LabelManaged: "true"}},
71 {Name: "other-unused", InUse: false, Size: 300},
72 {Name: "other-labelled-false", InUse: false, Size: 400, Labels: map[string]string{domain.LabelManaged: "false"}},
73 {Name: "other-used", InUse: true, Size: 500},
74 }
75 runtime.EXPECT().ListVolumes(context.Background()).Return(vols, nil)
76 runtime.EXPECT().RemoveVolume(context.Background(), "gordon-unused", false).Return(nil)
77
78 svc := NewService(runtime)
79 report, removed, err := svc.PruneVolumes(context.Background(), false)
80 require.NoError(t, err)
81 assert.Equal(t, 1, report.VolumesRemoved)
82 assert.Equal(t, int64(200), report.SpaceReclaimed)
83 require.Len(t, removed, 1)
84 assert.Equal(t, "gordon-unused", removed[0].Name)
85}
86
87func TestService_PruneVolumes_DryRunReportsOnlyUnusedGordonManagedVolumes(t *testing.T) {
88 runtime := outmocks.NewMockContainerRuntime(t)
89
90 vols := []*domain.VolumeInfo{
91 {Name: "gordon-used", InUse: true, Size: 100, Labels: map[string]string{domain.LabelManaged: "true"}},
92 {Name: "gordon-unused", InUse: false, Size: 200, Labels: map[string]string{domain.LabelManaged: "true"}},
93 {Name: "other-unused", InUse: false, Size: 300},
94 }
95 runtime.EXPECT().ListVolumes(context.Background()).Return(vols, nil)
96
97 svc := NewService(runtime)
98 report, removed, err := svc.PruneVolumes(context.Background(), true)
99 require.NoError(t, err)
100 assert.Equal(t, 1, report.VolumesRemoved)
101 assert.Equal(t, int64(200), report.SpaceReclaimed)
102 require.Len(t, removed, 1)
103 assert.Equal(t, "gordon-unused", removed[0].Name)
104}
105
106func TestService_PruneVolumes_AllInUse(t *testing.T) {
107 runtime := outmocks.NewMockContainerRuntime(t)
108
109 vols := []*domain.VolumeInfo{
110 {Name: "vol1", InUse: true, Containers: []string{"web"}},
111 }
112 runtime.EXPECT().ListVolumes(context.Background()).Return(vols, nil)
113
114 svc := NewService(runtime)
115 report, removed, err := svc.PruneVolumes(context.Background(), false)
116 require.NoError(t, err)
117 assert.Equal(t, 0, report.VolumesRemoved)
118 assert.Empty(t, removed)
119}
120
121func TestService_ListVolumes_PropagatesError(t *testing.T) {
122 runtime := outmocks.NewMockContainerRuntime(t)
123 runtime.EXPECT().ListVolumes(context.Background()).Return(nil, fmt.Errorf("connection refused"))
124
125 svc := NewService(runtime)
126 _, err := svc.ListVolumes(context.Background())
127 require.Error(t, err)
128 assert.Contains(t, err.Error(), "connection refused")
129}
130
131func TestService_PruneVolumes_SkipsFailedRemoval(t *testing.T) {
132 runtime := outmocks.NewMockContainerRuntime(t)
133
134 vols := []*domain.VolumeInfo{
135 {Name: "vol-fail", InUse: false, Size: 1024, Labels: map[string]string{domain.LabelManaged: "true"}},
136 {Name: "vol-ok", InUse: false, Size: 2048, Labels: map[string]string{domain.LabelManaged: "true"}},
137 }
138 runtime.EXPECT().ListVolumes(context.Background()).Return(vols, nil)
139 runtime.EXPECT().RemoveVolume(context.Background(), "vol-fail", false).Return(fmt.Errorf("volume in use"))
140 runtime.EXPECT().RemoveVolume(context.Background(), "vol-ok", false).Return(nil)
141
142 svc := NewService(runtime)
143 report, removed, err := svc.PruneVolumes(context.Background(), false)
144 require.NoError(t, err)
145 assert.Equal(t, 1, report.VolumesRemoved)
146 assert.Equal(t, int64(2048), report.SpaceReclaimed)
147 assert.Len(t, removed, 1)
148 assert.Equal(t, "vol-ok", removed[0].Name)
149}