cli + tui to publish to leaflet (wip) & manage tasks, notes & watch/read lists ๐Ÿƒ
charm leaflet readability golang

refactor: repository test utilities build: apply test utilities

+1334 -1272
+139 -3
internal/models/models_test.go
··· 1024 1024 t.Errorf("Expected 6 models, got %d", len(models)) 1025 1025 } 1026 1026 1027 - // Test that all models have the required methods 1028 1027 for i, model := range models { 1029 - // Test ID methods 1030 1028 model.SetID(int64(i + 1)) 1031 1029 if model.GetID() != int64(i+1) { 1032 1030 t.Errorf("Model %d: ID not set correctly", i) ··· 1103 1101 book := &Book{} 1104 1102 note := &Note{} 1105 1103 1106 - // Test that zero values don't cause panics 1107 1104 if task.IsCompleted() || task.IsPending() || task.IsDeleted() { 1108 1105 t.Error("Zero value task should have false status methods") 1109 1106 } ··· 1126 1123 1127 1124 if note.IsArchived() { 1128 1125 t.Error("Zero value note should not be archived") 1126 + } 1127 + }) 1128 + }) 1129 + 1130 + t.Run("TimeEntry Model", func(t *testing.T) { 1131 + t.Run("IsActive", func(t *testing.T) { 1132 + now := time.Now() 1133 + 1134 + t.Run("returns true when EndTime is nil", func(t *testing.T) { 1135 + te := &TimeEntry{ 1136 + TaskID: 1, 1137 + StartTime: now, 1138 + EndTime: nil, 1139 + } 1140 + 1141 + if !te.IsActive() { 1142 + t.Error("TimeEntry with nil EndTime should be active") 1143 + } 1144 + }) 1145 + 1146 + t.Run("returns false when EndTime is set", func(t *testing.T) { 1147 + endTime := now.Add(time.Hour) 1148 + te := &TimeEntry{ 1149 + TaskID: 1, 1150 + StartTime: now, 1151 + EndTime: &endTime, 1152 + } 1153 + 1154 + if te.IsActive() { 1155 + t.Error("TimeEntry with EndTime should not be active") 1156 + } 1157 + }) 1158 + }) 1159 + 1160 + t.Run("Stop", func(t *testing.T) { 1161 + startTime := time.Now().Add(-time.Hour) 1162 + te := &TimeEntry{ 1163 + TaskID: 1, 1164 + StartTime: startTime, 1165 + EndTime: nil, 1166 + Created: startTime, 1167 + Modified: startTime, 1168 + } 1169 + 1170 + if !te.IsActive() { 1171 + t.Error("TimeEntry should be active before Stop()") 1172 + } 1173 + 1174 + te.Stop() 1175 + 1176 + if te.IsActive() { 1177 + t.Error("TimeEntry should not be active after Stop()") 1178 + } 1179 + 1180 + if te.EndTime == nil { 1181 + t.Error("EndTime should be set after Stop()") 1182 + } 1183 + 1184 + if te.EndTime.Before(startTime) { 1185 + t.Error("EndTime should be after StartTime") 1186 + } 1187 + 1188 + expectedDuration := int64(te.EndTime.Sub(startTime).Seconds()) 1189 + if te.DurationSeconds != expectedDuration { 1190 + t.Errorf("Expected DurationSeconds %d, got %d", expectedDuration, te.DurationSeconds) 1191 + } 1192 + 1193 + if te.Modified.Before(startTime) { 1194 + t.Error("Modified time should be updated after Stop()") 1195 + } 1196 + }) 1197 + 1198 + t.Run("GetDuration", func(t *testing.T) { 1199 + startTime := time.Now().Add(-time.Hour) 1200 + 1201 + t.Run("returns calculated duration when stopped", func(t *testing.T) { 1202 + endTime := startTime.Add(30 * time.Minute) 1203 + te := &TimeEntry{ 1204 + TaskID: 1, 1205 + StartTime: startTime, 1206 + EndTime: &endTime, 1207 + DurationSeconds: 1800, 1208 + } 1209 + 1210 + duration := te.GetDuration() 1211 + expectedDuration := 30 * time.Minute 1212 + 1213 + if duration != expectedDuration { 1214 + t.Errorf("Expected duration %v, got %v", expectedDuration, duration) 1215 + } 1216 + }) 1217 + 1218 + t.Run("returns time since start when active", func(t *testing.T) { 1219 + te := &TimeEntry{ 1220 + TaskID: 1, 1221 + StartTime: startTime, 1222 + EndTime: nil, 1223 + } 1224 + 1225 + duration := te.GetDuration() 1226 + 1227 + if duration < 59*time.Minute || duration > 61*time.Minute { 1228 + t.Errorf("Expected duration around 1 hour, got %v", duration) 1229 + } 1230 + }) 1231 + }) 1232 + 1233 + t.Run("Model Interface Implementation", func(t *testing.T) { 1234 + now := time.Now() 1235 + te := &TimeEntry{ 1236 + ID: 1, 1237 + TaskID: 100, 1238 + Created: now, 1239 + Modified: now, 1240 + } 1241 + 1242 + if te.GetID() != 1 { 1243 + t.Errorf("Expected ID 1, got %d", te.GetID()) 1244 + } 1245 + 1246 + te.SetID(2) 1247 + if te.GetID() != 2 { 1248 + t.Errorf("Expected ID 2 after SetID, got %d", te.GetID()) 1249 + } 1250 + 1251 + if te.GetTableName() != "time_entries" { 1252 + t.Errorf("Expected table name 'time_entries', got '%s'", te.GetTableName()) 1253 + } 1254 + 1255 + createdAt := time.Now() 1256 + te.SetCreatedAt(createdAt) 1257 + if !te.GetCreatedAt().Equal(createdAt) { 1258 + t.Errorf("Expected created at %v, got %v", createdAt, te.GetCreatedAt()) 1259 + } 1260 + 1261 + updatedAt := time.Now().Add(time.Hour) 1262 + te.SetUpdatedAt(updatedAt) 1263 + if !te.GetUpdatedAt().Equal(updatedAt) { 1264 + t.Errorf("Expected updated at %v, got %v", updatedAt, te.GetUpdatedAt()) 1129 1265 } 1130 1266 }) 1131 1267 })
+98 -341
internal/repo/book_repository_test.go
··· 2 2 3 3 import ( 4 4 "context" 5 - "database/sql" 6 5 "testing" 7 6 "time" 8 7 ··· 10 9 "github.com/stormlightlabs/noteleaf/internal/models" 11 10 ) 12 11 13 - func createBookTestDB(t *testing.T) *sql.DB { 14 - db, err := sql.Open("sqlite3", ":memory:") 15 - if err != nil { 16 - t.Fatalf("Failed to create in-memory database: %v", err) 17 - } 18 - 19 - if _, err := db.Exec("PRAGMA foreign_keys = ON"); err != nil { 20 - t.Fatalf("Failed to enable foreign keys: %v", err) 21 - } 22 - 23 - schema := ` 24 - CREATE TABLE IF NOT EXISTS books ( 25 - id INTEGER PRIMARY KEY AUTOINCREMENT, 26 - title TEXT NOT NULL, 27 - author TEXT, 28 - status TEXT DEFAULT 'queued', 29 - progress INTEGER DEFAULT 0, 30 - pages INTEGER, 31 - rating REAL, 32 - notes TEXT, 33 - added DATETIME DEFAULT CURRENT_TIMESTAMP, 34 - started DATETIME, 35 - finished DATETIME 36 - ); 37 - ` 38 - 39 - if _, err := db.Exec(schema); err != nil { 40 - t.Fatalf("Failed to create schema: %v", err) 41 - } 42 - 43 - t.Cleanup(func() { 44 - db.Close() 45 - }) 46 - 47 - return db 48 - } 49 - 50 - func createSampleBook() *models.Book { 51 - return &models.Book{ 52 - Title: "Test Book", 53 - Author: "Test Author", 54 - Status: "queued", 55 - Progress: 25, 56 - Pages: 300, 57 - Rating: 4.5, 58 - Notes: "Interesting read", 59 - } 60 - } 61 - 62 12 func TestBookRepository(t *testing.T) { 63 13 t.Run("CRUD Operations", func(t *testing.T) { 64 - db := createBookTestDB(t) 14 + db := CreateTestDB(t) 65 15 repo := NewBookRepository(db) 66 16 ctx := context.Background() 67 17 68 18 t.Run("Create Book", func(t *testing.T) { 69 - book := createSampleBook() 19 + book := CreateSampleBook() 70 20 71 21 id, err := repo.Create(ctx, book) 72 - if err != nil { 73 - t.Errorf("Failed to create book: %v", err) 74 - } 75 - 76 - if id == 0 { 77 - t.Error("Expected non-zero ID") 78 - } 79 - 80 - if book.ID != id { 81 - t.Errorf("Expected book ID to be set to %d, got %d", id, book.ID) 82 - } 83 - 84 - if book.Added.IsZero() { 85 - t.Error("Expected Added timestamp to be set") 86 - } 22 + AssertNoError(t, err, "Failed to create book") 23 + AssertNotEqual(t, int64(0), id, "Expected non-zero ID") 24 + AssertEqual(t, id, book.ID, "Expected book ID to be set correctly") 25 + AssertFalse(t, book.Added.IsZero(), "Expected Added timestamp to be set") 87 26 }) 88 27 89 28 t.Run("Get Book", func(t *testing.T) { 90 - original := createSampleBook() 29 + original := CreateSampleBook() 91 30 id, err := repo.Create(ctx, original) 92 - if err != nil { 93 - t.Fatalf("Failed to create book: %v", err) 94 - } 31 + AssertNoError(t, err, "Failed to create book") 95 32 96 33 retrieved, err := repo.Get(ctx, id) 97 - if err != nil { 98 - t.Errorf("Failed to get book: %v", err) 99 - } 34 + AssertNoError(t, err, "Failed to get book") 100 35 101 - if retrieved.Title != original.Title { 102 - t.Errorf("Expected title %s, got %s", original.Title, retrieved.Title) 103 - } 104 - if retrieved.Author != original.Author { 105 - t.Errorf("Expected author %s, got %s", original.Author, retrieved.Author) 106 - } 107 - if retrieved.Status != original.Status { 108 - t.Errorf("Expected status %s, got %s", original.Status, retrieved.Status) 109 - } 110 - if retrieved.Progress != original.Progress { 111 - t.Errorf("Expected progress %d, got %d", original.Progress, retrieved.Progress) 112 - } 113 - if retrieved.Pages != original.Pages { 114 - t.Errorf("Expected pages %d, got %d", original.Pages, retrieved.Pages) 115 - } 116 - if retrieved.Rating != original.Rating { 117 - t.Errorf("Expected rating %f, got %f", original.Rating, retrieved.Rating) 118 - } 119 - if retrieved.Notes != original.Notes { 120 - t.Errorf("Expected notes %s, got %s", original.Notes, retrieved.Notes) 121 - } 36 + AssertEqual(t, original.Title, retrieved.Title, "Title mismatch") 37 + AssertEqual(t, original.Author, retrieved.Author, "Author mismatch") 38 + AssertEqual(t, original.Status, retrieved.Status, "Status mismatch") 39 + AssertEqual(t, original.Progress, retrieved.Progress, "Progress mismatch") 40 + AssertEqual(t, original.Pages, retrieved.Pages, "Pages mismatch") 41 + AssertEqual(t, original.Rating, retrieved.Rating, "Rating mismatch") 42 + AssertEqual(t, original.Notes, retrieved.Notes, "Notes mismatch") 122 43 }) 123 44 124 45 t.Run("Update Book", func(t *testing.T) { 125 - book := createSampleBook() 46 + book := CreateSampleBook() 126 47 id, err := repo.Create(ctx, book) 127 - if err != nil { 128 - t.Fatalf("Failed to create book: %v", err) 129 - } 48 + AssertNoError(t, err, "Failed to create book") 130 49 131 50 book.Title = "Updated Book" 132 51 book.Status = "reading" ··· 136 55 book.Started = &now 137 56 138 57 err = repo.Update(ctx, book) 139 - if err != nil { 140 - t.Errorf("Failed to update book: %v", err) 141 - } 58 + AssertNoError(t, err, "Failed to update book") 142 59 143 60 updated, err := repo.Get(ctx, id) 144 - if err != nil { 145 - t.Fatalf("Failed to get updated book: %v", err) 146 - } 61 + AssertNoError(t, err, "Failed to get updated book") 147 62 148 - if updated.Title != "Updated Book" { 149 - t.Errorf("Expected updated title, got %s", updated.Title) 150 - } 151 - if updated.Status != "reading" { 152 - t.Errorf("Expected status reading, got %s", updated.Status) 153 - } 154 - if updated.Progress != 50 { 155 - t.Errorf("Expected progress 50, got %d", updated.Progress) 156 - } 157 - if updated.Rating != 5.0 { 158 - t.Errorf("Expected rating 5.0, got %f", updated.Rating) 159 - } 160 - if updated.Started == nil { 161 - t.Error("Expected started time to be set") 162 - } 63 + AssertEqual(t, "Updated Book", updated.Title, "Expected updated title") 64 + AssertEqual(t, "reading", updated.Status, "Expected reading status") 65 + AssertEqual(t, 50, updated.Progress, "Expected progress 50") 66 + AssertEqual(t, 5.0, updated.Rating, "Expected rating 5.0") 67 + AssertTrue(t, updated.Started != nil, "Expected started time to be set") 163 68 }) 164 69 165 70 t.Run("Delete Book", func(t *testing.T) { 166 - book := createSampleBook() 71 + book := CreateSampleBook() 167 72 id, err := repo.Create(ctx, book) 168 - if err != nil { 169 - t.Fatalf("Failed to create book: %v", err) 170 - } 73 + AssertNoError(t, err, "Failed to create book") 171 74 172 75 err = repo.Delete(ctx, id) 173 - if err != nil { 174 - t.Errorf("Failed to delete book: %v", err) 175 - } 76 + AssertNoError(t, err, "Failed to delete book") 176 77 177 78 _, err = repo.Get(ctx, id) 178 - if err == nil { 179 - t.Error("Expected error when getting deleted book") 180 - } 79 + AssertError(t, err, "Expected error when getting deleted book") 181 80 }) 182 81 }) 183 82 184 83 t.Run("List", func(t *testing.T) { 185 - db := createBookTestDB(t) 84 + db := CreateTestDB(t) 186 85 repo := NewBookRepository(db) 187 86 ctx := context.Background() 188 87 ··· 195 94 196 95 for _, book := range books { 197 96 _, err := repo.Create(ctx, book) 198 - if err != nil { 199 - t.Fatalf("Failed to create book: %v", err) 200 - } 97 + AssertNoError(t, err, "Failed to create book") 201 98 } 202 99 203 100 t.Run("List All Books", func(t *testing.T) { 204 101 results, err := repo.List(ctx, BookListOptions{}) 205 - if err != nil { 206 - t.Errorf("Failed to list books: %v", err) 207 - } 208 - 209 - if len(results) != 4 { 210 - t.Errorf("Expected 4 books, got %d", len(results)) 211 - } 102 + AssertNoError(t, err, "Failed to list books") 103 + AssertEqual(t, 4, len(results), "Expected 4 books") 212 104 }) 213 105 214 106 t.Run("List Books with Status Filter", func(t *testing.T) { 215 107 results, err := repo.List(ctx, BookListOptions{Status: "queued"}) 216 - if err != nil { 217 - t.Errorf("Failed to list books: %v", err) 218 - } 219 - 220 - if len(results) != 2 { 221 - t.Errorf("Expected 2 queued books, got %d", len(results)) 222 - } 108 + AssertNoError(t, err, "Failed to list books") 109 + AssertEqual(t, 2, len(results), "Expected 2 queued books") 223 110 224 111 for _, book := range results { 225 - if book.Status != "queued" { 226 - t.Errorf("Expected queued status, got %s", book.Status) 227 - } 112 + AssertEqual(t, "queued", book.Status, "Expected queued status") 228 113 } 229 114 }) 230 115 231 116 t.Run("List Books by Author", func(t *testing.T) { 232 117 results, err := repo.List(ctx, BookListOptions{Author: "Author A"}) 233 - if err != nil { 234 - t.Errorf("Failed to list books: %v", err) 235 - } 236 - 237 - if len(results) != 2 { 238 - t.Errorf("Expected 2 books by Author A, got %d", len(results)) 239 - } 118 + AssertNoError(t, err, "Failed to list books") 119 + AssertEqual(t, 2, len(results), "Expected 2 books by Author A") 240 120 241 121 for _, book := range results { 242 - if book.Author != "Author A" { 243 - t.Errorf("Expected author 'Author A', got %s", book.Author) 244 - } 122 + AssertEqual(t, "Author A", book.Author, "Expected author 'Author A'") 245 123 } 246 124 }) 247 125 248 126 t.Run("List Books with Progress Filter", func(t *testing.T) { 249 127 results, err := repo.List(ctx, BookListOptions{MinProgress: 50}) 250 - if err != nil { 251 - t.Errorf("Failed to list books: %v", err) 252 - } 253 - 254 - if len(results) != 2 { 255 - t.Errorf("Expected 2 books with progress >= 50, got %d", len(results)) 256 - } 128 + AssertNoError(t, err, "Failed to list books") 129 + AssertEqual(t, 2, len(results), "Expected 2 books with progress >= 50") 257 130 258 131 for _, book := range results { 259 - if book.Progress < 50 { 260 - t.Errorf("Expected progress >= 50, got %d", book.Progress) 261 - } 132 + AssertTrue(t, book.Progress >= 50, "Expected progress >= 50") 262 133 } 263 134 }) 264 135 265 136 t.Run("List Books with Rating Filter", func(t *testing.T) { 266 137 results, err := repo.List(ctx, BookListOptions{MinRating: 4.5}) 267 - if err != nil { 268 - t.Errorf("Failed to list books: %v", err) 269 - } 270 - 271 - if len(results) != 2 { 272 - t.Errorf("Expected 2 books with rating >= 4.5, got %d", len(results)) 273 - } 138 + AssertNoError(t, err, "Failed to list books") 139 + AssertEqual(t, 2, len(results), "Expected 2 books with rating >= 4.5") 274 140 275 141 for _, book := range results { 276 - if book.Rating < 4.5 { 277 - t.Errorf("Expected rating >= 4.5, got %f", book.Rating) 278 - } 142 + AssertTrue(t, book.Rating >= 4.5, "Expected rating >= 4.5") 279 143 } 280 144 }) 281 145 282 146 t.Run("List Books with Search", func(t *testing.T) { 283 147 results, err := repo.List(ctx, BookListOptions{Search: "Book 1"}) 284 - if err != nil { 285 - t.Errorf("Failed to list books: %v", err) 286 - } 287 - 288 - if len(results) != 1 { 289 - t.Errorf("Expected 1 book matching search, got %d", len(results)) 290 - } 148 + AssertNoError(t, err, "Failed to list books") 149 + AssertEqual(t, 1, len(results), "Expected 1 book matching search") 291 150 292 - if len(results) > 0 && results[0].Title != "Book 1" { 293 - t.Errorf("Expected 'Book 1', got %s", results[0].Title) 151 + if len(results) > 0 { 152 + AssertEqual(t, "Book 1", results[0].Title, "Expected 'Book 1'") 294 153 } 295 154 }) 296 155 297 156 t.Run("List Books with Limit", func(t *testing.T) { 298 157 results, err := repo.List(ctx, BookListOptions{Limit: 2}) 299 - if err != nil { 300 - t.Errorf("Failed to list books: %v", err) 301 - } 302 - 303 - if len(results) != 2 { 304 - t.Errorf("Expected 2 books due to limit, got %d", len(results)) 305 - } 158 + AssertNoError(t, err, "Failed to list books") 159 + AssertEqual(t, 2, len(results), "Expected 2 books due to limit") 306 160 }) 307 161 }) 308 162 309 163 t.Run("Special Methods", func(t *testing.T) { 310 - db := createBookTestDB(t) 164 + db := CreateTestDB(t) 311 165 repo := NewBookRepository(db) 312 166 ctx := context.Background() 313 167 ··· 319 173 var book1ID int64 320 174 for _, book := range []*models.Book{book1, book2, book3, book4} { 321 175 id, err := repo.Create(ctx, book) 322 - if err != nil { 323 - t.Fatalf("Failed to create book: %v", err) 324 - } 176 + AssertNoError(t, err, "Failed to create book") 325 177 if book == book1 { 326 178 book1ID = id 327 179 } ··· 329 181 330 182 t.Run("GetQueued", func(t *testing.T) { 331 183 results, err := repo.GetQueued(ctx) 332 - if err != nil { 333 - t.Errorf("Failed to get queued books: %v", err) 334 - } 335 - 336 - if len(results) != 2 { 337 - t.Errorf("Expected 2 queued books, got %d", len(results)) 338 - } 184 + AssertNoError(t, err, "Failed to get queued books") 185 + AssertEqual(t, 2, len(results), "Expected 2 queued books") 339 186 340 187 for _, book := range results { 341 - if book.Status != "queued" { 342 - t.Errorf("Expected queued status, got %s", book.Status) 343 - } 188 + AssertEqual(t, "queued", book.Status, "Expected queued status") 344 189 } 345 190 }) 346 191 347 192 t.Run("GetReading", func(t *testing.T) { 348 193 results, err := repo.GetReading(ctx) 349 - if err != nil { 350 - t.Errorf("Failed to get reading books: %v", err) 351 - } 352 - 353 - if len(results) != 1 { 354 - t.Errorf("Expected 1 reading book, got %d", len(results)) 355 - } 194 + AssertNoError(t, err, "Failed to get reading books") 195 + AssertEqual(t, 1, len(results), "Expected 1 reading book") 356 196 357 - if len(results) > 0 && results[0].Status != "reading" { 358 - t.Errorf("Expected reading status, got %s", results[0].Status) 197 + if len(results) > 0 { 198 + AssertEqual(t, "reading", results[0].Status, "Expected reading status") 359 199 } 360 200 }) 361 201 362 202 t.Run("GetFinished", func(t *testing.T) { 363 203 results, err := repo.GetFinished(ctx) 364 - if err != nil { 365 - t.Errorf("Failed to get finished books: %v", err) 366 - } 367 - 368 - if len(results) != 1 { 369 - t.Errorf("Expected 1 finished book, got %d", len(results)) 370 - } 204 + AssertNoError(t, err, "Failed to get finished books") 205 + AssertEqual(t, 1, len(results), "Expected 1 finished book") 371 206 372 - if len(results) > 0 && results[0].Status != "finished" { 373 - t.Errorf("Expected finished status, got %s", results[0].Status) 207 + if len(results) > 0 { 208 + AssertEqual(t, "finished", results[0].Status, "Expected finished status") 374 209 } 375 210 }) 376 211 377 212 t.Run("GetByAuthor", func(t *testing.T) { 378 213 results, err := repo.GetByAuthor(ctx, "Author A") 379 - if err != nil { 380 - t.Errorf("Failed to get books by author: %v", err) 381 - } 382 - 383 - if len(results) != 2 { 384 - t.Errorf("Expected 2 books by Author A, got %d", len(results)) 385 - } 214 + AssertNoError(t, err, "Failed to get books by author") 215 + AssertEqual(t, 2, len(results), "Expected 2 books by Author A") 386 216 387 217 for _, book := range results { 388 - if book.Author != "Author A" { 389 - t.Errorf("Expected author 'Author A', got %s", book.Author) 390 - } 218 + AssertEqual(t, "Author A", book.Author, "Expected author 'Author A'") 391 219 } 392 220 }) 393 221 394 222 t.Run("StartReading", func(t *testing.T) { 395 223 err := repo.StartReading(ctx, book1ID) 396 - if err != nil { 397 - t.Errorf("Failed to start reading book: %v", err) 398 - } 224 + AssertNoError(t, err, "Failed to start reading book") 399 225 400 226 updated, err := repo.Get(ctx, book1ID) 401 - if err != nil { 402 - t.Fatalf("Failed to get updated book: %v", err) 403 - } 227 + AssertNoError(t, err, "Failed to get updated book") 404 228 405 - if updated.Status != "reading" { 406 - t.Errorf("Expected status to be reading, got %s", updated.Status) 407 - } 408 - 409 - if updated.Started == nil { 410 - t.Error("Expected started timestamp to be set") 411 - } 229 + AssertEqual(t, "reading", updated.Status, "Expected status to be reading") 230 + AssertTrue(t, updated.Started != nil, "Expected started timestamp to be set") 412 231 }) 413 232 414 233 t.Run("FinishReading", func(t *testing.T) { 415 234 newBook := &models.Book{Title: "New Book", Status: "reading", Progress: 80} 416 235 id, err := repo.Create(ctx, newBook) 417 - if err != nil { 418 - t.Fatalf("Failed to create new book: %v", err) 419 - } 236 + AssertNoError(t, err, "Failed to create new book") 420 237 421 238 err = repo.FinishReading(ctx, id) 422 - if err != nil { 423 - t.Errorf("Failed to finish reading book: %v", err) 424 - } 239 + AssertNoError(t, err, "Failed to finish reading book") 425 240 426 241 updated, err := repo.Get(ctx, id) 427 - if err != nil { 428 - t.Fatalf("Failed to get updated book: %v", err) 429 - } 430 - 431 - if updated.Status != "finished" { 432 - t.Errorf("Expected status to be finished, got %s", updated.Status) 433 - } 242 + AssertNoError(t, err, "Failed to get updated book") 434 243 435 - if updated.Progress != 100 { 436 - t.Errorf("Expected progress to be 100, got %d", updated.Progress) 437 - } 438 - 439 - if updated.Finished == nil { 440 - t.Error("Expected finished timestamp to be set") 441 - } 244 + AssertEqual(t, "finished", updated.Status, "Expected status to be finished") 245 + AssertEqual(t, 100, updated.Progress, "Expected progress to be 100") 246 + AssertTrue(t, updated.Finished != nil, "Expected finished timestamp to be set") 442 247 }) 443 248 444 249 t.Run("UpdateProgress", func(t *testing.T) { 445 250 newBook := &models.Book{Title: "Progress Book", Status: "queued", Progress: 0} 446 251 id, err := repo.Create(ctx, newBook) 447 - if err != nil { 448 - t.Fatalf("Failed to create new book: %v", err) 449 - } 252 + AssertNoError(t, err, "Failed to create new book") 450 253 451 254 err = repo.UpdateProgress(ctx, id, 25) 452 - if err != nil { 453 - t.Errorf("Failed to update progress: %v", err) 454 - } 255 + AssertNoError(t, err, "Failed to update progress") 455 256 456 257 updated, err := repo.Get(ctx, id) 457 - if err != nil { 458 - t.Fatalf("Failed to get updated book: %v", err) 459 - } 460 - 461 - if updated.Status != "reading" { 462 - t.Errorf("Expected status to be reading when progress > 0, got %s", updated.Status) 463 - } 258 + AssertNoError(t, err, "Failed to get updated book") 464 259 465 - if updated.Progress != 25 { 466 - t.Errorf("Expected progress 25, got %d", updated.Progress) 467 - } 468 - 469 - if updated.Started == nil { 470 - t.Error("Expected started timestamp to be set when progress > 0") 471 - } 260 + AssertEqual(t, "reading", updated.Status, "Expected status to be reading when progress > 0") 261 + AssertEqual(t, 25, updated.Progress, "Expected progress 25") 262 + AssertTrue(t, updated.Started != nil, "Expected started timestamp to be set when progress > 0") 472 263 473 264 err = repo.UpdateProgress(ctx, id, 100) 474 - if err != nil { 475 - t.Errorf("Failed to update progress to 100: %v", err) 476 - } 265 + AssertNoError(t, err, "Failed to update progress to 100") 477 266 478 267 updated, err = repo.Get(ctx, id) 479 - if err != nil { 480 - t.Fatalf("Failed to get updated book: %v", err) 481 - } 268 + AssertNoError(t, err, "Failed to get updated book") 482 269 483 - if updated.Status != "finished" { 484 - t.Errorf("Expected status to be finished when progress = 100, got %s", updated.Status) 485 - } 486 - 487 - if updated.Progress != 100 { 488 - t.Errorf("Expected progress 100, got %d", updated.Progress) 489 - } 490 - 491 - if updated.Finished == nil { 492 - t.Error("Expected finished timestamp to be set when progress = 100") 493 - } 270 + AssertEqual(t, "finished", updated.Status, "Expected status to be finished when progress = 100") 271 + AssertEqual(t, 100, updated.Progress, "Expected progress 100") 272 + AssertTrue(t, updated.Finished != nil, "Expected finished timestamp to be set when progress = 100") 494 273 }) 495 274 }) 496 275 497 276 t.Run("Count", func(t *testing.T) { 498 - db := createBookTestDB(t) 277 + db := CreateTestDB(t) 499 278 repo := NewBookRepository(db) 500 279 ctx := context.Background() 501 280 ··· 508 287 509 288 for _, book := range books { 510 289 _, err := repo.Create(ctx, book) 511 - if err != nil { 512 - t.Fatalf("Failed to create book: %v", err) 513 - } 290 + AssertNoError(t, err, "Failed to create book") 514 291 } 515 292 516 293 t.Run("Count all books", func(t *testing.T) { 517 294 count, err := repo.Count(ctx, BookListOptions{}) 518 - if err != nil { 519 - t.Errorf("Failed to count books: %v", err) 520 - } 521 - 522 - if count != 4 { 523 - t.Errorf("Expected 4 books, got %d", count) 524 - } 295 + AssertNoError(t, err, "Failed to count books") 296 + AssertEqual(t, int64(4), count, "Expected 4 books") 525 297 }) 526 298 527 299 t.Run("Count queued books", func(t *testing.T) { 528 300 count, err := repo.Count(ctx, BookListOptions{Status: "queued"}) 529 - if err != nil { 530 - t.Errorf("Failed to count queued books: %v", err) 531 - } 532 - 533 - if count != 2 { 534 - t.Errorf("Expected 2 queued books, got %d", count) 535 - } 301 + AssertNoError(t, err, "Failed to count queued books") 302 + AssertEqual(t, int64(2), count, "Expected 2 queued books") 536 303 }) 537 304 538 305 t.Run("Count books by progress", func(t *testing.T) { 539 306 count, err := repo.Count(ctx, BookListOptions{MinProgress: 50}) 540 - if err != nil { 541 - t.Errorf("Failed to count books with progress >= 50: %v", err) 542 - } 543 - 544 - if count != 2 { 545 - t.Errorf("Expected 2 books with progress >= 50, got %d", count) 546 - } 307 + AssertNoError(t, err, "Failed to count books with progress >= 50") 308 + AssertEqual(t, int64(2), count, "Expected 2 books with progress >= 50") 547 309 }) 548 310 549 311 t.Run("Count books by rating", func(t *testing.T) { 550 312 count, err := repo.Count(ctx, BookListOptions{MinRating: 4.0}) 551 - if err != nil { 552 - t.Errorf("Failed to count high-rated books: %v", err) 553 - } 554 - 555 - if count != 3 { 556 - t.Errorf("Expected 3 books with rating >= 4.0, got %d", count) 557 - } 313 + AssertNoError(t, err, "Failed to count high-rated books") 314 + AssertEqual(t, int64(3), count, "Expected 3 books with rating >= 4.0") 558 315 }) 559 316 }) 560 317 }
+300
internal/repo/find_methods_test.go
··· 1 + package repo 2 + 3 + import ( 4 + "context" 5 + "testing" 6 + ) 7 + 8 + func TestFindMethods(t *testing.T) { 9 + db := CreateTestDB(t) 10 + repos := SetupTestData(t, db) 11 + ctx := context.Background() 12 + 13 + t.Run("TaskRepository Find", func(t *testing.T) { 14 + t.Run("finds tasks by status", func(t *testing.T) { 15 + options := TaskListOptions{ 16 + Status: "pending", 17 + } 18 + tasks, err := repos.Tasks.Find(ctx, options) 19 + AssertNoError(t, err, "Find should succeed") 20 + AssertTrue(t, len(tasks) >= 1, "Should find at least one pending task") 21 + for _, task := range tasks { 22 + AssertEqual(t, "pending", task.Status, "All returned tasks should be pending") 23 + } 24 + }) 25 + 26 + t.Run("finds tasks by priority", func(t *testing.T) { 27 + options := TaskListOptions{ 28 + Priority: "high", 29 + } 30 + tasks, err := repos.Tasks.Find(ctx, options) 31 + AssertNoError(t, err, "Find should succeed") 32 + AssertTrue(t, len(tasks) >= 1, "Should find at least one high priority task") 33 + for _, task := range tasks { 34 + AssertEqual(t, "high", task.Priority, "All returned tasks should be high priority") 35 + } 36 + }) 37 + 38 + t.Run("finds tasks by project", func(t *testing.T) { 39 + options := TaskListOptions{ 40 + Project: "test-project", 41 + } 42 + tasks, err := repos.Tasks.Find(ctx, options) 43 + AssertNoError(t, err, "Find should succeed") 44 + AssertTrue(t, len(tasks) >= 1, "Should find tasks in test-project") 45 + for _, task := range tasks { 46 + AssertEqual(t, "test-project", task.Project, "All returned tasks should be in test-project") 47 + } 48 + }) 49 + 50 + t.Run("finds tasks by context", func(t *testing.T) { 51 + options := TaskListOptions{ 52 + Context: "test-context", 53 + } 54 + tasks, err := repos.Tasks.Find(ctx, options) 55 + AssertNoError(t, err, "Find should succeed") 56 + AssertTrue(t, len(tasks) >= 1, "Should find tasks in test-context") 57 + for _, task := range tasks { 58 + AssertEqual(t, "test-context", task.Context, "All returned tasks should be in test-context") 59 + } 60 + }) 61 + 62 + t.Run("finds tasks by multiple criteria", func(t *testing.T) { 63 + options := TaskListOptions{ 64 + Status: "pending", 65 + Priority: "high", 66 + Project: "test-project", 67 + } 68 + tasks, err := repos.Tasks.Find(ctx, options) 69 + AssertNoError(t, err, "Find should succeed") 70 + for _, task := range tasks { 71 + AssertEqual(t, "pending", task.Status, "Task should be pending") 72 + AssertEqual(t, "high", task.Priority, "Task should be high priority") 73 + AssertEqual(t, "test-project", task.Project, "Task should be in test-project") 74 + } 75 + }) 76 + 77 + t.Run("returns empty for non-matching criteria", func(t *testing.T) { 78 + options := TaskListOptions{ 79 + Status: "non-existent-status", 80 + } 81 + tasks, err := repos.Tasks.Find(ctx, options) 82 + AssertNoError(t, err, "Find should succeed even with no results") 83 + AssertEqual(t, 0, len(tasks), "Should find no tasks") 84 + }) 85 + 86 + t.Run("returns all tasks with empty options", func(t *testing.T) { 87 + options := TaskListOptions{} 88 + tasks, err := repos.Tasks.Find(ctx, options) 89 + AssertNoError(t, err, "Find should succeed with empty options") 90 + AssertTrue(t, len(tasks) >= 2, "Should return all tasks for empty options") 91 + }) 92 + }) 93 + 94 + t.Run("BookRepository Find", func(t *testing.T) { 95 + t.Run("finds books by status", func(t *testing.T) { 96 + options := BookListOptions{ 97 + Status: "reading", 98 + } 99 + books, err := repos.Books.Find(ctx, options) 100 + AssertNoError(t, err, "Find should succeed") 101 + AssertTrue(t, len(books) >= 1, "Should find at least one book being read") 102 + for _, book := range books { 103 + AssertEqual(t, "reading", book.Status, "All returned books should be reading") 104 + } 105 + }) 106 + 107 + t.Run("finds books by author", func(t *testing.T) { 108 + options := BookListOptions{ 109 + Author: "Test Author", 110 + } 111 + books, err := repos.Books.Find(ctx, options) 112 + AssertNoError(t, err, "Find should succeed") 113 + AssertTrue(t, len(books) >= 1, "Should find at least one book by Test Author") 114 + for _, book := range books { 115 + AssertEqual(t, "Test Author", book.Author, "All returned books should be by Test Author") 116 + } 117 + }) 118 + 119 + t.Run("finds books by minimum progress", func(t *testing.T) { 120 + options := BookListOptions{ 121 + MinProgress: 0, 122 + } 123 + books, err := repos.Books.Find(ctx, options) 124 + AssertNoError(t, err, "Find should succeed") 125 + AssertTrue(t, len(books) >= 1, "Should find books with progress >= 0") 126 + for _, book := range books { 127 + AssertTrue(t, book.Progress >= 0, "All returned books should have progress >= 0") 128 + } 129 + }) 130 + 131 + t.Run("finds books by multiple criteria", func(t *testing.T) { 132 + options := BookListOptions{ 133 + Status: "reading", 134 + Author: "Test Author", 135 + MinProgress: 0, 136 + } 137 + books, err := repos.Books.Find(ctx, options) 138 + AssertNoError(t, err, "Find should succeed") 139 + for _, book := range books { 140 + AssertEqual(t, "reading", book.Status, "Book should be reading") 141 + AssertEqual(t, "Test Author", book.Author, "Book should be by Test Author") 142 + AssertTrue(t, book.Progress >= 0, "Book should have progress >= 0") 143 + } 144 + }) 145 + 146 + t.Run("returns empty for non-matching criteria", func(t *testing.T) { 147 + options := BookListOptions{ 148 + Status: "non-existent-status", 149 + } 150 + books, err := repos.Books.Find(ctx, options) 151 + AssertNoError(t, err, "Find should succeed even with no results") 152 + AssertEqual(t, 0, len(books), "Should find no books") 153 + }) 154 + 155 + t.Run("returns all books with empty options", func(t *testing.T) { 156 + options := BookListOptions{} 157 + books, err := repos.Books.Find(ctx, options) 158 + AssertNoError(t, err, "Find should succeed with empty options") 159 + AssertTrue(t, len(books) >= 2, "Should return all books for empty options") 160 + }) 161 + }) 162 + 163 + t.Run("MovieRepository Find", func(t *testing.T) { 164 + t.Run("finds movies by status", func(t *testing.T) { 165 + options := MovieListOptions{ 166 + Status: "watched", 167 + } 168 + movies, err := repos.Movies.Find(ctx, options) 169 + AssertNoError(t, err, "Find should succeed") 170 + AssertTrue(t, len(movies) >= 1, "Should find at least one watched movie") 171 + for _, movie := range movies { 172 + AssertEqual(t, "watched", movie.Status, "All returned movies should be watched") 173 + } 174 + }) 175 + 176 + t.Run("finds movies by year", func(t *testing.T) { 177 + options := MovieListOptions{ 178 + Year: 2023, 179 + } 180 + movies, err := repos.Movies.Find(ctx, options) 181 + AssertNoError(t, err, "Find should succeed") 182 + AssertTrue(t, len(movies) >= 1, "Should find movies from 2023") 183 + for _, movie := range movies { 184 + AssertEqual(t, 2023, movie.Year, "Movie should be from 2023") 185 + } 186 + }) 187 + 188 + t.Run("finds movies by minimum rating", func(t *testing.T) { 189 + options := MovieListOptions{ 190 + MinRating: 0.0, 191 + } 192 + movies, err := repos.Movies.Find(ctx, options) 193 + AssertNoError(t, err, "Find should succeed") 194 + AssertTrue(t, len(movies) >= 1, "Should find movies with rating >= 0") 195 + for _, movie := range movies { 196 + AssertTrue(t, movie.Rating >= 0.0, "Movie rating should be >= 0") 197 + } 198 + }) 199 + 200 + t.Run("finds movies by multiple criteria", func(t *testing.T) { 201 + options := MovieListOptions{ 202 + Status: "watched", 203 + Year: 2023, 204 + MinRating: 0.0, 205 + } 206 + movies, err := repos.Movies.Find(ctx, options) 207 + AssertNoError(t, err, "Find should succeed") 208 + for _, movie := range movies { 209 + AssertEqual(t, "watched", movie.Status, "Movie should be watched") 210 + AssertEqual(t, 2023, movie.Year, "Movie should be from 2023") 211 + AssertTrue(t, movie.Rating >= 0.0, "Movie rating should be >= 0") 212 + } 213 + }) 214 + 215 + t.Run("returns empty for non-matching criteria", func(t *testing.T) { 216 + options := MovieListOptions{ 217 + Status: "non-existent-status", 218 + } 219 + movies, err := repos.Movies.Find(ctx, options) 220 + AssertNoError(t, err, "Find should succeed even with no results") 221 + AssertEqual(t, 0, len(movies), "Should find no movies") 222 + }) 223 + 224 + t.Run("returns all movies with empty options", func(t *testing.T) { 225 + options := MovieListOptions{} 226 + movies, err := repos.Movies.Find(ctx, options) 227 + AssertNoError(t, err, "Find should succeed with empty options") 228 + AssertTrue(t, len(movies) >= 2, "Should return all movies for empty options") 229 + }) 230 + }) 231 + 232 + t.Run("TVRepository Find", func(t *testing.T) { 233 + t.Run("finds TV shows by status", func(t *testing.T) { 234 + options := TVListOptions{ 235 + Status: "watching", 236 + } 237 + shows, err := repos.TV.Find(ctx, options) 238 + AssertNoError(t, err, "Find should succeed") 239 + AssertTrue(t, len(shows) >= 1, "Should find at least one TV show being watched") 240 + for _, show := range shows { 241 + AssertEqual(t, "watching", show.Status, "All returned shows should be watching") 242 + } 243 + }) 244 + 245 + t.Run("finds TV shows by season", func(t *testing.T) { 246 + options := TVListOptions{ 247 + Season: 1, 248 + } 249 + shows, err := repos.TV.Find(ctx, options) 250 + AssertNoError(t, err, "Find should succeed") 251 + AssertTrue(t, len(shows) >= 1, "Should find TV shows with season 1") 252 + for _, show := range shows { 253 + AssertEqual(t, 1, show.Season, "All returned shows should be season 1") 254 + } 255 + }) 256 + 257 + t.Run("finds TV shows by minimum rating", func(t *testing.T) { 258 + options := TVListOptions{ 259 + MinRating: 0.0, 260 + } 261 + shows, err := repos.TV.Find(ctx, options) 262 + AssertNoError(t, err, "Find should succeed") 263 + AssertTrue(t, len(shows) >= 1, "Should find TV shows with rating >= 0") 264 + for _, show := range shows { 265 + AssertTrue(t, show.Rating >= 0.0, "Show rating should be >= 0") 266 + } 267 + }) 268 + 269 + t.Run("finds TV shows by multiple criteria", func(t *testing.T) { 270 + options := TVListOptions{ 271 + Status: "watching", 272 + Season: 1, 273 + MinRating: 0.0, 274 + } 275 + shows, err := repos.TV.Find(ctx, options) 276 + AssertNoError(t, err, "Find should succeed") 277 + for _, show := range shows { 278 + AssertEqual(t, "watching", show.Status, "Show should be watching") 279 + AssertEqual(t, 1, show.Season, "Show should be season 1") 280 + AssertTrue(t, show.Rating >= 0.0, "Show rating should be >= 0") 281 + } 282 + }) 283 + 284 + t.Run("returns empty for non-matching criteria", func(t *testing.T) { 285 + options := TVListOptions{ 286 + Status: "non-existent-status", 287 + } 288 + shows, err := repos.TV.Find(ctx, options) 289 + AssertNoError(t, err, "Find should succeed even with no results") 290 + AssertEqual(t, 0, len(shows), "Should find no TV shows") 291 + }) 292 + 293 + t.Run("returns all TV shows with empty options", func(t *testing.T) { 294 + options := TVListOptions{} 295 + shows, err := repos.TV.Find(ctx, options) 296 + AssertNoError(t, err, "Find should succeed with empty options") 297 + AssertTrue(t, len(shows) >= 2, "Should return all TV shows for empty options") 298 + }) 299 + }) 300 + }
+67 -233
internal/repo/movie_repository_test.go
··· 2 2 3 3 import ( 4 4 "context" 5 - "database/sql" 6 5 "testing" 7 6 "time" 8 7 ··· 10 9 "github.com/stormlightlabs/noteleaf/internal/models" 11 10 ) 12 11 13 - func createMovieTestDB(t *testing.T) *sql.DB { 14 - db, err := sql.Open("sqlite3", ":memory:") 15 - if err != nil { 16 - t.Fatalf("Failed to create in-memory database: %v", err) 17 - } 18 - 19 - if _, err := db.Exec("PRAGMA foreign_keys = ON"); err != nil { 20 - t.Fatalf("Failed to enable foreign keys: %v", err) 21 - } 22 - 23 - schema := ` 24 - CREATE TABLE IF NOT EXISTS movies ( 25 - id INTEGER PRIMARY KEY AUTOINCREMENT, 26 - title TEXT NOT NULL, 27 - year INTEGER, 28 - status TEXT DEFAULT 'queued', 29 - rating REAL, 30 - notes TEXT, 31 - added DATETIME DEFAULT CURRENT_TIMESTAMP, 32 - watched DATETIME 33 - ); 34 - ` 35 - 36 - if _, err := db.Exec(schema); err != nil { 37 - t.Fatalf("Failed to create schema: %v", err) 38 - } 39 - 40 - t.Cleanup(func() { 41 - db.Close() 42 - }) 43 - 44 - return db 45 - } 46 - 47 - func createSampleMovie() *models.Movie { 48 - return &models.Movie{ 49 - Title: "Test Movie", 50 - Year: 2023, 51 - Status: "queued", 52 - Rating: 8.5, 53 - Notes: "Great movie to watch", 54 - } 55 - } 56 - 57 12 func TestMovieRepository(t *testing.T) { 58 13 t.Run("CRUD Operations", func(t *testing.T) { 59 - db := createMovieTestDB(t) 14 + db := CreateTestDB(t) 60 15 repo := NewMovieRepository(db) 61 16 ctx := context.Background() 62 17 63 18 t.Run("Create Movie", func(t *testing.T) { 64 - movie := createSampleMovie() 19 + movie := CreateSampleMovie() 65 20 66 21 id, err := repo.Create(ctx, movie) 67 - if err != nil { 68 - t.Errorf("Failed to create movie: %v", err) 69 - } 70 - 71 - if id == 0 { 72 - t.Error("Expected non-zero ID") 73 - } 74 - 75 - if movie.ID != id { 76 - t.Errorf("Expected movie ID to be set to %d, got %d", id, movie.ID) 77 - } 78 - 79 - if movie.Added.IsZero() { 80 - t.Error("Expected Added timestamp to be set") 81 - } 22 + AssertNoError(t, err, "Failed to create movie") 23 + AssertNotEqual(t, int64(0), id, "Expected non-zero ID") 24 + AssertEqual(t, id, movie.ID, "Expected movie ID to be set correctly") 25 + AssertFalse(t, movie.Added.IsZero(), "Expected Added timestamp to be set") 82 26 }) 83 27 84 28 t.Run("Get Movie", func(t *testing.T) { 85 - original := createSampleMovie() 29 + original := CreateSampleMovie() 86 30 id, err := repo.Create(ctx, original) 87 - if err != nil { 88 - t.Fatalf("Failed to create movie: %v", err) 89 - } 31 + AssertNoError(t, err, "Failed to create movie") 90 32 91 33 retrieved, err := repo.Get(ctx, id) 92 - if err != nil { 93 - t.Errorf("Failed to get movie: %v", err) 94 - } 34 + AssertNoError(t, err, "Failed to get movie") 95 35 96 - if retrieved.Title != original.Title { 97 - t.Errorf("Expected title %s, got %s", original.Title, retrieved.Title) 98 - } 99 - if retrieved.Year != original.Year { 100 - t.Errorf("Expected year %d, got %d", original.Year, retrieved.Year) 101 - } 102 - if retrieved.Status != original.Status { 103 - t.Errorf("Expected status %s, got %s", original.Status, retrieved.Status) 104 - } 105 - if retrieved.Rating != original.Rating { 106 - t.Errorf("Expected rating %f, got %f", original.Rating, retrieved.Rating) 107 - } 108 - if retrieved.Notes != original.Notes { 109 - t.Errorf("Expected notes %s, got %s", original.Notes, retrieved.Notes) 110 - } 36 + AssertEqual(t, original.Title, retrieved.Title, "Title mismatch") 37 + AssertEqual(t, original.Year, retrieved.Year, "Year mismatch") 38 + AssertEqual(t, original.Status, retrieved.Status, "Status mismatch") 39 + AssertEqual(t, original.Rating, retrieved.Rating, "Rating mismatch") 40 + AssertEqual(t, original.Notes, retrieved.Notes, "Notes mismatch") 111 41 }) 112 42 113 43 t.Run("Update Movie", func(t *testing.T) { 114 - movie := createSampleMovie() 44 + movie := CreateSampleMovie() 115 45 id, err := repo.Create(ctx, movie) 116 - if err != nil { 117 - t.Fatalf("Failed to create movie: %v", err) 118 - } 46 + AssertNoError(t, err, "Failed to create movie") 119 47 120 48 movie.Title = "Updated Movie" 121 49 movie.Status = "watched" ··· 124 52 movie.Watched = &now 125 53 126 54 err = repo.Update(ctx, movie) 127 - if err != nil { 128 - t.Errorf("Failed to update movie: %v", err) 129 - } 55 + AssertNoError(t, err, "Failed to update movie") 130 56 131 57 updated, err := repo.Get(ctx, id) 132 - if err != nil { 133 - t.Fatalf("Failed to get updated movie: %v", err) 134 - } 58 + AssertNoError(t, err, "Failed to get updated movie") 135 59 136 - if updated.Title != "Updated Movie" { 137 - t.Errorf("Expected updated title, got %s", updated.Title) 138 - } 139 - if updated.Status != "watched" { 140 - t.Errorf("Expected status watched, got %s", updated.Status) 141 - } 142 - if updated.Rating != 9.0 { 143 - t.Errorf("Expected rating 9.0, got %f", updated.Rating) 144 - } 145 - if updated.Watched == nil { 146 - t.Error("Expected watched time to be set") 147 - } 60 + AssertEqual(t, "Updated Movie", updated.Title, "Expected updated title") 61 + AssertEqual(t, "watched", updated.Status, "Expected watched status") 62 + AssertEqual(t, 9.0, updated.Rating, "Expected rating 9.0") 63 + AssertTrue(t, updated.Watched != nil, "Expected watched time to be set") 148 64 }) 149 65 150 66 t.Run("Delete Movie", func(t *testing.T) { 151 - movie := createSampleMovie() 67 + movie := CreateSampleMovie() 152 68 id, err := repo.Create(ctx, movie) 153 - if err != nil { 154 - t.Fatalf("Failed to create movie: %v", err) 155 - } 69 + AssertNoError(t, err, "Failed to create movie") 156 70 157 71 err = repo.Delete(ctx, id) 158 - if err != nil { 159 - t.Errorf("Failed to delete movie: %v", err) 160 - } 72 + AssertNoError(t, err, "Failed to delete movie") 161 73 162 74 _, err = repo.Get(ctx, id) 163 - if err == nil { 164 - t.Error("Expected error when getting deleted movie") 165 - } 75 + AssertError(t, err, "Expected error when getting deleted movie") 166 76 }) 167 77 }) 168 78 169 79 t.Run("List", func(t *testing.T) { 170 - db := createMovieTestDB(t) 80 + db := CreateTestDB(t) 171 81 repo := NewMovieRepository(db) 172 82 ctx := context.Background() 173 83 ··· 179 89 180 90 for _, movie := range movies { 181 91 _, err := repo.Create(ctx, movie) 182 - if err != nil { 183 - t.Fatalf("Failed to create movie: %v", err) 184 - } 92 + AssertNoError(t, err, "Failed to create movie") 185 93 } 186 94 187 95 t.Run("List All Movies", func(t *testing.T) { 188 96 results, err := repo.List(ctx, MovieListOptions{}) 189 - if err != nil { 190 - t.Errorf("Failed to list movies: %v", err) 191 - } 192 - 193 - if len(results) != 3 { 194 - t.Errorf("Expected 3 movies, got %d", len(results)) 195 - } 97 + AssertNoError(t, err, "Failed to list movies") 98 + AssertEqual(t, 3, len(results), "Expected 3 movies") 196 99 }) 197 100 198 101 t.Run("List Movies with Status Filter", func(t *testing.T) { 199 102 results, err := repo.List(ctx, MovieListOptions{Status: "queued"}) 200 - if err != nil { 201 - t.Errorf("Failed to list movies: %v", err) 202 - } 203 - 204 - if len(results) != 2 { 205 - t.Errorf("Expected 2 queued movies, got %d", len(results)) 206 - } 103 + AssertNoError(t, err, "Failed to list movies") 104 + AssertEqual(t, 2, len(results), "Expected 2 queued movies") 207 105 208 106 for _, movie := range results { 209 - if movie.Status != "queued" { 210 - t.Errorf("Expected queued status, got %s", movie.Status) 211 - } 107 + AssertEqual(t, "queued", movie.Status, "Expected queued status") 212 108 } 213 109 }) 214 110 215 111 t.Run("List Movies with Year Filter", func(t *testing.T) { 216 112 results, err := repo.List(ctx, MovieListOptions{Year: 2021}) 217 - if err != nil { 218 - t.Errorf("Failed to list movies: %v", err) 219 - } 113 + AssertNoError(t, err, "Failed to list movies") 114 + AssertEqual(t, 1, len(results), "Expected 1 movie from 2021") 220 115 221 - if len(results) != 1 { 222 - t.Errorf("Expected 1 movie from 2021, got %d", len(results)) 223 - } 224 - 225 - if len(results) > 0 && results[0].Year != 2021 { 226 - t.Errorf("Expected year 2021, got %d", results[0].Year) 116 + if len(results) > 0 { 117 + AssertEqual(t, 2021, results[0].Year, "Expected year 2021") 227 118 } 228 119 }) 229 120 230 121 t.Run("List Movies with Rating Filter", func(t *testing.T) { 231 122 results, err := repo.List(ctx, MovieListOptions{MinRating: 8.0}) 232 - if err != nil { 233 - t.Errorf("Failed to list movies: %v", err) 234 - } 235 - 236 - if len(results) != 2 { 237 - t.Errorf("Expected 2 movies with rating >= 8.0, got %d", len(results)) 238 - } 123 + AssertNoError(t, err, "Failed to list movies") 124 + AssertEqual(t, 2, len(results), "Expected 2 movies with rating >= 8.0") 239 125 240 126 for _, movie := range results { 241 - if movie.Rating < 8.0 { 242 - t.Errorf("Expected rating >= 8.0, got %f", movie.Rating) 243 - } 127 + AssertTrue(t, movie.Rating >= 8.0, "Expected rating >= 8.0") 244 128 } 245 129 }) 246 130 247 131 t.Run("List Movies with Search", func(t *testing.T) { 248 132 results, err := repo.List(ctx, MovieListOptions{Search: "Movie 1"}) 249 - if err != nil { 250 - t.Errorf("Failed to list movies: %v", err) 251 - } 133 + AssertNoError(t, err, "Failed to list movies") 134 + AssertEqual(t, 1, len(results), "Expected 1 movie matching search") 252 135 253 - if len(results) != 1 { 254 - t.Errorf("Expected 1 movie matching search, got %d", len(results)) 255 - } 256 - 257 - if len(results) > 0 && results[0].Title != "Movie 1" { 258 - t.Errorf("Expected 'Movie 1', got %s", results[0].Title) 136 + if len(results) > 0 { 137 + AssertEqual(t, "Movie 1", results[0].Title, "Expected 'Movie 1'") 259 138 } 260 139 }) 261 140 262 141 t.Run("List Movies with Limit", func(t *testing.T) { 263 142 results, err := repo.List(ctx, MovieListOptions{Limit: 2}) 264 - if err != nil { 265 - t.Errorf("Failed to list movies: %v", err) 266 - } 267 - 268 - if len(results) != 2 { 269 - t.Errorf("Expected 2 movies due to limit, got %d", len(results)) 270 - } 143 + AssertNoError(t, err, "Failed to list movies") 144 + AssertEqual(t, 2, len(results), "Expected 2 movies due to limit") 271 145 }) 272 146 }) 273 147 274 148 t.Run("Special Methods", func(t *testing.T) { 275 - db := createMovieTestDB(t) 149 + db := CreateTestDB(t) 276 150 repo := NewMovieRepository(db) 277 151 ctx := context.Background() 278 152 ··· 283 157 var movie1ID int64 284 158 for _, movie := range []*models.Movie{movie1, movie2, movie3} { 285 159 id, err := repo.Create(ctx, movie) 286 - if err != nil { 287 - t.Fatalf("Failed to create movie: %v", err) 288 - } 160 + AssertNoError(t, err, "Failed to create movie") 289 161 if movie == movie1 { 290 162 movie1ID = id 291 163 } ··· 293 165 294 166 t.Run("GetQueued", func(t *testing.T) { 295 167 results, err := repo.GetQueued(ctx) 296 - if err != nil { 297 - t.Errorf("Failed to get queued movies: %v", err) 298 - } 299 - 300 - if len(results) != 2 { 301 - t.Errorf("Expected 2 queued movies, got %d", len(results)) 302 - } 168 + AssertNoError(t, err, "Failed to get queued movies") 169 + AssertEqual(t, 2, len(results), "Expected 2 queued movies") 303 170 304 171 for _, movie := range results { 305 - if movie.Status != "queued" { 306 - t.Errorf("Expected queued status, got %s", movie.Status) 307 - } 172 + AssertEqual(t, "queued", movie.Status, "Expected queued status") 308 173 } 309 174 }) 310 175 311 176 t.Run("GetWatched", func(t *testing.T) { 312 177 results, err := repo.GetWatched(ctx) 313 - if err != nil { 314 - t.Errorf("Failed to get watched movies: %v", err) 315 - } 316 - 317 - if len(results) != 1 { 318 - t.Errorf("Expected 1 watched movie, got %d", len(results)) 319 - } 178 + AssertNoError(t, err, "Failed to get watched movies") 179 + AssertEqual(t, 1, len(results), "Expected 1 watched movie") 320 180 321 - if len(results) > 0 && results[0].Status != "watched" { 322 - t.Errorf("Expected watched status, got %s", results[0].Status) 181 + if len(results) > 0 { 182 + AssertEqual(t, "watched", results[0].Status, "Expected watched status") 323 183 } 324 184 }) 325 185 326 186 t.Run("MarkWatched", func(t *testing.T) { 327 187 err := repo.MarkWatched(ctx, movie1ID) 328 - if err != nil { 329 - t.Errorf("Failed to mark movie as watched: %v", err) 330 - } 188 + AssertNoError(t, err, "Failed to mark movie as watched") 331 189 332 190 updated, err := repo.Get(ctx, movie1ID) 333 - if err != nil { 334 - t.Fatalf("Failed to get updated movie: %v", err) 335 - } 191 + AssertNoError(t, err, "Failed to get updated movie") 336 192 337 - if updated.Status != "watched" { 338 - t.Errorf("Expected status to be watched, got %s", updated.Status) 339 - } 340 - 341 - if updated.Watched == nil { 342 - t.Error("Expected watched timestamp to be set") 343 - } 193 + AssertEqual(t, "watched", updated.Status, "Expected status to be watched") 194 + AssertTrue(t, updated.Watched != nil, "Expected watched timestamp to be set") 344 195 }) 345 196 }) 346 197 347 198 t.Run("Count", func(t *testing.T) { 348 - db := createMovieTestDB(t) 199 + db := CreateTestDB(t) 349 200 repo := NewMovieRepository(db) 350 201 ctx := context.Background() 351 202 ··· 357 208 358 209 for _, movie := range movies { 359 210 _, err := repo.Create(ctx, movie) 360 - if err != nil { 361 - t.Fatalf("Failed to create movie: %v", err) 362 - } 211 + AssertNoError(t, err, "Failed to create movie") 363 212 } 364 213 365 214 t.Run("Count all movies", func(t *testing.T) { 366 215 count, err := repo.Count(ctx, MovieListOptions{}) 367 - if err != nil { 368 - t.Errorf("Failed to count movies: %v", err) 369 - } 370 - 371 - if count != 3 { 372 - t.Errorf("Expected 3 movies, got %d", count) 373 - } 216 + AssertNoError(t, err, "Failed to count movies") 217 + AssertEqual(t, int64(3), count, "Expected 3 movies") 374 218 }) 375 219 376 220 t.Run("Count queued movies", func(t *testing.T) { 377 221 count, err := repo.Count(ctx, MovieListOptions{Status: "queued"}) 378 - if err != nil { 379 - t.Errorf("Failed to count queued movies: %v", err) 380 - } 381 - 382 - if count != 2 { 383 - t.Errorf("Expected 2 queued movies, got %d", count) 384 - } 222 + AssertNoError(t, err, "Failed to count queued movies") 223 + AssertEqual(t, int64(2), count, "Expected 2 queued movies") 385 224 }) 386 225 387 226 t.Run("Count movies by rating", func(t *testing.T) { 388 227 count, err := repo.Count(ctx, MovieListOptions{MinRating: 8.0}) 389 - if err != nil { 390 - t.Errorf("Failed to count high-rated movies: %v", err) 391 - } 392 - 393 - if count != 2 { 394 - t.Errorf("Expected 2 movies with rating >= 8.0, got %d", count) 395 - } 228 + AssertNoError(t, err, "Failed to count high-rated movies") 229 + AssertEqual(t, int64(2), count, "Expected 2 movies with rating >= 8.0") 396 230 }) 397 231 }) 398 232 }
+381 -569
internal/repo/note_repository_test.go
··· 2 2 3 3 import ( 4 4 "context" 5 - "database/sql" 6 5 "testing" 7 6 8 7 _ "github.com/mattn/go-sqlite3" 9 8 "github.com/stormlightlabs/noteleaf/internal/models" 10 9 ) 11 10 12 - func createNoteTestDB(t *testing.T) *sql.DB { 13 - db, err := sql.Open("sqlite3", ":memory:") 14 - if err != nil { 15 - t.Fatalf("Failed to create in-memory database: %v", err) 16 - } 11 + func TestNoteRepository(t *testing.T) { 12 + t.Run("CRUD", func(t *testing.T) { 13 + db := CreateTestDB(t) 14 + repo := NewNoteRepository(db) 15 + ctx := context.Background() 17 16 18 - if _, err := db.Exec("PRAGMA foreign_keys = ON"); err != nil { 19 - t.Fatalf("Failed to enable foreign keys: %v", err) 20 - } 17 + t.Run("Create Note", func(t *testing.T) { 18 + note := CreateSampleNote() 21 19 22 - schema := ` 23 - CREATE TABLE IF NOT EXISTS notes ( 24 - id INTEGER PRIMARY KEY AUTOINCREMENT, 25 - title TEXT NOT NULL, 26 - content TEXT NOT NULL, 27 - tags TEXT, 28 - archived BOOLEAN DEFAULT FALSE, 29 - created DATETIME DEFAULT CURRENT_TIMESTAMP, 30 - modified DATETIME DEFAULT CURRENT_TIMESTAMP, 31 - file_path TEXT 32 - ); 33 - ` 20 + id, err := repo.Create(ctx, note) 21 + AssertNoError(t, err, "Failed to create note") 22 + AssertNotEqual(t, int64(0), id, "Expected non-zero ID") 23 + AssertEqual(t, id, note.ID, "Expected note ID to be set correctly") 24 + AssertFalse(t, note.Created.IsZero(), "Expected Created timestamp to be set") 25 + AssertFalse(t, note.Modified.IsZero(), "Expected Modified timestamp to be set") 26 + }) 34 27 35 - if _, err := db.Exec(schema); err != nil { 36 - t.Fatalf("Failed to create schema: %v", err) 37 - } 28 + t.Run("Get Note", func(t *testing.T) { 29 + original := CreateSampleNote() 30 + id, err := repo.Create(ctx, original) 31 + AssertNoError(t, err, "Failed to create note") 38 32 39 - t.Cleanup(func() { 40 - db.Close() 41 - }) 33 + retrieved, err := repo.Get(ctx, id) 34 + AssertNoError(t, err, "Failed to get note") 42 35 43 - return db 44 - } 36 + AssertEqual(t, original.ID, retrieved.ID, "ID mismatch") 37 + AssertEqual(t, original.Title, retrieved.Title, "Title mismatch") 38 + AssertEqual(t, original.Content, retrieved.Content, "Content mismatch") 39 + AssertEqual(t, len(original.Tags), len(retrieved.Tags), "Tags length mismatch") 40 + AssertEqual(t, original.Archived, retrieved.Archived, "Archived mismatch") 41 + AssertEqual(t, original.FilePath, retrieved.FilePath, "FilePath mismatch") 42 + }) 45 43 46 - func createSampleNote() *models.Note { 47 - return &models.Note{ 48 - Title: "Test Note", 49 - Content: "This is test content with **markdown**", 50 - Tags: []string{"personal", "work"}, 51 - Archived: false, 52 - FilePath: "/path/to/note.md", 53 - } 54 - } 44 + t.Run("Update Note", func(t *testing.T) { 45 + note := CreateSampleNote() 46 + id, err := repo.Create(ctx, note) 47 + AssertNoError(t, err, "Failed to create note") 55 48 56 - func TestNoteRepository_CRUD(t *testing.T) { 57 - db := createNoteTestDB(t) 58 - repo := NewNoteRepository(db) 59 - ctx := context.Background() 49 + originalModified := note.Modified 60 50 61 - t.Run("Create Note", func(t *testing.T) { 62 - note := createSampleNote() 51 + note.Title = "Updated Title" 52 + note.Content = "Updated content" 53 + note.Tags = []string{"updated", "test"} 54 + note.Archived = true 55 + note.FilePath = "/new/path/note.md" 63 56 64 - id, err := repo.Create(ctx, note) 65 - if err != nil { 66 - t.Errorf("Failed to create note: %v", err) 67 - } 57 + err = repo.Update(ctx, note) 58 + AssertNoError(t, err, "Failed to update note") 68 59 69 - if id == 0 { 70 - t.Error("Expected non-zero ID") 71 - } 60 + retrieved, err := repo.Get(ctx, id) 61 + AssertNoError(t, err, "Failed to get updated note") 72 62 73 - if note.ID != id { 74 - t.Errorf("Expected note ID to be set to %d, got %d", id, note.ID) 75 - } 63 + AssertEqual(t, "Updated Title", retrieved.Title, "Expected updated title") 64 + AssertEqual(t, "Updated content", retrieved.Content, "Expected updated content") 65 + AssertEqual(t, 2, len(retrieved.Tags), "Expected 2 tags") 66 + if len(retrieved.Tags) >= 2 { 67 + AssertEqual(t, "updated", retrieved.Tags[0], "Expected first tag to be 'updated'") 68 + AssertEqual(t, "test", retrieved.Tags[1], "Expected second tag to be 'test'") 69 + } 70 + AssertTrue(t, retrieved.Archived, "Expected note to be archived") 71 + AssertEqual(t, "/new/path/note.md", retrieved.FilePath, "Expected updated file path") 72 + AssertTrue(t, retrieved.Modified.After(originalModified), "Expected Modified timestamp to be updated") 73 + }) 76 74 77 - if note.Created.IsZero() { 78 - t.Error("Expected Created timestamp to be set") 79 - } 80 - if note.Modified.IsZero() { 81 - t.Error("Expected Modified timestamp to be set") 82 - } 83 - }) 84 - 85 - t.Run("Get Note", func(t *testing.T) { 86 - original := createSampleNote() 87 - id, err := repo.Create(ctx, original) 88 - if err != nil { 89 - t.Fatalf("Failed to create note: %v", err) 90 - } 75 + t.Run("Delete Note", func(t *testing.T) { 76 + note := CreateSampleNote() 77 + id, err := repo.Create(ctx, note) 78 + AssertNoError(t, err, "Failed to create note") 91 79 92 - retrieved, err := repo.Get(ctx, id) 93 - if err != nil { 94 - t.Fatalf("Failed to get note: %v", err) 95 - } 80 + err = repo.Delete(ctx, id) 81 + AssertNoError(t, err, "Failed to delete note") 96 82 97 - if retrieved.ID != original.ID { 98 - t.Errorf("Expected ID %d, got %d", original.ID, retrieved.ID) 99 - } 100 - if retrieved.Title != original.Title { 101 - t.Errorf("Expected title %s, got %s", original.Title, retrieved.Title) 102 - } 103 - if retrieved.Content != original.Content { 104 - t.Errorf("Expected content %s, got %s", original.Content, retrieved.Content) 105 - } 106 - if len(retrieved.Tags) != len(original.Tags) { 107 - t.Errorf("Expected %d tags, got %d", len(original.Tags), len(retrieved.Tags)) 108 - } 109 - if retrieved.Archived != original.Archived { 110 - t.Errorf("Expected archived %v, got %v", original.Archived, retrieved.Archived) 111 - } 112 - if retrieved.FilePath != original.FilePath { 113 - t.Errorf("Expected file path %s, got %s", original.FilePath, retrieved.FilePath) 114 - } 83 + _, err = repo.Get(ctx, id) 84 + AssertError(t, err, "Expected error when getting deleted note") 85 + }) 115 86 }) 116 87 117 - t.Run("Update Note", func(t *testing.T) { 118 - note := createSampleNote() 119 - id, err := repo.Create(ctx, note) 120 - if err != nil { 121 - t.Fatalf("Failed to create note: %v", err) 122 - } 123 - 124 - originalModified := note.Modified 125 - 126 - note.Title = "Updated Title" 127 - note.Content = "Updated content" 128 - note.Tags = []string{"updated", "test"} 129 - note.Archived = true 130 - note.FilePath = "/new/path/note.md" 88 + t.Run("List", func(t *testing.T) { 89 + db := CreateTestDB(t) 90 + repo := NewNoteRepository(db) 91 + ctx := context.Background() 131 92 132 - err = repo.Update(ctx, note) 133 - if err != nil { 134 - t.Errorf("Failed to update note: %v", err) 93 + notes := []*models.Note{ 94 + {Title: "First Note", Content: "Content 1", Tags: []string{"work"}, Archived: false}, 95 + {Title: "Second Note", Content: "Content 2", Tags: []string{"personal"}, Archived: true}, 96 + {Title: "Third Note", Content: "Important content", Tags: []string{"work", "important"}, Archived: false}, 135 97 } 136 98 137 - retrieved, err := repo.Get(ctx, id) 138 - if err != nil { 139 - t.Fatalf("Failed to get updated note: %v", err) 99 + for _, note := range notes { 100 + _, err := repo.Create(ctx, note) 101 + AssertNoError(t, err, "Failed to create test note") 140 102 } 141 103 142 - if retrieved.Title != "Updated Title" { 143 - t.Errorf("Expected updated title, got %s", retrieved.Title) 144 - } 145 - if retrieved.Content != "Updated content" { 146 - t.Errorf("Expected updated content, got %s", retrieved.Content) 147 - } 148 - if len(retrieved.Tags) != 2 || retrieved.Tags[0] != "updated" || retrieved.Tags[1] != "test" { 149 - t.Errorf("Expected updated tags, got %v", retrieved.Tags) 150 - } 151 - if !retrieved.Archived { 152 - t.Error("Expected note to be archived") 153 - } 154 - if retrieved.FilePath != "/new/path/note.md" { 155 - t.Errorf("Expected updated file path, got %s", retrieved.FilePath) 156 - } 157 - if !retrieved.Modified.After(originalModified) { 158 - t.Error("Expected Modified timestamp to be updated") 159 - } 160 - }) 104 + t.Run("List All Notes", func(t *testing.T) { 105 + results, err := repo.List(ctx, NoteListOptions{}) 106 + AssertNoError(t, err, "Failed to list notes") 107 + AssertEqual(t, 3, len(results), "Expected 3 notes") 108 + }) 161 109 162 - t.Run("Delete Note", func(t *testing.T) { 163 - note := createSampleNote() 164 - id, err := repo.Create(ctx, note) 165 - if err != nil { 166 - t.Fatalf("Failed to create note: %v", err) 167 - } 110 + t.Run("List Archived Notes Only", func(t *testing.T) { 111 + archived := true 112 + results, err := repo.List(ctx, NoteListOptions{Archived: &archived}) 113 + AssertNoError(t, err, "Failed to list archived notes") 114 + AssertEqual(t, 1, len(results), "Expected 1 archived note") 115 + if len(results) > 0 { 116 + AssertTrue(t, results[0].Archived, "Retrieved note should be archived") 117 + } 118 + }) 168 119 169 - err = repo.Delete(ctx, id) 170 - if err != nil { 171 - t.Errorf("Failed to delete note: %v", err) 172 - } 120 + t.Run("List Active Notes Only", func(t *testing.T) { 121 + archived := false 122 + results, err := repo.List(ctx, NoteListOptions{Archived: &archived}) 123 + AssertNoError(t, err, "Failed to list active notes") 124 + AssertEqual(t, 2, len(results), "Expected 2 active notes") 125 + for _, note := range results { 126 + AssertFalse(t, note.Archived, "Retrieved note should not be archived") 127 + } 128 + }) 173 129 174 - _, err = repo.Get(ctx, id) 175 - if err == nil { 176 - t.Error("Expected error when getting deleted note") 177 - } 178 - }) 179 - } 180 - 181 - func TestNoteRepository_List(t *testing.T) { 182 - db := createNoteTestDB(t) 183 - repo := NewNoteRepository(db) 184 - ctx := context.Background() 185 - 186 - notes := []*models.Note{ 187 - {Title: "First Note", Content: "Content 1", Tags: []string{"work"}, Archived: false}, 188 - {Title: "Second Note", Content: "Content 2", Tags: []string{"personal"}, Archived: true}, 189 - {Title: "Third Note", Content: "Important content", Tags: []string{"work", "important"}, Archived: false}, 190 - } 130 + t.Run("Search by Title", func(t *testing.T) { 131 + results, err := repo.List(ctx, NoteListOptions{Title: "First"}) 132 + AssertNoError(t, err, "Failed to search by title") 133 + AssertEqual(t, 1, len(results), "Expected 1 note") 134 + if len(results) > 0 { 135 + AssertEqual(t, "First Note", results[0].Title, "Expected 'First Note'") 136 + } 137 + }) 191 138 192 - for _, note := range notes { 193 - _, err := repo.Create(ctx, note) 194 - if err != nil { 195 - t.Fatalf("Failed to create test note: %v", err) 196 - } 197 - } 139 + t.Run("Search by Content", func(t *testing.T) { 140 + results, err := repo.List(ctx, NoteListOptions{Content: "Important"}) 141 + AssertNoError(t, err, "Failed to search by content") 142 + AssertEqual(t, 1, len(results), "Expected 1 note") 143 + if len(results) > 0 { 144 + AssertEqual(t, "Third Note", results[0].Title, "Expected 'Third Note'") 145 + } 146 + }) 198 147 199 - t.Run("List All Notes", func(t *testing.T) { 200 - results, err := repo.List(ctx, NoteListOptions{}) 201 - if err != nil { 202 - t.Fatalf("Failed to list notes: %v", err) 203 - } 148 + t.Run("Limit and Offset", func(t *testing.T) { 149 + results, err := repo.List(ctx, NoteListOptions{Limit: 2}) 150 + AssertNoError(t, err, "Failed to list with limit") 151 + AssertEqual(t, 2, len(results), "Expected 2 notes") 204 152 205 - if len(results) != 3 { 206 - t.Errorf("Expected 3 notes, got %d", len(results)) 207 - } 153 + results, err = repo.List(ctx, NoteListOptions{Limit: 2, Offset: 1}) 154 + AssertNoError(t, err, "Failed to list with limit and offset") 155 + AssertEqual(t, 2, len(results), "Expected 2 notes with offset") 156 + }) 208 157 }) 209 158 210 - t.Run("List Archived Notes Only", func(t *testing.T) { 211 - archived := true 212 - results, err := repo.List(ctx, NoteListOptions{Archived: &archived}) 213 - if err != nil { 214 - t.Fatalf("Failed to list archived notes: %v", err) 215 - } 159 + t.Run("Specialized Methods", func(t *testing.T) { 160 + db := CreateTestDB(t) 161 + repo := NewNoteRepository(db) 162 + ctx := context.Background() 216 163 217 - if len(results) != 1 { 218 - t.Errorf("Expected 1 archived note, got %d", len(results)) 164 + notes := []*models.Note{ 165 + {Title: "Work Note", Content: "Work content", Tags: []string{"work"}, Archived: false}, 166 + {Title: "Personal Note", Content: "Personal content", Tags: []string{"personal"}, Archived: true}, 167 + {Title: "Important Note", Content: "Important content", Tags: []string{"work", "important"}, Archived: false}, 219 168 } 220 - if !results[0].Archived { 221 - t.Error("Retrieved note should be archived") 222 - } 223 - }) 224 169 225 - t.Run("List Active Notes Only", func(t *testing.T) { 226 - archived := false 227 - results, err := repo.List(ctx, NoteListOptions{Archived: &archived}) 228 - if err != nil { 229 - t.Fatalf("Failed to list active notes: %v", err) 170 + for _, note := range notes { 171 + _, err := repo.Create(ctx, note) 172 + AssertNoError(t, err, "Failed to create test note") 230 173 } 231 174 232 - if len(results) != 2 { 233 - t.Errorf("Expected 2 active notes, got %d", len(results)) 234 - } 235 - for _, note := range results { 236 - if note.Archived { 237 - t.Error("Retrieved note should not be archived") 175 + t.Run("GetByTitle", func(t *testing.T) { 176 + results, err := repo.GetByTitle(ctx, "Work") 177 + AssertNoError(t, err, "Failed to get by title") 178 + AssertEqual(t, 1, len(results), "Expected 1 note") 179 + if len(results) > 0 { 180 + AssertEqual(t, "Work Note", results[0].Title, "Expected 'Work Note'") 238 181 } 239 - } 240 - }) 182 + }) 241 183 242 - t.Run("Search by Title", func(t *testing.T) { 243 - results, err := repo.List(ctx, NoteListOptions{Title: "First"}) 244 - if err != nil { 245 - t.Fatalf("Failed to search by title: %v", err) 246 - } 184 + t.Run("GetArchived", func(t *testing.T) { 185 + results, err := repo.GetArchived(ctx) 186 + AssertNoError(t, err, "Failed to get archived notes") 187 + AssertEqual(t, 1, len(results), "Expected 1 archived note") 188 + if len(results) > 0 { 189 + AssertTrue(t, results[0].Archived, "Retrieved note should be archived") 190 + } 191 + }) 247 192 248 - if len(results) != 1 { 249 - t.Errorf("Expected 1 note, got %d", len(results)) 250 - } 251 - if results[0].Title != "First Note" { 252 - t.Errorf("Expected 'First Note', got %s", results[0].Title) 253 - } 254 - }) 193 + t.Run("GetActive", func(t *testing.T) { 194 + results, err := repo.GetActive(ctx) 195 + AssertNoError(t, err, "Failed to get active notes") 196 + AssertEqual(t, 2, len(results), "Expected 2 active notes") 197 + for _, note := range results { 198 + AssertFalse(t, note.Archived, "Retrieved note should not be archived") 199 + } 200 + }) 255 201 256 - t.Run("Search by Content", func(t *testing.T) { 257 - results, err := repo.List(ctx, NoteListOptions{Content: "Important"}) 258 - if err != nil { 259 - t.Fatalf("Failed to search by content: %v", err) 260 - } 202 + t.Run("Archive and Unarchive", func(t *testing.T) { 203 + note := &models.Note{ 204 + Title: "Test Archive", 205 + Content: "Archive test", 206 + Archived: false, 207 + } 208 + id, err := repo.Create(ctx, note) 209 + AssertNoError(t, err, "Failed to create note") 261 210 262 - if len(results) != 1 { 263 - t.Errorf("Expected 1 note, got %d", len(results)) 264 - } 265 - if results[0].Title != "Third Note" { 266 - t.Errorf("Expected 'Third Note', got %s", results[0].Title) 267 - } 268 - }) 211 + err = repo.Archive(ctx, id) 212 + AssertNoError(t, err, "Failed to archive note") 269 213 270 - t.Run("Limit and Offset", func(t *testing.T) { 271 - results, err := repo.List(ctx, NoteListOptions{Limit: 2}) 272 - if err != nil { 273 - t.Fatalf("Failed to list with limit: %v", err) 274 - } 275 - 276 - if len(results) != 2 { 277 - t.Errorf("Expected 2 notes, got %d", len(results)) 278 - } 279 - 280 - results, err = repo.List(ctx, NoteListOptions{Limit: 2, Offset: 1}) 281 - if err != nil { 282 - t.Fatalf("Failed to list with limit and offset: %v", err) 283 - } 284 - 285 - if len(results) != 2 { 286 - t.Errorf("Expected 2 notes with offset, got %d", len(results)) 287 - } 288 - }) 289 - } 290 - 291 - func TestNoteRepository_SpecializedMethods(t *testing.T) { 292 - db := createNoteTestDB(t) 293 - repo := NewNoteRepository(db) 294 - ctx := context.Background() 295 - 296 - notes := []*models.Note{ 297 - {Title: "Work Note", Content: "Work content", Tags: []string{"work"}, Archived: false}, 298 - {Title: "Personal Note", Content: "Personal content", Tags: []string{"personal"}, Archived: true}, 299 - {Title: "Important Note", Content: "Important content", Tags: []string{"work", "important"}, Archived: false}, 300 - } 301 - 302 - for _, note := range notes { 303 - _, err := repo.Create(ctx, note) 304 - if err != nil { 305 - t.Fatalf("Failed to create test note: %v", err) 306 - } 307 - } 214 + retrieved, err := repo.Get(ctx, id) 215 + AssertNoError(t, err, "Failed to get note") 216 + AssertTrue(t, retrieved.Archived, "Note should be archived") 308 217 309 - t.Run("GetByTitle", func(t *testing.T) { 310 - results, err := repo.GetByTitle(ctx, "Work") 311 - if err != nil { 312 - t.Fatalf("Failed to get by title: %v", err) 313 - } 218 + err = repo.Unarchive(ctx, id) 219 + AssertNoError(t, err, "Failed to unarchive note") 314 220 315 - if len(results) != 1 { 316 - t.Errorf("Expected 1 note, got %d", len(results)) 317 - } 318 - if results[0].Title != "Work Note" { 319 - t.Errorf("Expected 'Work Note', got %s", results[0].Title) 320 - } 321 - }) 221 + retrieved, err = repo.Get(ctx, id) 222 + AssertNoError(t, err, "Failed to get note") 223 + AssertFalse(t, retrieved.Archived, "Note should not be archived") 224 + }) 322 225 323 - t.Run("GetArchived", func(t *testing.T) { 324 - results, err := repo.GetArchived(ctx) 325 - if err != nil { 326 - t.Fatalf("Failed to get archived notes: %v", err) 327 - } 226 + t.Run("SearchContent", func(t *testing.T) { 227 + results, err := repo.SearchContent(ctx, "Important") 228 + AssertNoError(t, err, "Failed to search content") 229 + AssertEqual(t, 1, len(results), "Expected 1 note") 230 + if len(results) > 0 { 231 + AssertEqual(t, "Important Note", results[0].Title, "Expected 'Important Note'") 232 + } 233 + }) 328 234 329 - if len(results) != 1 { 330 - t.Errorf("Expected 1 archived note, got %d", len(results)) 331 - } 332 - if !results[0].Archived { 333 - t.Error("Retrieved note should be archived") 334 - } 235 + t.Run("GetRecent", func(t *testing.T) { 236 + results, err := repo.GetRecent(ctx, 2) 237 + AssertNoError(t, err, "Failed to get recent notes") 238 + AssertEqual(t, 2, len(results), "Expected 2 notes") 239 + }) 335 240 }) 336 241 337 - t.Run("GetActive", func(t *testing.T) { 338 - results, err := repo.GetActive(ctx) 339 - if err != nil { 340 - t.Fatalf("Failed to get active notes: %v", err) 341 - } 342 - 343 - if len(results) != 2 { 344 - t.Errorf("Expected 2 active notes, got %d", len(results)) 345 - } 346 - for _, note := range results { 347 - if note.Archived { 348 - t.Error("Retrieved note should not be archived") 349 - } 350 - } 351 - }) 242 + t.Run("Tag Methods", func(t *testing.T) { 243 + db := CreateTestDB(t) 244 + repo := NewNoteRepository(db) 245 + ctx := context.Background() 352 246 353 - t.Run("Archive and Unarchive", func(t *testing.T) { 354 247 note := &models.Note{ 355 - Title: "Test Archive", 356 - Content: "Archive test", 357 - Archived: false, 248 + Title: "Tag Test Note", 249 + Content: "Testing tags", 250 + Tags: []string{"initial"}, 358 251 } 359 252 id, err := repo.Create(ctx, note) 360 253 if err != nil { 361 254 t.Fatalf("Failed to create note: %v", err) 362 255 } 363 256 364 - err = repo.Archive(ctx, id) 365 - if err != nil { 366 - t.Fatalf("Failed to archive note: %v", err) 367 - } 257 + t.Run("AddTag", func(t *testing.T) { 258 + err := repo.AddTag(ctx, id, "new-tag") 259 + if err != nil { 260 + t.Fatalf("Failed to add tag: %v", err) 261 + } 368 262 369 - retrieved, err := repo.Get(ctx, id) 370 - if err != nil { 371 - t.Fatalf("Failed to get note: %v", err) 372 - } 373 - if !retrieved.Archived { 374 - t.Error("Note should be archived") 375 - } 263 + retrieved, err := repo.Get(ctx, id) 264 + if err != nil { 265 + t.Fatalf("Failed to get note: %v", err) 266 + } 376 267 377 - err = repo.Unarchive(ctx, id) 378 - if err != nil { 379 - t.Fatalf("Failed to unarchive note: %v", err) 380 - } 268 + if len(retrieved.Tags) != 2 { 269 + t.Errorf("Expected 2 tags, got %d", len(retrieved.Tags)) 270 + } 381 271 382 - retrieved, err = repo.Get(ctx, id) 383 - if err != nil { 384 - t.Fatalf("Failed to get note: %v", err) 385 - } 386 - if retrieved.Archived { 387 - t.Error("Note should not be archived") 388 - } 389 - }) 272 + found := false 273 + for _, tag := range retrieved.Tags { 274 + if tag == "new-tag" { 275 + found = true 276 + break 277 + } 278 + } 279 + if !found { 280 + t.Error("New tag not found in note") 281 + } 282 + }) 390 283 391 - t.Run("SearchContent", func(t *testing.T) { 392 - results, err := repo.SearchContent(ctx, "Important") 393 - if err != nil { 394 - t.Fatalf("Failed to search content: %v", err) 395 - } 284 + t.Run("AddTag Duplicate", func(t *testing.T) { 285 + err := repo.AddTag(ctx, id, "new-tag") 286 + AssertNoError(t, err, "Failed to add duplicate tag") 396 287 397 - if len(results) != 1 { 398 - t.Errorf("Expected 1 note, got %d", len(results)) 399 - } 400 - if results[0].Title != "Important Note" { 401 - t.Errorf("Expected 'Important Note', got %s", results[0].Title) 402 - } 403 - }) 288 + retrieved, err := repo.Get(ctx, id) 289 + AssertNoError(t, err, "Failed to get note") 404 290 405 - t.Run("GetRecent", func(t *testing.T) { 406 - results, err := repo.GetRecent(ctx, 2) 407 - if err != nil { 408 - t.Fatalf("Failed to get recent notes: %v", err) 409 - } 291 + AssertEqual(t, 2, len(retrieved.Tags), "Expected 2 tags (no duplicate)") 292 + }) 410 293 411 - if len(results) != 2 { 412 - t.Errorf("Expected 2 notes, got %d", len(results)) 413 - } 414 - }) 415 - } 294 + t.Run("RemoveTag", func(t *testing.T) { 295 + err := repo.RemoveTag(ctx, id, "initial") 296 + AssertNoError(t, err, "Failed to remove tag") 416 297 417 - func TestNoteRepository_TagMethods(t *testing.T) { 418 - db := createNoteTestDB(t) 419 - repo := NewNoteRepository(db) 420 - ctx := context.Background() 298 + retrieved, err := repo.Get(ctx, id) 299 + AssertNoError(t, err, "Failed to get note") 421 300 422 - note := &models.Note{ 423 - Title: "Tag Test Note", 424 - Content: "Testing tags", 425 - Tags: []string{"initial"}, 426 - } 427 - id, err := repo.Create(ctx, note) 428 - if err != nil { 429 - t.Fatalf("Failed to create note: %v", err) 430 - } 431 - 432 - t.Run("AddTag", func(t *testing.T) { 433 - err := repo.AddTag(ctx, id, "new-tag") 434 - if err != nil { 435 - t.Fatalf("Failed to add tag: %v", err) 436 - } 437 - 438 - retrieved, err := repo.Get(ctx, id) 439 - if err != nil { 440 - t.Fatalf("Failed to get note: %v", err) 441 - } 301 + AssertEqual(t, 1, len(retrieved.Tags), "Expected 1 tag after removal") 442 302 443 - if len(retrieved.Tags) != 2 { 444 - t.Errorf("Expected 2 tags, got %d", len(retrieved.Tags)) 445 - } 303 + for _, tag := range retrieved.Tags { 304 + AssertNotEqual(t, "initial", tag, "Removed tag still found in note") 305 + } 306 + }) 446 307 447 - found := false 448 - for _, tag := range retrieved.Tags { 449 - if tag == "new-tag" { 450 - found = true 451 - break 308 + t.Run("GetByTags", func(t *testing.T) { 309 + note1 := &models.Note{ 310 + Title: "Note 1", 311 + Content: "Content 1", 312 + Tags: []string{"work", "urgent"}, 452 313 } 453 - } 454 - if !found { 455 - t.Error("New tag not found in note") 456 - } 457 - }) 314 + note2 := &models.Note{ 315 + Title: "Note 2", 316 + Content: "Content 2", 317 + Tags: []string{"personal", "ideas"}, 318 + } 319 + note3 := &models.Note{ 320 + Title: "Note 3", 321 + Content: "Content 3", 322 + Tags: []string{"work", "planning"}, 323 + } 458 324 459 - t.Run("AddTag Duplicate", func(t *testing.T) { 460 - err := repo.AddTag(ctx, id, "new-tag") 461 - if err != nil { 462 - t.Fatalf("Failed to add duplicate tag: %v", err) 463 - } 325 + _, err := repo.Create(ctx, note1) 326 + if err != nil { 327 + t.Fatalf("Failed to create note1: %v", err) 328 + } 329 + _, err = repo.Create(ctx, note2) 330 + if err != nil { 331 + t.Fatalf("Failed to create note2: %v", err) 332 + } 333 + _, err = repo.Create(ctx, note3) 334 + if err != nil { 335 + t.Fatalf("Failed to create note3: %v", err) 336 + } 464 337 465 - retrieved, err := repo.Get(ctx, id) 466 - if err != nil { 467 - t.Fatalf("Failed to get note: %v", err) 468 - } 338 + results, err := repo.GetByTags(ctx, []string{"work"}) 339 + if err != nil { 340 + t.Fatalf("Failed to get notes by tag: %v", err) 341 + } 469 342 470 - if len(retrieved.Tags) != 2 { 471 - t.Errorf("Expected 2 tags (no duplicate), got %d", len(retrieved.Tags)) 472 - } 473 - }) 343 + if len(results) < 2 { 344 + t.Errorf("Expected at least 2 notes with 'work' tag, got %d", len(results)) 345 + } 474 346 475 - t.Run("RemoveTag", func(t *testing.T) { 476 - err := repo.RemoveTag(ctx, id, "initial") 477 - if err != nil { 478 - t.Fatalf("Failed to remove tag: %v", err) 479 - } 347 + results, err = repo.GetByTags(ctx, []string{"nonexistent"}) 348 + if err != nil { 349 + t.Fatalf("Failed to get notes by nonexistent tag: %v", err) 350 + } 480 351 481 - retrieved, err := repo.Get(ctx, id) 482 - if err != nil { 483 - t.Fatalf("Failed to get note: %v", err) 484 - } 352 + if len(results) != 0 { 353 + t.Errorf("Expected 0 notes with nonexistent tag, got %d", len(results)) 354 + } 485 355 486 - if len(retrieved.Tags) != 1 { 487 - t.Errorf("Expected 1 tag after removal, got %d", len(retrieved.Tags)) 488 - } 356 + results, err = repo.GetByTags(ctx, []string{}) 357 + if err != nil { 358 + t.Fatalf("Failed to get notes with empty tags: %v", err) 359 + } 489 360 490 - for _, tag := range retrieved.Tags { 491 - if tag == "initial" { 492 - t.Error("Removed tag still found in note") 361 + if len(results) != 0 { 362 + t.Errorf("Expected 0 notes with empty tag list, got %d", len(results)) 493 363 } 494 - } 364 + }) 495 365 }) 496 366 497 - t.Run("GetByTags", func(t *testing.T) { 498 - note1 := &models.Note{ 499 - Title: "Note 1", 500 - Content: "Content 1", 501 - Tags: []string{"work", "urgent"}, 502 - } 503 - note2 := &models.Note{ 504 - Title: "Note 2", 505 - Content: "Content 2", 506 - Tags: []string{"personal", "ideas"}, 507 - } 508 - note3 := &models.Note{ 509 - Title: "Note 3", 510 - Content: "Content 3", 511 - Tags: []string{"work", "planning"}, 512 - } 367 + t.Run("Error Cases", func(t *testing.T) { 368 + db := CreateTestDB(t) 369 + repo := NewNoteRepository(db) 370 + ctx := context.Background() 513 371 514 - _, err := repo.Create(ctx, note1) 515 - if err != nil { 516 - t.Fatalf("Failed to create note1: %v", err) 517 - } 518 - _, err = repo.Create(ctx, note2) 519 - if err != nil { 520 - t.Fatalf("Failed to create note2: %v", err) 521 - } 522 - _, err = repo.Create(ctx, note3) 523 - if err != nil { 524 - t.Fatalf("Failed to create note3: %v", err) 525 - } 372 + t.Run("Get Nonexistent Note", func(t *testing.T) { 373 + _, err := repo.Get(ctx, 999) 374 + if err == nil { 375 + t.Error("Expected error when getting nonexistent note") 376 + } 377 + }) 526 378 527 - results, err := repo.GetByTags(ctx, []string{"work"}) 528 - if err != nil { 529 - t.Fatalf("Failed to get notes by tag: %v", err) 530 - } 379 + t.Run("Update Nonexistent Note", func(t *testing.T) { 380 + note := &models.Note{ 381 + ID: 999, 382 + Title: "Nonexistent", 383 + Content: "Should fail", 384 + } 531 385 532 - if len(results) < 2 { 533 - t.Errorf("Expected at least 2 notes with 'work' tag, got %d", len(results)) 534 - } 386 + err := repo.Update(ctx, note) 387 + if err == nil { 388 + t.Error("Expected error when updating nonexistent note") 389 + } 390 + }) 535 391 536 - results, err = repo.GetByTags(ctx, []string{"nonexistent"}) 537 - if err != nil { 538 - t.Fatalf("Failed to get notes by nonexistent tag: %v", err) 539 - } 392 + t.Run("Delete Nonexistent Note", func(t *testing.T) { 393 + err := repo.Delete(ctx, 999) 394 + if err == nil { 395 + t.Error("Expected error when deleting nonexistent note") 396 + } 397 + }) 540 398 541 - if len(results) != 0 { 542 - t.Errorf("Expected 0 notes with nonexistent tag, got %d", len(results)) 543 - } 399 + t.Run("Archive Nonexistent Note", func(t *testing.T) { 400 + err := repo.Archive(ctx, 999) 401 + if err == nil { 402 + t.Error("Expected error when archiving nonexistent note") 403 + } 404 + }) 544 405 545 - results, err = repo.GetByTags(ctx, []string{}) 546 - if err != nil { 547 - t.Fatalf("Failed to get notes with empty tags: %v", err) 548 - } 549 - 550 - if len(results) != 0 { 551 - t.Errorf("Expected 0 notes with empty tag list, got %d", len(results)) 552 - } 406 + t.Run("AddTag to Nonexistent Note", func(t *testing.T) { 407 + err := repo.AddTag(ctx, 999, "tag") 408 + if err == nil { 409 + t.Error("Expected error when adding tag to nonexistent note") 410 + } 411 + }) 553 412 }) 554 - } 555 413 556 - func TestNoteRepository_ErrorCases(t *testing.T) { 557 - db := createNoteTestDB(t) 558 - repo := NewNoteRepository(db) 559 - ctx := context.Background() 414 + t.Run("Edge Cases", func(t *testing.T) { 415 + db := CreateTestDB(t) 416 + repo := NewNoteRepository(db) 417 + ctx := context.Background() 560 418 561 - t.Run("Get Nonexistent Note", func(t *testing.T) { 562 - _, err := repo.Get(ctx, 999) 563 - if err == nil { 564 - t.Error("Expected error when getting nonexistent note") 565 - } 566 - }) 419 + t.Run("Note with Empty Tags", func(t *testing.T) { 420 + note := &models.Note{ 421 + Title: "No Tags Note", 422 + Content: "This note has no tags", 423 + Tags: []string{}, 424 + } 567 425 568 - t.Run("Update Nonexistent Note", func(t *testing.T) { 569 - note := &models.Note{ 570 - ID: 999, 571 - Title: "Nonexistent", 572 - Content: "Should fail", 573 - } 426 + id, err := repo.Create(ctx, note) 427 + if err != nil { 428 + t.Fatalf("Failed to create note with empty tags: %v", err) 429 + } 574 430 575 - err := repo.Update(ctx, note) 576 - if err == nil { 577 - t.Error("Expected error when updating nonexistent note") 578 - } 579 - }) 431 + retrieved, err := repo.Get(ctx, id) 432 + if err != nil { 433 + t.Fatalf("Failed to get note: %v", err) 434 + } 580 435 581 - t.Run("Delete Nonexistent Note", func(t *testing.T) { 582 - err := repo.Delete(ctx, 999) 583 - if err == nil { 584 - t.Error("Expected error when deleting nonexistent note") 585 - } 586 - }) 436 + if len(retrieved.Tags) != 0 { 437 + t.Errorf("Expected empty tags slice, got %d tags", len(retrieved.Tags)) 438 + } 439 + }) 587 440 588 - t.Run("Archive Nonexistent Note", func(t *testing.T) { 589 - err := repo.Archive(ctx, 999) 590 - if err == nil { 591 - t.Error("Expected error when archiving nonexistent note") 592 - } 593 - }) 594 - 595 - t.Run("AddTag to Nonexistent Note", func(t *testing.T) { 596 - err := repo.AddTag(ctx, 999, "tag") 597 - if err == nil { 598 - t.Error("Expected error when adding tag to nonexistent note") 599 - } 600 - }) 601 - } 602 - 603 - func TestNoteRepository_EdgeCases(t *testing.T) { 604 - db := createNoteTestDB(t) 605 - repo := NewNoteRepository(db) 606 - ctx := context.Background() 441 + t.Run("Note with Nil Tags", func(t *testing.T) { 442 + note := &models.Note{ 443 + Title: "Nil Tags Note", 444 + Content: "This note has nil tags", 445 + Tags: nil, 446 + } 607 447 608 - t.Run("Note with Empty Tags", func(t *testing.T) { 609 - note := &models.Note{ 610 - Title: "No Tags Note", 611 - Content: "This note has no tags", 612 - Tags: []string{}, 613 - } 614 - 615 - id, err := repo.Create(ctx, note) 616 - if err != nil { 617 - t.Fatalf("Failed to create note with empty tags: %v", err) 618 - } 619 - 620 - retrieved, err := repo.Get(ctx, id) 621 - if err != nil { 622 - t.Fatalf("Failed to get note: %v", err) 623 - } 624 - 625 - if len(retrieved.Tags) != 0 { 626 - t.Errorf("Expected empty tags slice, got %d tags", len(retrieved.Tags)) 627 - } 628 - }) 629 - 630 - t.Run("Note with Nil Tags", func(t *testing.T) { 631 - note := &models.Note{ 632 - Title: "Nil Tags Note", 633 - Content: "This note has nil tags", 634 - Tags: nil, 635 - } 636 - 637 - id, err := repo.Create(ctx, note) 638 - if err != nil { 639 - t.Fatalf("Failed to create note with nil tags: %v", err) 640 - } 448 + id, err := repo.Create(ctx, note) 449 + if err != nil { 450 + t.Fatalf("Failed to create note with nil tags: %v", err) 451 + } 641 452 642 - retrieved, err := repo.Get(ctx, id) 643 - if err != nil { 644 - t.Fatalf("Failed to get note: %v", err) 645 - } 453 + retrieved, err := repo.Get(ctx, id) 454 + if err != nil { 455 + t.Fatalf("Failed to get note: %v", err) 456 + } 646 457 647 - if retrieved.Tags != nil { 648 - t.Errorf("Expected nil tags, got %v", retrieved.Tags) 649 - } 650 - }) 458 + if retrieved.Tags != nil { 459 + t.Errorf("Expected nil tags, got %v", retrieved.Tags) 460 + } 461 + }) 651 462 652 - t.Run("Note with Long Content", func(t *testing.T) { 653 - longContent := "" 654 - for i := 0; i < 1000; i++ { 655 - longContent += "This is a very long content string. " 656 - } 463 + t.Run("Note with Long Content", func(t *testing.T) { 464 + longContent := "" 465 + for i := 0; i < 1000; i++ { 466 + longContent += "This is a very long content string. " 467 + } 657 468 658 - note := &models.Note{ 659 - Title: "Long Content Note", 660 - Content: longContent, 661 - } 469 + note := &models.Note{ 470 + Title: "Long Content Note", 471 + Content: longContent, 472 + } 662 473 663 - id, err := repo.Create(ctx, note) 664 - if err != nil { 665 - t.Fatalf("Failed to create note with long content: %v", err) 666 - } 474 + id, err := repo.Create(ctx, note) 475 + if err != nil { 476 + t.Fatalf("Failed to create note with long content: %v", err) 477 + } 667 478 668 - retrieved, err := repo.Get(ctx, id) 669 - if err != nil { 670 - t.Fatalf("Failed to get note: %v", err) 671 - } 479 + retrieved, err := repo.Get(ctx, id) 480 + if err != nil { 481 + t.Fatalf("Failed to get note: %v", err) 482 + } 672 483 673 - if retrieved.Content != longContent { 674 - t.Error("Long content was not stored/retrieved correctly") 675 - } 484 + if retrieved.Content != longContent { 485 + t.Error("Long content was not stored/retrieved correctly") 486 + } 487 + }) 676 488 }) 677 - } 489 + }
+15 -71
internal/repo/task_repository_test.go
··· 2 2 3 3 import ( 4 4 "context" 5 - "database/sql" 6 5 "slices" 7 6 "testing" 8 7 "time" ··· 12 11 "github.com/stormlightlabs/noteleaf/internal/models" 13 12 ) 14 13 15 - func createTaskTestDB(t *testing.T) *sql.DB { 16 - db, err := sql.Open("sqlite3", ":memory:") 17 - if err != nil { 18 - t.Fatalf("Failed to create in-memory database: %v", err) 19 - } 20 - 21 - if _, err := db.Exec("PRAGMA foreign_keys = ON"); err != nil { 22 - t.Fatalf("Failed to enable foreign keys: %v", err) 23 - } 24 - 25 - schema := ` 26 - CREATE TABLE IF NOT EXISTS tasks ( 27 - id INTEGER PRIMARY KEY AUTOINCREMENT, 28 - uuid TEXT UNIQUE NOT NULL, 29 - description TEXT NOT NULL, 30 - status TEXT DEFAULT 'pending', 31 - priority TEXT, 32 - project TEXT, 33 - context TEXT, 34 - tags TEXT, 35 - due DATETIME, 36 - entry DATETIME DEFAULT CURRENT_TIMESTAMP, 37 - modified DATETIME DEFAULT CURRENT_TIMESTAMP, 38 - end DATETIME, 39 - start DATETIME, 40 - annotations TEXT 41 - ); 42 - ` 43 - 44 - if _, err := db.Exec(schema); err != nil { 45 - t.Fatalf("Failed to create schema: %v", err) 46 - } 47 - 48 - t.Cleanup(func() { 49 - db.Close() 50 - }) 51 - 52 - return db 53 - } 54 - 55 - func createSampleTask() *models.Task { 56 - return &models.Task{ 57 - UUID: newUUID(), 58 - Description: "Test task", 59 - Status: "pending", 60 - Priority: "H", 61 - Project: "test-project", 62 - Context: "test-context", 63 - Tags: []string{"test", "important"}, 64 - Annotations: []string{"This is a test", "Another annotation"}, 65 - } 66 - } 67 - 68 14 func newUUID() string { 69 15 return uuid.New().String() 70 16 } 71 17 72 18 func TestTaskRepository(t *testing.T) { 73 - db := createTaskTestDB(t) 19 + db := CreateTestDB(t) 74 20 repo := NewTaskRepository(db) 75 21 ctx := context.Background() 76 22 77 23 t.Run("Create Task", func(t *testing.T) { 78 - task := createSampleTask() 24 + task := CreateSampleTask() 79 25 80 26 id, err := repo.Create(ctx, task) 81 27 if err != nil { ··· 99 45 }) 100 46 101 47 t.Run("Get Task", func(t *testing.T) { 102 - original := createSampleTask() 48 + original := CreateSampleTask() 103 49 id, err := repo.Create(ctx, original) 104 50 if err != nil { 105 51 t.Fatalf("Failed to create task: %v", err) ··· 144 90 }) 145 91 146 92 t.Run("Update Task", func(t *testing.T) { 147 - task := createSampleTask() 93 + task := CreateSampleTask() 148 94 id, err := repo.Create(ctx, task) 149 95 if err != nil { 150 96 t.Fatalf("Failed to create task: %v", err) ··· 181 127 }) 182 128 183 129 t.Run("Delete Task", func(t *testing.T) { 184 - task := createSampleTask() 130 + task := CreateSampleTask() 185 131 id, err := repo.Create(ctx, task) 186 132 if err != nil { 187 133 t.Fatalf("Failed to create task: %v", err) ··· 681 627 }) 682 628 683 629 t.Run("GetByPriority", func(t *testing.T) { 684 - // Test numeric priority 685 630 results, err := repo.GetByPriority(ctx, "5") 686 631 if err != nil { 687 632 t.Errorf("Failed to get tasks by priority 5: %v", err) ··· 713 658 } 714 659 } 715 660 716 - // Test empty priority - create a specific task with no priority for this test 717 661 noPriorityTask := &models.Task{ 718 662 UUID: newUUID(), 719 663 Description: "No priority task for test", ··· 828 772 } 829 773 830 774 func TestTaskRepository_GetContexts(t *testing.T) { 831 - db := createTaskTestDB(t) 775 + db := CreateTestDB(t) 832 776 repo := NewTaskRepository(db) 833 777 ctx := context.Background() 834 778 835 779 // Create tasks with different contexts 836 - task1 := createSampleTask() 780 + task1 := CreateSampleTask() 837 781 task1.Context = "work" 838 782 _, err := repo.Create(ctx, task1) 839 783 if err != nil { 840 784 t.Fatalf("Failed to create task1: %v", err) 841 785 } 842 786 843 - task2 := createSampleTask() 787 + task2 := CreateSampleTask() 844 788 task2.Context = "home" 845 789 _, err = repo.Create(ctx, task2) 846 790 if err != nil { 847 791 t.Fatalf("Failed to create task2: %v", err) 848 792 } 849 793 850 - task3 := createSampleTask() 794 + task3 := CreateSampleTask() 851 795 task3.Context = "work" 852 796 _, err = repo.Create(ctx, task3) 853 797 if err != nil { ··· 855 799 } 856 800 857 801 // Task with empty context should not be included 858 - task4 := createSampleTask() 802 + task4 := CreateSampleTask() 859 803 task4.Context = "" 860 804 _, err = repo.Create(ctx, task4) 861 805 if err != nil { ··· 888 832 } 889 833 890 834 func TestTaskRepository_GetByContext(t *testing.T) { 891 - db := createTaskTestDB(t) 835 + db := CreateTestDB(t) 892 836 repo := NewTaskRepository(db) 893 837 ctx := context.Background() 894 838 895 839 // Create tasks with different contexts 896 - task1 := createSampleTask() 840 + task1 := CreateSampleTask() 897 841 task1.Context = "work" 898 842 task1.Description = "Work task 1" 899 843 _, err := repo.Create(ctx, task1) ··· 901 845 t.Fatalf("Failed to create task1: %v", err) 902 846 } 903 847 904 - task2 := createSampleTask() 848 + task2 := CreateSampleTask() 905 849 task2.Context = "home" 906 850 task2.Description = "Home task 1" 907 851 _, err = repo.Create(ctx, task2) ··· 909 853 t.Fatalf("Failed to create task2: %v", err) 910 854 } 911 855 912 - task3 := createSampleTask() 856 + task3 := CreateSampleTask() 913 857 task3.Context = "work" 914 858 task3.Description = "Work task 2" 915 859 _, err = repo.Create(ctx, task3) ··· 933 877 } 934 878 } 935 879 936 - // Get tasks by home context 880 + // Get tasks by home context 937 881 homeTasks, err := repo.GetByContext(ctx, "home") 938 882 if err != nil { 939 883 t.Fatalf("Failed to get tasks by context: %v", err)
+326
internal/repo/test_utilities.go
··· 1 + package repo 2 + 3 + import ( 4 + "context" 5 + "database/sql" 6 + "testing" 7 + "time" 8 + 9 + "github.com/google/uuid" 10 + _ "github.com/mattn/go-sqlite3" 11 + "github.com/stormlightlabs/noteleaf/internal/models" 12 + ) 13 + 14 + // CreateTestDB creates an in-memory SQLite database with the full schema for testing 15 + func CreateTestDB(t *testing.T) *sql.DB { 16 + db, err := sql.Open("sqlite3", ":memory:") 17 + if err != nil { 18 + t.Fatalf("Failed to create in-memory database: %v", err) 19 + } 20 + 21 + if _, err := db.Exec("PRAGMA foreign_keys = ON"); err != nil { 22 + t.Fatalf("Failed to enable foreign keys: %v", err) 23 + } 24 + 25 + // Full schema for all tables 26 + schema := ` 27 + CREATE TABLE IF NOT EXISTS tasks ( 28 + id INTEGER PRIMARY KEY AUTOINCREMENT, 29 + uuid TEXT UNIQUE NOT NULL, 30 + description TEXT NOT NULL, 31 + status TEXT DEFAULT 'pending', 32 + priority TEXT, 33 + project TEXT, 34 + context TEXT, 35 + tags TEXT, 36 + due DATETIME, 37 + entry DATETIME DEFAULT CURRENT_TIMESTAMP, 38 + modified DATETIME DEFAULT CURRENT_TIMESTAMP, 39 + end DATETIME, 40 + start DATETIME, 41 + annotations TEXT 42 + ); 43 + 44 + CREATE TABLE IF NOT EXISTS books ( 45 + id INTEGER PRIMARY KEY AUTOINCREMENT, 46 + title TEXT NOT NULL, 47 + author TEXT, 48 + status TEXT DEFAULT 'queued', 49 + progress INTEGER DEFAULT 0, 50 + pages INTEGER, 51 + rating REAL, 52 + notes TEXT, 53 + added DATETIME DEFAULT CURRENT_TIMESTAMP, 54 + started DATETIME, 55 + finished DATETIME 56 + ); 57 + 58 + CREATE TABLE IF NOT EXISTS movies ( 59 + id INTEGER PRIMARY KEY AUTOINCREMENT, 60 + title TEXT NOT NULL, 61 + year INTEGER, 62 + status TEXT DEFAULT 'queued', 63 + rating REAL, 64 + notes TEXT, 65 + added DATETIME DEFAULT CURRENT_TIMESTAMP, 66 + watched DATETIME 67 + ); 68 + 69 + CREATE TABLE IF NOT EXISTS tv_shows ( 70 + id INTEGER PRIMARY KEY AUTOINCREMENT, 71 + title TEXT NOT NULL, 72 + season INTEGER, 73 + episode INTEGER, 74 + status TEXT DEFAULT 'queued', 75 + rating REAL, 76 + notes TEXT, 77 + added DATETIME DEFAULT CURRENT_TIMESTAMP, 78 + last_watched DATETIME 79 + ); 80 + 81 + CREATE TABLE IF NOT EXISTS notes ( 82 + id INTEGER PRIMARY KEY AUTOINCREMENT, 83 + title TEXT NOT NULL, 84 + content TEXT, 85 + tags TEXT, 86 + archived BOOLEAN DEFAULT FALSE, 87 + created DATETIME DEFAULT CURRENT_TIMESTAMP, 88 + modified DATETIME DEFAULT CURRENT_TIMESTAMP, 89 + file_path TEXT 90 + ); 91 + 92 + CREATE TABLE IF NOT EXISTS time_entries ( 93 + id INTEGER PRIMARY KEY AUTOINCREMENT, 94 + task_id INTEGER NOT NULL, 95 + start_time DATETIME NOT NULL, 96 + end_time DATETIME, 97 + duration_seconds INTEGER, 98 + created DATETIME DEFAULT CURRENT_TIMESTAMP, 99 + modified DATETIME DEFAULT CURRENT_TIMESTAMP, 100 + FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE CASCADE 101 + ); 102 + ` 103 + 104 + if _, err := db.Exec(schema); err != nil { 105 + t.Fatalf("Failed to create schema: %v", err) 106 + } 107 + 108 + t.Cleanup(func() { 109 + db.Close() 110 + }) 111 + 112 + return db 113 + } 114 + 115 + // Sample data creators 116 + func CreateSampleTask() *models.Task { 117 + return &models.Task{ 118 + UUID: uuid.New().String(), 119 + Description: "Test Task", 120 + Status: "pending", 121 + Priority: "medium", 122 + Project: "test-project", 123 + Context: "test-context", 124 + Tags: []string{"test", "sample"}, 125 + Entry: time.Now(), 126 + Modified: time.Now(), 127 + } 128 + } 129 + 130 + func CreateSampleBook() *models.Book { 131 + return &models.Book{ 132 + Title: "Test Book", 133 + Author: "Test Author", 134 + Status: "queued", 135 + Progress: 0, 136 + Pages: 300, 137 + Rating: 4.5, 138 + Notes: "Great book!", 139 + Added: time.Now(), 140 + } 141 + } 142 + 143 + func CreateSampleMovie() *models.Movie { 144 + return &models.Movie{ 145 + Title: "Test Movie", 146 + Year: 2023, 147 + Status: "queued", 148 + Rating: 8.5, 149 + Notes: "Excellent film", 150 + Added: time.Now(), 151 + } 152 + } 153 + 154 + func CreateSampleTVShow() *models.TVShow { 155 + return &models.TVShow{ 156 + Title: "Test TV Show", 157 + Season: 1, 158 + Episode: 1, 159 + Status: "queued", 160 + Rating: 9.0, 161 + Notes: "Amazing series", 162 + Added: time.Now(), 163 + } 164 + } 165 + 166 + func CreateSampleNote() *models.Note { 167 + return &models.Note{ 168 + Title: "Test Note", 169 + Content: "This is a test note content", 170 + Tags: []string{"test", "sample"}, 171 + Archived: false, 172 + Created: time.Now(), 173 + Modified: time.Now(), 174 + } 175 + } 176 + 177 + func CreateSampleTimeEntry(taskID int64) *models.TimeEntry { 178 + startTime := time.Now().Add(-time.Hour) 179 + return &models.TimeEntry{ 180 + TaskID: taskID, 181 + StartTime: startTime, 182 + EndTime: nil, 183 + DurationSeconds: 0, 184 + Created: startTime, 185 + Modified: startTime, 186 + } 187 + } 188 + 189 + // Test helpers for common operations 190 + func AssertNoError(t *testing.T, err error, msg string) { 191 + t.Helper() 192 + if err != nil { 193 + t.Fatalf("%s: %v", msg, err) 194 + } 195 + } 196 + 197 + func AssertError(t *testing.T, err error, msg string) { 198 + t.Helper() 199 + if err == nil { 200 + t.Fatalf("%s: expected error but got none", msg) 201 + } 202 + } 203 + 204 + func AssertEqual[T comparable](t *testing.T, expected, actual T, msg string) { 205 + t.Helper() 206 + if expected != actual { 207 + t.Fatalf("%s: expected %v, got %v", msg, expected, actual) 208 + } 209 + } 210 + 211 + func AssertNotEqual[T comparable](t *testing.T, notExpected, actual T, msg string) { 212 + t.Helper() 213 + if notExpected == actual { 214 + t.Fatalf("%s: expected value to not equal %v", msg, notExpected) 215 + } 216 + } 217 + 218 + func AssertTrue(t *testing.T, condition bool, msg string) { 219 + t.Helper() 220 + if !condition { 221 + t.Fatalf("%s: expected true", msg) 222 + } 223 + } 224 + 225 + func AssertFalse(t *testing.T, condition bool, msg string) { 226 + t.Helper() 227 + if condition { 228 + t.Fatalf("%s: expected false", msg) 229 + } 230 + } 231 + 232 + // SetupTestData creates sample data in the database and returns the repositories 233 + func SetupTestData(t *testing.T, db *sql.DB) *Repositories { 234 + ctx := context.Background() 235 + repos := NewRepositories(db) 236 + 237 + // Create sample tasks 238 + task1 := CreateSampleTask() 239 + task1.Description = "Sample Task 1" 240 + task1.Status = "pending" 241 + task1.Priority = "high" 242 + 243 + task2 := CreateSampleTask() 244 + task2.Description = "Sample Task 2" 245 + task2.Status = "completed" 246 + task2.Priority = "low" 247 + 248 + id1, err := repos.Tasks.Create(ctx, task1) 249 + AssertNoError(t, err, "Failed to create sample task 1") 250 + task1.ID = id1 251 + 252 + id2, err := repos.Tasks.Create(ctx, task2) 253 + AssertNoError(t, err, "Failed to create sample task 2") 254 + task2.ID = id2 255 + 256 + // Create sample books 257 + book1 := CreateSampleBook() 258 + book1.Title = "Sample Book 1" 259 + book1.Status = "reading" 260 + 261 + book2 := CreateSampleBook() 262 + book2.Title = "Sample Book 2" 263 + book2.Status = "finished" 264 + 265 + bookID1, err := repos.Books.Create(ctx, book1) 266 + AssertNoError(t, err, "Failed to create sample book 1") 267 + book1.ID = bookID1 268 + 269 + bookID2, err := repos.Books.Create(ctx, book2) 270 + AssertNoError(t, err, "Failed to create sample book 2") 271 + book2.ID = bookID2 272 + 273 + // Create sample movies 274 + movie1 := CreateSampleMovie() 275 + movie1.Title = "Sample Movie 1" 276 + movie1.Status = "queued" 277 + 278 + movie2 := CreateSampleMovie() 279 + movie2.Title = "Sample Movie 2" 280 + movie2.Status = "watched" 281 + 282 + movieID1, err := repos.Movies.Create(ctx, movie1) 283 + AssertNoError(t, err, "Failed to create sample movie 1") 284 + movie1.ID = movieID1 285 + 286 + movieID2, err := repos.Movies.Create(ctx, movie2) 287 + AssertNoError(t, err, "Failed to create sample movie 2") 288 + movie2.ID = movieID2 289 + 290 + // Create sample TV shows 291 + tv1 := CreateSampleTVShow() 292 + tv1.Title = "Sample TV Show 1" 293 + tv1.Status = "queued" 294 + 295 + tv2 := CreateSampleTVShow() 296 + tv2.Title = "Sample TV Show 2" 297 + tv2.Status = "watching" 298 + 299 + tvID1, err := repos.TV.Create(ctx, tv1) 300 + AssertNoError(t, err, "Failed to create sample TV show 1") 301 + tv1.ID = tvID1 302 + 303 + tvID2, err := repos.TV.Create(ctx, tv2) 304 + AssertNoError(t, err, "Failed to create sample TV show 2") 305 + tv2.ID = tvID2 306 + 307 + // Create sample notes 308 + note1 := CreateSampleNote() 309 + note1.Title = "Sample Note 1" 310 + note1.Content = "Content for note 1" 311 + 312 + note2 := CreateSampleNote() 313 + note2.Title = "Sample Note 2" 314 + note2.Content = "Content for note 2" 315 + note2.Archived = true 316 + 317 + noteID1, err := repos.Notes.Create(ctx, note1) 318 + AssertNoError(t, err, "Failed to create sample note 1") 319 + note1.ID = noteID1 320 + 321 + noteID2, err := repos.Notes.Create(ctx, note2) 322 + AssertNoError(t, err, "Failed to create sample note 2") 323 + note2.ID = noteID2 324 + 325 + return repos 326 + }
+8 -55
internal/repo/tv_repository_test.go
··· 2 2 3 3 import ( 4 4 "context" 5 - "database/sql" 6 5 "testing" 7 6 "time" 8 7 ··· 10 9 "github.com/stormlightlabs/noteleaf/internal/models" 11 10 ) 12 11 13 - func createTVTestDB(t *testing.T) *sql.DB { 14 - db, err := sql.Open("sqlite3", ":memory:") 15 - if err != nil { 16 - t.Fatalf("Failed to create in-memory database: %v", err) 17 - } 18 - 19 - if _, err := db.Exec("PRAGMA foreign_keys = ON"); err != nil { 20 - t.Fatalf("Failed to enable foreign keys: %v", err) 21 - } 22 - 23 - schema := ` 24 - CREATE TABLE IF NOT EXISTS tv_shows ( 25 - id INTEGER PRIMARY KEY AUTOINCREMENT, 26 - title TEXT NOT NULL, 27 - season INTEGER, 28 - episode INTEGER, 29 - status TEXT DEFAULT 'queued', 30 - rating REAL, 31 - notes TEXT, 32 - added DATETIME DEFAULT CURRENT_TIMESTAMP, 33 - last_watched DATETIME 34 - ); 35 - ` 36 - 37 - if _, err := db.Exec(schema); err != nil { 38 - t.Fatalf("Failed to create schema: %v", err) 39 - } 40 - 41 - t.Cleanup(func() { 42 - db.Close() 43 - }) 44 - 45 - return db 46 - } 47 - 48 - func createSampleTVShow() *models.TVShow { 49 - return &models.TVShow{ 50 - Title: "Test Show", 51 - Season: 1, 52 - Episode: 1, 53 - Status: "queued", 54 - Rating: 9.0, 55 - Notes: "Excellent series", 56 - } 57 - } 58 - 59 12 func TestTVRepository(t *testing.T) { 60 13 t.Run("CRUD Operations", func(t *testing.T) { 61 - db := createTVTestDB(t) 14 + db := CreateTestDB(t) 62 15 repo := NewTVRepository(db) 63 16 ctx := context.Background() 64 17 65 18 t.Run("Create TV Show", func(t *testing.T) { 66 - tvShow := createSampleTVShow() 19 + tvShow := CreateSampleTVShow() 67 20 68 21 id, err := repo.Create(ctx, tvShow) 69 22 if err != nil { ··· 84 37 }) 85 38 86 39 t.Run("Get TV Show", func(t *testing.T) { 87 - original := createSampleTVShow() 40 + original := CreateSampleTVShow() 88 41 id, err := repo.Create(ctx, original) 89 42 if err != nil { 90 43 t.Fatalf("Failed to create TV show: %v", err) ··· 116 69 }) 117 70 118 71 t.Run("Update TV Show", func(t *testing.T) { 119 - tvShow := createSampleTVShow() 72 + tvShow := CreateSampleTVShow() 120 73 id, err := repo.Create(ctx, tvShow) 121 74 if err != nil { 122 75 t.Fatalf("Failed to create TV show: %v", err) ··· 161 114 }) 162 115 163 116 t.Run("Delete TV Show", func(t *testing.T) { 164 - tvShow := createSampleTVShow() 117 + tvShow := CreateSampleTVShow() 165 118 id, err := repo.Create(ctx, tvShow) 166 119 if err != nil { 167 120 t.Fatalf("Failed to create TV show: %v", err) ··· 180 133 }) 181 134 182 135 t.Run("List", func(t *testing.T) { 183 - db := createTVTestDB(t) 136 + db := CreateTestDB(t) 184 137 repo := NewTVRepository(db) 185 138 ctx := context.Background() 186 139 ··· 307 260 }) 308 261 309 262 t.Run("Special Methods", func(t *testing.T) { 310 - db := createTVTestDB(t) 263 + db := CreateTestDB(t) 311 264 repo := NewTVRepository(db) 312 265 ctx := context.Background() 313 266 ··· 458 411 }) 459 412 460 413 t.Run("Count", func(t *testing.T) { 461 - db := createTVTestDB(t) 414 + db := CreateTestDB(t) 462 415 repo := NewTVRepository(db) 463 416 ctx := context.Background() 464 417