From 1f31dae6f5a0f0424d138b7a3960a506eb6d9840 Mon Sep 17 00:00:00 2001 From: Owais Jamil Date: Mon, 8 Sep 2025 04:44:40 -0500 Subject: [PATCH] refactor: repository test utilities build: apply test utilities --- internal/models/models_test.go | 142 +++- internal/repo/book_repository_test.go | 441 +++-------- internal/repo/find_methods_test.go | 300 ++++++++ internal/repo/movie_repository_test.go | 300 ++------ internal/repo/note_repository_test.go | 998 ++++++++++--------------- internal/repo/task_repository_test.go | 86 +-- internal/repo/test_utilities.go | 326 ++++++++ internal/repo/tv_repository_test.go | 63 +- 8 files changed, 1359 insertions(+), 1297 deletions(-) create mode 100644 internal/repo/find_methods_test.go create mode 100644 internal/repo/test_utilities.go diff --git a/internal/models/models_test.go b/internal/models/models_test.go index e2d111c..3c02868 100644 --- a/internal/models/models_test.go +++ b/internal/models/models_test.go @@ -1024,9 +1024,7 @@ func TestModels(t *testing.T) { t.Errorf("Expected 6 models, got %d", len(models)) } - // Test that all models have the required methods for i, model := range models { - // Test ID methods model.SetID(int64(i + 1)) if model.GetID() != int64(i+1) { t.Errorf("Model %d: ID not set correctly", i) @@ -1103,7 +1101,6 @@ func TestModels(t *testing.T) { book := &Book{} note := &Note{} - // Test that zero values don't cause panics if task.IsCompleted() || task.IsPending() || task.IsDeleted() { t.Error("Zero value task should have false status methods") } @@ -1129,4 +1126,143 @@ func TestModels(t *testing.T) { } }) }) + + t.Run("TimeEntry Model", func(t *testing.T) { + t.Run("IsActive", func(t *testing.T) { + now := time.Now() + + t.Run("returns true when EndTime is nil", func(t *testing.T) { + te := &TimeEntry{ + TaskID: 1, + StartTime: now, + EndTime: nil, + } + + if !te.IsActive() { + t.Error("TimeEntry with nil EndTime should be active") + } + }) + + t.Run("returns false when EndTime is set", func(t *testing.T) { + endTime := now.Add(time.Hour) + te := &TimeEntry{ + TaskID: 1, + StartTime: now, + EndTime: &endTime, + } + + if te.IsActive() { + t.Error("TimeEntry with EndTime should not be active") + } + }) + }) + + t.Run("Stop", func(t *testing.T) { + startTime := time.Now().Add(-time.Hour) + te := &TimeEntry{ + TaskID: 1, + StartTime: startTime, + EndTime: nil, + Created: startTime, + Modified: startTime, + } + + if !te.IsActive() { + t.Error("TimeEntry should be active before Stop()") + } + + te.Stop() + + if te.IsActive() { + t.Error("TimeEntry should not be active after Stop()") + } + + if te.EndTime == nil { + t.Error("EndTime should be set after Stop()") + } + + if te.EndTime.Before(startTime) { + t.Error("EndTime should be after StartTime") + } + + expectedDuration := int64(te.EndTime.Sub(startTime).Seconds()) + if te.DurationSeconds != expectedDuration { + t.Errorf("Expected DurationSeconds %d, got %d", expectedDuration, te.DurationSeconds) + } + + if te.Modified.Before(startTime) { + t.Error("Modified time should be updated after Stop()") + } + }) + + t.Run("GetDuration", func(t *testing.T) { + startTime := time.Now().Add(-time.Hour) + + t.Run("returns calculated duration when stopped", func(t *testing.T) { + endTime := startTime.Add(30 * time.Minute) + te := &TimeEntry{ + TaskID: 1, + StartTime: startTime, + EndTime: &endTime, + DurationSeconds: 1800, + } + + duration := te.GetDuration() + expectedDuration := 30 * time.Minute + + if duration != expectedDuration { + t.Errorf("Expected duration %v, got %v", expectedDuration, duration) + } + }) + + t.Run("returns time since start when active", func(t *testing.T) { + te := &TimeEntry{ + TaskID: 1, + StartTime: startTime, + EndTime: nil, + } + + duration := te.GetDuration() + + if duration < 59*time.Minute || duration > 61*time.Minute { + t.Errorf("Expected duration around 1 hour, got %v", duration) + } + }) + }) + + t.Run("Model Interface Implementation", func(t *testing.T) { + now := time.Now() + te := &TimeEntry{ + ID: 1, + TaskID: 100, + Created: now, + Modified: now, + } + + if te.GetID() != 1 { + t.Errorf("Expected ID 1, got %d", te.GetID()) + } + + te.SetID(2) + if te.GetID() != 2 { + t.Errorf("Expected ID 2 after SetID, got %d", te.GetID()) + } + + if te.GetTableName() != "time_entries" { + t.Errorf("Expected table name 'time_entries', got '%s'", te.GetTableName()) + } + + createdAt := time.Now() + te.SetCreatedAt(createdAt) + if !te.GetCreatedAt().Equal(createdAt) { + t.Errorf("Expected created at %v, got %v", createdAt, te.GetCreatedAt()) + } + + updatedAt := time.Now().Add(time.Hour) + te.SetUpdatedAt(updatedAt) + if !te.GetUpdatedAt().Equal(updatedAt) { + t.Errorf("Expected updated at %v, got %v", updatedAt, te.GetUpdatedAt()) + } + }) + }) } diff --git a/internal/repo/book_repository_test.go b/internal/repo/book_repository_test.go index 7a2d7a9..1447216 100644 --- a/internal/repo/book_repository_test.go +++ b/internal/repo/book_repository_test.go @@ -2,7 +2,6 @@ package repo import ( "context" - "database/sql" "testing" "time" @@ -10,123 +9,43 @@ import ( "github.com/stormlightlabs/noteleaf/internal/models" ) -func createBookTestDB(t *testing.T) *sql.DB { - db, err := sql.Open("sqlite3", ":memory:") - if err != nil { - t.Fatalf("Failed to create in-memory database: %v", err) - } - - if _, err := db.Exec("PRAGMA foreign_keys = ON"); err != nil { - t.Fatalf("Failed to enable foreign keys: %v", err) - } - - schema := ` - CREATE TABLE IF NOT EXISTS books ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - title TEXT NOT NULL, - author TEXT, - status TEXT DEFAULT 'queued', - progress INTEGER DEFAULT 0, - pages INTEGER, - rating REAL, - notes TEXT, - added DATETIME DEFAULT CURRENT_TIMESTAMP, - started DATETIME, - finished DATETIME - ); - ` - - if _, err := db.Exec(schema); err != nil { - t.Fatalf("Failed to create schema: %v", err) - } - - t.Cleanup(func() { - db.Close() - }) - - return db -} - -func createSampleBook() *models.Book { - return &models.Book{ - Title: "Test Book", - Author: "Test Author", - Status: "queued", - Progress: 25, - Pages: 300, - Rating: 4.5, - Notes: "Interesting read", - } -} - func TestBookRepository(t *testing.T) { t.Run("CRUD Operations", func(t *testing.T) { - db := createBookTestDB(t) + db := CreateTestDB(t) repo := NewBookRepository(db) ctx := context.Background() t.Run("Create Book", func(t *testing.T) { - book := createSampleBook() + book := CreateSampleBook() id, err := repo.Create(ctx, book) - if err != nil { - t.Errorf("Failed to create book: %v", err) - } - - if id == 0 { - t.Error("Expected non-zero ID") - } - - if book.ID != id { - t.Errorf("Expected book ID to be set to %d, got %d", id, book.ID) - } - - if book.Added.IsZero() { - t.Error("Expected Added timestamp to be set") - } + AssertNoError(t, err, "Failed to create book") + AssertNotEqual(t, int64(0), id, "Expected non-zero ID") + AssertEqual(t, id, book.ID, "Expected book ID to be set correctly") + AssertFalse(t, book.Added.IsZero(), "Expected Added timestamp to be set") }) t.Run("Get Book", func(t *testing.T) { - original := createSampleBook() + original := CreateSampleBook() id, err := repo.Create(ctx, original) - if err != nil { - t.Fatalf("Failed to create book: %v", err) - } + AssertNoError(t, err, "Failed to create book") retrieved, err := repo.Get(ctx, id) - if err != nil { - t.Errorf("Failed to get book: %v", err) - } - - if retrieved.Title != original.Title { - t.Errorf("Expected title %s, got %s", original.Title, retrieved.Title) - } - if retrieved.Author != original.Author { - t.Errorf("Expected author %s, got %s", original.Author, retrieved.Author) - } - if retrieved.Status != original.Status { - t.Errorf("Expected status %s, got %s", original.Status, retrieved.Status) - } - if retrieved.Progress != original.Progress { - t.Errorf("Expected progress %d, got %d", original.Progress, retrieved.Progress) - } - if retrieved.Pages != original.Pages { - t.Errorf("Expected pages %d, got %d", original.Pages, retrieved.Pages) - } - if retrieved.Rating != original.Rating { - t.Errorf("Expected rating %f, got %f", original.Rating, retrieved.Rating) - } - if retrieved.Notes != original.Notes { - t.Errorf("Expected notes %s, got %s", original.Notes, retrieved.Notes) - } + AssertNoError(t, err, "Failed to get book") + + AssertEqual(t, original.Title, retrieved.Title, "Title mismatch") + AssertEqual(t, original.Author, retrieved.Author, "Author mismatch") + AssertEqual(t, original.Status, retrieved.Status, "Status mismatch") + AssertEqual(t, original.Progress, retrieved.Progress, "Progress mismatch") + AssertEqual(t, original.Pages, retrieved.Pages, "Pages mismatch") + AssertEqual(t, original.Rating, retrieved.Rating, "Rating mismatch") + AssertEqual(t, original.Notes, retrieved.Notes, "Notes mismatch") }) t.Run("Update Book", func(t *testing.T) { - book := createSampleBook() + book := CreateSampleBook() id, err := repo.Create(ctx, book) - if err != nil { - t.Fatalf("Failed to create book: %v", err) - } + AssertNoError(t, err, "Failed to create book") book.Title = "Updated Book" book.Status = "reading" @@ -136,53 +55,33 @@ func TestBookRepository(t *testing.T) { book.Started = &now err = repo.Update(ctx, book) - if err != nil { - t.Errorf("Failed to update book: %v", err) - } + AssertNoError(t, err, "Failed to update book") updated, err := repo.Get(ctx, id) - if err != nil { - t.Fatalf("Failed to get updated book: %v", err) - } + AssertNoError(t, err, "Failed to get updated book") - if updated.Title != "Updated Book" { - t.Errorf("Expected updated title, got %s", updated.Title) - } - if updated.Status != "reading" { - t.Errorf("Expected status reading, got %s", updated.Status) - } - if updated.Progress != 50 { - t.Errorf("Expected progress 50, got %d", updated.Progress) - } - if updated.Rating != 5.0 { - t.Errorf("Expected rating 5.0, got %f", updated.Rating) - } - if updated.Started == nil { - t.Error("Expected started time to be set") - } + AssertEqual(t, "Updated Book", updated.Title, "Expected updated title") + AssertEqual(t, "reading", updated.Status, "Expected reading status") + AssertEqual(t, 50, updated.Progress, "Expected progress 50") + AssertEqual(t, 5.0, updated.Rating, "Expected rating 5.0") + AssertTrue(t, updated.Started != nil, "Expected started time to be set") }) t.Run("Delete Book", func(t *testing.T) { - book := createSampleBook() + book := CreateSampleBook() id, err := repo.Create(ctx, book) - if err != nil { - t.Fatalf("Failed to create book: %v", err) - } + AssertNoError(t, err, "Failed to create book") err = repo.Delete(ctx, id) - if err != nil { - t.Errorf("Failed to delete book: %v", err) - } + AssertNoError(t, err, "Failed to delete book") _, err = repo.Get(ctx, id) - if err == nil { - t.Error("Expected error when getting deleted book") - } + AssertError(t, err, "Expected error when getting deleted book") }) }) t.Run("List", func(t *testing.T) { - db := createBookTestDB(t) + db := CreateTestDB(t) repo := NewBookRepository(db) ctx := context.Background() @@ -195,119 +94,74 @@ func TestBookRepository(t *testing.T) { for _, book := range books { _, err := repo.Create(ctx, book) - if err != nil { - t.Fatalf("Failed to create book: %v", err) - } + AssertNoError(t, err, "Failed to create book") } t.Run("List All Books", func(t *testing.T) { results, err := repo.List(ctx, BookListOptions{}) - if err != nil { - t.Errorf("Failed to list books: %v", err) - } - - if len(results) != 4 { - t.Errorf("Expected 4 books, got %d", len(results)) - } + AssertNoError(t, err, "Failed to list books") + AssertEqual(t, 4, len(results), "Expected 4 books") }) t.Run("List Books with Status Filter", func(t *testing.T) { results, err := repo.List(ctx, BookListOptions{Status: "queued"}) - if err != nil { - t.Errorf("Failed to list books: %v", err) - } - - if len(results) != 2 { - t.Errorf("Expected 2 queued books, got %d", len(results)) - } + AssertNoError(t, err, "Failed to list books") + AssertEqual(t, 2, len(results), "Expected 2 queued books") for _, book := range results { - if book.Status != "queued" { - t.Errorf("Expected queued status, got %s", book.Status) - } + AssertEqual(t, "queued", book.Status, "Expected queued status") } }) t.Run("List Books by Author", func(t *testing.T) { results, err := repo.List(ctx, BookListOptions{Author: "Author A"}) - if err != nil { - t.Errorf("Failed to list books: %v", err) - } - - if len(results) != 2 { - t.Errorf("Expected 2 books by Author A, got %d", len(results)) - } + AssertNoError(t, err, "Failed to list books") + AssertEqual(t, 2, len(results), "Expected 2 books by Author A") for _, book := range results { - if book.Author != "Author A" { - t.Errorf("Expected author 'Author A', got %s", book.Author) - } + AssertEqual(t, "Author A", book.Author, "Expected author 'Author A'") } }) t.Run("List Books with Progress Filter", func(t *testing.T) { results, err := repo.List(ctx, BookListOptions{MinProgress: 50}) - if err != nil { - t.Errorf("Failed to list books: %v", err) - } - - if len(results) != 2 { - t.Errorf("Expected 2 books with progress >= 50, got %d", len(results)) - } + AssertNoError(t, err, "Failed to list books") + AssertEqual(t, 2, len(results), "Expected 2 books with progress >= 50") for _, book := range results { - if book.Progress < 50 { - t.Errorf("Expected progress >= 50, got %d", book.Progress) - } + AssertTrue(t, book.Progress >= 50, "Expected progress >= 50") } }) t.Run("List Books with Rating Filter", func(t *testing.T) { results, err := repo.List(ctx, BookListOptions{MinRating: 4.5}) - if err != nil { - t.Errorf("Failed to list books: %v", err) - } - - if len(results) != 2 { - t.Errorf("Expected 2 books with rating >= 4.5, got %d", len(results)) - } + AssertNoError(t, err, "Failed to list books") + AssertEqual(t, 2, len(results), "Expected 2 books with rating >= 4.5") for _, book := range results { - if book.Rating < 4.5 { - t.Errorf("Expected rating >= 4.5, got %f", book.Rating) - } + AssertTrue(t, book.Rating >= 4.5, "Expected rating >= 4.5") } }) t.Run("List Books with Search", func(t *testing.T) { results, err := repo.List(ctx, BookListOptions{Search: "Book 1"}) - if err != nil { - t.Errorf("Failed to list books: %v", err) - } - - if len(results) != 1 { - t.Errorf("Expected 1 book matching search, got %d", len(results)) - } + AssertNoError(t, err, "Failed to list books") + AssertEqual(t, 1, len(results), "Expected 1 book matching search") - if len(results) > 0 && results[0].Title != "Book 1" { - t.Errorf("Expected 'Book 1', got %s", results[0].Title) + if len(results) > 0 { + AssertEqual(t, "Book 1", results[0].Title, "Expected 'Book 1'") } }) t.Run("List Books with Limit", func(t *testing.T) { results, err := repo.List(ctx, BookListOptions{Limit: 2}) - if err != nil { - t.Errorf("Failed to list books: %v", err) - } - - if len(results) != 2 { - t.Errorf("Expected 2 books due to limit, got %d", len(results)) - } + AssertNoError(t, err, "Failed to list books") + AssertEqual(t, 2, len(results), "Expected 2 books due to limit") }) }) t.Run("Special Methods", func(t *testing.T) { - db := createBookTestDB(t) + db := CreateTestDB(t) repo := NewBookRepository(db) ctx := context.Background() @@ -319,9 +173,7 @@ func TestBookRepository(t *testing.T) { var book1ID int64 for _, book := range []*models.Book{book1, book2, book3, book4} { id, err := repo.Create(ctx, book) - if err != nil { - t.Fatalf("Failed to create book: %v", err) - } + AssertNoError(t, err, "Failed to create book") if book == book1 { book1ID = id } @@ -329,173 +181,100 @@ func TestBookRepository(t *testing.T) { t.Run("GetQueued", func(t *testing.T) { results, err := repo.GetQueued(ctx) - if err != nil { - t.Errorf("Failed to get queued books: %v", err) - } - - if len(results) != 2 { - t.Errorf("Expected 2 queued books, got %d", len(results)) - } + AssertNoError(t, err, "Failed to get queued books") + AssertEqual(t, 2, len(results), "Expected 2 queued books") for _, book := range results { - if book.Status != "queued" { - t.Errorf("Expected queued status, got %s", book.Status) - } + AssertEqual(t, "queued", book.Status, "Expected queued status") } }) t.Run("GetReading", func(t *testing.T) { results, err := repo.GetReading(ctx) - if err != nil { - t.Errorf("Failed to get reading books: %v", err) - } - - if len(results) != 1 { - t.Errorf("Expected 1 reading book, got %d", len(results)) - } + AssertNoError(t, err, "Failed to get reading books") + AssertEqual(t, 1, len(results), "Expected 1 reading book") - if len(results) > 0 && results[0].Status != "reading" { - t.Errorf("Expected reading status, got %s", results[0].Status) + if len(results) > 0 { + AssertEqual(t, "reading", results[0].Status, "Expected reading status") } }) t.Run("GetFinished", func(t *testing.T) { results, err := repo.GetFinished(ctx) - if err != nil { - t.Errorf("Failed to get finished books: %v", err) - } - - if len(results) != 1 { - t.Errorf("Expected 1 finished book, got %d", len(results)) - } + AssertNoError(t, err, "Failed to get finished books") + AssertEqual(t, 1, len(results), "Expected 1 finished book") - if len(results) > 0 && results[0].Status != "finished" { - t.Errorf("Expected finished status, got %s", results[0].Status) + if len(results) > 0 { + AssertEqual(t, "finished", results[0].Status, "Expected finished status") } }) t.Run("GetByAuthor", func(t *testing.T) { results, err := repo.GetByAuthor(ctx, "Author A") - if err != nil { - t.Errorf("Failed to get books by author: %v", err) - } - - if len(results) != 2 { - t.Errorf("Expected 2 books by Author A, got %d", len(results)) - } + AssertNoError(t, err, "Failed to get books by author") + AssertEqual(t, 2, len(results), "Expected 2 books by Author A") for _, book := range results { - if book.Author != "Author A" { - t.Errorf("Expected author 'Author A', got %s", book.Author) - } + AssertEqual(t, "Author A", book.Author, "Expected author 'Author A'") } }) t.Run("StartReading", func(t *testing.T) { err := repo.StartReading(ctx, book1ID) - if err != nil { - t.Errorf("Failed to start reading book: %v", err) - } + AssertNoError(t, err, "Failed to start reading book") updated, err := repo.Get(ctx, book1ID) - if err != nil { - t.Fatalf("Failed to get updated book: %v", err) - } - - if updated.Status != "reading" { - t.Errorf("Expected status to be reading, got %s", updated.Status) - } + AssertNoError(t, err, "Failed to get updated book") - if updated.Started == nil { - t.Error("Expected started timestamp to be set") - } + AssertEqual(t, "reading", updated.Status, "Expected status to be reading") + AssertTrue(t, updated.Started != nil, "Expected started timestamp to be set") }) t.Run("FinishReading", func(t *testing.T) { newBook := &models.Book{Title: "New Book", Status: "reading", Progress: 80} id, err := repo.Create(ctx, newBook) - if err != nil { - t.Fatalf("Failed to create new book: %v", err) - } + AssertNoError(t, err, "Failed to create new book") err = repo.FinishReading(ctx, id) - if err != nil { - t.Errorf("Failed to finish reading book: %v", err) - } + AssertNoError(t, err, "Failed to finish reading book") updated, err := repo.Get(ctx, id) - if err != nil { - t.Fatalf("Failed to get updated book: %v", err) - } - - if updated.Status != "finished" { - t.Errorf("Expected status to be finished, got %s", updated.Status) - } + AssertNoError(t, err, "Failed to get updated book") - if updated.Progress != 100 { - t.Errorf("Expected progress to be 100, got %d", updated.Progress) - } - - if updated.Finished == nil { - t.Error("Expected finished timestamp to be set") - } + AssertEqual(t, "finished", updated.Status, "Expected status to be finished") + AssertEqual(t, 100, updated.Progress, "Expected progress to be 100") + AssertTrue(t, updated.Finished != nil, "Expected finished timestamp to be set") }) t.Run("UpdateProgress", func(t *testing.T) { newBook := &models.Book{Title: "Progress Book", Status: "queued", Progress: 0} id, err := repo.Create(ctx, newBook) - if err != nil { - t.Fatalf("Failed to create new book: %v", err) - } + AssertNoError(t, err, "Failed to create new book") err = repo.UpdateProgress(ctx, id, 25) - if err != nil { - t.Errorf("Failed to update progress: %v", err) - } + AssertNoError(t, err, "Failed to update progress") updated, err := repo.Get(ctx, id) - if err != nil { - t.Fatalf("Failed to get updated book: %v", err) - } - - if updated.Status != "reading" { - t.Errorf("Expected status to be reading when progress > 0, got %s", updated.Status) - } + AssertNoError(t, err, "Failed to get updated book") - if updated.Progress != 25 { - t.Errorf("Expected progress 25, got %d", updated.Progress) - } - - if updated.Started == nil { - t.Error("Expected started timestamp to be set when progress > 0") - } + AssertEqual(t, "reading", updated.Status, "Expected status to be reading when progress > 0") + AssertEqual(t, 25, updated.Progress, "Expected progress 25") + AssertTrue(t, updated.Started != nil, "Expected started timestamp to be set when progress > 0") err = repo.UpdateProgress(ctx, id, 100) - if err != nil { - t.Errorf("Failed to update progress to 100: %v", err) - } + AssertNoError(t, err, "Failed to update progress to 100") updated, err = repo.Get(ctx, id) - if err != nil { - t.Fatalf("Failed to get updated book: %v", err) - } - - if updated.Status != "finished" { - t.Errorf("Expected status to be finished when progress = 100, got %s", updated.Status) - } + AssertNoError(t, err, "Failed to get updated book") - if updated.Progress != 100 { - t.Errorf("Expected progress 100, got %d", updated.Progress) - } - - if updated.Finished == nil { - t.Error("Expected finished timestamp to be set when progress = 100") - } + AssertEqual(t, "finished", updated.Status, "Expected status to be finished when progress = 100") + AssertEqual(t, 100, updated.Progress, "Expected progress 100") + AssertTrue(t, updated.Finished != nil, "Expected finished timestamp to be set when progress = 100") }) }) t.Run("Count", func(t *testing.T) { - db := createBookTestDB(t) + db := CreateTestDB(t) repo := NewBookRepository(db) ctx := context.Background() @@ -508,53 +287,31 @@ func TestBookRepository(t *testing.T) { for _, book := range books { _, err := repo.Create(ctx, book) - if err != nil { - t.Fatalf("Failed to create book: %v", err) - } + AssertNoError(t, err, "Failed to create book") } t.Run("Count all books", func(t *testing.T) { count, err := repo.Count(ctx, BookListOptions{}) - if err != nil { - t.Errorf("Failed to count books: %v", err) - } - - if count != 4 { - t.Errorf("Expected 4 books, got %d", count) - } + AssertNoError(t, err, "Failed to count books") + AssertEqual(t, int64(4), count, "Expected 4 books") }) t.Run("Count queued books", func(t *testing.T) { count, err := repo.Count(ctx, BookListOptions{Status: "queued"}) - if err != nil { - t.Errorf("Failed to count queued books: %v", err) - } - - if count != 2 { - t.Errorf("Expected 2 queued books, got %d", count) - } + AssertNoError(t, err, "Failed to count queued books") + AssertEqual(t, int64(2), count, "Expected 2 queued books") }) t.Run("Count books by progress", func(t *testing.T) { count, err := repo.Count(ctx, BookListOptions{MinProgress: 50}) - if err != nil { - t.Errorf("Failed to count books with progress >= 50: %v", err) - } - - if count != 2 { - t.Errorf("Expected 2 books with progress >= 50, got %d", count) - } + AssertNoError(t, err, "Failed to count books with progress >= 50") + AssertEqual(t, int64(2), count, "Expected 2 books with progress >= 50") }) t.Run("Count books by rating", func(t *testing.T) { count, err := repo.Count(ctx, BookListOptions{MinRating: 4.0}) - if err != nil { - t.Errorf("Failed to count high-rated books: %v", err) - } - - if count != 3 { - t.Errorf("Expected 3 books with rating >= 4.0, got %d", count) - } + AssertNoError(t, err, "Failed to count high-rated books") + AssertEqual(t, int64(3), count, "Expected 3 books with rating >= 4.0") }) }) } diff --git a/internal/repo/find_methods_test.go b/internal/repo/find_methods_test.go new file mode 100644 index 0000000..6888a4e --- /dev/null +++ b/internal/repo/find_methods_test.go @@ -0,0 +1,300 @@ +package repo + +import ( + "context" + "testing" +) + +func TestFindMethods(t *testing.T) { + db := CreateTestDB(t) + repos := SetupTestData(t, db) + ctx := context.Background() + + t.Run("TaskRepository Find", func(t *testing.T) { + t.Run("finds tasks by status", func(t *testing.T) { + options := TaskListOptions{ + Status: "pending", + } + tasks, err := repos.Tasks.Find(ctx, options) + AssertNoError(t, err, "Find should succeed") + AssertTrue(t, len(tasks) >= 1, "Should find at least one pending task") + for _, task := range tasks { + AssertEqual(t, "pending", task.Status, "All returned tasks should be pending") + } + }) + + t.Run("finds tasks by priority", func(t *testing.T) { + options := TaskListOptions{ + Priority: "high", + } + tasks, err := repos.Tasks.Find(ctx, options) + AssertNoError(t, err, "Find should succeed") + AssertTrue(t, len(tasks) >= 1, "Should find at least one high priority task") + for _, task := range tasks { + AssertEqual(t, "high", task.Priority, "All returned tasks should be high priority") + } + }) + + t.Run("finds tasks by project", func(t *testing.T) { + options := TaskListOptions{ + Project: "test-project", + } + tasks, err := repos.Tasks.Find(ctx, options) + AssertNoError(t, err, "Find should succeed") + AssertTrue(t, len(tasks) >= 1, "Should find tasks in test-project") + for _, task := range tasks { + AssertEqual(t, "test-project", task.Project, "All returned tasks should be in test-project") + } + }) + + t.Run("finds tasks by context", func(t *testing.T) { + options := TaskListOptions{ + Context: "test-context", + } + tasks, err := repos.Tasks.Find(ctx, options) + AssertNoError(t, err, "Find should succeed") + AssertTrue(t, len(tasks) >= 1, "Should find tasks in test-context") + for _, task := range tasks { + AssertEqual(t, "test-context", task.Context, "All returned tasks should be in test-context") + } + }) + + t.Run("finds tasks by multiple criteria", func(t *testing.T) { + options := TaskListOptions{ + Status: "pending", + Priority: "high", + Project: "test-project", + } + tasks, err := repos.Tasks.Find(ctx, options) + AssertNoError(t, err, "Find should succeed") + for _, task := range tasks { + AssertEqual(t, "pending", task.Status, "Task should be pending") + AssertEqual(t, "high", task.Priority, "Task should be high priority") + AssertEqual(t, "test-project", task.Project, "Task should be in test-project") + } + }) + + t.Run("returns empty for non-matching criteria", func(t *testing.T) { + options := TaskListOptions{ + Status: "non-existent-status", + } + tasks, err := repos.Tasks.Find(ctx, options) + AssertNoError(t, err, "Find should succeed even with no results") + AssertEqual(t, 0, len(tasks), "Should find no tasks") + }) + + t.Run("returns all tasks with empty options", func(t *testing.T) { + options := TaskListOptions{} + tasks, err := repos.Tasks.Find(ctx, options) + AssertNoError(t, err, "Find should succeed with empty options") + AssertTrue(t, len(tasks) >= 2, "Should return all tasks for empty options") + }) + }) + + t.Run("BookRepository Find", func(t *testing.T) { + t.Run("finds books by status", func(t *testing.T) { + options := BookListOptions{ + Status: "reading", + } + books, err := repos.Books.Find(ctx, options) + AssertNoError(t, err, "Find should succeed") + AssertTrue(t, len(books) >= 1, "Should find at least one book being read") + for _, book := range books { + AssertEqual(t, "reading", book.Status, "All returned books should be reading") + } + }) + + t.Run("finds books by author", func(t *testing.T) { + options := BookListOptions{ + Author: "Test Author", + } + books, err := repos.Books.Find(ctx, options) + AssertNoError(t, err, "Find should succeed") + AssertTrue(t, len(books) >= 1, "Should find at least one book by Test Author") + for _, book := range books { + AssertEqual(t, "Test Author", book.Author, "All returned books should be by Test Author") + } + }) + + t.Run("finds books by minimum progress", func(t *testing.T) { + options := BookListOptions{ + MinProgress: 0, + } + books, err := repos.Books.Find(ctx, options) + AssertNoError(t, err, "Find should succeed") + AssertTrue(t, len(books) >= 1, "Should find books with progress >= 0") + for _, book := range books { + AssertTrue(t, book.Progress >= 0, "All returned books should have progress >= 0") + } + }) + + t.Run("finds books by multiple criteria", func(t *testing.T) { + options := BookListOptions{ + Status: "reading", + Author: "Test Author", + MinProgress: 0, + } + books, err := repos.Books.Find(ctx, options) + AssertNoError(t, err, "Find should succeed") + for _, book := range books { + AssertEqual(t, "reading", book.Status, "Book should be reading") + AssertEqual(t, "Test Author", book.Author, "Book should be by Test Author") + AssertTrue(t, book.Progress >= 0, "Book should have progress >= 0") + } + }) + + t.Run("returns empty for non-matching criteria", func(t *testing.T) { + options := BookListOptions{ + Status: "non-existent-status", + } + books, err := repos.Books.Find(ctx, options) + AssertNoError(t, err, "Find should succeed even with no results") + AssertEqual(t, 0, len(books), "Should find no books") + }) + + t.Run("returns all books with empty options", func(t *testing.T) { + options := BookListOptions{} + books, err := repos.Books.Find(ctx, options) + AssertNoError(t, err, "Find should succeed with empty options") + AssertTrue(t, len(books) >= 2, "Should return all books for empty options") + }) + }) + + t.Run("MovieRepository Find", func(t *testing.T) { + t.Run("finds movies by status", func(t *testing.T) { + options := MovieListOptions{ + Status: "watched", + } + movies, err := repos.Movies.Find(ctx, options) + AssertNoError(t, err, "Find should succeed") + AssertTrue(t, len(movies) >= 1, "Should find at least one watched movie") + for _, movie := range movies { + AssertEqual(t, "watched", movie.Status, "All returned movies should be watched") + } + }) + + t.Run("finds movies by year", func(t *testing.T) { + options := MovieListOptions{ + Year: 2023, + } + movies, err := repos.Movies.Find(ctx, options) + AssertNoError(t, err, "Find should succeed") + AssertTrue(t, len(movies) >= 1, "Should find movies from 2023") + for _, movie := range movies { + AssertEqual(t, 2023, movie.Year, "Movie should be from 2023") + } + }) + + t.Run("finds movies by minimum rating", func(t *testing.T) { + options := MovieListOptions{ + MinRating: 0.0, + } + movies, err := repos.Movies.Find(ctx, options) + AssertNoError(t, err, "Find should succeed") + AssertTrue(t, len(movies) >= 1, "Should find movies with rating >= 0") + for _, movie := range movies { + AssertTrue(t, movie.Rating >= 0.0, "Movie rating should be >= 0") + } + }) + + t.Run("finds movies by multiple criteria", func(t *testing.T) { + options := MovieListOptions{ + Status: "watched", + Year: 2023, + MinRating: 0.0, + } + movies, err := repos.Movies.Find(ctx, options) + AssertNoError(t, err, "Find should succeed") + for _, movie := range movies { + AssertEqual(t, "watched", movie.Status, "Movie should be watched") + AssertEqual(t, 2023, movie.Year, "Movie should be from 2023") + AssertTrue(t, movie.Rating >= 0.0, "Movie rating should be >= 0") + } + }) + + t.Run("returns empty for non-matching criteria", func(t *testing.T) { + options := MovieListOptions{ + Status: "non-existent-status", + } + movies, err := repos.Movies.Find(ctx, options) + AssertNoError(t, err, "Find should succeed even with no results") + AssertEqual(t, 0, len(movies), "Should find no movies") + }) + + t.Run("returns all movies with empty options", func(t *testing.T) { + options := MovieListOptions{} + movies, err := repos.Movies.Find(ctx, options) + AssertNoError(t, err, "Find should succeed with empty options") + AssertTrue(t, len(movies) >= 2, "Should return all movies for empty options") + }) + }) + + t.Run("TVRepository Find", func(t *testing.T) { + t.Run("finds TV shows by status", func(t *testing.T) { + options := TVListOptions{ + Status: "watching", + } + shows, err := repos.TV.Find(ctx, options) + AssertNoError(t, err, "Find should succeed") + AssertTrue(t, len(shows) >= 1, "Should find at least one TV show being watched") + for _, show := range shows { + AssertEqual(t, "watching", show.Status, "All returned shows should be watching") + } + }) + + t.Run("finds TV shows by season", func(t *testing.T) { + options := TVListOptions{ + Season: 1, + } + shows, err := repos.TV.Find(ctx, options) + AssertNoError(t, err, "Find should succeed") + AssertTrue(t, len(shows) >= 1, "Should find TV shows with season 1") + for _, show := range shows { + AssertEqual(t, 1, show.Season, "All returned shows should be season 1") + } + }) + + t.Run("finds TV shows by minimum rating", func(t *testing.T) { + options := TVListOptions{ + MinRating: 0.0, + } + shows, err := repos.TV.Find(ctx, options) + AssertNoError(t, err, "Find should succeed") + AssertTrue(t, len(shows) >= 1, "Should find TV shows with rating >= 0") + for _, show := range shows { + AssertTrue(t, show.Rating >= 0.0, "Show rating should be >= 0") + } + }) + + t.Run("finds TV shows by multiple criteria", func(t *testing.T) { + options := TVListOptions{ + Status: "watching", + Season: 1, + MinRating: 0.0, + } + shows, err := repos.TV.Find(ctx, options) + AssertNoError(t, err, "Find should succeed") + for _, show := range shows { + AssertEqual(t, "watching", show.Status, "Show should be watching") + AssertEqual(t, 1, show.Season, "Show should be season 1") + AssertTrue(t, show.Rating >= 0.0, "Show rating should be >= 0") + } + }) + + t.Run("returns empty for non-matching criteria", func(t *testing.T) { + options := TVListOptions{ + Status: "non-existent-status", + } + shows, err := repos.TV.Find(ctx, options) + AssertNoError(t, err, "Find should succeed even with no results") + AssertEqual(t, 0, len(shows), "Should find no TV shows") + }) + + t.Run("returns all TV shows with empty options", func(t *testing.T) { + options := TVListOptions{} + shows, err := repos.TV.Find(ctx, options) + AssertNoError(t, err, "Find should succeed with empty options") + AssertTrue(t, len(shows) >= 2, "Should return all TV shows for empty options") + }) + }) +} diff --git a/internal/repo/movie_repository_test.go b/internal/repo/movie_repository_test.go index 7d314d6..42b7b06 100644 --- a/internal/repo/movie_repository_test.go +++ b/internal/repo/movie_repository_test.go @@ -2,7 +2,6 @@ package repo import ( "context" - "database/sql" "testing" "time" @@ -10,112 +9,41 @@ import ( "github.com/stormlightlabs/noteleaf/internal/models" ) -func createMovieTestDB(t *testing.T) *sql.DB { - db, err := sql.Open("sqlite3", ":memory:") - if err != nil { - t.Fatalf("Failed to create in-memory database: %v", err) - } - - if _, err := db.Exec("PRAGMA foreign_keys = ON"); err != nil { - t.Fatalf("Failed to enable foreign keys: %v", err) - } - - schema := ` - CREATE TABLE IF NOT EXISTS movies ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - title TEXT NOT NULL, - year INTEGER, - status TEXT DEFAULT 'queued', - rating REAL, - notes TEXT, - added DATETIME DEFAULT CURRENT_TIMESTAMP, - watched DATETIME - ); - ` - - if _, err := db.Exec(schema); err != nil { - t.Fatalf("Failed to create schema: %v", err) - } - - t.Cleanup(func() { - db.Close() - }) - - return db -} - -func createSampleMovie() *models.Movie { - return &models.Movie{ - Title: "Test Movie", - Year: 2023, - Status: "queued", - Rating: 8.5, - Notes: "Great movie to watch", - } -} - func TestMovieRepository(t *testing.T) { t.Run("CRUD Operations", func(t *testing.T) { - db := createMovieTestDB(t) + db := CreateTestDB(t) repo := NewMovieRepository(db) ctx := context.Background() t.Run("Create Movie", func(t *testing.T) { - movie := createSampleMovie() + movie := CreateSampleMovie() id, err := repo.Create(ctx, movie) - if err != nil { - t.Errorf("Failed to create movie: %v", err) - } - - if id == 0 { - t.Error("Expected non-zero ID") - } - - if movie.ID != id { - t.Errorf("Expected movie ID to be set to %d, got %d", id, movie.ID) - } - - if movie.Added.IsZero() { - t.Error("Expected Added timestamp to be set") - } + AssertNoError(t, err, "Failed to create movie") + AssertNotEqual(t, int64(0), id, "Expected non-zero ID") + AssertEqual(t, id, movie.ID, "Expected movie ID to be set correctly") + AssertFalse(t, movie.Added.IsZero(), "Expected Added timestamp to be set") }) t.Run("Get Movie", func(t *testing.T) { - original := createSampleMovie() + original := CreateSampleMovie() id, err := repo.Create(ctx, original) - if err != nil { - t.Fatalf("Failed to create movie: %v", err) - } + AssertNoError(t, err, "Failed to create movie") retrieved, err := repo.Get(ctx, id) - if err != nil { - t.Errorf("Failed to get movie: %v", err) - } + AssertNoError(t, err, "Failed to get movie") - if retrieved.Title != original.Title { - t.Errorf("Expected title %s, got %s", original.Title, retrieved.Title) - } - if retrieved.Year != original.Year { - t.Errorf("Expected year %d, got %d", original.Year, retrieved.Year) - } - if retrieved.Status != original.Status { - t.Errorf("Expected status %s, got %s", original.Status, retrieved.Status) - } - if retrieved.Rating != original.Rating { - t.Errorf("Expected rating %f, got %f", original.Rating, retrieved.Rating) - } - if retrieved.Notes != original.Notes { - t.Errorf("Expected notes %s, got %s", original.Notes, retrieved.Notes) - } + AssertEqual(t, original.Title, retrieved.Title, "Title mismatch") + AssertEqual(t, original.Year, retrieved.Year, "Year mismatch") + AssertEqual(t, original.Status, retrieved.Status, "Status mismatch") + AssertEqual(t, original.Rating, retrieved.Rating, "Rating mismatch") + AssertEqual(t, original.Notes, retrieved.Notes, "Notes mismatch") }) t.Run("Update Movie", func(t *testing.T) { - movie := createSampleMovie() + movie := CreateSampleMovie() id, err := repo.Create(ctx, movie) - if err != nil { - t.Fatalf("Failed to create movie: %v", err) - } + AssertNoError(t, err, "Failed to create movie") movie.Title = "Updated Movie" movie.Status = "watched" @@ -124,50 +52,32 @@ func TestMovieRepository(t *testing.T) { movie.Watched = &now err = repo.Update(ctx, movie) - if err != nil { - t.Errorf("Failed to update movie: %v", err) - } + AssertNoError(t, err, "Failed to update movie") updated, err := repo.Get(ctx, id) - if err != nil { - t.Fatalf("Failed to get updated movie: %v", err) - } + AssertNoError(t, err, "Failed to get updated movie") - if updated.Title != "Updated Movie" { - t.Errorf("Expected updated title, got %s", updated.Title) - } - if updated.Status != "watched" { - t.Errorf("Expected status watched, got %s", updated.Status) - } - if updated.Rating != 9.0 { - t.Errorf("Expected rating 9.0, got %f", updated.Rating) - } - if updated.Watched == nil { - t.Error("Expected watched time to be set") - } + AssertEqual(t, "Updated Movie", updated.Title, "Expected updated title") + AssertEqual(t, "watched", updated.Status, "Expected watched status") + AssertEqual(t, 9.0, updated.Rating, "Expected rating 9.0") + AssertTrue(t, updated.Watched != nil, "Expected watched time to be set") }) t.Run("Delete Movie", func(t *testing.T) { - movie := createSampleMovie() + movie := CreateSampleMovie() id, err := repo.Create(ctx, movie) - if err != nil { - t.Fatalf("Failed to create movie: %v", err) - } + AssertNoError(t, err, "Failed to create movie") err = repo.Delete(ctx, id) - if err != nil { - t.Errorf("Failed to delete movie: %v", err) - } + AssertNoError(t, err, "Failed to delete movie") _, err = repo.Get(ctx, id) - if err == nil { - t.Error("Expected error when getting deleted movie") - } + AssertError(t, err, "Expected error when getting deleted movie") }) }) t.Run("List", func(t *testing.T) { - db := createMovieTestDB(t) + db := CreateTestDB(t) repo := NewMovieRepository(db) ctx := context.Background() @@ -179,100 +89,64 @@ func TestMovieRepository(t *testing.T) { for _, movie := range movies { _, err := repo.Create(ctx, movie) - if err != nil { - t.Fatalf("Failed to create movie: %v", err) - } + AssertNoError(t, err, "Failed to create movie") } t.Run("List All Movies", func(t *testing.T) { results, err := repo.List(ctx, MovieListOptions{}) - if err != nil { - t.Errorf("Failed to list movies: %v", err) - } - - if len(results) != 3 { - t.Errorf("Expected 3 movies, got %d", len(results)) - } + AssertNoError(t, err, "Failed to list movies") + AssertEqual(t, 3, len(results), "Expected 3 movies") }) t.Run("List Movies with Status Filter", func(t *testing.T) { results, err := repo.List(ctx, MovieListOptions{Status: "queued"}) - if err != nil { - t.Errorf("Failed to list movies: %v", err) - } - - if len(results) != 2 { - t.Errorf("Expected 2 queued movies, got %d", len(results)) - } + AssertNoError(t, err, "Failed to list movies") + AssertEqual(t, 2, len(results), "Expected 2 queued movies") for _, movie := range results { - if movie.Status != "queued" { - t.Errorf("Expected queued status, got %s", movie.Status) - } + AssertEqual(t, "queued", movie.Status, "Expected queued status") } }) t.Run("List Movies with Year Filter", func(t *testing.T) { results, err := repo.List(ctx, MovieListOptions{Year: 2021}) - if err != nil { - t.Errorf("Failed to list movies: %v", err) - } + AssertNoError(t, err, "Failed to list movies") + AssertEqual(t, 1, len(results), "Expected 1 movie from 2021") - if len(results) != 1 { - t.Errorf("Expected 1 movie from 2021, got %d", len(results)) - } - - if len(results) > 0 && results[0].Year != 2021 { - t.Errorf("Expected year 2021, got %d", results[0].Year) + if len(results) > 0 { + AssertEqual(t, 2021, results[0].Year, "Expected year 2021") } }) t.Run("List Movies with Rating Filter", func(t *testing.T) { results, err := repo.List(ctx, MovieListOptions{MinRating: 8.0}) - if err != nil { - t.Errorf("Failed to list movies: %v", err) - } - - if len(results) != 2 { - t.Errorf("Expected 2 movies with rating >= 8.0, got %d", len(results)) - } + AssertNoError(t, err, "Failed to list movies") + AssertEqual(t, 2, len(results), "Expected 2 movies with rating >= 8.0") for _, movie := range results { - if movie.Rating < 8.0 { - t.Errorf("Expected rating >= 8.0, got %f", movie.Rating) - } + AssertTrue(t, movie.Rating >= 8.0, "Expected rating >= 8.0") } }) t.Run("List Movies with Search", func(t *testing.T) { results, err := repo.List(ctx, MovieListOptions{Search: "Movie 1"}) - if err != nil { - t.Errorf("Failed to list movies: %v", err) - } - - if len(results) != 1 { - t.Errorf("Expected 1 movie matching search, got %d", len(results)) - } + AssertNoError(t, err, "Failed to list movies") + AssertEqual(t, 1, len(results), "Expected 1 movie matching search") - if len(results) > 0 && results[0].Title != "Movie 1" { - t.Errorf("Expected 'Movie 1', got %s", results[0].Title) + if len(results) > 0 { + AssertEqual(t, "Movie 1", results[0].Title, "Expected 'Movie 1'") } }) t.Run("List Movies with Limit", func(t *testing.T) { results, err := repo.List(ctx, MovieListOptions{Limit: 2}) - if err != nil { - t.Errorf("Failed to list movies: %v", err) - } - - if len(results) != 2 { - t.Errorf("Expected 2 movies due to limit, got %d", len(results)) - } + AssertNoError(t, err, "Failed to list movies") + AssertEqual(t, 2, len(results), "Expected 2 movies due to limit") }) }) t.Run("Special Methods", func(t *testing.T) { - db := createMovieTestDB(t) + db := CreateTestDB(t) repo := NewMovieRepository(db) ctx := context.Background() @@ -283,9 +157,7 @@ func TestMovieRepository(t *testing.T) { var movie1ID int64 for _, movie := range []*models.Movie{movie1, movie2, movie3} { id, err := repo.Create(ctx, movie) - if err != nil { - t.Fatalf("Failed to create movie: %v", err) - } + AssertNoError(t, err, "Failed to create movie") if movie == movie1 { movie1ID = id } @@ -293,59 +165,38 @@ func TestMovieRepository(t *testing.T) { t.Run("GetQueued", func(t *testing.T) { results, err := repo.GetQueued(ctx) - if err != nil { - t.Errorf("Failed to get queued movies: %v", err) - } - - if len(results) != 2 { - t.Errorf("Expected 2 queued movies, got %d", len(results)) - } + AssertNoError(t, err, "Failed to get queued movies") + AssertEqual(t, 2, len(results), "Expected 2 queued movies") for _, movie := range results { - if movie.Status != "queued" { - t.Errorf("Expected queued status, got %s", movie.Status) - } + AssertEqual(t, "queued", movie.Status, "Expected queued status") } }) t.Run("GetWatched", func(t *testing.T) { results, err := repo.GetWatched(ctx) - if err != nil { - t.Errorf("Failed to get watched movies: %v", err) - } + AssertNoError(t, err, "Failed to get watched movies") + AssertEqual(t, 1, len(results), "Expected 1 watched movie") - if len(results) != 1 { - t.Errorf("Expected 1 watched movie, got %d", len(results)) - } - - if len(results) > 0 && results[0].Status != "watched" { - t.Errorf("Expected watched status, got %s", results[0].Status) + if len(results) > 0 { + AssertEqual(t, "watched", results[0].Status, "Expected watched status") } }) t.Run("MarkWatched", func(t *testing.T) { err := repo.MarkWatched(ctx, movie1ID) - if err != nil { - t.Errorf("Failed to mark movie as watched: %v", err) - } + AssertNoError(t, err, "Failed to mark movie as watched") updated, err := repo.Get(ctx, movie1ID) - if err != nil { - t.Fatalf("Failed to get updated movie: %v", err) - } + AssertNoError(t, err, "Failed to get updated movie") - if updated.Status != "watched" { - t.Errorf("Expected status to be watched, got %s", updated.Status) - } - - if updated.Watched == nil { - t.Error("Expected watched timestamp to be set") - } + AssertEqual(t, "watched", updated.Status, "Expected status to be watched") + AssertTrue(t, updated.Watched != nil, "Expected watched timestamp to be set") }) }) t.Run("Count", func(t *testing.T) { - db := createMovieTestDB(t) + db := CreateTestDB(t) repo := NewMovieRepository(db) ctx := context.Background() @@ -357,42 +208,25 @@ func TestMovieRepository(t *testing.T) { for _, movie := range movies { _, err := repo.Create(ctx, movie) - if err != nil { - t.Fatalf("Failed to create movie: %v", err) - } + AssertNoError(t, err, "Failed to create movie") } t.Run("Count all movies", func(t *testing.T) { count, err := repo.Count(ctx, MovieListOptions{}) - if err != nil { - t.Errorf("Failed to count movies: %v", err) - } - - if count != 3 { - t.Errorf("Expected 3 movies, got %d", count) - } + AssertNoError(t, err, "Failed to count movies") + AssertEqual(t, int64(3), count, "Expected 3 movies") }) t.Run("Count queued movies", func(t *testing.T) { count, err := repo.Count(ctx, MovieListOptions{Status: "queued"}) - if err != nil { - t.Errorf("Failed to count queued movies: %v", err) - } - - if count != 2 { - t.Errorf("Expected 2 queued movies, got %d", count) - } + AssertNoError(t, err, "Failed to count queued movies") + AssertEqual(t, int64(2), count, "Expected 2 queued movies") }) t.Run("Count movies by rating", func(t *testing.T) { count, err := repo.Count(ctx, MovieListOptions{MinRating: 8.0}) - if err != nil { - t.Errorf("Failed to count high-rated movies: %v", err) - } - - if count != 2 { - t.Errorf("Expected 2 movies with rating >= 8.0, got %d", count) - } + AssertNoError(t, err, "Failed to count high-rated movies") + AssertEqual(t, int64(2), count, "Expected 2 movies with rating >= 8.0") }) }) } diff --git a/internal/repo/note_repository_test.go b/internal/repo/note_repository_test.go index 6e7de37..8a78d1b 100644 --- a/internal/repo/note_repository_test.go +++ b/internal/repo/note_repository_test.go @@ -2,676 +2,488 @@ package repo import ( "context" - "database/sql" "testing" _ "github.com/mattn/go-sqlite3" "github.com/stormlightlabs/noteleaf/internal/models" ) -func createNoteTestDB(t *testing.T) *sql.DB { - db, err := sql.Open("sqlite3", ":memory:") - if err != nil { - t.Fatalf("Failed to create in-memory database: %v", err) - } - - if _, err := db.Exec("PRAGMA foreign_keys = ON"); err != nil { - t.Fatalf("Failed to enable foreign keys: %v", err) - } - - schema := ` - CREATE TABLE IF NOT EXISTS notes ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - title TEXT NOT NULL, - content TEXT NOT NULL, - tags TEXT, - archived BOOLEAN DEFAULT FALSE, - created DATETIME DEFAULT CURRENT_TIMESTAMP, - modified DATETIME DEFAULT CURRENT_TIMESTAMP, - file_path TEXT - ); - ` - - if _, err := db.Exec(schema); err != nil { - t.Fatalf("Failed to create schema: %v", err) - } - - t.Cleanup(func() { - db.Close() - }) - - return db -} - -func createSampleNote() *models.Note { - return &models.Note{ - Title: "Test Note", - Content: "This is test content with **markdown**", - Tags: []string{"personal", "work"}, - Archived: false, - FilePath: "/path/to/note.md", - } -} - -func TestNoteRepository_CRUD(t *testing.T) { - db := createNoteTestDB(t) - repo := NewNoteRepository(db) - ctx := context.Background() - - t.Run("Create Note", func(t *testing.T) { - note := createSampleNote() - - id, err := repo.Create(ctx, note) - if err != nil { - t.Errorf("Failed to create note: %v", err) - } - - if id == 0 { - t.Error("Expected non-zero ID") - } - - if note.ID != id { - t.Errorf("Expected note ID to be set to %d, got %d", id, note.ID) - } - - if note.Created.IsZero() { - t.Error("Expected Created timestamp to be set") - } - if note.Modified.IsZero() { - t.Error("Expected Modified timestamp to be set") - } - }) - - t.Run("Get Note", func(t *testing.T) { - original := createSampleNote() - id, err := repo.Create(ctx, original) - if err != nil { - t.Fatalf("Failed to create note: %v", err) - } - - retrieved, err := repo.Get(ctx, id) - if err != nil { - t.Fatalf("Failed to get note: %v", err) - } - - if retrieved.ID != original.ID { - t.Errorf("Expected ID %d, got %d", original.ID, retrieved.ID) - } - if retrieved.Title != original.Title { - t.Errorf("Expected title %s, got %s", original.Title, retrieved.Title) - } - if retrieved.Content != original.Content { - t.Errorf("Expected content %s, got %s", original.Content, retrieved.Content) - } - if len(retrieved.Tags) != len(original.Tags) { - t.Errorf("Expected %d tags, got %d", len(original.Tags), len(retrieved.Tags)) - } - if retrieved.Archived != original.Archived { - t.Errorf("Expected archived %v, got %v", original.Archived, retrieved.Archived) - } - if retrieved.FilePath != original.FilePath { - t.Errorf("Expected file path %s, got %s", original.FilePath, retrieved.FilePath) - } - }) - - t.Run("Update Note", func(t *testing.T) { - note := createSampleNote() - id, err := repo.Create(ctx, note) - if err != nil { - t.Fatalf("Failed to create note: %v", err) - } - - originalModified := note.Modified - - note.Title = "Updated Title" - note.Content = "Updated content" - note.Tags = []string{"updated", "test"} - note.Archived = true - note.FilePath = "/new/path/note.md" - - err = repo.Update(ctx, note) - if err != nil { - t.Errorf("Failed to update note: %v", err) - } - - retrieved, err := repo.Get(ctx, id) - if err != nil { - t.Fatalf("Failed to get updated note: %v", err) - } - - if retrieved.Title != "Updated Title" { - t.Errorf("Expected updated title, got %s", retrieved.Title) - } - if retrieved.Content != "Updated content" { - t.Errorf("Expected updated content, got %s", retrieved.Content) - } - if len(retrieved.Tags) != 2 || retrieved.Tags[0] != "updated" || retrieved.Tags[1] != "test" { - t.Errorf("Expected updated tags, got %v", retrieved.Tags) - } - if !retrieved.Archived { - t.Error("Expected note to be archived") - } - if retrieved.FilePath != "/new/path/note.md" { - t.Errorf("Expected updated file path, got %s", retrieved.FilePath) - } - if !retrieved.Modified.After(originalModified) { - t.Error("Expected Modified timestamp to be updated") - } - }) - - t.Run("Delete Note", func(t *testing.T) { - note := createSampleNote() - id, err := repo.Create(ctx, note) - if err != nil { - t.Fatalf("Failed to create note: %v", err) - } - - err = repo.Delete(ctx, id) - if err != nil { - t.Errorf("Failed to delete note: %v", err) - } - - _, err = repo.Get(ctx, id) - if err == nil { - t.Error("Expected error when getting deleted note") - } - }) -} - -func TestNoteRepository_List(t *testing.T) { - db := createNoteTestDB(t) - repo := NewNoteRepository(db) - ctx := context.Background() - - notes := []*models.Note{ - {Title: "First Note", Content: "Content 1", Tags: []string{"work"}, Archived: false}, - {Title: "Second Note", Content: "Content 2", Tags: []string{"personal"}, Archived: true}, - {Title: "Third Note", Content: "Important content", Tags: []string{"work", "important"}, Archived: false}, - } - - for _, note := range notes { - _, err := repo.Create(ctx, note) - if err != nil { - t.Fatalf("Failed to create test note: %v", err) - } - } - - t.Run("List All Notes", func(t *testing.T) { - results, err := repo.List(ctx, NoteListOptions{}) - if err != nil { - t.Fatalf("Failed to list notes: %v", err) - } - - if len(results) != 3 { - t.Errorf("Expected 3 notes, got %d", len(results)) - } - }) - - t.Run("List Archived Notes Only", func(t *testing.T) { - archived := true - results, err := repo.List(ctx, NoteListOptions{Archived: &archived}) - if err != nil { - t.Fatalf("Failed to list archived notes: %v", err) - } - - if len(results) != 1 { - t.Errorf("Expected 1 archived note, got %d", len(results)) - } - if !results[0].Archived { - t.Error("Retrieved note should be archived") - } - }) - - t.Run("List Active Notes Only", func(t *testing.T) { - archived := false - results, err := repo.List(ctx, NoteListOptions{Archived: &archived}) - if err != nil { - t.Fatalf("Failed to list active notes: %v", err) - } - - if len(results) != 2 { - t.Errorf("Expected 2 active notes, got %d", len(results)) - } - for _, note := range results { - if note.Archived { - t.Error("Retrieved note should not be archived") +func TestNoteRepository(t *testing.T) { + t.Run("CRUD", func(t *testing.T) { + db := CreateTestDB(t) + repo := NewNoteRepository(db) + ctx := context.Background() + + t.Run("Create Note", func(t *testing.T) { + note := CreateSampleNote() + + id, err := repo.Create(ctx, note) + AssertNoError(t, err, "Failed to create note") + AssertNotEqual(t, int64(0), id, "Expected non-zero ID") + AssertEqual(t, id, note.ID, "Expected note ID to be set correctly") + AssertFalse(t, note.Created.IsZero(), "Expected Created timestamp to be set") + AssertFalse(t, note.Modified.IsZero(), "Expected Modified timestamp to be set") + }) + + t.Run("Get Note", func(t *testing.T) { + original := CreateSampleNote() + id, err := repo.Create(ctx, original) + AssertNoError(t, err, "Failed to create note") + + retrieved, err := repo.Get(ctx, id) + AssertNoError(t, err, "Failed to get note") + + AssertEqual(t, original.ID, retrieved.ID, "ID mismatch") + AssertEqual(t, original.Title, retrieved.Title, "Title mismatch") + AssertEqual(t, original.Content, retrieved.Content, "Content mismatch") + AssertEqual(t, len(original.Tags), len(retrieved.Tags), "Tags length mismatch") + AssertEqual(t, original.Archived, retrieved.Archived, "Archived mismatch") + AssertEqual(t, original.FilePath, retrieved.FilePath, "FilePath mismatch") + }) + + t.Run("Update Note", func(t *testing.T) { + note := CreateSampleNote() + id, err := repo.Create(ctx, note) + AssertNoError(t, err, "Failed to create note") + + originalModified := note.Modified + + note.Title = "Updated Title" + note.Content = "Updated content" + note.Tags = []string{"updated", "test"} + note.Archived = true + note.FilePath = "/new/path/note.md" + + err = repo.Update(ctx, note) + AssertNoError(t, err, "Failed to update note") + + retrieved, err := repo.Get(ctx, id) + AssertNoError(t, err, "Failed to get updated note") + + AssertEqual(t, "Updated Title", retrieved.Title, "Expected updated title") + AssertEqual(t, "Updated content", retrieved.Content, "Expected updated content") + AssertEqual(t, 2, len(retrieved.Tags), "Expected 2 tags") + if len(retrieved.Tags) >= 2 { + AssertEqual(t, "updated", retrieved.Tags[0], "Expected first tag to be 'updated'") + AssertEqual(t, "test", retrieved.Tags[1], "Expected second tag to be 'test'") } - } + AssertTrue(t, retrieved.Archived, "Expected note to be archived") + AssertEqual(t, "/new/path/note.md", retrieved.FilePath, "Expected updated file path") + AssertTrue(t, retrieved.Modified.After(originalModified), "Expected Modified timestamp to be updated") + }) + + t.Run("Delete Note", func(t *testing.T) { + note := CreateSampleNote() + id, err := repo.Create(ctx, note) + AssertNoError(t, err, "Failed to create note") + + err = repo.Delete(ctx, id) + AssertNoError(t, err, "Failed to delete note") + + _, err = repo.Get(ctx, id) + AssertError(t, err, "Expected error when getting deleted note") + }) }) - t.Run("Search by Title", func(t *testing.T) { - results, err := repo.List(ctx, NoteListOptions{Title: "First"}) - if err != nil { - t.Fatalf("Failed to search by title: %v", err) - } + t.Run("List", func(t *testing.T) { + db := CreateTestDB(t) + repo := NewNoteRepository(db) + ctx := context.Background() - if len(results) != 1 { - t.Errorf("Expected 1 note, got %d", len(results)) + notes := []*models.Note{ + {Title: "First Note", Content: "Content 1", Tags: []string{"work"}, Archived: false}, + {Title: "Second Note", Content: "Content 2", Tags: []string{"personal"}, Archived: true}, + {Title: "Third Note", Content: "Important content", Tags: []string{"work", "important"}, Archived: false}, } - if results[0].Title != "First Note" { - t.Errorf("Expected 'First Note', got %s", results[0].Title) - } - }) - t.Run("Search by Content", func(t *testing.T) { - results, err := repo.List(ctx, NoteListOptions{Content: "Important"}) - if err != nil { - t.Fatalf("Failed to search by content: %v", err) + for _, note := range notes { + _, err := repo.Create(ctx, note) + AssertNoError(t, err, "Failed to create test note") } - if len(results) != 1 { - t.Errorf("Expected 1 note, got %d", len(results)) - } - if results[0].Title != "Third Note" { - t.Errorf("Expected 'Third Note', got %s", results[0].Title) - } - }) - - t.Run("Limit and Offset", func(t *testing.T) { - results, err := repo.List(ctx, NoteListOptions{Limit: 2}) - if err != nil { - t.Fatalf("Failed to list with limit: %v", err) - } + t.Run("List All Notes", func(t *testing.T) { + results, err := repo.List(ctx, NoteListOptions{}) + AssertNoError(t, err, "Failed to list notes") + AssertEqual(t, 3, len(results), "Expected 3 notes") + }) - if len(results) != 2 { - t.Errorf("Expected 2 notes, got %d", len(results)) - } + t.Run("List Archived Notes Only", func(t *testing.T) { + archived := true + results, err := repo.List(ctx, NoteListOptions{Archived: &archived}) + AssertNoError(t, err, "Failed to list archived notes") + AssertEqual(t, 1, len(results), "Expected 1 archived note") + if len(results) > 0 { + AssertTrue(t, results[0].Archived, "Retrieved note should be archived") + } + }) + + t.Run("List Active Notes Only", func(t *testing.T) { + archived := false + results, err := repo.List(ctx, NoteListOptions{Archived: &archived}) + AssertNoError(t, err, "Failed to list active notes") + AssertEqual(t, 2, len(results), "Expected 2 active notes") + for _, note := range results { + AssertFalse(t, note.Archived, "Retrieved note should not be archived") + } + }) + + t.Run("Search by Title", func(t *testing.T) { + results, err := repo.List(ctx, NoteListOptions{Title: "First"}) + AssertNoError(t, err, "Failed to search by title") + AssertEqual(t, 1, len(results), "Expected 1 note") + if len(results) > 0 { + AssertEqual(t, "First Note", results[0].Title, "Expected 'First Note'") + } + }) + + t.Run("Search by Content", func(t *testing.T) { + results, err := repo.List(ctx, NoteListOptions{Content: "Important"}) + AssertNoError(t, err, "Failed to search by content") + AssertEqual(t, 1, len(results), "Expected 1 note") + if len(results) > 0 { + AssertEqual(t, "Third Note", results[0].Title, "Expected 'Third Note'") + } + }) - results, err = repo.List(ctx, NoteListOptions{Limit: 2, Offset: 1}) - if err != nil { - t.Fatalf("Failed to list with limit and offset: %v", err) - } + t.Run("Limit and Offset", func(t *testing.T) { + results, err := repo.List(ctx, NoteListOptions{Limit: 2}) + AssertNoError(t, err, "Failed to list with limit") + AssertEqual(t, 2, len(results), "Expected 2 notes") - if len(results) != 2 { - t.Errorf("Expected 2 notes with offset, got %d", len(results)) - } + results, err = repo.List(ctx, NoteListOptions{Limit: 2, Offset: 1}) + AssertNoError(t, err, "Failed to list with limit and offset") + AssertEqual(t, 2, len(results), "Expected 2 notes with offset") + }) }) -} - -func TestNoteRepository_SpecializedMethods(t *testing.T) { - db := createNoteTestDB(t) - repo := NewNoteRepository(db) - ctx := context.Background() - notes := []*models.Note{ - {Title: "Work Note", Content: "Work content", Tags: []string{"work"}, Archived: false}, - {Title: "Personal Note", Content: "Personal content", Tags: []string{"personal"}, Archived: true}, - {Title: "Important Note", Content: "Important content", Tags: []string{"work", "important"}, Archived: false}, - } + t.Run("Specialized Methods", func(t *testing.T) { + db := CreateTestDB(t) + repo := NewNoteRepository(db) + ctx := context.Background() - for _, note := range notes { - _, err := repo.Create(ctx, note) - if err != nil { - t.Fatalf("Failed to create test note: %v", err) + notes := []*models.Note{ + {Title: "Work Note", Content: "Work content", Tags: []string{"work"}, Archived: false}, + {Title: "Personal Note", Content: "Personal content", Tags: []string{"personal"}, Archived: true}, + {Title: "Important Note", Content: "Important content", Tags: []string{"work", "important"}, Archived: false}, } - } - t.Run("GetByTitle", func(t *testing.T) { - results, err := repo.GetByTitle(ctx, "Work") - if err != nil { - t.Fatalf("Failed to get by title: %v", err) + for _, note := range notes { + _, err := repo.Create(ctx, note) + AssertNoError(t, err, "Failed to create test note") } - if len(results) != 1 { - t.Errorf("Expected 1 note, got %d", len(results)) - } - if results[0].Title != "Work Note" { - t.Errorf("Expected 'Work Note', got %s", results[0].Title) - } - }) + t.Run("GetByTitle", func(t *testing.T) { + results, err := repo.GetByTitle(ctx, "Work") + AssertNoError(t, err, "Failed to get by title") + AssertEqual(t, 1, len(results), "Expected 1 note") + if len(results) > 0 { + AssertEqual(t, "Work Note", results[0].Title, "Expected 'Work Note'") + } + }) + + t.Run("GetArchived", func(t *testing.T) { + results, err := repo.GetArchived(ctx) + AssertNoError(t, err, "Failed to get archived notes") + AssertEqual(t, 1, len(results), "Expected 1 archived note") + if len(results) > 0 { + AssertTrue(t, results[0].Archived, "Retrieved note should be archived") + } + }) + + t.Run("GetActive", func(t *testing.T) { + results, err := repo.GetActive(ctx) + AssertNoError(t, err, "Failed to get active notes") + AssertEqual(t, 2, len(results), "Expected 2 active notes") + for _, note := range results { + AssertFalse(t, note.Archived, "Retrieved note should not be archived") + } + }) - t.Run("GetArchived", func(t *testing.T) { - results, err := repo.GetArchived(ctx) - if err != nil { - t.Fatalf("Failed to get archived notes: %v", err) - } + t.Run("Archive and Unarchive", func(t *testing.T) { + note := &models.Note{ + Title: "Test Archive", + Content: "Archive test", + Archived: false, + } + id, err := repo.Create(ctx, note) + AssertNoError(t, err, "Failed to create note") + + err = repo.Archive(ctx, id) + AssertNoError(t, err, "Failed to archive note") + + retrieved, err := repo.Get(ctx, id) + AssertNoError(t, err, "Failed to get note") + AssertTrue(t, retrieved.Archived, "Note should be archived") + + err = repo.Unarchive(ctx, id) + AssertNoError(t, err, "Failed to unarchive note") + + retrieved, err = repo.Get(ctx, id) + AssertNoError(t, err, "Failed to get note") + AssertFalse(t, retrieved.Archived, "Note should not be archived") + }) + + t.Run("SearchContent", func(t *testing.T) { + results, err := repo.SearchContent(ctx, "Important") + AssertNoError(t, err, "Failed to search content") + AssertEqual(t, 1, len(results), "Expected 1 note") + if len(results) > 0 { + AssertEqual(t, "Important Note", results[0].Title, "Expected 'Important Note'") + } + }) - if len(results) != 1 { - t.Errorf("Expected 1 archived note, got %d", len(results)) - } - if !results[0].Archived { - t.Error("Retrieved note should be archived") - } + t.Run("GetRecent", func(t *testing.T) { + results, err := repo.GetRecent(ctx, 2) + AssertNoError(t, err, "Failed to get recent notes") + AssertEqual(t, 2, len(results), "Expected 2 notes") + }) }) - t.Run("GetActive", func(t *testing.T) { - results, err := repo.GetActive(ctx) - if err != nil { - t.Fatalf("Failed to get active notes: %v", err) - } + t.Run("Tag Methods", func(t *testing.T) { + db := CreateTestDB(t) + repo := NewNoteRepository(db) + ctx := context.Background() - if len(results) != 2 { - t.Errorf("Expected 2 active notes, got %d", len(results)) - } - for _, note := range results { - if note.Archived { - t.Error("Retrieved note should not be archived") - } - } - }) - - t.Run("Archive and Unarchive", func(t *testing.T) { note := &models.Note{ - Title: "Test Archive", - Content: "Archive test", - Archived: false, + Title: "Tag Test Note", + Content: "Testing tags", + Tags: []string{"initial"}, } id, err := repo.Create(ctx, note) if err != nil { t.Fatalf("Failed to create note: %v", err) } - err = repo.Archive(ctx, id) - if err != nil { - t.Fatalf("Failed to archive note: %v", err) - } - - retrieved, err := repo.Get(ctx, id) - if err != nil { - t.Fatalf("Failed to get note: %v", err) - } - if !retrieved.Archived { - t.Error("Note should be archived") - } - - err = repo.Unarchive(ctx, id) - if err != nil { - t.Fatalf("Failed to unarchive note: %v", err) - } - - retrieved, err = repo.Get(ctx, id) - if err != nil { - t.Fatalf("Failed to get note: %v", err) - } - if retrieved.Archived { - t.Error("Note should not be archived") - } - }) - - t.Run("SearchContent", func(t *testing.T) { - results, err := repo.SearchContent(ctx, "Important") - if err != nil { - t.Fatalf("Failed to search content: %v", err) - } - - if len(results) != 1 { - t.Errorf("Expected 1 note, got %d", len(results)) - } - if results[0].Title != "Important Note" { - t.Errorf("Expected 'Important Note', got %s", results[0].Title) - } - }) - - t.Run("GetRecent", func(t *testing.T) { - results, err := repo.GetRecent(ctx, 2) - if err != nil { - t.Fatalf("Failed to get recent notes: %v", err) - } - - if len(results) != 2 { - t.Errorf("Expected 2 notes, got %d", len(results)) - } - }) -} - -func TestNoteRepository_TagMethods(t *testing.T) { - db := createNoteTestDB(t) - repo := NewNoteRepository(db) - ctx := context.Background() - - note := &models.Note{ - Title: "Tag Test Note", - Content: "Testing tags", - Tags: []string{"initial"}, - } - id, err := repo.Create(ctx, note) - if err != nil { - t.Fatalf("Failed to create note: %v", err) - } - - t.Run("AddTag", func(t *testing.T) { - err := repo.AddTag(ctx, id, "new-tag") - if err != nil { - t.Fatalf("Failed to add tag: %v", err) - } + t.Run("AddTag", func(t *testing.T) { + err := repo.AddTag(ctx, id, "new-tag") + if err != nil { + t.Fatalf("Failed to add tag: %v", err) + } - retrieved, err := repo.Get(ctx, id) - if err != nil { - t.Fatalf("Failed to get note: %v", err) - } + retrieved, err := repo.Get(ctx, id) + if err != nil { + t.Fatalf("Failed to get note: %v", err) + } - if len(retrieved.Tags) != 2 { - t.Errorf("Expected 2 tags, got %d", len(retrieved.Tags)) - } + if len(retrieved.Tags) != 2 { + t.Errorf("Expected 2 tags, got %d", len(retrieved.Tags)) + } - found := false - for _, tag := range retrieved.Tags { - if tag == "new-tag" { - found = true - break + found := false + for _, tag := range retrieved.Tags { + if tag == "new-tag" { + found = true + break + } } - } - if !found { - t.Error("New tag not found in note") - } - }) + if !found { + t.Error("New tag not found in note") + } + }) - t.Run("AddTag Duplicate", func(t *testing.T) { - err := repo.AddTag(ctx, id, "new-tag") - if err != nil { - t.Fatalf("Failed to add duplicate tag: %v", err) - } + t.Run("AddTag Duplicate", func(t *testing.T) { + err := repo.AddTag(ctx, id, "new-tag") + AssertNoError(t, err, "Failed to add duplicate tag") - retrieved, err := repo.Get(ctx, id) - if err != nil { - t.Fatalf("Failed to get note: %v", err) - } + retrieved, err := repo.Get(ctx, id) + AssertNoError(t, err, "Failed to get note") - if len(retrieved.Tags) != 2 { - t.Errorf("Expected 2 tags (no duplicate), got %d", len(retrieved.Tags)) - } - }) + AssertEqual(t, 2, len(retrieved.Tags), "Expected 2 tags (no duplicate)") + }) - t.Run("RemoveTag", func(t *testing.T) { - err := repo.RemoveTag(ctx, id, "initial") - if err != nil { - t.Fatalf("Failed to remove tag: %v", err) - } + t.Run("RemoveTag", func(t *testing.T) { + err := repo.RemoveTag(ctx, id, "initial") + AssertNoError(t, err, "Failed to remove tag") - retrieved, err := repo.Get(ctx, id) - if err != nil { - t.Fatalf("Failed to get note: %v", err) - } + retrieved, err := repo.Get(ctx, id) + AssertNoError(t, err, "Failed to get note") - if len(retrieved.Tags) != 1 { - t.Errorf("Expected 1 tag after removal, got %d", len(retrieved.Tags)) - } + AssertEqual(t, 1, len(retrieved.Tags), "Expected 1 tag after removal") - for _, tag := range retrieved.Tags { - if tag == "initial" { - t.Error("Removed tag still found in note") + for _, tag := range retrieved.Tags { + AssertNotEqual(t, "initial", tag, "Removed tag still found in note") } - } - }) + }) - t.Run("GetByTags", func(t *testing.T) { - note1 := &models.Note{ - Title: "Note 1", - Content: "Content 1", - Tags: []string{"work", "urgent"}, - } - note2 := &models.Note{ - Title: "Note 2", - Content: "Content 2", - Tags: []string{"personal", "ideas"}, - } - note3 := &models.Note{ - Title: "Note 3", - Content: "Content 3", - Tags: []string{"work", "planning"}, - } + t.Run("GetByTags", func(t *testing.T) { + note1 := &models.Note{ + Title: "Note 1", + Content: "Content 1", + Tags: []string{"work", "urgent"}, + } + note2 := &models.Note{ + Title: "Note 2", + Content: "Content 2", + Tags: []string{"personal", "ideas"}, + } + note3 := &models.Note{ + Title: "Note 3", + Content: "Content 3", + Tags: []string{"work", "planning"}, + } - _, err := repo.Create(ctx, note1) - if err != nil { - t.Fatalf("Failed to create note1: %v", err) - } - _, err = repo.Create(ctx, note2) - if err != nil { - t.Fatalf("Failed to create note2: %v", err) - } - _, err = repo.Create(ctx, note3) - if err != nil { - t.Fatalf("Failed to create note3: %v", err) - } + _, err := repo.Create(ctx, note1) + if err != nil { + t.Fatalf("Failed to create note1: %v", err) + } + _, err = repo.Create(ctx, note2) + if err != nil { + t.Fatalf("Failed to create note2: %v", err) + } + _, err = repo.Create(ctx, note3) + if err != nil { + t.Fatalf("Failed to create note3: %v", err) + } - results, err := repo.GetByTags(ctx, []string{"work"}) - if err != nil { - t.Fatalf("Failed to get notes by tag: %v", err) - } + results, err := repo.GetByTags(ctx, []string{"work"}) + if err != nil { + t.Fatalf("Failed to get notes by tag: %v", err) + } - if len(results) < 2 { - t.Errorf("Expected at least 2 notes with 'work' tag, got %d", len(results)) - } + if len(results) < 2 { + t.Errorf("Expected at least 2 notes with 'work' tag, got %d", len(results)) + } - results, err = repo.GetByTags(ctx, []string{"nonexistent"}) - if err != nil { - t.Fatalf("Failed to get notes by nonexistent tag: %v", err) - } + results, err = repo.GetByTags(ctx, []string{"nonexistent"}) + if err != nil { + t.Fatalf("Failed to get notes by nonexistent tag: %v", err) + } - if len(results) != 0 { - t.Errorf("Expected 0 notes with nonexistent tag, got %d", len(results)) - } + if len(results) != 0 { + t.Errorf("Expected 0 notes with nonexistent tag, got %d", len(results)) + } - results, err = repo.GetByTags(ctx, []string{}) - if err != nil { - t.Fatalf("Failed to get notes with empty tags: %v", err) - } + results, err = repo.GetByTags(ctx, []string{}) + if err != nil { + t.Fatalf("Failed to get notes with empty tags: %v", err) + } - if len(results) != 0 { - t.Errorf("Expected 0 notes with empty tag list, got %d", len(results)) - } + if len(results) != 0 { + t.Errorf("Expected 0 notes with empty tag list, got %d", len(results)) + } + }) }) -} -func TestNoteRepository_ErrorCases(t *testing.T) { - db := createNoteTestDB(t) - repo := NewNoteRepository(db) - ctx := context.Background() + t.Run("Error Cases", func(t *testing.T) { + db := CreateTestDB(t) + repo := NewNoteRepository(db) + ctx := context.Background() - t.Run("Get Nonexistent Note", func(t *testing.T) { - _, err := repo.Get(ctx, 999) - if err == nil { - t.Error("Expected error when getting nonexistent note") - } - }) + t.Run("Get Nonexistent Note", func(t *testing.T) { + _, err := repo.Get(ctx, 999) + if err == nil { + t.Error("Expected error when getting nonexistent note") + } + }) - t.Run("Update Nonexistent Note", func(t *testing.T) { - note := &models.Note{ - ID: 999, - Title: "Nonexistent", - Content: "Should fail", - } + t.Run("Update Nonexistent Note", func(t *testing.T) { + note := &models.Note{ + ID: 999, + Title: "Nonexistent", + Content: "Should fail", + } - err := repo.Update(ctx, note) - if err == nil { - t.Error("Expected error when updating nonexistent note") - } - }) + err := repo.Update(ctx, note) + if err == nil { + t.Error("Expected error when updating nonexistent note") + } + }) - t.Run("Delete Nonexistent Note", func(t *testing.T) { - err := repo.Delete(ctx, 999) - if err == nil { - t.Error("Expected error when deleting nonexistent note") - } - }) + t.Run("Delete Nonexistent Note", func(t *testing.T) { + err := repo.Delete(ctx, 999) + if err == nil { + t.Error("Expected error when deleting nonexistent note") + } + }) - t.Run("Archive Nonexistent Note", func(t *testing.T) { - err := repo.Archive(ctx, 999) - if err == nil { - t.Error("Expected error when archiving nonexistent note") - } - }) + t.Run("Archive Nonexistent Note", func(t *testing.T) { + err := repo.Archive(ctx, 999) + if err == nil { + t.Error("Expected error when archiving nonexistent note") + } + }) - t.Run("AddTag to Nonexistent Note", func(t *testing.T) { - err := repo.AddTag(ctx, 999, "tag") - if err == nil { - t.Error("Expected error when adding tag to nonexistent note") - } + t.Run("AddTag to Nonexistent Note", func(t *testing.T) { + err := repo.AddTag(ctx, 999, "tag") + if err == nil { + t.Error("Expected error when adding tag to nonexistent note") + } + }) }) -} -func TestNoteRepository_EdgeCases(t *testing.T) { - db := createNoteTestDB(t) - repo := NewNoteRepository(db) - ctx := context.Background() + t.Run("Edge Cases", func(t *testing.T) { + db := CreateTestDB(t) + repo := NewNoteRepository(db) + ctx := context.Background() - t.Run("Note with Empty Tags", func(t *testing.T) { - note := &models.Note{ - Title: "No Tags Note", - Content: "This note has no tags", - Tags: []string{}, - } + t.Run("Note with Empty Tags", func(t *testing.T) { + note := &models.Note{ + Title: "No Tags Note", + Content: "This note has no tags", + Tags: []string{}, + } - id, err := repo.Create(ctx, note) - if err != nil { - t.Fatalf("Failed to create note with empty tags: %v", err) - } + id, err := repo.Create(ctx, note) + if err != nil { + t.Fatalf("Failed to create note with empty tags: %v", err) + } - retrieved, err := repo.Get(ctx, id) - if err != nil { - t.Fatalf("Failed to get note: %v", err) - } + retrieved, err := repo.Get(ctx, id) + if err != nil { + t.Fatalf("Failed to get note: %v", err) + } - if len(retrieved.Tags) != 0 { - t.Errorf("Expected empty tags slice, got %d tags", len(retrieved.Tags)) - } - }) + if len(retrieved.Tags) != 0 { + t.Errorf("Expected empty tags slice, got %d tags", len(retrieved.Tags)) + } + }) - t.Run("Note with Nil Tags", func(t *testing.T) { - note := &models.Note{ - Title: "Nil Tags Note", - Content: "This note has nil tags", - Tags: nil, - } + t.Run("Note with Nil Tags", func(t *testing.T) { + note := &models.Note{ + Title: "Nil Tags Note", + Content: "This note has nil tags", + Tags: nil, + } - id, err := repo.Create(ctx, note) - if err != nil { - t.Fatalf("Failed to create note with nil tags: %v", err) - } + id, err := repo.Create(ctx, note) + if err != nil { + t.Fatalf("Failed to create note with nil tags: %v", err) + } - retrieved, err := repo.Get(ctx, id) - if err != nil { - t.Fatalf("Failed to get note: %v", err) - } + retrieved, err := repo.Get(ctx, id) + if err != nil { + t.Fatalf("Failed to get note: %v", err) + } - if retrieved.Tags != nil { - t.Errorf("Expected nil tags, got %v", retrieved.Tags) - } - }) + if retrieved.Tags != nil { + t.Errorf("Expected nil tags, got %v", retrieved.Tags) + } + }) - t.Run("Note with Long Content", func(t *testing.T) { - longContent := "" - for i := 0; i < 1000; i++ { - longContent += "This is a very long content string. " - } + t.Run("Note with Long Content", func(t *testing.T) { + longContent := "" + for i := 0; i < 1000; i++ { + longContent += "This is a very long content string. " + } - note := &models.Note{ - Title: "Long Content Note", - Content: longContent, - } + note := &models.Note{ + Title: "Long Content Note", + Content: longContent, + } - id, err := repo.Create(ctx, note) - if err != nil { - t.Fatalf("Failed to create note with long content: %v", err) - } + id, err := repo.Create(ctx, note) + if err != nil { + t.Fatalf("Failed to create note with long content: %v", err) + } - retrieved, err := repo.Get(ctx, id) - if err != nil { - t.Fatalf("Failed to get note: %v", err) - } + retrieved, err := repo.Get(ctx, id) + if err != nil { + t.Fatalf("Failed to get note: %v", err) + } - if retrieved.Content != longContent { - t.Error("Long content was not stored/retrieved correctly") - } + if retrieved.Content != longContent { + t.Error("Long content was not stored/retrieved correctly") + } + }) }) -} \ No newline at end of file +} diff --git a/internal/repo/task_repository_test.go b/internal/repo/task_repository_test.go index ae6da1b..77f2cd1 100644 --- a/internal/repo/task_repository_test.go +++ b/internal/repo/task_repository_test.go @@ -2,7 +2,6 @@ package repo import ( "context" - "database/sql" "slices" "testing" "time" @@ -12,70 +11,17 @@ import ( "github.com/stormlightlabs/noteleaf/internal/models" ) -func createTaskTestDB(t *testing.T) *sql.DB { - db, err := sql.Open("sqlite3", ":memory:") - if err != nil { - t.Fatalf("Failed to create in-memory database: %v", err) - } - - if _, err := db.Exec("PRAGMA foreign_keys = ON"); err != nil { - t.Fatalf("Failed to enable foreign keys: %v", err) - } - - schema := ` - CREATE TABLE IF NOT EXISTS tasks ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - uuid TEXT UNIQUE NOT NULL, - description TEXT NOT NULL, - status TEXT DEFAULT 'pending', - priority TEXT, - project TEXT, - context TEXT, - tags TEXT, - due DATETIME, - entry DATETIME DEFAULT CURRENT_TIMESTAMP, - modified DATETIME DEFAULT CURRENT_TIMESTAMP, - end DATETIME, - start DATETIME, - annotations TEXT - ); - ` - - if _, err := db.Exec(schema); err != nil { - t.Fatalf("Failed to create schema: %v", err) - } - - t.Cleanup(func() { - db.Close() - }) - - return db -} - -func createSampleTask() *models.Task { - return &models.Task{ - UUID: newUUID(), - Description: "Test task", - Status: "pending", - Priority: "H", - Project: "test-project", - Context: "test-context", - Tags: []string{"test", "important"}, - Annotations: []string{"This is a test", "Another annotation"}, - } -} - func newUUID() string { return uuid.New().String() } func TestTaskRepository(t *testing.T) { - db := createTaskTestDB(t) + db := CreateTestDB(t) repo := NewTaskRepository(db) ctx := context.Background() t.Run("Create Task", func(t *testing.T) { - task := createSampleTask() + task := CreateSampleTask() id, err := repo.Create(ctx, task) if err != nil { @@ -99,7 +45,7 @@ func TestTaskRepository(t *testing.T) { }) t.Run("Get Task", func(t *testing.T) { - original := createSampleTask() + original := CreateSampleTask() id, err := repo.Create(ctx, original) if err != nil { t.Fatalf("Failed to create task: %v", err) @@ -144,7 +90,7 @@ func TestTaskRepository(t *testing.T) { }) t.Run("Update Task", func(t *testing.T) { - task := createSampleTask() + task := CreateSampleTask() id, err := repo.Create(ctx, task) if err != nil { t.Fatalf("Failed to create task: %v", err) @@ -181,7 +127,7 @@ func TestTaskRepository(t *testing.T) { }) t.Run("Delete Task", func(t *testing.T) { - task := createSampleTask() + task := CreateSampleTask() id, err := repo.Create(ctx, task) if err != nil { t.Fatalf("Failed to create task: %v", err) @@ -681,7 +627,6 @@ func TestTaskRepository(t *testing.T) { }) t.Run("GetByPriority", func(t *testing.T) { - // Test numeric priority results, err := repo.GetByPriority(ctx, "5") if err != nil { t.Errorf("Failed to get tasks by priority 5: %v", err) @@ -713,7 +658,6 @@ func TestTaskRepository(t *testing.T) { } } - // Test empty priority - create a specific task with no priority for this test noPriorityTask := &models.Task{ UUID: newUUID(), Description: "No priority task for test", @@ -828,26 +772,26 @@ func TestTaskRepository(t *testing.T) { } func TestTaskRepository_GetContexts(t *testing.T) { - db := createTaskTestDB(t) + db := CreateTestDB(t) repo := NewTaskRepository(db) ctx := context.Background() // Create tasks with different contexts - task1 := createSampleTask() + task1 := CreateSampleTask() task1.Context = "work" _, err := repo.Create(ctx, task1) if err != nil { t.Fatalf("Failed to create task1: %v", err) } - task2 := createSampleTask() + task2 := CreateSampleTask() task2.Context = "home" _, err = repo.Create(ctx, task2) if err != nil { t.Fatalf("Failed to create task2: %v", err) } - task3 := createSampleTask() + task3 := CreateSampleTask() task3.Context = "work" _, err = repo.Create(ctx, task3) if err != nil { @@ -855,7 +799,7 @@ func TestTaskRepository_GetContexts(t *testing.T) { } // Task with empty context should not be included - task4 := createSampleTask() + task4 := CreateSampleTask() task4.Context = "" _, err = repo.Create(ctx, task4) if err != nil { @@ -888,12 +832,12 @@ func TestTaskRepository_GetContexts(t *testing.T) { } func TestTaskRepository_GetByContext(t *testing.T) { - db := createTaskTestDB(t) + db := CreateTestDB(t) repo := NewTaskRepository(db) ctx := context.Background() // Create tasks with different contexts - task1 := createSampleTask() + task1 := CreateSampleTask() task1.Context = "work" task1.Description = "Work task 1" _, err := repo.Create(ctx, task1) @@ -901,7 +845,7 @@ func TestTaskRepository_GetByContext(t *testing.T) { t.Fatalf("Failed to create task1: %v", err) } - task2 := createSampleTask() + task2 := CreateSampleTask() task2.Context = "home" task2.Description = "Home task 1" _, err = repo.Create(ctx, task2) @@ -909,7 +853,7 @@ func TestTaskRepository_GetByContext(t *testing.T) { t.Fatalf("Failed to create task2: %v", err) } - task3 := createSampleTask() + task3 := CreateSampleTask() task3.Context = "work" task3.Description = "Work task 2" _, err = repo.Create(ctx, task3) @@ -933,7 +877,7 @@ func TestTaskRepository_GetByContext(t *testing.T) { } } - // Get tasks by home context + // Get tasks by home context homeTasks, err := repo.GetByContext(ctx, "home") if err != nil { t.Fatalf("Failed to get tasks by context: %v", err) diff --git a/internal/repo/test_utilities.go b/internal/repo/test_utilities.go new file mode 100644 index 0000000..5f85ab0 --- /dev/null +++ b/internal/repo/test_utilities.go @@ -0,0 +1,326 @@ +package repo + +import ( + "context" + "database/sql" + "testing" + "time" + + "github.com/google/uuid" + _ "github.com/mattn/go-sqlite3" + "github.com/stormlightlabs/noteleaf/internal/models" +) + +// CreateTestDB creates an in-memory SQLite database with the full schema for testing +func CreateTestDB(t *testing.T) *sql.DB { + db, err := sql.Open("sqlite3", ":memory:") + if err != nil { + t.Fatalf("Failed to create in-memory database: %v", err) + } + + if _, err := db.Exec("PRAGMA foreign_keys = ON"); err != nil { + t.Fatalf("Failed to enable foreign keys: %v", err) + } + + // Full schema for all tables + schema := ` + CREATE TABLE IF NOT EXISTS tasks ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + uuid TEXT UNIQUE NOT NULL, + description TEXT NOT NULL, + status TEXT DEFAULT 'pending', + priority TEXT, + project TEXT, + context TEXT, + tags TEXT, + due DATETIME, + entry DATETIME DEFAULT CURRENT_TIMESTAMP, + modified DATETIME DEFAULT CURRENT_TIMESTAMP, + end DATETIME, + start DATETIME, + annotations TEXT + ); + + CREATE TABLE IF NOT EXISTS books ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + title TEXT NOT NULL, + author TEXT, + status TEXT DEFAULT 'queued', + progress INTEGER DEFAULT 0, + pages INTEGER, + rating REAL, + notes TEXT, + added DATETIME DEFAULT CURRENT_TIMESTAMP, + started DATETIME, + finished DATETIME + ); + + CREATE TABLE IF NOT EXISTS movies ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + title TEXT NOT NULL, + year INTEGER, + status TEXT DEFAULT 'queued', + rating REAL, + notes TEXT, + added DATETIME DEFAULT CURRENT_TIMESTAMP, + watched DATETIME + ); + + CREATE TABLE IF NOT EXISTS tv_shows ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + title TEXT NOT NULL, + season INTEGER, + episode INTEGER, + status TEXT DEFAULT 'queued', + rating REAL, + notes TEXT, + added DATETIME DEFAULT CURRENT_TIMESTAMP, + last_watched DATETIME + ); + + CREATE TABLE IF NOT EXISTS notes ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + title TEXT NOT NULL, + content TEXT, + tags TEXT, + archived BOOLEAN DEFAULT FALSE, + created DATETIME DEFAULT CURRENT_TIMESTAMP, + modified DATETIME DEFAULT CURRENT_TIMESTAMP, + file_path TEXT + ); + + CREATE TABLE IF NOT EXISTS time_entries ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + task_id INTEGER NOT NULL, + start_time DATETIME NOT NULL, + end_time DATETIME, + duration_seconds INTEGER, + created DATETIME DEFAULT CURRENT_TIMESTAMP, + modified DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE CASCADE + ); + ` + + if _, err := db.Exec(schema); err != nil { + t.Fatalf("Failed to create schema: %v", err) + } + + t.Cleanup(func() { + db.Close() + }) + + return db +} + +// Sample data creators +func CreateSampleTask() *models.Task { + return &models.Task{ + UUID: uuid.New().String(), + Description: "Test Task", + Status: "pending", + Priority: "medium", + Project: "test-project", + Context: "test-context", + Tags: []string{"test", "sample"}, + Entry: time.Now(), + Modified: time.Now(), + } +} + +func CreateSampleBook() *models.Book { + return &models.Book{ + Title: "Test Book", + Author: "Test Author", + Status: "queued", + Progress: 0, + Pages: 300, + Rating: 4.5, + Notes: "Great book!", + Added: time.Now(), + } +} + +func CreateSampleMovie() *models.Movie { + return &models.Movie{ + Title: "Test Movie", + Year: 2023, + Status: "queued", + Rating: 8.5, + Notes: "Excellent film", + Added: time.Now(), + } +} + +func CreateSampleTVShow() *models.TVShow { + return &models.TVShow{ + Title: "Test TV Show", + Season: 1, + Episode: 1, + Status: "queued", + Rating: 9.0, + Notes: "Amazing series", + Added: time.Now(), + } +} + +func CreateSampleNote() *models.Note { + return &models.Note{ + Title: "Test Note", + Content: "This is a test note content", + Tags: []string{"test", "sample"}, + Archived: false, + Created: time.Now(), + Modified: time.Now(), + } +} + +func CreateSampleTimeEntry(taskID int64) *models.TimeEntry { + startTime := time.Now().Add(-time.Hour) + return &models.TimeEntry{ + TaskID: taskID, + StartTime: startTime, + EndTime: nil, + DurationSeconds: 0, + Created: startTime, + Modified: startTime, + } +} + +// Test helpers for common operations +func AssertNoError(t *testing.T, err error, msg string) { + t.Helper() + if err != nil { + t.Fatalf("%s: %v", msg, err) + } +} + +func AssertError(t *testing.T, err error, msg string) { + t.Helper() + if err == nil { + t.Fatalf("%s: expected error but got none", msg) + } +} + +func AssertEqual[T comparable](t *testing.T, expected, actual T, msg string) { + t.Helper() + if expected != actual { + t.Fatalf("%s: expected %v, got %v", msg, expected, actual) + } +} + +func AssertNotEqual[T comparable](t *testing.T, notExpected, actual T, msg string) { + t.Helper() + if notExpected == actual { + t.Fatalf("%s: expected value to not equal %v", msg, notExpected) + } +} + +func AssertTrue(t *testing.T, condition bool, msg string) { + t.Helper() + if !condition { + t.Fatalf("%s: expected true", msg) + } +} + +func AssertFalse(t *testing.T, condition bool, msg string) { + t.Helper() + if condition { + t.Fatalf("%s: expected false", msg) + } +} + +// SetupTestData creates sample data in the database and returns the repositories +func SetupTestData(t *testing.T, db *sql.DB) *Repositories { + ctx := context.Background() + repos := NewRepositories(db) + + // Create sample tasks + task1 := CreateSampleTask() + task1.Description = "Sample Task 1" + task1.Status = "pending" + task1.Priority = "high" + + task2 := CreateSampleTask() + task2.Description = "Sample Task 2" + task2.Status = "completed" + task2.Priority = "low" + + id1, err := repos.Tasks.Create(ctx, task1) + AssertNoError(t, err, "Failed to create sample task 1") + task1.ID = id1 + + id2, err := repos.Tasks.Create(ctx, task2) + AssertNoError(t, err, "Failed to create sample task 2") + task2.ID = id2 + + // Create sample books + book1 := CreateSampleBook() + book1.Title = "Sample Book 1" + book1.Status = "reading" + + book2 := CreateSampleBook() + book2.Title = "Sample Book 2" + book2.Status = "finished" + + bookID1, err := repos.Books.Create(ctx, book1) + AssertNoError(t, err, "Failed to create sample book 1") + book1.ID = bookID1 + + bookID2, err := repos.Books.Create(ctx, book2) + AssertNoError(t, err, "Failed to create sample book 2") + book2.ID = bookID2 + + // Create sample movies + movie1 := CreateSampleMovie() + movie1.Title = "Sample Movie 1" + movie1.Status = "queued" + + movie2 := CreateSampleMovie() + movie2.Title = "Sample Movie 2" + movie2.Status = "watched" + + movieID1, err := repos.Movies.Create(ctx, movie1) + AssertNoError(t, err, "Failed to create sample movie 1") + movie1.ID = movieID1 + + movieID2, err := repos.Movies.Create(ctx, movie2) + AssertNoError(t, err, "Failed to create sample movie 2") + movie2.ID = movieID2 + + // Create sample TV shows + tv1 := CreateSampleTVShow() + tv1.Title = "Sample TV Show 1" + tv1.Status = "queued" + + tv2 := CreateSampleTVShow() + tv2.Title = "Sample TV Show 2" + tv2.Status = "watching" + + tvID1, err := repos.TV.Create(ctx, tv1) + AssertNoError(t, err, "Failed to create sample TV show 1") + tv1.ID = tvID1 + + tvID2, err := repos.TV.Create(ctx, tv2) + AssertNoError(t, err, "Failed to create sample TV show 2") + tv2.ID = tvID2 + + // Create sample notes + note1 := CreateSampleNote() + note1.Title = "Sample Note 1" + note1.Content = "Content for note 1" + + note2 := CreateSampleNote() + note2.Title = "Sample Note 2" + note2.Content = "Content for note 2" + note2.Archived = true + + noteID1, err := repos.Notes.Create(ctx, note1) + AssertNoError(t, err, "Failed to create sample note 1") + note1.ID = noteID1 + + noteID2, err := repos.Notes.Create(ctx, note2) + AssertNoError(t, err, "Failed to create sample note 2") + note2.ID = noteID2 + + return repos +} \ No newline at end of file diff --git a/internal/repo/tv_repository_test.go b/internal/repo/tv_repository_test.go index b0433fb..9a3dea5 100644 --- a/internal/repo/tv_repository_test.go +++ b/internal/repo/tv_repository_test.go @@ -2,7 +2,6 @@ package repo import ( "context" - "database/sql" "testing" "time" @@ -10,60 +9,14 @@ import ( "github.com/stormlightlabs/noteleaf/internal/models" ) -func createTVTestDB(t *testing.T) *sql.DB { - db, err := sql.Open("sqlite3", ":memory:") - if err != nil { - t.Fatalf("Failed to create in-memory database: %v", err) - } - - if _, err := db.Exec("PRAGMA foreign_keys = ON"); err != nil { - t.Fatalf("Failed to enable foreign keys: %v", err) - } - - schema := ` - CREATE TABLE IF NOT EXISTS tv_shows ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - title TEXT NOT NULL, - season INTEGER, - episode INTEGER, - status TEXT DEFAULT 'queued', - rating REAL, - notes TEXT, - added DATETIME DEFAULT CURRENT_TIMESTAMP, - last_watched DATETIME - ); - ` - - if _, err := db.Exec(schema); err != nil { - t.Fatalf("Failed to create schema: %v", err) - } - - t.Cleanup(func() { - db.Close() - }) - - return db -} - -func createSampleTVShow() *models.TVShow { - return &models.TVShow{ - Title: "Test Show", - Season: 1, - Episode: 1, - Status: "queued", - Rating: 9.0, - Notes: "Excellent series", - } -} - func TestTVRepository(t *testing.T) { t.Run("CRUD Operations", func(t *testing.T) { - db := createTVTestDB(t) + db := CreateTestDB(t) repo := NewTVRepository(db) ctx := context.Background() t.Run("Create TV Show", func(t *testing.T) { - tvShow := createSampleTVShow() + tvShow := CreateSampleTVShow() id, err := repo.Create(ctx, tvShow) if err != nil { @@ -84,7 +37,7 @@ func TestTVRepository(t *testing.T) { }) t.Run("Get TV Show", func(t *testing.T) { - original := createSampleTVShow() + original := CreateSampleTVShow() id, err := repo.Create(ctx, original) if err != nil { t.Fatalf("Failed to create TV show: %v", err) @@ -116,7 +69,7 @@ func TestTVRepository(t *testing.T) { }) t.Run("Update TV Show", func(t *testing.T) { - tvShow := createSampleTVShow() + tvShow := CreateSampleTVShow() id, err := repo.Create(ctx, tvShow) if err != nil { t.Fatalf("Failed to create TV show: %v", err) @@ -161,7 +114,7 @@ func TestTVRepository(t *testing.T) { }) t.Run("Delete TV Show", func(t *testing.T) { - tvShow := createSampleTVShow() + tvShow := CreateSampleTVShow() id, err := repo.Create(ctx, tvShow) if err != nil { t.Fatalf("Failed to create TV show: %v", err) @@ -180,7 +133,7 @@ func TestTVRepository(t *testing.T) { }) t.Run("List", func(t *testing.T) { - db := createTVTestDB(t) + db := CreateTestDB(t) repo := NewTVRepository(db) ctx := context.Background() @@ -307,7 +260,7 @@ func TestTVRepository(t *testing.T) { }) t.Run("Special Methods", func(t *testing.T) { - db := createTVTestDB(t) + db := CreateTestDB(t) repo := NewTVRepository(db) ctx := context.Background() @@ -458,7 +411,7 @@ func TestTVRepository(t *testing.T) { }) t.Run("Count", func(t *testing.T) { - db := createTVTestDB(t) + db := CreateTestDB(t) repo := NewTVRepository(db) ctx := context.Background() -- 2.51.2