aboutsummaryrefslogtreecommitdiffstats
path: root/models
diff options
context:
space:
mode:
authorAdam Mathes <adam@adammathes.com>2026-02-12 21:35:46 -0800
committerAdam Mathes <adam@adammathes.com>2026-02-12 21:35:46 -0800
commitde96851d8eb0a0b45d7bf0cee67339fea54349f0 (patch)
treed9ca55835743e2254732ea68a674e7007f3554eb /models
parent8a8f516ebd1115eed6256cd1b60be6393fd42c26 (diff)
downloadneko-de96851d8eb0a0b45d7bf0cee67339fea54349f0.tar.gz
neko-de96851d8eb0a0b45d7bf0cee67339fea54349f0.tar.bz2
neko-de96851d8eb0a0b45d7bf0cee67339fea54349f0.zip
wip: tui updates (buggy)
Diffstat (limited to 'models')
-rw-r--r--models/db_test.go17
-rw-r--r--models/feed/feed.go9
-rw-r--r--models/feed/feed_test.go35
-rw-r--r--models/item/item.go5
-rw-r--r--models/item/item_test.go58
5 files changed, 113 insertions, 11 deletions
diff --git a/models/db_test.go b/models/db_test.go
index 08ceb44..8c61d20 100644
--- a/models/db_test.go
+++ b/models/db_test.go
@@ -1,6 +1,7 @@
package models
import (
+ "path/filepath"
"testing"
"adammathes.com/neko/config"
@@ -33,11 +34,21 @@ func TestInitDB(t *testing.T) {
}
}
-// SetupTestDB initializes an in-memory SQLite database for testing.
-// Call this from other packages' tests to get a working DB.
+func TestInitDBError(t *testing.T) {
+ defer func() {
+ if r := recover(); r == nil {
+ t.Errorf("InitDB should panic for invalid DB file")
+ }
+ }()
+ // Using a directory path instead of a file should cause a failure in Ping
+ config.Config.DBFile = t.TempDir()
+ InitDB()
+}
+
+// SetupTestDB initializes an isolated SQLite database for testing.
func SetupTestDB(t *testing.T) {
t.Helper()
- config.Config.DBFile = ":memory:"
+ config.Config.DBFile = filepath.Join(t.TempDir(), "test.db")
InitDB()
t.Cleanup(func() {
if DB != nil {
diff --git a/models/feed/feed.go b/models/feed/feed.go
index e2730c7..b0f7c69 100644
--- a/models/feed/feed.go
+++ b/models/feed/feed.go
@@ -1,10 +1,11 @@
package feed
import (
- "adammathes.com/neko/models"
- "github.com/PuerkitoBio/goquery"
"log"
"net/http"
+
+ "adammathes.com/neko/models"
+ "github.com/PuerkitoBio/goquery"
)
type Feed struct {
@@ -103,8 +104,8 @@ func (f *Feed) ByUrl(url string) error {
}
func (f *Feed) Create() error {
- res, err := models.DB.Exec(`INSERT INTO feed(url, title)
- VALUES(?, ?)`, f.Url, f.Title)
+ res, err := models.DB.Exec(`INSERT INTO feed(url, title, category)
+ VALUES(?, ?, ?)`, f.Url, f.Title, f.Category)
if err != nil {
return err
}
diff --git a/models/feed/feed_test.go b/models/feed/feed_test.go
index d02916a..715a705 100644
--- a/models/feed/feed_test.go
+++ b/models/feed/feed_test.go
@@ -3,6 +3,7 @@ package feed
import (
"net/http"
"net/http/httptest"
+ "path/filepath"
"testing"
"adammathes.com/neko/config"
@@ -11,7 +12,7 @@ import (
func setupTestDB(t *testing.T) {
t.Helper()
- config.Config.DBFile = ":memory:"
+ config.Config.DBFile = filepath.Join(t.TempDir(), "test.db")
models.InitDB()
t.Cleanup(func() {
if models.DB != nil {
@@ -409,3 +410,35 @@ func TestCategoriesEmpty(t *testing.T) {
t.Errorf("Should have 0 categories on empty DB, got %d", len(cats))
}
}
+
+func TestResolveFeedURLRelativePaths(t *testing.T) {
+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "text/html")
+ w.WriteHeader(200)
+ w.Write([]byte(`<html><head><link rel="alternate" type="application/rss+xml" href="/rss.xml"/></head></html>`))
+ }))
+ defer ts.Close()
+
+ result := ResolveFeedURL(ts.URL)
+ if result != ts.URL+"/rss.xml" {
+ t.Errorf("Expected joined relative URL, got %q", result)
+ }
+}
+
+func TestResolveFeedURLMultipleLinks(t *testing.T) {
+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "text/html")
+ w.WriteHeader(200)
+ w.Write([]byte(`<html><head>
+ <link rel="alternate" type="application/atom+xml" href="http://example.com/atom.xml"/>
+ <link rel="alternate" type="application/rss+xml" href="http://example.com/rss.xml"/>
+ </head></html>`))
+ }))
+ defer ts.Close()
+
+ result := ResolveFeedURL(ts.URL)
+ // it should pick the first matched one or whichever type is handled first in the code
+ if result != "http://example.com/atom.xml" && result != "http://example.com/rss.xml" {
+ t.Errorf("Expected one of the discovered feed URLs, got %q", result)
+ }
+}
diff --git a/models/item/item.go b/models/item/item.go
index 9639595..571e942 100644
--- a/models/item/item.go
+++ b/models/item/item.go
@@ -83,7 +83,10 @@ func filterPolicy() *bluemonday.Policy {
}
func ItemById(id int64) *Item {
- items, _ := Filter(0, 0, "", false, false, id, "")
+ items, err := Filter(0, 0, "", false, false, id, "")
+ if err != nil || len(items) == 0 {
+ return nil
+ }
return items[0]
}
diff --git a/models/item/item_test.go b/models/item/item_test.go
index ef81c76..cbd0ca0 100644
--- a/models/item/item_test.go
+++ b/models/item/item_test.go
@@ -3,7 +3,11 @@ package item
import (
"bytes"
"fmt"
+ "net/http"
+ "net/http/httptest"
"os"
+ "path/filepath"
+ "strings"
"testing"
"adammathes.com/neko/config"
@@ -12,7 +16,7 @@ import (
func setupTestDB(t *testing.T) {
t.Helper()
- config.Config.DBFile = ":memory:"
+ config.Config.DBFile = filepath.Join(t.TempDir(), "test.db")
models.InitDB()
t.Cleanup(func() {
if models.DB != nil {
@@ -46,7 +50,7 @@ func TestPrint(t *testing.T) {
buf.ReadFrom(r)
output := buf.String()
- expected := fmt.Sprintf("id: 42\ntitle: Test Title\nReadState: true\n")
+ expected := "id: 42\ntitle: Test Title\nReadState: true\n"
if output != expected {
t.Errorf("Print() output mismatch:\ngot: %q\nwant: %q", output, expected)
}
@@ -513,6 +517,56 @@ func TestFilterCombined(t *testing.T) {
}
}
+func TestGetFullContent(t *testing.T) {
+ setupTestDB(t)
+ feedId := createTestFeed(t)
+
+ // Mock server to provide article content
+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ fmt.Fprint(w, `<html><body><article><h1>Test Title</h1><p>Full Article Content is here and it is long enough to be detected as content by GoOse.</p></article></body></html>`)
+ }))
+ defer ts.Close()
+
+ i := &Item{
+ Title: "Test Item",
+ Url: ts.URL,
+ FeedId: feedId,
+ }
+ i.Create()
+
+ i.GetFullContent()
+
+ // Verify content was fetched and saved
+ if !strings.Contains(i.FullContent, "Full Article Content") {
+ t.Errorf("Expected full content to be fetched, got %q", i.FullContent)
+ }
+
+ var fullContent string
+ err := models.DB.QueryRow("SELECT full_content FROM item WHERE id=?", i.Id).Scan(&fullContent)
+ if err != nil {
+ t.Fatal(err)
+ }
+ // The DB uses md which is from article.CleanedText
+ if !strings.Contains(fullContent, "Full Article Content") {
+ t.Errorf("Full content (markdown) should be saved in DB, got %q", fullContent)
+ }
+}
+
+func TestGetFullContentEmpty(t *testing.T) {
+ setupTestDB(t)
+ // Provide HTML without an article or clear content
+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ fmt.Fprint(w, `<html><body></body></html>`)
+ }))
+ defer ts.Close()
+
+ i := &Item{Url: ts.URL}
+ i.GetFullContent()
+ if i.FullContent != "" {
+ t.Errorf("Expected empty content for empty HTML, got %q", i.FullContent)
+ }
+}
+
func TestRewriteImagesWithSrcset(t *testing.T) {
input := `<html><head></head><body><img src="https://example.com/image.jpg" srcset="https://example.com/big.jpg 2x"/></body></html>`
result := rewriteImages(input)