package storage import ( "log" "reflect" "strconv" "testing" "time" ) /* - folder1 - feed11 - item111 (unread) - item112 (read) - item113 (starred) - feed12 - item121 (unread) - item122 (read) - folder2 - feed21 - item211 (read) - item212 (starred) - feed01 - item011 (unread) - item012 (read) - item013 (starred) */ type testItemScope struct { feed11, feed12 *Feed feed21, feed01 *Feed folder1, folder2 *Folder } func testItemsSetup(db *Storage) testItemScope { folder1 := db.CreateFolder("folder1") folder2 := db.CreateFolder("folder2") feed11 := db.CreateFeed("feed11", "", "", "http://test.com/feed11.xml", &folder1.Id) feed12 := db.CreateFeed("feed12", "", "", "http://test.com/feed12.xml", &folder1.Id) feed21 := db.CreateFeed("feed21", "", "", "http://test.com/feed21.xml", &folder2.Id) feed01 := db.CreateFeed("feed01", "", "", "http://test.com/feed01.xml", nil) now := time.Now() db.CreateItems([]Item{ // feed11 {GUID: "item111", FeedId: feed11.Id, Title: "title111", Date: now.Add(time.Hour * 24 * 1)}, {GUID: "item112", FeedId: feed11.Id, Title: "title112", Date: now.Add(time.Hour * 24 * 2)}, // read {GUID: "item113", FeedId: feed11.Id, Title: "title113", Date: now.Add(time.Hour * 24 * 3)}, // starred // feed12 {GUID: "item121", FeedId: feed12.Id, Title: "title121", Date: now.Add(time.Hour * 24 * 4)}, {GUID: "item122", FeedId: feed12.Id, Title: "title122", Date: now.Add(time.Hour * 24 * 5)}, // read // feed21 {GUID: "item211", FeedId: feed21.Id, Title: "title211", Date: now.Add(time.Hour * 24 * 6)}, // read {GUID: "item212", FeedId: feed21.Id, Title: "title212", Date: now.Add(time.Hour * 24 * 7)}, // starred // feed01 {GUID: "item011", FeedId: feed01.Id, Title: "title011", Date: now.Add(time.Hour * 24 * 8)}, {GUID: "item012", FeedId: feed01.Id, Title: "title012", Date: now.Add(time.Hour * 24 * 9)}, // read {GUID: "item013", FeedId: feed01.Id, Title: "title013", Date: now.Add(time.Hour * 24 * 10)}, // starred }) db.db.Exec(`update items set status = ? where guid in ("item112", "item122", "item211", "item012")`, READ) db.db.Exec(`update items set status = ? where guid in ("item113", "item212", "item013")`, STARRED) return testItemScope{ feed11: feed11, feed12: feed12, feed21: feed21, feed01: feed01, folder1: folder1, folder2: folder2, } } func getItem(db *Storage, guid string) *Item { i := &Item{} err := db.db.QueryRow(` select i.id, i.guid, i.feed_id, i.title, i.link, i.content, i.date, i.status, i.image, i.podcast_url from items i where i.guid = ? `, guid).Scan( &i.Id, &i.GUID, &i.FeedId, &i.Title, &i.Link, &i.Content, &i.Date, &i.Status, &i.ImageURL, &i.AudioURL, ) if err != nil { log.Fatal(err) } return i } func getItemGuids(items []Item) []string { guids := make([]string, 0) for _, item := range items { guids = append(guids, item.GUID) } return guids } func TestListItems(t *testing.T) { db := testDB() scope := testItemsSetup(db) // filter by folder_id have := getItemGuids(db.ListItems(ItemFilter{FolderID: &scope.folder1.Id}, 10, false, false)) want := []string{"item111", "item112", "item113", "item121", "item122"} if !reflect.DeepEqual(have, want) { t.Logf("want: %#v", want) t.Logf("have: %#v", have) t.Fail() } have = getItemGuids(db.ListItems(ItemFilter{FolderID: &scope.folder2.Id}, 10, false, false)) want = []string{"item211", "item212"} if !reflect.DeepEqual(have, want) { t.Logf("want: %#v", want) t.Logf("have: %#v", have) t.Fail() } // filter by feed_id have = getItemGuids(db.ListItems(ItemFilter{FeedID: &scope.feed11.Id}, 10, false, false)) want = []string{"item111", "item112", "item113"} if !reflect.DeepEqual(have, want) { t.Logf("want: %#v", want) t.Logf("have: %#v", have) t.Fail() } have = getItemGuids(db.ListItems(ItemFilter{FeedID: &scope.feed01.Id}, 10, false, false)) want = []string{"item011", "item012", "item013"} if !reflect.DeepEqual(have, want) { t.Logf("want: %#v", want) t.Logf("have: %#v", have) t.Fail() } // filter by status var starred ItemStatus = STARRED have = getItemGuids(db.ListItems(ItemFilter{Status: &starred}, 10, false, false)) want = []string{"item113", "item212", "item013"} if !reflect.DeepEqual(have, want) { t.Logf("want: %#v", want) t.Logf("have: %#v", have) t.Fail() } var unread ItemStatus = UNREAD have = getItemGuids(db.ListItems(ItemFilter{Status: &unread}, 10, false, false)) want = []string{"item111", "item121", "item011"} if !reflect.DeepEqual(have, want) { t.Logf("want: %#v", want) t.Logf("have: %#v", have) t.Fail() } // limit have = getItemGuids(db.ListItems(ItemFilter{}, 2, false, false)) want = []string{"item111", "item112"} if !reflect.DeepEqual(have, want) { t.Logf("want: %#v", want) t.Logf("have: %#v", have) t.Fail() } // filter by search db.SyncSearch() search1 := "title111" have = getItemGuids(db.ListItems(ItemFilter{Search: &search1}, 4, true, false)) want = []string{"item111"} if !reflect.DeepEqual(have, want) { t.Logf("want: %#v", want) t.Logf("have: %#v", have) t.Fail() } // sort by date have = getItemGuids(db.ListItems(ItemFilter{}, 4, true, false)) want = []string{"item013", "item012", "item011", "item212"} if !reflect.DeepEqual(have, want) { t.Logf("want: %#v", want) t.Logf("have: %#v", have) t.Fail() } } func TestListItemsPaginated(t *testing.T) { db := testDB() testItemsSetup(db) item012 := getItem(db, "item012") item121 := getItem(db, "item121") // all, newest first have := getItemGuids(db.ListItems(ItemFilter{After: &item012.Id}, 3, true, false)) want := []string{"item011", "item212", "item211"} if !reflect.DeepEqual(have, want) { t.Logf("want: %#v", want) t.Logf("have: %#v", have) t.Fail() } // unread, newest first unread := UNREAD have = getItemGuids(db.ListItems(ItemFilter{After: &item012.Id, Status: &unread}, 3, true, false)) want = []string{"item011", "item121", "item111"} if !reflect.DeepEqual(have, want) { t.Logf("want: %#v", want) t.Logf("have: %#v", have) t.Fail() } // starred, oldest first starred := STARRED have = getItemGuids(db.ListItems(ItemFilter{After: &item121.Id, Status: &starred}, 3, false, false)) want = []string{"item212", "item013"} if !reflect.DeepEqual(have, want) { t.Logf("want: %#v", want) t.Logf("have: %#v", have) t.Fail() } } func TestMarkItemsRead(t *testing.T) { // NOTE: starred items must not be marked as read var read ItemStatus = READ db1 := testDB() testItemsSetup(db1) db1.MarkItemsRead(MarkFilter{}) have := getItemGuids(db1.ListItems(ItemFilter{Status: &read}, 10, false, false)) want := []string{ "item111", "item112", "item121", "item122", "item211", "item011", "item012", } if !reflect.DeepEqual(have, want) { t.Logf("want: %#v", want) t.Logf("have: %#v", have) t.Fail() } db2 := testDB() scope2 := testItemsSetup(db2) db2.MarkItemsRead(MarkFilter{FolderID: &scope2.folder1.Id}) have = getItemGuids(db2.ListItems(ItemFilter{Status: &read}, 10, false, false)) want = []string{ "item111", "item112", "item121", "item122", "item211", "item012", } if !reflect.DeepEqual(have, want) { t.Logf("want: %#v", want) t.Logf("have: %#v", have) t.Fail() } db3 := testDB() scope3 := testItemsSetup(db3) db3.MarkItemsRead(MarkFilter{FeedID: &scope3.feed11.Id}) have = getItemGuids(db3.ListItems(ItemFilter{Status: &read}, 10, false, false)) want = []string{ "item111", "item112", "item122", "item211", "item012", } if !reflect.DeepEqual(have, want) { t.Logf("want: %#v", want) t.Logf("have: %#v", have) t.Fail() } } func TestDeleteOldItems(t *testing.T) { extraItems := 10 now := time.Now().UTC() db := testDB() feed := db.CreateFeed("feed", "", "", "http://test.com/feed11.xml", nil) items := make([]Item, 0) for i := 0; i < itemsKeepSize+extraItems; i++ { istr := strconv.Itoa(i) items = append(items, Item{ GUID: istr, FeedId: feed.Id, Title: istr, Date: now.Add(time.Hour * time.Duration(i)), }) } db.CreateItems(items) db.SetFeedSize(feed.Id, itemsKeepSize) var feedSize int err := db.db.QueryRow( `select size from feed_sizes where feed_id = ?`, feed.Id, ).Scan(&feedSize) if err != nil { t.Fatal(err) } if feedSize != itemsKeepSize { t.Fatalf( "expected feed size to get updated\nwant: %d\nhave: %d", itemsKeepSize+extraItems, feedSize, ) } // expire only the first 3 articles _, err = db.db.Exec( `update items set date_arrived = ? where id in (select id from items limit 3)`, now.Add(-time.Hour*time.Duration(itemsKeepDays*24)), ) if err != nil { t.Fatal(err) } db.DeleteOldItems() feedItems := db.ListItems(ItemFilter{FeedID: &feed.Id}, 1000, false, false) if len(feedItems) != len(items)-3 { t.Fatalf( "invalid number of old items kept\nwant: %d\nhave: %d", len(items)-3, len(feedItems), ) } }