1package usecase_test
2
3import (
4 "errors"
5 "testing"
6 "time"
7
8 "github.com/bnema/dumber/internal/application/usecase"
9 repomocks "github.com/bnema/dumber/internal/domain/repository/mocks"
10 "github.com/stretchr/testify/assert"
11 "github.com/stretchr/testify/mock"
12 "github.com/stretchr/testify/require"
13)
14
15func TestCleanupSessionsUseCase_Execute_AgeAndCountCleanup(t *testing.T) {
16 ctx := testContext()
17
18 sessionRepo := repomocks.NewMockSessionRepository(t)
19
20 // Expect age-based deletion
21 sessionRepo.EXPECT().
22 DeleteExitedBefore(ctx, mock.MatchedBy(func(cutoff time.Time) bool {
23 // Should be approximately 7 days ago
24 expected := time.Now().AddDate(0, 0, -7)
25 diff := expected.Sub(cutoff)
26 return diff > -time.Minute && diff < time.Minute
27 })).
28 Return(int64(3), nil)
29
30 // Expect count-based deletion
31 sessionRepo.EXPECT().
32 DeleteOldestExited(ctx, 50).
33 Return(int64(2), nil)
34
35 uc := usecase.NewCleanupSessionsUseCase(sessionRepo)
36
37 output, err := uc.Execute(ctx, usecase.CleanupSessionsInput{
38 MaxExitedSessions: 50,
39 MaxExitedSessionAgeDays: 7,
40 })
41
42 require.NoError(t, err)
43 assert.Equal(t, int64(3), output.DeletedByAge)
44 assert.Equal(t, int64(2), output.DeletedByCount)
45 assert.Equal(t, int64(5), output.TotalDeleted)
46}
47
48func TestCleanupSessionsUseCase_Execute_AgeOnlyCleanup(t *testing.T) {
49 ctx := testContext()
50
51 sessionRepo := repomocks.NewMockSessionRepository(t)
52
53 // Expect age-based deletion
54 sessionRepo.EXPECT().
55 DeleteExitedBefore(ctx, mock.Anything).
56 Return(int64(5), nil)
57
58 // Count-based with -1 should be skipped (disabled)
59 // MaxExitedSessions >= 0 triggers count cleanup, so use -1 to disable
60 uc := usecase.NewCleanupSessionsUseCase(sessionRepo)
61
62 output, err := uc.Execute(ctx, usecase.CleanupSessionsInput{
63 MaxExitedSessions: -1, // Disabled
64 MaxExitedSessionAgeDays: 30,
65 })
66
67 require.NoError(t, err)
68 assert.Equal(t, int64(5), output.DeletedByAge)
69 assert.Equal(t, int64(0), output.DeletedByCount)
70 assert.Equal(t, int64(5), output.TotalDeleted)
71}
72
73func TestCleanupSessionsUseCase_Execute_CountOnlyCleanup(t *testing.T) {
74 ctx := testContext()
75
76 sessionRepo := repomocks.NewMockSessionRepository(t)
77
78 // Count-based deletion only (age disabled with 0)
79 sessionRepo.EXPECT().
80 DeleteOldestExited(ctx, 10).
81 Return(int64(4), nil)
82
83 uc := usecase.NewCleanupSessionsUseCase(sessionRepo)
84
85 output, err := uc.Execute(ctx, usecase.CleanupSessionsInput{
86 MaxExitedSessions: 10,
87 MaxExitedSessionAgeDays: 0, // Disabled
88 })
89
90 require.NoError(t, err)
91 assert.Equal(t, int64(0), output.DeletedByAge)
92 assert.Equal(t, int64(4), output.DeletedByCount)
93 assert.Equal(t, int64(4), output.TotalDeleted)
94}
95
96func TestCleanupSessionsUseCase_Execute_NoCleanupNeeded(t *testing.T) {
97 ctx := testContext()
98
99 sessionRepo := repomocks.NewMockSessionRepository(t)
100
101 // Nothing to delete
102 sessionRepo.EXPECT().
103 DeleteExitedBefore(ctx, mock.Anything).
104 Return(int64(0), nil)
105
106 sessionRepo.EXPECT().
107 DeleteOldestExited(ctx, 50).
108 Return(int64(0), nil)
109
110 uc := usecase.NewCleanupSessionsUseCase(sessionRepo)
111
112 output, err := uc.Execute(ctx, usecase.CleanupSessionsInput{
113 MaxExitedSessions: 50,
114 MaxExitedSessionAgeDays: 7,
115 })
116
117 require.NoError(t, err)
118 assert.Equal(t, int64(0), output.DeletedByAge)
119 assert.Equal(t, int64(0), output.DeletedByCount)
120 assert.Equal(t, int64(0), output.TotalDeleted)
121}
122
123func TestCleanupSessionsUseCase_Execute_AgeDeleteError_ContinuesToCount(t *testing.T) {
124 ctx := testContext()
125
126 sessionRepo := repomocks.NewMockSessionRepository(t)
127
128 // Age-based deletion fails
129 sessionRepo.EXPECT().
130 DeleteExitedBefore(ctx, mock.Anything).
131 Return(int64(0), errors.New("db error"))
132
133 // Should still attempt count-based deletion
134 sessionRepo.EXPECT().
135 DeleteOldestExited(ctx, 50).
136 Return(int64(3), nil)
137
138 uc := usecase.NewCleanupSessionsUseCase(sessionRepo)
139
140 output, err := uc.Execute(ctx, usecase.CleanupSessionsInput{
141 MaxExitedSessions: 50,
142 MaxExitedSessionAgeDays: 7,
143 })
144
145 // Should not return error, just log warning
146 require.NoError(t, err)
147 assert.Equal(t, int64(0), output.DeletedByAge)
148 assert.Equal(t, int64(3), output.DeletedByCount)
149 assert.Equal(t, int64(3), output.TotalDeleted)
150}
151
152func TestCleanupSessionsUseCase_Execute_CountDeleteError(t *testing.T) {
153 ctx := testContext()
154
155 sessionRepo := repomocks.NewMockSessionRepository(t)
156
157 // Age-based deletion succeeds
158 sessionRepo.EXPECT().
159 DeleteExitedBefore(ctx, mock.Anything).
160 Return(int64(2), nil)
161
162 // Count-based deletion fails
163 sessionRepo.EXPECT().
164 DeleteOldestExited(ctx, 50).
165 Return(int64(0), errors.New("db error"))
166
167 uc := usecase.NewCleanupSessionsUseCase(sessionRepo)
168
169 output, err := uc.Execute(ctx, usecase.CleanupSessionsInput{
170 MaxExitedSessions: 50,
171 MaxExitedSessionAgeDays: 7,
172 })
173
174 // Should not return error, just log warning
175 require.NoError(t, err)
176 assert.Equal(t, int64(2), output.DeletedByAge)
177 assert.Equal(t, int64(0), output.DeletedByCount)
178 assert.Equal(t, int64(2), output.TotalDeleted)
179}
180
181func TestCleanupSessionsUseCase_Execute_BothDisabled(t *testing.T) {
182 ctx := testContext()
183
184 sessionRepo := repomocks.NewMockSessionRepository(t)
185
186 // No expectations - nothing should be called
187 // Actually, count with 0 means "keep 0" which will trigger cleanup
188 // So we need -1 for count and 0 for age to fully disable
189
190 uc := usecase.NewCleanupSessionsUseCase(sessionRepo)
191
192 output, err := uc.Execute(ctx, usecase.CleanupSessionsInput{
193 MaxExitedSessions: -1, // Disabled
194 MaxExitedSessionAgeDays: 0, // Disabled
195 })
196
197 require.NoError(t, err)
198 assert.Equal(t, int64(0), output.DeletedByAge)
199 assert.Equal(t, int64(0), output.DeletedByCount)
200 assert.Equal(t, int64(0), output.TotalDeleted)
201}
202
203func TestCleanupSessionsUseCase_Execute_ZeroMaxSessions(t *testing.T) {
204 ctx := testContext()
205
206 sessionRepo := repomocks.NewMockSessionRepository(t)
207
208 // MaxExitedSessions = 0 means keep 0 sessions (delete all)
209 sessionRepo.EXPECT().
210 DeleteOldestExited(ctx, 0).
211 Return(int64(10), nil)
212
213 uc := usecase.NewCleanupSessionsUseCase(sessionRepo)
214
215 output, err := uc.Execute(ctx, usecase.CleanupSessionsInput{
216 MaxExitedSessions: 0, // Keep 0 = delete all
217 MaxExitedSessionAgeDays: 0, // Disabled
218 })
219
220 require.NoError(t, err)
221 assert.Equal(t, int64(0), output.DeletedByAge)
222 assert.Equal(t, int64(10), output.DeletedByCount)
223 assert.Equal(t, int64(10), output.TotalDeleted)
224}