aboutsummaryrefslogtreecommitdiffstats
path: root/models
diff options
context:
space:
mode:
Diffstat (limited to 'models')
-rw-r--r--models/item/item.go30
-rw-r--r--models/item/item_test.go76
2 files changed, 100 insertions, 6 deletions
diff --git a/models/item/item.go b/models/item/item.go
index 3722e90..f960c65 100644
--- a/models/item/item.go
+++ b/models/item/item.go
@@ -57,8 +57,8 @@ func (i *Item) Print() {
func (i *Item) Create() error {
res, err := models.DB.Exec(`INSERT INTO
- item(title, url, description, publish_date, feed_id)
- VALUES(?, ?, ?, ?, ?)`, i.Title, i.Url, i.Description, i.PublishDate, i.FeedId)
+ item(title, url, description, publish_date, feed_id, read_state, starred)
+ VALUES(?, ?, ?, ?, ?, ?, ?)`, i.Title, i.Url, i.Description, i.PublishDate, i.FeedId, i.ReadState, i.Starred)
if err != nil {
vlog.Printf("Error on item.Create\n%v\n%v\n", i.Url, err)
return err
@@ -230,6 +230,32 @@ func Filter(max_id int64, feed_id int64, category string, unread_only bool, star
return items, nil
}
+// Purge deletes items older than the specified number of days.
+// By default it only deletes read items.
+// If allItems is true, it also deletes unread items.
+// Starred items are NEVER deleted.
+func Purge(days int, allItems bool) (int64, error) {
+ query := `DELETE FROM item WHERE datetime(publish_date) < datetime('now', ?) AND starred == 0`
+ if !allItems {
+ query = query + ` AND read_state == 1`
+ }
+ vlog.Printf("Purge query: %s with param %s\n", query, fmt.Sprintf("-%d days", days))
+ res, err := models.DB.Exec(query, fmt.Sprintf("-%d days", days))
+ if err != nil {
+ return 0, err
+ }
+ affected, _ := res.RowsAffected()
+ vlog.Printf("Purge affected rows: %d\n", affected)
+
+ // Cleanup FTS table - SQLite FTS4 doesn't automatically cleanup content table rows
+ // if we are using "content=item" (which we are).
+ // Actually we have triggers, so it should be fine.
+ // But VACUUM is good to reclaim space.
+ // _, _ = models.DB.Exec("VACUUM")
+
+ return affected, nil
+}
+
func (i *Item) CleanHeaderImage() {
// TODO: blacklist of bad imgs
if i.HeaderImage == "https://s0.wp.com/i/blank.jpg" {
diff --git a/models/item/item_test.go b/models/item/item_test.go
index 805c588..2f31ac7 100644
--- a/models/item/item_test.go
+++ b/models/item/item_test.go
@@ -9,6 +9,7 @@ import (
"path/filepath"
"strings"
"testing"
+ "time"
goose "github.com/advancedlogic/GoOse"
@@ -638,16 +639,83 @@ func TestRewriteImagesSrcset(t *testing.T) {
}
func TestItemSaveError(t *testing.T) {
- setupTestDB(t)
+ config.Config.DBFile = filepath.Join(t.TempDir(), "test_bad.db")
+ models.InitDB()
+
i := &Item{Id: 1}
- // Close DB to force error
models.DB.Close()
- i.Save() // Should not panic, just log error
+ i.Save()
}
func TestItemFullSaveError(t *testing.T) {
- setupTestDB(t)
+ config.Config.DBFile = filepath.Join(t.TempDir(), "test_bad_2.db")
+ models.InitDB()
i := &Item{Id: 1}
models.DB.Close()
i.FullSave() // Should not panic
}
+func TestPurge(t *testing.T) {
+ setupTestDB(t)
+ feedId := createTestFeed(t)
+
+ // Create items with different dates and states
+ now := time.Now()
+ // Use explicit dates, ensure SQLite parses them correctly
+ // Old date: 60 days ago
+ oldDate := now.AddDate(0, 0, -60).Format("2006-01-02 15:04:05")
+ // Very old date: 100 days ago
+ veryOldDate := now.AddDate(0, 0, -100).Format("2006-01-02 15:04:05")
+
+ items := []*Item{
+ {Title: "Old Read", Url: "http://example.com/1", PublishDate: oldDate, FeedId: feedId, ReadState: true},
+ {Title: "Old Unread", Url: "http://example.com/2", PublishDate: oldDate, FeedId: feedId, ReadState: false},
+ {Title: "Old Starred", Url: "http://example.com/3", PublishDate: oldDate, FeedId: feedId, ReadState: true, Starred: true},
+ {Title: "Very Old Read", Url: "http://example.com/4", PublishDate: veryOldDate, FeedId: feedId, ReadState: true},
+ }
+
+ for _, i := range items {
+ err := i.Create()
+ if err != nil {
+ t.Fatal(err)
+ }
+ }
+
+ // Purge read items older than 30 days
+ affected, err := Purge(30, false)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // Should have purged "Old Read" and "Very Old Read"
+ if affected != 2 {
+ t.Errorf("Expected 2 items purged, got %d", affected)
+ // Debug logging to see what's left
+ remaining, _ := Filter(0, 0, "", false, false, 0, "")
+ for _, r := range remaining {
+ t.Logf("Remaining: %s (%s) Read: %t Starred: %t", r.Title, r.PublishDate, r.ReadState, r.Starred)
+ }
+ }
+
+ // Verify remaining items count
+ remaining, _ := Filter(0, 0, "", false, false, 0, "")
+ if len(remaining) != 2 {
+ t.Errorf("Expected 2 items remaining, got %d", len(remaining))
+ }
+
+ // Purge all items older than 30 days (including unread)
+ affected, err = Purge(30, true)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // Should have purged "Old Unread"
+ if affected != 1 {
+ t.Errorf("Expected 1 item purged, got %d", affected)
+ }
+
+ //Verify "Old Starred" is still there
+ remaining, _ = Filter(0, 0, "", false, false, 0, "")
+ if len(remaining) != 1 || remaining[0].Title != "Old Starred" {
+ t.Errorf("Expected only 'Old Starred' to remain, got %v", remaining)
+ }
+}