1package usecase_test
2
3import (
4 "context"
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/mock"
13 "github.com/stretchr/testify/require"
14)
15
16func TestSnapshotSessionUseCase_Execute_SavesSnapshot(t *testing.T) {
17 ctx := testContext()
18
19 stateRepo := repomocks.NewMockSessionStateRepository(t)
20
21 // Create a tab list with some tabs
22 tabList := entity.NewTabList()
23 pane := entity.NewPane("pane-1")
24 pane.URI = "https://example.com"
25 pane.Title = "Example"
26 tab := entity.NewTab("tab-1", "ws-1", pane)
27 tab.Name = "Test Tab"
28 tabList.Add(tab)
29
30 sessionID := entity.SessionID("20251224_120000_test")
31
32 stateRepo.EXPECT().SaveSnapshot(mock.Anything, mock.AnythingOfType("*entity.SessionState")).
33 Run(func(_ context.Context, state *entity.SessionState) {
34 require.Equal(t, sessionID, state.SessionID)
35 require.Len(t, state.Tabs, 1)
36 require.Equal(t, "Test Tab", state.Tabs[0].Name)
37 require.Equal(t, entity.SessionStateVersion, state.Version)
38 }).
39 Return(nil)
40
41 uc := usecase.NewSnapshotSessionUseCase(stateRepo)
42
43 err := uc.Execute(ctx, usecase.SnapshotInput{
44 SessionID: sessionID,
45 TabList: tabList,
46 })
47 require.NoError(t, err)
48}
49
50func TestSnapshotSessionUseCase_Execute_EmptyTabList(t *testing.T) {
51 ctx := testContext()
52
53 stateRepo := repomocks.NewMockSessionStateRepository(t)
54
55 sessionID := entity.SessionID("20251224_120000_empty")
56
57 stateRepo.EXPECT().SaveSnapshot(mock.Anything, mock.AnythingOfType("*entity.SessionState")).
58 Run(func(_ context.Context, state *entity.SessionState) {
59 require.Equal(t, sessionID, state.SessionID)
60 require.Empty(t, state.Tabs)
61 }).
62 Return(nil)
63
64 uc := usecase.NewSnapshotSessionUseCase(stateRepo)
65
66 err := uc.Execute(ctx, usecase.SnapshotInput{
67 SessionID: sessionID,
68 TabList: nil,
69 })
70 require.NoError(t, err)
71}
72
73func TestSnapshotSessionUseCase_Execute_RequiresSessionID(t *testing.T) {
74 ctx := testContext()
75
76 stateRepo := repomocks.NewMockSessionStateRepository(t)
77
78 uc := usecase.NewSnapshotSessionUseCase(stateRepo)
79
80 err := uc.Execute(ctx, usecase.SnapshotInput{
81 SessionID: "",
82 TabList: entity.NewTabList(),
83 })
84 require.Error(t, err)
85 assert.Contains(t, err.Error(), "session id required")
86}
87
88func TestSnapshotFromTabList_CreatesCorrectSnapshot(t *testing.T) {
89 sessionID := entity.SessionID("20251224_120000_snap")
90
91 // Create a workspace with multiple panes
92 pane1 := entity.NewPane("pane-1")
93 pane1.URI = "https://github.com"
94 pane1.Title = "GitHub"
95 pane1.ZoomFactor = 1.2
96
97 pane2 := entity.NewPane("pane-2")
98 pane2.URI = "https://google.com"
99 pane2.Title = "Google"
100
101 tab := entity.NewTab("tab-1", "ws-1", pane1)
102 tab.Name = "Dev Tab"
103 tab.IsPinned = true
104
105 tabList := entity.NewTabList()
106 tabList.Add(tab)
107
108 state := entity.SnapshotFromTabList(sessionID, tabList)
109
110 require.NotNil(t, state)
111 assert.Equal(t, entity.SessionStateVersion, state.Version)
112 assert.Equal(t, sessionID, state.SessionID)
113 assert.Len(t, state.Tabs, 1)
114 assert.Equal(t, "Dev Tab", state.Tabs[0].Name)
115 assert.True(t, state.Tabs[0].IsPinned)
116 assert.NotNil(t, state.Tabs[0].Workspace.Root)
117 assert.Equal(t, "https://github.com", state.Tabs[0].Workspace.Root.Pane.URI)
118 assert.InDelta(t, 1.2, state.Tabs[0].Workspace.Root.Pane.ZoomFactor, 0.001)
119 assert.False(t, state.SavedAt.IsZero())
120}
121
122func TestSessionState_CountPanes(t *testing.T) {
123 state := &entity.SessionState{
124 Tabs: []entity.TabSnapshot{
125 {
126 Workspace: entity.WorkspaceSnapshot{
127 Root: &entity.PaneNodeSnapshot{
128 Pane: &entity.PaneSnapshot{ID: "pane-1"},
129 },
130 },
131 },
132 {
133 Workspace: entity.WorkspaceSnapshot{
134 Root: &entity.PaneNodeSnapshot{
135 Children: []*entity.PaneNodeSnapshot{
136 {Pane: &entity.PaneSnapshot{ID: "pane-2"}},
137 {Pane: &entity.PaneSnapshot{ID: "pane-3"}},
138 },
139 },
140 },
141 },
142 },
143 }
144
145 assert.Equal(t, 3, state.CountPanes())
146}
147
148func TestGetRelativeTime(t *testing.T) {
149 now := time.Now()
150
151 tests := []struct {
152 name string
153 t time.Time
154 expected string
155 }{
156 {"just now", now.Add(-30 * time.Second), "just now"},
157 {"1 minute", now.Add(-1 * time.Minute), "1m ago"},
158 {"5 minutes", now.Add(-5 * time.Minute), "05m ago"},
159 {"1 hour", now.Add(-1 * time.Hour), "1h ago"},
160 {"3 hours", now.Add(-3 * time.Hour), "03h ago"},
161 {"1 day", now.Add(-24 * time.Hour), "1d ago"},
162 {"2 days", now.Add(-48 * time.Hour), "02d ago"},
163 {"1 week", now.Add(-7 * 24 * time.Hour), "1w ago"},
164 {"2 weeks", now.Add(-14 * 24 * time.Hour), "02w ago"},
165 }
166
167 for _, tt := range tests {
168 t.Run(tt.name, func(t *testing.T) {
169 result := usecase.GetRelativeTime(tt.t)
170 assert.Equal(t, tt.expected, result)
171 })
172 }
173}