package importer import ( "os" "path/filepath" "strings" "testing" "adammathes.com/neko/config" "adammathes.com/neko/models" ) func setupTestDB(t *testing.T) { t.Helper() config.Config.DBFile = filepath.Join(t.TempDir(), "test.db") models.InitDB() t.Cleanup(func() { if models.DB != nil { models.DB.Close() } }) } func TestInsertIItem(t *testing.T) { setupTestDB(t) ii := &IItem{ Title: "Test Article", Url: "https://example.com/article", Description: "A test article description", ReadState: false, Starred: true, Date: &IDate{Date: "2024-01-15 10:00:00"}, Feed: &IFeed{ Url: "https://example.com/feed", Title: "Example Feed", }, } InsertIItem(ii) // Verify the feed was created var feedCount int models.DB.QueryRow("SELECT COUNT(*) FROM feed").Scan(&feedCount) if feedCount != 1 { t.Errorf("Expected 1 feed, got %d", feedCount) } // Verify the item was created var itemCount int models.DB.QueryRow("SELECT COUNT(*) FROM item").Scan(&itemCount) if itemCount != 1 { t.Errorf("Expected 1 item, got %d", itemCount) } } func TestInsertIItemNilFeed(t *testing.T) { setupTestDB(t) ii := &IItem{ Title: "No Feed Item", Url: "https://example.com/nofeed", Feed: nil, } // Should not panic InsertIItem(ii) var itemCount int models.DB.QueryRow("SELECT COUNT(*) FROM item").Scan(&itemCount) if itemCount != 0 { t.Errorf("Expected 0 items (nil feed should be skipped), got %d", itemCount) } } func TestInsertIItemExistingFeed(t *testing.T) { setupTestDB(t) // Insert feed first models.DB.Exec("INSERT INTO feed(url, title) VALUES(?, ?)", "https://example.com/feed", "Existing Feed") ii := &IItem{ Title: "New Article", Url: "https://example.com/new-article", Description: "New article desc", Date: &IDate{Date: "2024-01-15"}, Feed: &IFeed{ Url: "https://example.com/feed", Title: "Existing Feed", }, } InsertIItem(ii) // Should still be just 1 feed var feedCount int models.DB.QueryRow("SELECT COUNT(*) FROM feed").Scan(&feedCount) if feedCount != 1 { t.Errorf("Expected 1 feed (reuse existing), got %d", feedCount) } } func TestImportJSON(t *testing.T) { setupTestDB(t) dir := t.TempDir() jsonFile := filepath.Join(dir, "import.json") content := `{"title":"Article 1","url":"https://example.com/1","description":"desc1","read":false,"starred":false,"date":{"$date":"2024-01-01"},"feed":{"url":"https://example.com/feed","title":"Feed 1"}} {"title":"Article 2","url":"https://example.com/2","description":"desc2","read":true,"starred":true,"date":{"$date":"2024-01-02"},"feed":{"url":"https://example.com/feed","title":"Feed 1"}}` err := os.WriteFile(jsonFile, []byte(content), 0644) if err != nil { t.Fatal(err) } ImportJSON(jsonFile) var itemCount int models.DB.QueryRow("SELECT COUNT(*) FROM item").Scan(&itemCount) if itemCount != 2 { t.Errorf("Expected 2 items after import, got %d", itemCount) } var feedCount int models.DB.QueryRow("SELECT COUNT(*) FROM feed").Scan(&feedCount) if feedCount != 1 { t.Errorf("Expected 1 feed after import, got %d", feedCount) } } func TestImportJSONInvalid(t *testing.T) { setupTestDB(t) dir := t.TempDir() jsonFile := filepath.Join(dir, "invalid.json") os.WriteFile(jsonFile, []byte("not json"), 0644) err := ImportJSON(jsonFile) if err == nil { t.Error("ImportJSON should error on invalid JSON") } } func TestImportJSONNonexistent(t *testing.T) { setupTestDB(t) err := ImportJSON("/nonexistent/file.json") if err == nil { t.Error("ImportJSON should error on nonexistent file") } } // Test the ImportFeeds dispatcher function (previously 0% coverage) func TestImportFeedsOPML(t *testing.T) { setupTestDB(t) opml := ` test ` err := ImportFeeds("opml", strings.NewReader(opml)) if err != nil { t.Fatalf("ImportFeeds(opml) failed: %v", err) } var count int models.DB.QueryRow("SELECT COUNT(*) FROM feed").Scan(&count) if count != 1 { t.Errorf("expected 1 feed, got %d", count) } } func TestImportFeedsText(t *testing.T) { setupTestDB(t) text := "https://example.com/feed1\nhttps://example.com/feed2\n" err := ImportFeeds("text", strings.NewReader(text)) if err != nil { t.Fatalf("ImportFeeds(text) failed: %v", err) } var count int models.DB.QueryRow("SELECT COUNT(*) FROM feed").Scan(&count) if count != 2 { t.Errorf("expected 2 feeds, got %d", count) } } func TestImportFeedsJSON(t *testing.T) { setupTestDB(t) jsonContent := `{"title":"A1","url":"https://example.com/1","description":"d","feed":{"url":"https://example.com/feed","title":"F1"}}` err := ImportFeeds("json", strings.NewReader(jsonContent)) if err != nil { t.Fatalf("ImportFeeds(json) failed: %v", err) } var count int models.DB.QueryRow("SELECT COUNT(*) FROM item").Scan(&count) if count != 1 { t.Errorf("expected 1 item, got %d", count) } } func TestImportFeedsUnsupported(t *testing.T) { err := ImportFeeds("csv", strings.NewReader("data")) if err == nil { t.Error("ImportFeeds should error for unsupported format") } if err != nil && !strings.Contains(err.Error(), "unsupported") { t.Errorf("expected 'unsupported' error, got: %v", err) } } func TestImportOPMLInvalid(t *testing.T) { setupTestDB(t) err := ImportOPML(strings.NewReader("not valid xml")) if err == nil { t.Error("ImportOPML should error on invalid XML") } } func TestImportOPMLNestedCategories(t *testing.T) { setupTestDB(t) opml := ` test ` err := ImportOPML(strings.NewReader(opml)) if err != nil { t.Fatalf("ImportOPML failed: %v", err) } var count int models.DB.QueryRow("SELECT COUNT(*) FROM feed").Scan(&count) if count != 2 { t.Errorf("expected 2 feeds, got %d", count) } // Verify nested category is inherited var category string models.DB.QueryRow("SELECT category FROM feed WHERE url=?", "https://a.com/feed").Scan(&category) if category != "Programming" { t.Errorf("expected category 'Programming' for nested feed, got %q", category) } // Feed with category attribute models.DB.QueryRow("SELECT category FROM feed WHERE url=?", "https://b.com/feed").Scan(&category) if category != "news" { t.Errorf("expected category 'news' for feed with category attr, got %q", category) } } func TestInsertIItemNilDate(t *testing.T) { setupTestDB(t) ii := &IItem{ Title: "No Date Article", Url: "https://example.com/nodate", Description: "Article without date", Date: nil, Feed: &IFeed{ Url: "https://example.com/feed", Title: "Test Feed", }, } err := InsertIItem(ii) if err != nil { t.Errorf("InsertIItem with nil date should not error, got %v", err) } var count int models.DB.QueryRow("SELECT COUNT(*) FROM item").Scan(&count) if count != 1 { t.Errorf("expected 1 item, got %d", count) } } func TestImportJSONReaderEmpty(t *testing.T) { setupTestDB(t) // Empty reader - should not error (just EOF immediately) err := ImportJSONReader(strings.NewReader("")) if err != nil { t.Errorf("ImportJSONReader with empty input should not error, got %v", err) } } func TestImportTextSkipsCommentsAndBlanks(t *testing.T) { setupTestDB(t) text := ` # This is a comment https://example.com/feed1 # Another comment https://example.com/feed2 ` err := ImportText(strings.NewReader(text)) if err != nil { t.Fatalf("ImportText failed: %v", err) } var count int models.DB.QueryRow("SELECT COUNT(*) FROM feed").Scan(&count) if count != 2 { t.Errorf("expected 2 feeds (comments and blanks skipped), got %d", count) } }