v0.21.0 73bf644

snapshot_session_test.go RawHistoryFolder

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}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close