-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdatabase_test.go
More file actions
326 lines (267 loc) · 9.64 KB
/
database_test.go
File metadata and controls
326 lines (267 loc) · 9.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
package main
import (
"database/sql"
"fmt"
"os"
"testing"
"time"
"charm.land/bubbles/v2/progress"
tea "charm.land/bubbletea/v2"
"charm.land/lipgloss/v2"
"github.com/stretchr/testify/assert"
)
func TestInitDB(t *testing.T) {
// Set up a temporary database path
tempDBPath, cleanup := setupTestDB(t)
defer cleanup()
// Initialize the database
err := initDB()
assert.NoError(t, err, "initDB should not return an error")
// Verify the database file was created
_, err = os.Stat(tempDBPath)
assert.NoError(t, err, "Database file should exist after initDB")
// Verify the sessions table was created
db, err := sql.Open("sqlite", tempDBPath)
assert.NoError(t, err)
defer db.Close()
// Query the table to ensure it exists
var tableName string
err = db.QueryRow("SELECT name FROM sqlite_master WHERE type='table' AND name='sessions'").Scan(&tableName)
assert.NoError(t, err, "sessions table should exist")
assert.Equal(t, "sessions", tableName, "Table name should be 'sessions'")
}
func TestInitDBIdempotent(t *testing.T) {
// Set up a temporary database path
_, cleanup := setupTestDB(t)
defer cleanup()
// Initialize the database twice
err := initDB()
assert.NoError(t, err, "First initDB should not return an error")
err = initDB()
assert.NoError(t, err, "Second initDB should not return an error (idempotent)")
}
func TestGetRecentSessionsOnEmptyDB(t *testing.T) {
// Set up a temporary database path
_, cleanup := setupTestDB(t)
defer cleanup()
// Initialize the database
err := initDB()
assert.NoError(t, err)
// Fetch recent sessions from empty database
sessions, err := getRecentSessions(10)
assert.NoError(t, err, "getRecentSessions should not error on empty database")
assert.Empty(t, sessions, "Expected no sessions in empty database")
}
func TestSaveSessionToDB(t *testing.T) {
// Set up a temporary database path
tempDBPath, cleanup := setupTestDB(t)
defer cleanup()
// Test data.
// Assumes that each tuple of (duration, completed, title) is unique.
tests := []struct {
duration int64
completed bool
title string
}{
{60, true, "Test Session 1"},
{120, false, "Test Session 2"},
{180, true, ""},
}
for _, test := range tests {
err := saveSessionToDB(test.duration, test.completed, test.title)
assert.NoError(t, err, "saveSessionToDB(%d, %v, %s)", test.duration, test.completed, test.title)
// Verify the session was saved correctly
db, err := sql.Open("sqlite", tempDBPath)
assert.NoError(t, err)
defer db.Close()
var count int
err = db.QueryRow("SELECT COUNT(*) FROM sessions WHERE duration = ? AND completed = ? AND title = ?", test.duration, test.completed, test.title).Scan(&count)
assert.NoError(t, err)
assert.Equal(t, 1, count, "Expected one session with duration %d, completed %v, and title %s", test.duration, test.completed, test.title)
}
}
func TestSaveSessionToDBWithTitle(t *testing.T) {
// Set up a temporary database path
tempDBPath, cleanup := setupTestDB(t)
defer cleanup()
// Test with a specific title
title := "Important Work Session"
duration := int64(1500)
completed := true
err := saveSessionToDB(duration, completed, title)
assert.NoError(t, err)
// Verify the title was saved correctly
db, err := sql.Open("sqlite", tempDBPath)
assert.NoError(t, err)
defer db.Close()
var savedTitle string
err = db.QueryRow("SELECT title FROM sessions WHERE duration = ? AND completed = ?", duration, completed).Scan(&savedTitle)
assert.NoError(t, err)
assert.Equal(t, title, savedTitle, "Expected title to be saved correctly")
}
func TestSaveSessionToDBWithoutTitle(t *testing.T) {
// Set up a temporary database path
tempDBPath, cleanup := setupTestDB(t)
defer cleanup()
// Test without title (empty string)
duration := int64(900)
completed := false
err := saveSessionToDB(duration, completed, "")
assert.NoError(t, err)
// Verify the empty title was saved correctly
db, err := sql.Open("sqlite", tempDBPath)
assert.NoError(t, err)
defer db.Close()
var savedTitle string
err = db.QueryRow("SELECT title FROM sessions WHERE duration = ? AND completed = ?", duration, completed).Scan(&savedTitle)
assert.NoError(t, err)
assert.Equal(t, "", savedTitle, "Expected title to be empty")
}
func TestDatabaseSchemaHasTitleColumn(t *testing.T) {
// Set up a temporary database path
tempDBPath, cleanup := setupTestDB(t)
defer cleanup()
// Create a session to initialize the database
err := saveSessionToDB(60, true, "Test")
assert.NoError(t, err)
// Verify the title column exists
db, err := sql.Open("sqlite", tempDBPath)
assert.NoError(t, err)
defer db.Close()
rows, err := db.Query("PRAGMA table_info(sessions)")
assert.NoError(t, err)
defer rows.Close()
columnNames := make(map[string]bool)
for rows.Next() {
var cid int
var name string
var ctype string
var notnull int
var dfltValue interface{}
var pk int
err = rows.Scan(&cid, &name, &ctype, ¬null, &dfltValue, &pk)
assert.NoError(t, err)
columnNames[name] = true
}
// Verify all expected columns exist
assert.True(t, columnNames["id"], "Expected 'id' column to exist")
assert.True(t, columnNames["datetime"], "Expected 'datetime' column to exist")
assert.True(t, columnNames["duration"], "Expected 'duration' column to exist")
assert.True(t, columnNames["completed"], "Expected 'completed' column to exist")
assert.True(t, columnNames["title"], "Expected 'title' column to exist")
}
func TestGetRecentSessions(t *testing.T) {
// Set up a temporary database path
_, cleanup := setupTestDB(t)
defer cleanup()
// Create several test sessions
sessions := []struct {
duration int64
completed bool
title string
}{
{1500, true, "Task 1"},
{900, false, "Task 2"},
{1800, true, "Task 3"},
{600, true, "Task 4"},
}
for _, s := range sessions {
err := saveSessionToDB(s.duration, s.completed, s.title)
assert.NoError(t, err)
}
// Fetch recent sessions
recentSessions, err := getRecentSessions(10)
assert.NoError(t, err)
assert.Equal(t, len(sessions), len(recentSessions), "Expected to retrieve all sessions")
// Verify that all session titles are present
titles := make(map[string]bool)
for _, s := range recentSessions {
titles[s.title] = true
}
for _, s := range sessions {
assert.True(t, titles[s.title], "Expected session with title '%s' to be in results", s.title)
}
}
func TestGetRecentSessionsWithLimit(t *testing.T) {
// Set up a temporary database path
_, cleanup := setupTestDB(t)
defer cleanup()
// Create several test sessions
for i := 0; i < 5; i++ {
err := saveSessionToDB(1500, true, fmt.Sprintf("Task %d", i))
assert.NoError(t, err)
time.Sleep(10 * time.Millisecond)
}
// Fetch only 3 most recent sessions
recentSessions, err := getRecentSessions(3)
assert.NoError(t, err)
assert.Equal(t, 3, len(recentSessions), "Expected to retrieve only 3 sessions")
}
func TestDateFormatInTableFromDatabase(t *testing.T) {
// Set up a temporary database path
_, cleanup := setupTestDB(t)
defer cleanup()
// Save a session to the database
err := saveSessionToDB(1500, true, "Test Task")
assert.NoError(t, err)
// Retrieve the session
sessions, err := getRecentSessions(1)
assert.NoError(t, err)
assert.Equal(t, 1, len(sessions), "Expected one session")
// Verify the datetime can be parsed and formatted correctly
s := sessions[0]
// SQLite DATETIME can store dates in different formats, try both
var parsedTime time.Time
if parsedTime, err = time.Parse("2006-01-02 15:04:05", s.datetime); err != nil {
parsedTime, err = time.Parse("2006-01-02T15:04:05Z", s.datetime)
}
assert.NoError(t, err, "Should be able to parse datetime from database: %s", s.datetime)
// Format it the way the table displays it
formatted := parsedTime.Format("Monday, 2 Jan 06")
// Verify the format matches expected pattern (e.g., "Wednesday, 7 Jan 26")
assert.Regexp(t, `^\w+, \d{1,2} \w+ \d{2}$`, formatted, "Date should match pattern 'DayName, D Mon YY'")
}
func TestFirstSaveThenImmediateHistoryRead(t *testing.T) {
_, cleanup := setupTestDB(t)
defer cleanup()
// Initialize database (creates new empty DB)
err := initDB()
assert.NoError(t, err)
startTime := time.Now().Unix() - 120
m := model{
progress: progress.New(progress.WithColors(lipgloss.Color(colorMontezumaGold), lipgloss.Color(colorCream)), progress.WithoutPercentage()),
startTime: startTime,
targetDuration: 3600,
countUpMode: true,
mode: timerView,
title: "First Task",
}
// Press 'd' to mark done and activate prompt
keyMsg := tea.KeyPressMsg{Code: 'd', Text: "d"}
newModel, _ := m.Update(keyMsg)
modelTyped := newModel.(model)
assert.True(t, modelTyped.promptActive, "Expected prompt to be active")
assert.Equal(t, promptLogAndReset, modelTyped.promptType, "Expected promptType to be promptLogAndReset")
// Complete the prompt (this saves to DB and refreshes table)
newModel, _ = modelTyped.Update(promptMsg{title: "First Task", logDB: true})
modelTyped = newModel.(model)
// Immediately press 'h' to show history (this is the bug scenario)
keyMsg = tea.KeyPressMsg{Code: 'h', Text: "h"}
newModel, _ = modelTyped.Update(keyMsg)
modelTyped = newModel.(model)
// Verify we're in table view
assert.Equal(t, tableView, modelTyped.mode, "Expected to switch to table view")
// Verify the table has rows (this would fail with the bug)
tableRows := modelTyped.table.Rows()
assert.NotEmpty(t, tableRows, "Expected table to have rows after saving first task")
assert.GreaterOrEqual(t, len(tableRows), 1, "Expected at least one row in history table")
// Verify the saved task appears in the table
found := false
for _, row := range tableRows {
if len(row) > 0 && row[0] == "First Task" {
found = true
break
}
}
assert.True(t, found, "Expected 'First Task' to appear in history table")
}