cli + tui to publish to leaflet (wip) & manage tasks, notes & watch/read lists 馃崈
charm leaflet readability golang
29
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 588 lines 17 kB view raw
1package repo 2 3import ( 4 "context" 5 "testing" 6 "time" 7 8 _ "github.com/mattn/go-sqlite3" 9 "github.com/stormlightlabs/noteleaf/internal/models" 10 "github.com/stormlightlabs/noteleaf/internal/shared" 11) 12 13func TestTVRepository(t *testing.T) { 14 t.Run("CRUD Operations", func(t *testing.T) { 15 db := CreateTestDB(t) 16 repo := NewTVRepository(db) 17 ctx := context.Background() 18 19 t.Run("Create TV Show", func(t *testing.T) { 20 tvShow := CreateSampleTVShow() 21 22 id, err := repo.Create(ctx, tvShow) 23 if err != nil { 24 t.Errorf("Failed to create TV show: %v", err) 25 } 26 27 if id == 0 { 28 t.Error("Expected non-zero ID") 29 } 30 31 if tvShow.ID != id { 32 t.Errorf("Expected TV show ID to be set to %d, got %d", id, tvShow.ID) 33 } 34 35 if tvShow.Added.IsZero() { 36 t.Error("Expected Added timestamp to be set") 37 } 38 }) 39 40 t.Run("Get TV Show", func(t *testing.T) { 41 original := CreateSampleTVShow() 42 id, err := repo.Create(ctx, original) 43 if err != nil { 44 t.Fatalf("Failed to create TV show: %v", err) 45 } 46 47 retrieved, err := repo.Get(ctx, id) 48 if err != nil { 49 t.Errorf("Failed to get TV show: %v", err) 50 } 51 52 if retrieved.Title != original.Title { 53 t.Errorf("Expected title %s, got %s", original.Title, retrieved.Title) 54 } 55 if retrieved.Season != original.Season { 56 t.Errorf("Expected season %d, got %d", original.Season, retrieved.Season) 57 } 58 if retrieved.Episode != original.Episode { 59 t.Errorf("Expected episode %d, got %d", original.Episode, retrieved.Episode) 60 } 61 if retrieved.Status != original.Status { 62 t.Errorf("Expected status %s, got %s", original.Status, retrieved.Status) 63 } 64 if retrieved.Rating != original.Rating { 65 t.Errorf("Expected rating %f, got %f", original.Rating, retrieved.Rating) 66 } 67 if retrieved.Notes != original.Notes { 68 t.Errorf("Expected notes %s, got %s", original.Notes, retrieved.Notes) 69 } 70 }) 71 72 t.Run("Update TV Show", func(t *testing.T) { 73 tvShow := CreateSampleTVShow() 74 id, err := repo.Create(ctx, tvShow) 75 if err != nil { 76 t.Fatalf("Failed to create TV show: %v", err) 77 } 78 79 tvShow.Title = "Updated Show" 80 tvShow.Season = 2 81 tvShow.Episode = 5 82 tvShow.Status = "watching" 83 tvShow.Rating = 9.5 84 now := time.Now() 85 tvShow.LastWatched = &now 86 87 err = repo.Update(ctx, tvShow) 88 if err != nil { 89 t.Errorf("Failed to update TV show: %v", err) 90 } 91 92 updated, err := repo.Get(ctx, id) 93 if err != nil { 94 t.Fatalf("Failed to get updated TV show: %v", err) 95 } 96 97 if updated.Title != "Updated Show" { 98 t.Errorf("Expected updated title, got %s", updated.Title) 99 } 100 if updated.Season != 2 { 101 t.Errorf("Expected season 2, got %d", updated.Season) 102 } 103 if updated.Episode != 5 { 104 t.Errorf("Expected episode 5, got %d", updated.Episode) 105 } 106 if updated.Status != "watching" { 107 t.Errorf("Expected status watching, got %s", updated.Status) 108 } 109 if updated.Rating != 9.5 { 110 t.Errorf("Expected rating 9.5, got %f", updated.Rating) 111 } 112 if updated.LastWatched == nil { 113 t.Error("Expected last watched time to be set") 114 } 115 }) 116 117 t.Run("Delete TV Show", func(t *testing.T) { 118 tvShow := CreateSampleTVShow() 119 id, err := repo.Create(ctx, tvShow) 120 if err != nil { 121 t.Fatalf("Failed to create TV show: %v", err) 122 } 123 124 err = repo.Delete(ctx, id) 125 if err != nil { 126 t.Errorf("Failed to delete TV show: %v", err) 127 } 128 129 _, err = repo.Get(ctx, id) 130 if err == nil { 131 t.Error("Expected error when getting deleted TV show") 132 } 133 }) 134 }) 135 136 t.Run("List", func(t *testing.T) { 137 db := CreateTestDB(t) 138 repo := NewTVRepository(db) 139 ctx := context.Background() 140 141 tvShows := []*models.TVShow{ 142 {Title: "Show A", Season: 1, Episode: 1, Status: "queued", Rating: 8.0}, 143 {Title: "Show A", Season: 1, Episode: 2, Status: "watching", Rating: 8.5}, 144 {Title: "Show B", Season: 1, Episode: 1, Status: "queued", Rating: 9.0}, 145 {Title: "Show B", Season: 2, Episode: 1, Status: "watched", Rating: 9.2}, 146 } 147 148 for _, tvShow := range tvShows { 149 _, err := repo.Create(ctx, tvShow) 150 if err != nil { 151 t.Fatalf("Failed to create TV show: %v", err) 152 } 153 } 154 155 t.Run("List All TV Shows", func(t *testing.T) { 156 results, err := repo.List(ctx, TVListOptions{}) 157 if err != nil { 158 t.Errorf("Failed to list TV shows: %v", err) 159 } 160 161 if len(results) != 4 { 162 t.Errorf("Expected 4 TV shows, got %d", len(results)) 163 } 164 }) 165 166 t.Run("List TV Shows with Status Filter", func(t *testing.T) { 167 results, err := repo.List(ctx, TVListOptions{Status: "queued"}) 168 if err != nil { 169 t.Errorf("Failed to list TV shows: %v", err) 170 } 171 172 if len(results) != 2 { 173 t.Errorf("Expected 2 queued TV shows, got %d", len(results)) 174 } 175 176 for _, tvShow := range results { 177 if tvShow.Status != "queued" { 178 t.Errorf("Expected queued status, got %s", tvShow.Status) 179 } 180 } 181 }) 182 183 t.Run("List TV Shows by Title", func(t *testing.T) { 184 results, err := repo.List(ctx, TVListOptions{Title: "Show A"}) 185 if err != nil { 186 t.Errorf("Failed to list TV shows: %v", err) 187 } 188 189 if len(results) != 2 { 190 t.Errorf("Expected 2 episodes of Show A, got %d", len(results)) 191 } 192 193 for _, tvShow := range results { 194 if tvShow.Title != "Show A" { 195 t.Errorf("Expected title 'Show A', got %s", tvShow.Title) 196 } 197 } 198 }) 199 200 t.Run("List TV Shows by Season", func(t *testing.T) { 201 results, err := repo.List(ctx, TVListOptions{Title: "Show B", Season: 1}) 202 if err != nil { 203 t.Errorf("Failed to list TV shows: %v", err) 204 } 205 206 if len(results) != 1 { 207 t.Errorf("Expected 1 episode of Show B season 1, got %d", len(results)) 208 } 209 210 if len(results) > 0 { 211 if results[0].Title != "Show B" || results[0].Season != 1 { 212 t.Errorf("Expected Show B season 1, got %s season %d", results[0].Title, results[0].Season) 213 } 214 } 215 }) 216 217 t.Run("List TV Shows with Rating Filter", func(t *testing.T) { 218 results, err := repo.List(ctx, TVListOptions{MinRating: 9.0}) 219 if err != nil { 220 t.Errorf("Failed to list TV shows: %v", err) 221 } 222 223 if len(results) != 2 { 224 t.Errorf("Expected 2 TV shows with rating >= 9.0, got %d", len(results)) 225 } 226 227 for _, tvShow := range results { 228 if tvShow.Rating < 9.0 { 229 t.Errorf("Expected rating >= 9.0, got %f", tvShow.Rating) 230 } 231 } 232 }) 233 234 t.Run("List TV Shows with Search", func(t *testing.T) { 235 results, err := repo.List(ctx, TVListOptions{Search: "Show A"}) 236 if err != nil { 237 t.Errorf("Failed to list TV shows: %v", err) 238 } 239 240 if len(results) != 2 { 241 t.Errorf("Expected 2 TV shows matching search, got %d", len(results)) 242 } 243 244 for _, tvShow := range results { 245 if tvShow.Title != "Show A" { 246 t.Errorf("Expected 'Show A', got %s", tvShow.Title) 247 } 248 } 249 }) 250 251 t.Run("List TV Shows with Limit", func(t *testing.T) { 252 results, err := repo.List(ctx, TVListOptions{Limit: 2}) 253 if err != nil { 254 t.Errorf("Failed to list TV shows: %v", err) 255 } 256 257 if len(results) != 2 { 258 t.Errorf("Expected 2 TV shows due to limit, got %d", len(results)) 259 } 260 }) 261 }) 262 263 t.Run("Special Methods", func(t *testing.T) { 264 db := CreateTestDB(t) 265 repo := NewTVRepository(db) 266 ctx := context.Background() 267 268 tvShow1 := &models.TVShow{Title: "Queued Show", Status: "queued", Rating: 8.0} 269 tvShow2 := &models.TVShow{Title: "Watching Show", Status: "watching", Rating: 9.0} 270 tvShow3 := &models.TVShow{Title: "Watched Show", Status: "watched", Rating: 8.5} 271 tvShow4 := &models.TVShow{Title: "Test Series", Season: 1, Episode: 1, Status: "queued"} 272 tvShow5 := &models.TVShow{Title: "Test Series", Season: 1, Episode: 2, Status: "queued"} 273 tvShow6 := &models.TVShow{Title: "Test Series", Season: 2, Episode: 1, Status: "queued"} 274 275 var tvShow1ID int64 276 for _, tvShow := range []*models.TVShow{tvShow1, tvShow2, tvShow3, tvShow4, tvShow5, tvShow6} { 277 id, err := repo.Create(ctx, tvShow) 278 if err != nil { 279 t.Fatalf("Failed to create TV show: %v", err) 280 } 281 if tvShow == tvShow1 { 282 tvShow1ID = id 283 } 284 } 285 286 t.Run("GetQueued", func(t *testing.T) { 287 results, err := repo.GetQueued(ctx) 288 if err != nil { 289 t.Errorf("Failed to get queued TV shows: %v", err) 290 } 291 292 if len(results) != 4 { 293 t.Errorf("Expected 4 queued TV shows, got %d", len(results)) 294 } 295 296 for _, tvShow := range results { 297 if tvShow.Status != "queued" { 298 t.Errorf("Expected queued status, got %s", tvShow.Status) 299 } 300 } 301 }) 302 303 t.Run("GetWatching", func(t *testing.T) { 304 results, err := repo.GetWatching(ctx) 305 if err != nil { 306 t.Errorf("Failed to get watching TV shows: %v", err) 307 } 308 309 if len(results) != 1 { 310 t.Errorf("Expected 1 watching TV show, got %d", len(results)) 311 } 312 313 if len(results) > 0 && results[0].Status != "watching" { 314 t.Errorf("Expected watching status, got %s", results[0].Status) 315 } 316 }) 317 318 t.Run("GetWatched", func(t *testing.T) { 319 results, err := repo.GetWatched(ctx) 320 if err != nil { 321 t.Errorf("Failed to get watched TV shows: %v", err) 322 } 323 324 if len(results) != 1 { 325 t.Errorf("Expected 1 watched TV show, got %d", len(results)) 326 } 327 328 if len(results) > 0 && results[0].Status != "watched" { 329 t.Errorf("Expected watched status, got %s", results[0].Status) 330 } 331 }) 332 333 t.Run("GetByTitle", func(t *testing.T) { 334 results, err := repo.GetByTitle(ctx, "Test Series") 335 if err != nil { 336 t.Errorf("Failed to get TV shows by title: %v", err) 337 } 338 339 if len(results) != 3 { 340 t.Errorf("Expected 3 episodes of Test Series, got %d", len(results)) 341 } 342 343 for _, tvShow := range results { 344 if tvShow.Title != "Test Series" { 345 t.Errorf("Expected title 'Test Series', got %s", tvShow.Title) 346 } 347 } 348 }) 349 350 t.Run("GetBySeason", func(t *testing.T) { 351 results, err := repo.GetBySeason(ctx, "Test Series", 1) 352 if err != nil { 353 t.Errorf("Failed to get TV shows by season: %v", err) 354 } 355 356 if len(results) != 2 { 357 t.Errorf("Expected 2 episodes of Test Series season 1, got %d", len(results)) 358 } 359 360 for _, tvShow := range results { 361 if tvShow.Title != "Test Series" || tvShow.Season != 1 { 362 t.Errorf("Expected Test Series season 1, got %s season %d", tvShow.Title, tvShow.Season) 363 } 364 } 365 }) 366 367 t.Run("MarkWatched", func(t *testing.T) { 368 err := repo.MarkWatched(ctx, tvShow1ID) 369 if err != nil { 370 t.Errorf("Failed to mark TV show as watched: %v", err) 371 } 372 373 updated, err := repo.Get(ctx, tvShow1ID) 374 if err != nil { 375 t.Fatalf("Failed to get updated TV show: %v", err) 376 } 377 378 if updated.Status != "watched" { 379 t.Errorf("Expected status to be watched, got %s", updated.Status) 380 } 381 382 if updated.LastWatched == nil { 383 t.Error("Expected last watched timestamp to be set") 384 } 385 }) 386 387 t.Run("StartWatching", func(t *testing.T) { 388 newShow := &models.TVShow{Title: "New Show", Status: "queued"} 389 id, err := repo.Create(ctx, newShow) 390 if err != nil { 391 t.Fatalf("Failed to create new TV show: %v", err) 392 } 393 394 err = repo.StartWatching(ctx, id) 395 if err != nil { 396 t.Errorf("Failed to start watching TV show: %v", err) 397 } 398 399 updated, err := repo.Get(ctx, id) 400 if err != nil { 401 t.Fatalf("Failed to get updated TV show: %v", err) 402 } 403 404 if updated.Status != "watching" { 405 t.Errorf("Expected status to be watching, got %s", updated.Status) 406 } 407 408 if updated.LastWatched == nil { 409 t.Error("Expected last watched timestamp to be set") 410 } 411 }) 412 }) 413 414 t.Run("Count", func(t *testing.T) { 415 db := CreateTestDB(t) 416 repo := NewTVRepository(db) 417 ctx := context.Background() 418 419 tvShows := []*models.TVShow{ 420 {Title: "Show 1", Status: "queued", Rating: 8.0}, 421 {Title: "Show 2", Status: "watching", Rating: 7.0}, 422 {Title: "Show 3", Status: "watched", Rating: 9.0}, 423 {Title: "Show 4", Status: "queued", Rating: 8.5}, 424 } 425 426 for _, tvShow := range tvShows { 427 _, err := repo.Create(ctx, tvShow) 428 if err != nil { 429 t.Fatalf("Failed to create TV show: %v", err) 430 } 431 } 432 433 t.Run("Count all TV shows", func(t *testing.T) { 434 count, err := repo.Count(ctx, TVListOptions{}) 435 if err != nil { 436 t.Errorf("Failed to count TV shows: %v", err) 437 } 438 439 if count != 4 { 440 t.Errorf("Expected 4 TV shows, got %d", count) 441 } 442 }) 443 444 t.Run("Count queued TV shows", func(t *testing.T) { 445 count, err := repo.Count(ctx, TVListOptions{Status: "queued"}) 446 if err != nil { 447 t.Errorf("Failed to count queued TV shows: %v", err) 448 } 449 450 if count != 2 { 451 t.Errorf("Expected 2 queued TV shows, got %d", count) 452 } 453 }) 454 455 t.Run("Count TV shows by rating", func(t *testing.T) { 456 count, err := repo.Count(ctx, TVListOptions{MinRating: 8.0}) 457 if err != nil { 458 t.Errorf("Failed to count high-rated TV shows: %v", err) 459 } 460 461 if count != 3 { 462 t.Errorf("Expected 3 TV shows with rating >= 8.0, got %d", count) 463 } 464 }) 465 466 t.Run("Count with context cancellation", func(t *testing.T) { 467 _, err := repo.Count(NewCanceledContext(), TVListOptions{}) 468 AssertCancelledContext(t, err) 469 }) 470 }) 471 472 t.Run("Context Cancellation Error Paths", func(t *testing.T) { 473 db := CreateTestDB(t) 474 repo := NewTVRepository(db) 475 ctx := context.Background() 476 477 tvShow := NewTVShowBuilder().WithTitle("Test Show").WithSeason(1).WithEpisode(1).Build() 478 id, err := repo.Create(ctx, tvShow) 479 shared.AssertNoError(t, err, "Failed to create TV show") 480 481 t.Run("Create with cancelled context", func(t *testing.T) { 482 newShow := NewTVShowBuilder().WithTitle("Cancelled").Build() 483 _, err := repo.Create(NewCanceledContext(), newShow) 484 AssertCancelledContext(t, err) 485 }) 486 487 t.Run("Get with cancelled context", func(t *testing.T) { 488 _, err := repo.Get(NewCanceledContext(), id) 489 AssertCancelledContext(t, err) 490 }) 491 492 t.Run("Update with cancelled context", func(t *testing.T) { 493 tvShow.Title = "Updated" 494 err := repo.Update(NewCanceledContext(), tvShow) 495 AssertCancelledContext(t, err) 496 }) 497 498 t.Run("Delete with cancelled context", func(t *testing.T) { 499 err := repo.Delete(NewCanceledContext(), id) 500 AssertCancelledContext(t, err) 501 }) 502 503 t.Run("List with cancelled context", func(t *testing.T) { 504 _, err := repo.List(NewCanceledContext(), TVListOptions{}) 505 AssertCancelledContext(t, err) 506 }) 507 508 t.Run("GetQueued with cancelled context", func(t *testing.T) { 509 _, err := repo.GetQueued(NewCanceledContext()) 510 AssertCancelledContext(t, err) 511 }) 512 513 t.Run("GetWatching with cancelled context", func(t *testing.T) { 514 _, err := repo.GetWatching(NewCanceledContext()) 515 AssertCancelledContext(t, err) 516 }) 517 518 t.Run("GetWatched with cancelled context", func(t *testing.T) { 519 _, err := repo.GetWatched(NewCanceledContext()) 520 AssertCancelledContext(t, err) 521 }) 522 523 t.Run("GetByTitle with cancelled context", func(t *testing.T) { 524 _, err := repo.GetByTitle(NewCanceledContext(), "Test Show") 525 AssertCancelledContext(t, err) 526 }) 527 528 t.Run("GetBySeason with cancelled context", func(t *testing.T) { 529 _, err := repo.GetBySeason(NewCanceledContext(), "Test Show", 1) 530 AssertCancelledContext(t, err) 531 }) 532 533 t.Run("MarkWatched with cancelled context", func(t *testing.T) { 534 err := repo.MarkWatched(NewCanceledContext(), id) 535 AssertCancelledContext(t, err) 536 }) 537 538 t.Run("StartWatching with cancelled context", func(t *testing.T) { 539 err := repo.StartWatching(NewCanceledContext(), id) 540 AssertCancelledContext(t, err) 541 }) 542 }) 543 544 t.Run("Edge Cases", func(t *testing.T) { 545 db := CreateTestDB(t) 546 repo := NewTVRepository(db) 547 ctx := context.Background() 548 549 t.Run("Get non-existent TV show", func(t *testing.T) { 550 _, err := repo.Get(ctx, 99999) 551 shared.AssertError(t, err, "Expected error for non-existent TV show") 552 }) 553 554 t.Run("Update non-existent TV show succeeds with no rows affected", func(t *testing.T) { 555 show := NewTVShowBuilder().WithTitle("Non-existent").Build() 556 show.ID = 99999 557 err := repo.Update(ctx, show) 558 shared.AssertNoError(t, err, "Update should not error when no rows affected") 559 }) 560 561 t.Run("Delete non-existent TV show succeeds with no rows affected", func(t *testing.T) { 562 err := repo.Delete(ctx, 99999) 563 shared.AssertNoError(t, err, "Delete should not error when no rows affected") 564 }) 565 566 t.Run("MarkWatched non-existent TV show", func(t *testing.T) { 567 err := repo.MarkWatched(ctx, 99999) 568 shared.AssertError(t, err, "Expected error for non-existent TV show") 569 }) 570 571 t.Run("StartWatching non-existent TV show", func(t *testing.T) { 572 err := repo.StartWatching(ctx, 99999) 573 shared.AssertError(t, err, "Expected error for non-existent TV show") 574 }) 575 576 t.Run("GetByTitle with no results", func(t *testing.T) { 577 shows, err := repo.GetByTitle(ctx, "NonExistentShow") 578 shared.AssertNoError(t, err, "Should not error when no shows found") 579 shared.AssertEqual(t, 0, len(shows), "Expected empty result set") 580 }) 581 582 t.Run("GetBySeason with no results", func(t *testing.T) { 583 shows, err := repo.GetBySeason(ctx, "NonExistentShow", 1) 584 shared.AssertNoError(t, err, "Should not error when no shows found") 585 shared.AssertEqual(t, 0, len(shows), "Expected empty result set") 586 }) 587 }) 588}