1package usecase_test
2
3import (
4 "errors"
5 "testing"
6 "time"
7
8 "github.com/bnema/dumber/internal/application/usecase"
9 "github.com/bnema/dumber/internal/domain/entity"
10 repomocks "github.com/bnema/dumber/internal/domain/repository/mocks"
11 "github.com/stretchr/testify/assert"
12 "github.com/stretchr/testify/require"
13)
14
15func TestDeleteSessionUseCase_Execute_Success(t *testing.T) {
16 ctx := testContext()
17
18 stateRepo := repomocks.NewMockSessionStateRepository(t)
19 sessionRepo := repomocks.NewMockSessionRepository(t)
20
21 sessionID := entity.SessionID("20251224_120000_delete")
22
23 endedAt := time.Now().Add(-time.Hour)
24 session := &entity.Session{
25 ID: sessionID,
26 Type: entity.SessionTypeBrowser,
27 StartedAt: time.Now().Add(-2 * time.Hour),
28 EndedAt: &endedAt,
29 }
30
31 sessionRepo.EXPECT().FindByID(ctx, sessionID).Return(session, nil)
32 stateRepo.EXPECT().DeleteSnapshot(ctx, sessionID).Return(nil)
33 sessionRepo.EXPECT().Delete(ctx, sessionID).Return(nil)
34
35 uc := usecase.NewDeleteSessionUseCase(stateRepo, sessionRepo)
36
37 err := uc.Execute(ctx, usecase.DeleteSessionInput{
38 SessionID: sessionID,
39 CurrentSessionID: "other-session",
40 })
41 require.NoError(t, err)
42}
43
44func TestDeleteSessionUseCase_Execute_CannotDeleteCurrentSession(t *testing.T) {
45 ctx := testContext()
46
47 stateRepo := repomocks.NewMockSessionStateRepository(t)
48 sessionRepo := repomocks.NewMockSessionRepository(t)
49
50 sessionID := entity.SessionID("20251224_120000_current")
51
52 uc := usecase.NewDeleteSessionUseCase(stateRepo, sessionRepo)
53
54 err := uc.Execute(ctx, usecase.DeleteSessionInput{
55 SessionID: sessionID,
56 CurrentSessionID: sessionID, // Same as current
57 })
58 require.Error(t, err)
59 assert.ErrorIs(t, err, usecase.ErrCannotDeleteCurrentSession)
60}
61
62func TestDeleteSessionUseCase_Execute_SessionNotFound(t *testing.T) {
63 ctx := testContext()
64
65 stateRepo := repomocks.NewMockSessionStateRepository(t)
66 sessionRepo := repomocks.NewMockSessionRepository(t)
67
68 sessionID := entity.SessionID("20251224_120000_notfound")
69
70 sessionRepo.EXPECT().FindByID(ctx, sessionID).Return(nil, nil)
71
72 uc := usecase.NewDeleteSessionUseCase(stateRepo, sessionRepo)
73
74 err := uc.Execute(ctx, usecase.DeleteSessionInput{
75 SessionID: sessionID,
76 CurrentSessionID: "other-session",
77 })
78 require.Error(t, err)
79 assert.ErrorIs(t, err, usecase.ErrSessionNotFound)
80}
81
82func TestDeleteSessionUseCase_Execute_CannotDeleteActiveSession(t *testing.T) {
83 ctx := testContext()
84
85 stateRepo := repomocks.NewMockSessionStateRepository(t)
86 sessionRepo := repomocks.NewMockSessionRepository(t)
87
88 sessionID := entity.SessionID("20251224_120000_active")
89
90 // Session with nil EndedAt (still active)
91 session := &entity.Session{
92 ID: sessionID,
93 Type: entity.SessionTypeBrowser,
94 StartedAt: time.Now().Add(-time.Hour),
95 EndedAt: nil, // Active
96 }
97
98 sessionRepo.EXPECT().FindByID(ctx, sessionID).Return(session, nil)
99
100 uc := usecase.NewDeleteSessionUseCase(stateRepo, sessionRepo)
101
102 err := uc.Execute(ctx, usecase.DeleteSessionInput{
103 SessionID: sessionID,
104 CurrentSessionID: "other-session",
105 })
106 require.Error(t, err)
107 assert.ErrorIs(t, err, usecase.ErrCannotDeleteActiveSession)
108}
109
110func TestDeleteSessionUseCase_Execute_FindByIDError(t *testing.T) {
111 ctx := testContext()
112
113 stateRepo := repomocks.NewMockSessionStateRepository(t)
114 sessionRepo := repomocks.NewMockSessionRepository(t)
115
116 sessionID := entity.SessionID("20251224_120000_dberror")
117
118 sessionRepo.EXPECT().FindByID(ctx, sessionID).Return(nil, errors.New("db error"))
119
120 uc := usecase.NewDeleteSessionUseCase(stateRepo, sessionRepo)
121
122 err := uc.Execute(ctx, usecase.DeleteSessionInput{
123 SessionID: sessionID,
124 CurrentSessionID: "other-session",
125 })
126 require.Error(t, err)
127 assert.Contains(t, err.Error(), "db error")
128}
129
130func TestDeleteSessionUseCase_Execute_DeleteSnapshotErrorIgnored(t *testing.T) {
131 ctx := testContext()
132
133 stateRepo := repomocks.NewMockSessionStateRepository(t)
134 sessionRepo := repomocks.NewMockSessionRepository(t)
135
136 sessionID := entity.SessionID("20251224_120000_nosnapshot")
137
138 endedAt := time.Now().Add(-time.Hour)
139 session := &entity.Session{
140 ID: sessionID,
141 Type: entity.SessionTypeBrowser,
142 StartedAt: time.Now().Add(-2 * time.Hour),
143 EndedAt: &endedAt,
144 }
145
146 sessionRepo.EXPECT().FindByID(ctx, sessionID).Return(session, nil)
147 // Snapshot deletion fails (e.g., no snapshot exists) - should be ignored
148 stateRepo.EXPECT().DeleteSnapshot(ctx, sessionID).Return(errors.New("no rows"))
149 sessionRepo.EXPECT().Delete(ctx, sessionID).Return(nil)
150
151 uc := usecase.NewDeleteSessionUseCase(stateRepo, sessionRepo)
152
153 err := uc.Execute(ctx, usecase.DeleteSessionInput{
154 SessionID: sessionID,
155 CurrentSessionID: "other-session",
156 })
157 // Should succeed even though DeleteSnapshot failed
158 require.NoError(t, err)
159}
160
161func TestDeleteSessionUseCase_Execute_DeleteSessionError(t *testing.T) {
162 ctx := testContext()
163
164 stateRepo := repomocks.NewMockSessionStateRepository(t)
165 sessionRepo := repomocks.NewMockSessionRepository(t)
166
167 sessionID := entity.SessionID("20251224_120000_deleteerror")
168
169 endedAt := time.Now().Add(-time.Hour)
170 session := &entity.Session{
171 ID: sessionID,
172 Type: entity.SessionTypeBrowser,
173 StartedAt: time.Now().Add(-2 * time.Hour),
174 EndedAt: &endedAt,
175 }
176
177 sessionRepo.EXPECT().FindByID(ctx, sessionID).Return(session, nil)
178 stateRepo.EXPECT().DeleteSnapshot(ctx, sessionID).Return(nil)
179 sessionRepo.EXPECT().Delete(ctx, sessionID).Return(errors.New("delete failed"))
180
181 uc := usecase.NewDeleteSessionUseCase(stateRepo, sessionRepo)
182
183 err := uc.Execute(ctx, usecase.DeleteSessionInput{
184 SessionID: sessionID,
185 CurrentSessionID: "other-session",
186 })
187 require.Error(t, err)
188 assert.Contains(t, err.Error(), "delete failed")
189}
190
191func TestDeleteSessionUseCase_Execute_EmptyCurrentSessionAllowed(t *testing.T) {
192 ctx := testContext()
193
194 stateRepo := repomocks.NewMockSessionStateRepository(t)
195 sessionRepo := repomocks.NewMockSessionRepository(t)
196
197 sessionID := entity.SessionID("20251224_120000_nocurrent")
198
199 endedAt := time.Now().Add(-time.Hour)
200 session := &entity.Session{
201 ID: sessionID,
202 Type: entity.SessionTypeBrowser,
203 StartedAt: time.Now().Add(-2 * time.Hour),
204 EndedAt: &endedAt,
205 }
206
207 sessionRepo.EXPECT().FindByID(ctx, sessionID).Return(session, nil)
208 stateRepo.EXPECT().DeleteSnapshot(ctx, sessionID).Return(nil)
209 sessionRepo.EXPECT().Delete(ctx, sessionID).Return(nil)
210
211 uc := usecase.NewDeleteSessionUseCase(stateRepo, sessionRepo)
212
213 // CurrentSessionID is empty (e.g., CLI context)
214 err := uc.Execute(ctx, usecase.DeleteSessionInput{
215 SessionID: sessionID,
216 CurrentSessionID: "",
217 })
218 require.NoError(t, err)
219}