mirror of
https://github.com/nkanaev/yarr.git
synced 2026-06-15 12:35:04 +00:00
fix references
This commit is contained in:
@@ -9,24 +9,24 @@ type Storage interface {
|
||||
Migrate() error
|
||||
CountItems() int
|
||||
CreateFeed(params model.CreateFeedParams) *model.Feed
|
||||
CreateFolder(title string) *Folder
|
||||
CreateItems(items []Item) bool
|
||||
CreateFolder(title string) *model.Folder
|
||||
CreateItems(items []model.Item) bool
|
||||
DeleteFeed(feedId int64) bool
|
||||
DeleteFolder(folderId int64) bool
|
||||
DeleteOldItems()
|
||||
FeedStats() []FeedStat
|
||||
FeedStats() []model.FeedStat
|
||||
GetFeed(id int64) *model.Feed
|
||||
GetFeedState(feedID int64) (*FeedState, error)
|
||||
GetItem(id int64) *Item
|
||||
GetSettings() Settings
|
||||
ListFeedStates() ([]FeedState, error)
|
||||
GetFeedState(feedID int64) (*model.FeedState, error)
|
||||
GetItem(id int64) *model.Item
|
||||
GetSettings() model.Settings
|
||||
ListFeedStates() ([]model.FeedState, error)
|
||||
ListFeeds() []model.Feed
|
||||
ListFolders() []model.Folder
|
||||
ListItems(filter ItemFilter, limit int, newestFirst bool, withContent bool) []Item
|
||||
MarkItemsRead(filter MarkFilter) bool
|
||||
UpdateFeed(feedId int64, params UpdateFeedParams) (bool, error)
|
||||
UpdateFeedState(feedID int64, params UpdateFeedStateParams) (bool, error)
|
||||
UpdateFolder(folderId int64, params UpdateFolderParams) (bool, error)
|
||||
UpdateItemStatus(item_id int64, status ItemStatus) bool
|
||||
UpdateSettings(params UpdateSettingsParams) bool
|
||||
ListItems(filter model.ItemFilter, limit int, newestFirst bool, withContent bool) []model.Item
|
||||
MarkItemsRead(filter model.MarkFilter) bool
|
||||
UpdateFeed(feedId int64, params model.UpdateFeedParams) (bool, error)
|
||||
UpdateFeedState(feedID int64, params model.UpdateFeedStateParams) (bool, error)
|
||||
UpdateFolder(folderId int64, params model.UpdateFolderParams) (bool, error)
|
||||
UpdateItemStatus(item_id int64, status model.ItemStatus) bool
|
||||
UpdateSettings(params model.UpdateSettingsParams) bool
|
||||
}
|
||||
|
||||
@@ -108,6 +108,12 @@ type UpdateFolderParams struct {
|
||||
IsExpanded *bool
|
||||
}
|
||||
|
||||
type FeedStat struct {
|
||||
FeedId int64 `json:"feed_id"`
|
||||
UnreadCount int64 `json:"unread"`
|
||||
StarredCount int64 `json:"starred"`
|
||||
}
|
||||
|
||||
|
||||
type Settings struct {
|
||||
Filter string `json:"filter"`
|
||||
|
||||
@@ -30,7 +30,7 @@ func (s *SQLiteStorage) CreateFeed(params model.CreateFeedParams) *model.Feed {
|
||||
log.Print(err)
|
||||
return nil
|
||||
}
|
||||
return &Feed{
|
||||
return &model.Feed{
|
||||
Id: id,
|
||||
Title: title,
|
||||
Description: params.Description,
|
||||
@@ -56,7 +56,7 @@ func (s *SQLiteStorage) DeleteFeed(feedId int64) bool {
|
||||
return nrows == 1
|
||||
}
|
||||
|
||||
func (s *SQLiteStorage) UpdateFeed(feedId int64, params UpdateFeedParams) (bool, error) {
|
||||
func (s *SQLiteStorage) UpdateFeed(feedId int64, params model.UpdateFeedParams) (bool, error) {
|
||||
_, err := s.db.Exec(`
|
||||
update feeds set
|
||||
title = coalesce(:title, title),
|
||||
@@ -112,8 +112,8 @@ func (s *SQLiteStorage) ListFeeds() []model.Feed {
|
||||
return result
|
||||
}
|
||||
|
||||
func (s *SQLiteStorage) GetFeed(id int64) *Feed {
|
||||
var f Feed
|
||||
func (s *SQLiteStorage) GetFeed(id int64) *model.Feed {
|
||||
var f model.Feed
|
||||
err := s.db.QueryRow(`
|
||||
select
|
||||
id, folder_id, title, link, feed_link,
|
||||
|
||||
@@ -3,11 +3,13 @@ package sqlite
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/nkanaev/yarr/src/storage/model"
|
||||
)
|
||||
|
||||
func TestCreateFeed(t *testing.T) {
|
||||
db := testDB()
|
||||
feed1 := db.CreateFeed(CreateFeedParams{Title: "title", Link: "http://example.com", FeedLink: "http://example.com/feed.xml"})
|
||||
feed1 := db.CreateFeed(model.CreateFeedParams{Title: "title", Link: "http://example.com", FeedLink: "http://example.com/feed.xml"})
|
||||
if feed1 == nil || feed1.Id == 0 {
|
||||
t.Fatal("expected feed")
|
||||
}
|
||||
@@ -19,16 +21,16 @@ func TestCreateFeed(t *testing.T) {
|
||||
|
||||
func TestCreateFeedSameLink(t *testing.T) {
|
||||
db := testDB()
|
||||
feed1 := db.CreateFeed(CreateFeedParams{Title: "title", FeedLink: "http://example1.com/feed.xml"})
|
||||
feed1 := db.CreateFeed(model.CreateFeedParams{Title: "title", FeedLink: "http://example1.com/feed.xml"})
|
||||
if feed1 == nil || feed1.Id == 0 {
|
||||
t.Fatal("expected feed")
|
||||
}
|
||||
|
||||
for range 10 {
|
||||
db.CreateFeed(CreateFeedParams{Title: "title", FeedLink: "http://example2.com/feed.xml"})
|
||||
db.CreateFeed(model.CreateFeedParams{Title: "title", FeedLink: "http://example2.com/feed.xml"})
|
||||
}
|
||||
|
||||
feed2 := db.CreateFeed(CreateFeedParams{Title: "title", Link: "http://example.com", FeedLink: "http://example1.com/feed.xml"})
|
||||
feed2 := db.CreateFeed(model.CreateFeedParams{Title: "title", Link: "http://example.com", FeedLink: "http://example1.com/feed.xml"})
|
||||
if feed1.Id != feed2.Id {
|
||||
t.Fatalf("expected the same feed.\nwant: %#v\nhave: %#v", feed1, feed2)
|
||||
}
|
||||
@@ -40,25 +42,25 @@ func TestReadFeed(t *testing.T) {
|
||||
t.Fatal("cannot get nonexistent feed")
|
||||
}
|
||||
|
||||
feed1 := db.CreateFeed(CreateFeedParams{Title: "feed 1", Link: "http://example1.com", FeedLink: "http://example1.com/feed.xml"})
|
||||
feed2 := db.CreateFeed(CreateFeedParams{Title: "feed 2", Link: "http://example2.com", FeedLink: "http://example2.com/feed.xml"})
|
||||
feed1 := db.CreateFeed(model.CreateFeedParams{Title: "feed 1", Link: "http://example1.com", FeedLink: "http://example1.com/feed.xml"})
|
||||
feed2 := db.CreateFeed(model.CreateFeedParams{Title: "feed 2", Link: "http://example2.com", FeedLink: "http://example2.com/feed.xml"})
|
||||
feeds := db.ListFeeds()
|
||||
if !reflect.DeepEqual(feeds, []Feed{*feed1, *feed2}) {
|
||||
if !reflect.DeepEqual(feeds, []model.Feed{*feed1, *feed2}) {
|
||||
t.Fatalf("invalid feed list: %#v", feeds)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateFeed(t *testing.T) {
|
||||
db := testDB()
|
||||
feed1 := db.CreateFeed(CreateFeedParams{Title: "feed 1", Link: "http://example1.com", FeedLink: "http://example1.com/feed.xml"})
|
||||
feed1 := db.CreateFeed(model.CreateFeedParams{Title: "feed 1", Link: "http://example1.com", FeedLink: "http://example1.com/feed.xml"})
|
||||
folder := db.CreateFolder("test")
|
||||
icon := []byte("icon")
|
||||
|
||||
title := "newtitle"
|
||||
db.UpdateFeed(feed1.Id, UpdateFeedParams{
|
||||
db.UpdateFeed(feed1.Id, model.UpdateFeedParams{
|
||||
Title: &title,
|
||||
FolderID: SetNullable(&folder.Id),
|
||||
Icon: SetNullable(&icon),
|
||||
FolderID: model.SetNullable(&folder.Id),
|
||||
Icon: model.SetNullable(&icon),
|
||||
})
|
||||
|
||||
feed2 := db.GetFeed(feed1.Id)
|
||||
@@ -75,7 +77,7 @@ func TestUpdateFeed(t *testing.T) {
|
||||
|
||||
func TestDeleteFeed(t *testing.T) {
|
||||
db := testDB()
|
||||
feed1 := db.CreateFeed(CreateFeedParams{Title: "title", Link: "http://example.com", FeedLink: "http://example.com/feed.xml"})
|
||||
feed1 := db.CreateFeed(model.CreateFeedParams{Title: "title", Link: "http://example.com", FeedLink: "http://example.com/feed.xml"})
|
||||
|
||||
if db.DeleteFeed(100500) {
|
||||
t.Error("cannot delete what does not exist")
|
||||
|
||||
@@ -2,10 +2,11 @@ package sqlite
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"github.com/nkanaev/yarr/src/storage/model"
|
||||
)
|
||||
|
||||
func (s *SQLiteStorage) ListFeedStates() ([]FeedState, error) {
|
||||
func (s *SQLiteStorage) ListFeedStates() ([]model.FeedState, error) {
|
||||
rows, err := s.db.Query(`
|
||||
select
|
||||
feed_id
|
||||
@@ -20,9 +21,9 @@ func (s *SQLiteStorage) ListFeedStates() ([]FeedState, error) {
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
states := make([]FeedState, 0)
|
||||
states := make([]model.FeedState, 0)
|
||||
for rows.Next() {
|
||||
var state FeedState
|
||||
var state model.FeedState
|
||||
err := rows.Scan(
|
||||
&state.FeedID,
|
||||
&state.LastRefreshed,
|
||||
@@ -38,8 +39,8 @@ func (s *SQLiteStorage) ListFeedStates() ([]FeedState, error) {
|
||||
return states, nil
|
||||
}
|
||||
|
||||
func (s *SQLiteStorage) GetFeedState(feedID int64) (*FeedState, error) {
|
||||
var state FeedState
|
||||
func (s *SQLiteStorage) GetFeedState(feedID int64) (*model.FeedState, error) {
|
||||
var state model.FeedState
|
||||
err := s.db.QueryRow(`
|
||||
select
|
||||
feed_id
|
||||
@@ -64,7 +65,7 @@ func (s *SQLiteStorage) GetFeedState(feedID int64) (*FeedState, error) {
|
||||
return &state, nil
|
||||
}
|
||||
|
||||
func (s *SQLiteStorage) UpdateFeedState(feedID int64, params UpdateFeedStateParams) (bool, error) {
|
||||
func (s *SQLiteStorage) UpdateFeedState(feedID int64, params model.UpdateFeedStateParams) (bool, error) {
|
||||
lastError := params.LastError
|
||||
if lastError != nil && *lastError == "" {
|
||||
lastError = nil
|
||||
|
||||
@@ -3,20 +3,22 @@ package sqlite
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/nkanaev/yarr/src/storage/model"
|
||||
)
|
||||
|
||||
func TestUpdateFeedState_Full(t *testing.T) {
|
||||
s := testDB()
|
||||
defer s.Close()
|
||||
|
||||
f := s.CreateFeed(CreateFeedParams{Title: "Test", FeedLink: "http://example.com"})
|
||||
f := s.CreateFeed(model.CreateFeedParams{Title: "Test", FeedLink: "http://example.com"})
|
||||
|
||||
now := time.Now().UTC().Truncate(time.Second)
|
||||
errMsg := "error"
|
||||
lmod := "today"
|
||||
etag := "v1"
|
||||
|
||||
ok, err := s.UpdateFeedState(f.Id, UpdateFeedStateParams{
|
||||
ok, err := s.UpdateFeedState(f.Id, model.UpdateFeedStateParams{
|
||||
LastRefreshed: &now,
|
||||
LastError: &errMsg,
|
||||
HTTPLastModified: &lmod,
|
||||
@@ -54,12 +56,12 @@ func TestUpdateFeedState_Partial(t *testing.T) {
|
||||
s := testDB()
|
||||
defer s.Close()
|
||||
|
||||
f := s.CreateFeed(CreateFeedParams{Title: "Test", FeedLink: "http://example.com"})
|
||||
f := s.CreateFeed(model.CreateFeedParams{Title: "Test", FeedLink: "http://example.com"})
|
||||
etag := "v1"
|
||||
s.UpdateFeedState(f.Id, UpdateFeedStateParams{HTTPEtag: &etag})
|
||||
s.UpdateFeedState(f.Id, model.UpdateFeedStateParams{HTTPEtag: &etag})
|
||||
|
||||
newErr := "new error"
|
||||
_, err := s.UpdateFeedState(f.Id, UpdateFeedStateParams{
|
||||
_, err := s.UpdateFeedState(f.Id, model.UpdateFeedStateParams{
|
||||
LastError: &newErr,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -82,12 +84,12 @@ func TestUpdateFeedState_ClearError(t *testing.T) {
|
||||
s := testDB()
|
||||
defer s.Close()
|
||||
|
||||
f := s.CreateFeed(CreateFeedParams{Title: "Test", FeedLink: "http://example.com"})
|
||||
f := s.CreateFeed(model.CreateFeedParams{Title: "Test", FeedLink: "http://example.com"})
|
||||
errMsg := "error"
|
||||
s.UpdateFeedState(f.Id, UpdateFeedStateParams{LastError: &errMsg})
|
||||
s.UpdateFeedState(f.Id, model.UpdateFeedStateParams{LastError: &errMsg})
|
||||
|
||||
empty := ""
|
||||
_, err := s.UpdateFeedState(f.Id, UpdateFeedStateParams{
|
||||
_, err := s.UpdateFeedState(f.Id, model.UpdateFeedStateParams{
|
||||
LastError: &empty,
|
||||
})
|
||||
if err != nil {
|
||||
@@ -107,12 +109,12 @@ func TestListFeedStates(t *testing.T) {
|
||||
s := testDB()
|
||||
defer s.Close()
|
||||
|
||||
f1 := s.CreateFeed(CreateFeedParams{Title: "F1", FeedLink: "L1"})
|
||||
f2 := s.CreateFeed(CreateFeedParams{Title: "F2", FeedLink: "L2"})
|
||||
f1 := s.CreateFeed(model.CreateFeedParams{Title: "F1", FeedLink: "L1"})
|
||||
f2 := s.CreateFeed(model.CreateFeedParams{Title: "F2", FeedLink: "L2"})
|
||||
|
||||
errMsg := "fail"
|
||||
s.UpdateFeedState(f1.Id, UpdateFeedStateParams{LastError: &errMsg})
|
||||
s.UpdateFeedState(f2.Id, UpdateFeedStateParams{HTTPEtag: ptr("e")})
|
||||
s.UpdateFeedState(f1.Id, model.UpdateFeedStateParams{LastError: &errMsg})
|
||||
s.UpdateFeedState(f2.Id, model.UpdateFeedStateParams{HTTPEtag: ptr("e")})
|
||||
|
||||
states, err := s.ListFeedStates()
|
||||
if err != nil {
|
||||
|
||||
@@ -3,9 +3,11 @@ package sqlite
|
||||
import (
|
||||
"database/sql"
|
||||
"log"
|
||||
|
||||
"github.com/nkanaev/yarr/src/storage/model"
|
||||
)
|
||||
|
||||
func (s *SQLiteStorage) CreateFolder(title string) *Folder {
|
||||
func (s *SQLiteStorage) CreateFolder(title string) *model.Folder {
|
||||
expanded := true
|
||||
row := s.db.QueryRow(`
|
||||
insert into folders (title, is_expanded) values (:title, :is_expanded)
|
||||
@@ -21,7 +23,7 @@ func (s *SQLiteStorage) CreateFolder(title string) *Folder {
|
||||
log.Print(err)
|
||||
return nil
|
||||
}
|
||||
return &Folder{Id: id, Title: title, IsExpanded: expanded}
|
||||
return &model.Folder{Id: id, Title: title, IsExpanded: expanded}
|
||||
}
|
||||
|
||||
func (s *SQLiteStorage) DeleteFolder(folderId int64) bool {
|
||||
@@ -32,7 +34,7 @@ func (s *SQLiteStorage) DeleteFolder(folderId int64) bool {
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func (s *SQLiteStorage) UpdateFolder(folderId int64, params UpdateFolderParams) (bool, error) {
|
||||
func (s *SQLiteStorage) UpdateFolder(folderId int64, params model.UpdateFolderParams) (bool, error) {
|
||||
_, err := s.db.Exec(`
|
||||
update folders set
|
||||
title = coalesce(:title, title),
|
||||
@@ -50,8 +52,8 @@ func (s *SQLiteStorage) UpdateFolder(folderId int64, params UpdateFolderParams)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (s *SQLiteStorage) ListFolders() []Folder {
|
||||
result := make([]Folder, 0)
|
||||
func (s *SQLiteStorage) ListFolders() []model.Folder {
|
||||
result := make([]model.Folder, 0)
|
||||
rows, err := s.db.Query(`
|
||||
select id, title, is_expanded
|
||||
from folders
|
||||
@@ -62,7 +64,7 @@ func (s *SQLiteStorage) ListFolders() []Folder {
|
||||
return result
|
||||
}
|
||||
for rows.Next() {
|
||||
var f Folder
|
||||
var f model.Folder
|
||||
err = rows.Scan(&f.Id, &f.Title, &f.IsExpanded)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
|
||||
@@ -2,6 +2,8 @@ package sqlite
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/nkanaev/yarr/src/storage/model"
|
||||
)
|
||||
|
||||
func TestUpdateFolder(t *testing.T) {
|
||||
@@ -13,7 +15,7 @@ func TestUpdateFolder(t *testing.T) {
|
||||
|
||||
t.Run("rename only", func(t *testing.T) {
|
||||
newTitle := "new title"
|
||||
ok, err := db.UpdateFolder(folder.Id, UpdateFolderParams{
|
||||
ok, err := db.UpdateFolder(folder.Id, model.UpdateFolderParams{
|
||||
Title: &newTitle,
|
||||
})
|
||||
if !ok || err != nil {
|
||||
@@ -31,7 +33,7 @@ func TestUpdateFolder(t *testing.T) {
|
||||
|
||||
t.Run("toggle expanded only", func(t *testing.T) {
|
||||
isExpanded := false
|
||||
ok, err := db.UpdateFolder(folder.Id, UpdateFolderParams{
|
||||
ok, err := db.UpdateFolder(folder.Id, model.UpdateFolderParams{
|
||||
IsExpanded: &isExpanded,
|
||||
})
|
||||
if !ok || err != nil {
|
||||
@@ -50,7 +52,7 @@ func TestUpdateFolder(t *testing.T) {
|
||||
t.Run("update both", func(t *testing.T) {
|
||||
bothTitle := "both"
|
||||
isExpanded := true
|
||||
ok, err := db.UpdateFolder(folder.Id, UpdateFolderParams{
|
||||
ok, err := db.UpdateFolder(folder.Id, model.UpdateFolderParams{
|
||||
Title: &bothTitle,
|
||||
IsExpanded: &isExpanded,
|
||||
})
|
||||
@@ -65,7 +67,7 @@ func TestUpdateFolder(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("update none", func(t *testing.T) {
|
||||
ok, err := db.UpdateFolder(folder.Id, UpdateFolderParams{})
|
||||
ok, err := db.UpdateFolder(folder.Id, model.UpdateFolderParams{})
|
||||
if !ok || err != nil {
|
||||
t.Fatalf("UpdateFolder failed: %v", err)
|
||||
}
|
||||
|
||||
@@ -14,7 +14,8 @@ import (
|
||||
"github.com/nkanaev/yarr/src/storage/model"
|
||||
)
|
||||
|
||||
// TODO: serialize/deserialize
|
||||
type MediaLinks model.MediaLinks
|
||||
|
||||
func (m *MediaLinks) Scan(src any) error {
|
||||
switch data := src.(type) {
|
||||
case []byte:
|
||||
@@ -30,7 +31,7 @@ func (m MediaLinks) Value() (driver.Value, error) {
|
||||
return json.Marshal(m)
|
||||
}
|
||||
|
||||
func (s *SQLiteStorage) CreateItems(items []Item) bool {
|
||||
func (s *SQLiteStorage) CreateItems(items []model.Item) bool {
|
||||
tx, err := s.db.Begin()
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
@@ -65,7 +66,7 @@ func (s *SQLiteStorage) CreateItems(items []Item) bool {
|
||||
sql.Named("link", item.Link),
|
||||
sql.Named("date", item.Date),
|
||||
sql.Named("content", item.Content),
|
||||
sql.Named("media_links", item.MediaLinks),
|
||||
sql.Named("media_links", MediaLinks(item.MediaLinks)),
|
||||
sql.Named("date_arrived", now),
|
||||
sql.Named("last_arrived", now),
|
||||
sql.Named("status", model.UNREAD),
|
||||
@@ -86,7 +87,7 @@ func (s *SQLiteStorage) CreateItems(items []Item) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func listQueryPredicate(filter ItemFilter, newestFirst bool) (string, []any) {
|
||||
func listQueryPredicate(filter model.ItemFilter, newestFirst bool) (string, []any) {
|
||||
cond := make([]string, 0)
|
||||
args := make([]any, 0)
|
||||
if filter.FolderID != nil {
|
||||
@@ -169,13 +170,13 @@ func (s *SQLiteStorage) CountItems() int {
|
||||
}
|
||||
|
||||
func (s *SQLiteStorage) ListItems(
|
||||
filter ItemFilter,
|
||||
filter model.ItemFilter,
|
||||
limit int,
|
||||
newestFirst bool,
|
||||
withContent bool,
|
||||
) []Item {
|
||||
) []model.Item {
|
||||
predicate, args := listQueryPredicate(filter, newestFirst)
|
||||
result := make([]Item, 0)
|
||||
result := make([]model.Item, 0)
|
||||
|
||||
order := "date desc, id desc"
|
||||
if !newestFirst {
|
||||
@@ -207,11 +208,11 @@ func (s *SQLiteStorage) ListItems(
|
||||
return result
|
||||
}
|
||||
for rows.Next() {
|
||||
var x Item
|
||||
var x model.Item
|
||||
err = rows.Scan(
|
||||
&x.Id, &x.GUID, &x.FeedId,
|
||||
&x.Title, &x.Link, &x.Date,
|
||||
&x.Status, &x.MediaLinks, &x.Content,
|
||||
&x.Status, (*MediaLinks)(&x.MediaLinks), &x.Content,
|
||||
)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
@@ -222,8 +223,8 @@ func (s *SQLiteStorage) ListItems(
|
||||
return result
|
||||
}
|
||||
|
||||
func (s *SQLiteStorage) GetItem(id int64) *Item {
|
||||
i := &Item{}
|
||||
func (s *SQLiteStorage) GetItem(id int64) *model.Item {
|
||||
i := &model.Item{}
|
||||
err := s.db.QueryRow(`
|
||||
select
|
||||
i.id, i.guid, i.feed_id, i.title, i.link, i.content,
|
||||
@@ -232,7 +233,7 @@ func (s *SQLiteStorage) GetItem(id int64) *Item {
|
||||
where i.id = :id
|
||||
`, sql.Named("id", id)).Scan(
|
||||
&i.Id, &i.GUID, &i.FeedId, &i.Title, &i.Link, &i.Content,
|
||||
&i.Date, &i.Status, &i.MediaLinks,
|
||||
&i.Date, &i.Status, (*MediaLinks)(&i.MediaLinks),
|
||||
)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
@@ -241,7 +242,7 @@ func (s *SQLiteStorage) GetItem(id int64) *Item {
|
||||
return i
|
||||
}
|
||||
|
||||
func (s *SQLiteStorage) UpdateItemStatus(item_id int64, status ItemStatus) bool {
|
||||
func (s *SQLiteStorage) UpdateItemStatus(item_id int64, status model.ItemStatus) bool {
|
||||
_, err := s.db.Exec(`update items set status = :status where id = :id`,
|
||||
sql.Named("status", status),
|
||||
sql.Named("id", item_id),
|
||||
@@ -249,8 +250,8 @@ func (s *SQLiteStorage) UpdateItemStatus(item_id int64, status ItemStatus) bool
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func (s *SQLiteStorage) MarkItemsRead(filter MarkFilter) bool {
|
||||
predicate, args := listQueryPredicate(ItemFilter{
|
||||
func (s *SQLiteStorage) MarkItemsRead(filter model.MarkFilter) bool {
|
||||
predicate, args := listQueryPredicate(model.ItemFilter{
|
||||
FolderID: filter.FolderID,
|
||||
FeedID: filter.FeedID,
|
||||
Before: filter.Before,
|
||||
@@ -258,7 +259,7 @@ func (s *SQLiteStorage) MarkItemsRead(filter MarkFilter) bool {
|
||||
query := fmt.Sprintf(`
|
||||
update items as i set status = %d
|
||||
where %s and i.status != %d
|
||||
`, READ, predicate, STARRED)
|
||||
`, model.READ, predicate, model.STARRED)
|
||||
_, err := s.db.Exec(query, args...)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
@@ -266,14 +267,8 @@ func (s *SQLiteStorage) MarkItemsRead(filter MarkFilter) bool {
|
||||
return err == nil
|
||||
}
|
||||
|
||||
type FeedStat struct {
|
||||
FeedId int64 `json:"feed_id"`
|
||||
UnreadCount int64 `json:"unread"`
|
||||
StarredCount int64 `json:"starred"`
|
||||
}
|
||||
|
||||
func (s *SQLiteStorage) FeedStats() []FeedStat {
|
||||
result := make([]FeedStat, 0)
|
||||
func (s *SQLiteStorage) FeedStats() []model.FeedStat {
|
||||
result := make([]model.FeedStat, 0)
|
||||
rows, err := s.db.Query(fmt.Sprintf(`
|
||||
select
|
||||
feed_id,
|
||||
@@ -281,13 +276,13 @@ func (s *SQLiteStorage) FeedStats() []FeedStat {
|
||||
sum(case status when %d then 1 else 0 end)
|
||||
from items
|
||||
group by feed_id
|
||||
`, UNREAD, STARRED))
|
||||
`, model.UNREAD, model.STARRED))
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return result
|
||||
}
|
||||
for rows.Next() {
|
||||
stat := FeedStat{}
|
||||
stat := model.FeedStat{}
|
||||
rows.Scan(&stat.FeedId, &stat.UnreadCount, &stat.StarredCount)
|
||||
result = append(result, stat)
|
||||
}
|
||||
@@ -322,7 +317,7 @@ func (s *SQLiteStorage) DeleteOldItems() {
|
||||
where rn > :keep_size
|
||||
and last_arrived < datetime(max_la, :keep_days_limit)
|
||||
)`,
|
||||
sql.Named("starred_status", STARRED),
|
||||
sql.Named("starred_status", model.STARRED),
|
||||
sql.Named("keep_size", itemsKeepSize),
|
||||
sql.Named("keep_days_limit", fmt.Sprintf("-%d days", itemsKeepDays)),
|
||||
)
|
||||
|
||||
@@ -8,6 +8,8 @@ import (
|
||||
"testing"
|
||||
"testing/synctest"
|
||||
"time"
|
||||
|
||||
"github.com/nkanaev/yarr/src/storage/model"
|
||||
)
|
||||
|
||||
/*
|
||||
@@ -30,22 +32,22 @@ import (
|
||||
*/
|
||||
|
||||
type testItemScope struct {
|
||||
feed11, feed12 *Feed
|
||||
feed21, feed01 *Feed
|
||||
folder1, folder2 *Folder
|
||||
feed11, feed12 *model.Feed
|
||||
feed21, feed01 *model.Feed
|
||||
folder1, folder2 *model.Folder
|
||||
}
|
||||
|
||||
func testItemsSetup(db *SQLiteStorage) testItemScope {
|
||||
folder1 := db.CreateFolder("folder1")
|
||||
folder2 := db.CreateFolder("folder2")
|
||||
|
||||
feed11 := db.CreateFeed(CreateFeedParams{Title: "feed11", FeedLink: "http://test.com/feed11.xml", FolderID: &folder1.Id})
|
||||
feed12 := db.CreateFeed(CreateFeedParams{Title: "feed12", FeedLink: "http://test.com/feed12.xml", FolderID: &folder1.Id})
|
||||
feed21 := db.CreateFeed(CreateFeedParams{Title: "feed21", FeedLink: "http://test.com/feed21.xml", FolderID: &folder2.Id})
|
||||
feed01 := db.CreateFeed(CreateFeedParams{Title: "feed01", FeedLink: "http://test.com/feed01.xml"})
|
||||
feed11 := db.CreateFeed(model.CreateFeedParams{Title: "feed11", FeedLink: "http://test.com/feed11.xml", FolderID: &folder1.Id})
|
||||
feed12 := db.CreateFeed(model.CreateFeedParams{Title: "feed12", FeedLink: "http://test.com/feed12.xml", FolderID: &folder1.Id})
|
||||
feed21 := db.CreateFeed(model.CreateFeedParams{Title: "feed21", FeedLink: "http://test.com/feed21.xml", FolderID: &folder2.Id})
|
||||
feed01 := db.CreateFeed(model.CreateFeedParams{Title: "feed01", FeedLink: "http://test.com/feed01.xml"})
|
||||
|
||||
now := time.Now()
|
||||
db.CreateItems([]Item{
|
||||
db.CreateItems([]model.Item{
|
||||
// feed11
|
||||
{GUID: "item111", FeedId: feed11.Id, Title: "title111", Date: now.Add(time.Hour * 24 * 1)},
|
||||
{
|
||||
@@ -98,11 +100,11 @@ func testItemsSetup(db *SQLiteStorage) testItemScope {
|
||||
})
|
||||
db.db.Exec(
|
||||
`update items set status = :status where guid in ("item112", "item122", "item211", "item012")`,
|
||||
sql.Named("status", READ),
|
||||
sql.Named("status", model.READ),
|
||||
)
|
||||
db.db.Exec(
|
||||
`update items set status = :status where guid in ("item113", "item212", "item013")`,
|
||||
sql.Named("status", STARRED),
|
||||
sql.Named("status", model.STARRED),
|
||||
)
|
||||
|
||||
return testItemScope{
|
||||
@@ -115,8 +117,8 @@ func testItemsSetup(db *SQLiteStorage) testItemScope {
|
||||
}
|
||||
}
|
||||
|
||||
func getItem(db *SQLiteStorage, guid string) *Item {
|
||||
i := &Item{}
|
||||
func getItem(db *SQLiteStorage, guid string) *model.Item {
|
||||
i := &model.Item{}
|
||||
err := db.db.QueryRow(`
|
||||
select
|
||||
i.id, i.guid, i.feed_id, i.title, i.link, i.content,
|
||||
@@ -125,7 +127,7 @@ func getItem(db *SQLiteStorage, guid string) *Item {
|
||||
where i.guid = :guid
|
||||
`, sql.Named("guid", guid)).Scan(
|
||||
&i.Id, &i.GUID, &i.FeedId, &i.Title, &i.Link, &i.Content,
|
||||
&i.Date, &i.Status, &i.MediaLinks,
|
||||
&i.Date, &i.Status, (*MediaLinks)(&i.MediaLinks),
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
@@ -133,7 +135,7 @@ func getItem(db *SQLiteStorage, guid string) *Item {
|
||||
return i
|
||||
}
|
||||
|
||||
func getItemGuids(items []Item) []string {
|
||||
func getItemGuids(items []model.Item) []string {
|
||||
guids := make([]string, 0)
|
||||
for _, item := range items {
|
||||
guids = append(guids, item.GUID)
|
||||
@@ -147,7 +149,7 @@ func TestListItems(t *testing.T) {
|
||||
|
||||
// filter by folder_id
|
||||
|
||||
have := getItemGuids(db.ListItems(ItemFilter{FolderID: &scope.folder1.Id}, 10, false, false))
|
||||
have := getItemGuids(db.ListItems(model.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)
|
||||
@@ -155,7 +157,7 @@ func TestListItems(t *testing.T) {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
have = getItemGuids(db.ListItems(ItemFilter{FolderID: &scope.folder2.Id}, 10, false, false))
|
||||
have = getItemGuids(db.ListItems(model.ItemFilter{FolderID: &scope.folder2.Id}, 10, false, false))
|
||||
want = []string{"item211", "item212"}
|
||||
if !reflect.DeepEqual(have, want) {
|
||||
t.Logf("want: %#v", want)
|
||||
@@ -165,7 +167,7 @@ func TestListItems(t *testing.T) {
|
||||
|
||||
// filter by feed_id
|
||||
|
||||
have = getItemGuids(db.ListItems(ItemFilter{FeedID: &scope.feed11.Id}, 10, false, false))
|
||||
have = getItemGuids(db.ListItems(model.ItemFilter{FeedID: &scope.feed11.Id}, 10, false, false))
|
||||
want = []string{"item111", "item112", "item113"}
|
||||
if !reflect.DeepEqual(have, want) {
|
||||
t.Logf("want: %#v", want)
|
||||
@@ -173,7 +175,7 @@ func TestListItems(t *testing.T) {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
have = getItemGuids(db.ListItems(ItemFilter{FeedID: &scope.feed01.Id}, 10, false, false))
|
||||
have = getItemGuids(db.ListItems(model.ItemFilter{FeedID: &scope.feed01.Id}, 10, false, false))
|
||||
want = []string{"item011", "item012", "item013"}
|
||||
if !reflect.DeepEqual(have, want) {
|
||||
t.Logf("want: %#v", want)
|
||||
@@ -183,8 +185,8 @@ func TestListItems(t *testing.T) {
|
||||
|
||||
// filter by status
|
||||
|
||||
var starred ItemStatus = STARRED
|
||||
have = getItemGuids(db.ListItems(ItemFilter{Status: &starred}, 10, false, false))
|
||||
var starred model.ItemStatus = model.STARRED
|
||||
have = getItemGuids(db.ListItems(model.ItemFilter{Status: &starred}, 10, false, false))
|
||||
want = []string{"item113", "item212", "item013"}
|
||||
if !reflect.DeepEqual(have, want) {
|
||||
t.Logf("want: %#v", want)
|
||||
@@ -192,8 +194,8 @@ func TestListItems(t *testing.T) {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
var unread ItemStatus = UNREAD
|
||||
have = getItemGuids(db.ListItems(ItemFilter{Status: &unread}, 10, false, false))
|
||||
var unread model.ItemStatus = model.UNREAD
|
||||
have = getItemGuids(db.ListItems(model.ItemFilter{Status: &unread}, 10, false, false))
|
||||
want = []string{"item111", "item121", "item011"}
|
||||
if !reflect.DeepEqual(have, want) {
|
||||
t.Logf("want: %#v", want)
|
||||
@@ -203,7 +205,7 @@ func TestListItems(t *testing.T) {
|
||||
|
||||
// limit
|
||||
|
||||
have = getItemGuids(db.ListItems(ItemFilter{}, 2, false, false))
|
||||
have = getItemGuids(db.ListItems(model.ItemFilter{}, 2, false, false))
|
||||
want = []string{"item111", "item112"}
|
||||
if !reflect.DeepEqual(have, want) {
|
||||
t.Logf("want: %#v", want)
|
||||
@@ -213,7 +215,7 @@ func TestListItems(t *testing.T) {
|
||||
|
||||
// filter by search
|
||||
search1 := "title111"
|
||||
have = getItemGuids(db.ListItems(ItemFilter{Search: &search1}, 4, true, false))
|
||||
have = getItemGuids(db.ListItems(model.ItemFilter{Search: &search1}, 4, true, false))
|
||||
want = []string{"item111"}
|
||||
if !reflect.DeepEqual(have, want) {
|
||||
t.Logf("want: %#v", want)
|
||||
@@ -222,7 +224,7 @@ func TestListItems(t *testing.T) {
|
||||
}
|
||||
|
||||
// sort by date
|
||||
have = getItemGuids(db.ListItems(ItemFilter{}, 4, true, false))
|
||||
have = getItemGuids(db.ListItems(model.ItemFilter{}, 4, true, false))
|
||||
want = []string{"item013", "item012", "item011", "item212"}
|
||||
if !reflect.DeepEqual(have, want) {
|
||||
t.Logf("want: %#v", want)
|
||||
@@ -239,7 +241,7 @@ func TestListItemsPaginated(t *testing.T) {
|
||||
item121 := getItem(db, "item121")
|
||||
|
||||
// all, newest first
|
||||
have := getItemGuids(db.ListItems(ItemFilter{After: &item012.Id}, 3, true, false))
|
||||
have := getItemGuids(db.ListItems(model.ItemFilter{After: &item012.Id}, 3, true, false))
|
||||
want := []string{"item011", "item212", "item211"}
|
||||
if !reflect.DeepEqual(have, want) {
|
||||
t.Logf("want: %#v", want)
|
||||
@@ -248,9 +250,9 @@ func TestListItemsPaginated(t *testing.T) {
|
||||
}
|
||||
|
||||
// unread, newest first
|
||||
unread := UNREAD
|
||||
unread := model.UNREAD
|
||||
have = getItemGuids(
|
||||
db.ListItems(ItemFilter{After: &item012.Id, Status: &unread}, 3, true, false),
|
||||
db.ListItems(model.ItemFilter{After: &item012.Id, Status: &unread}, 3, true, false),
|
||||
)
|
||||
want = []string{"item011", "item121", "item111"}
|
||||
if !reflect.DeepEqual(have, want) {
|
||||
@@ -260,9 +262,9 @@ func TestListItemsPaginated(t *testing.T) {
|
||||
}
|
||||
|
||||
// starred, oldest first
|
||||
starred := STARRED
|
||||
starred := model.STARRED
|
||||
have = getItemGuids(
|
||||
db.ListItems(ItemFilter{After: &item121.Id, Status: &starred}, 3, false, false),
|
||||
db.ListItems(model.ItemFilter{After: &item121.Id, Status: &starred}, 3, false, false),
|
||||
)
|
||||
want = []string{"item212", "item013"}
|
||||
if !reflect.DeepEqual(have, want) {
|
||||
@@ -274,12 +276,12 @@ func TestListItemsPaginated(t *testing.T) {
|
||||
|
||||
func TestMarkItemsRead(t *testing.T) {
|
||||
// NOTE: starred items must not be marked as read
|
||||
var read ItemStatus = READ
|
||||
var read model.ItemStatus = model.READ
|
||||
|
||||
db1 := testDB()
|
||||
testItemsSetup(db1)
|
||||
db1.MarkItemsRead(MarkFilter{})
|
||||
have := getItemGuids(db1.ListItems(ItemFilter{Status: &read}, 10, false, false))
|
||||
db1.MarkItemsRead(model.MarkFilter{})
|
||||
have := getItemGuids(db1.ListItems(model.ItemFilter{Status: &read}, 10, false, false))
|
||||
want := []string{
|
||||
"item111", "item112", "item121", "item122",
|
||||
"item211", "item011", "item012",
|
||||
@@ -292,8 +294,8 @@ func TestMarkItemsRead(t *testing.T) {
|
||||
|
||||
db2 := testDB()
|
||||
scope2 := testItemsSetup(db2)
|
||||
db2.MarkItemsRead(MarkFilter{FolderID: &scope2.folder1.Id})
|
||||
have = getItemGuids(db2.ListItems(ItemFilter{Status: &read}, 10, false, false))
|
||||
db2.MarkItemsRead(model.MarkFilter{FolderID: &scope2.folder1.Id})
|
||||
have = getItemGuids(db2.ListItems(model.ItemFilter{Status: &read}, 10, false, false))
|
||||
want = []string{
|
||||
"item111", "item112", "item121", "item122",
|
||||
"item211", "item012",
|
||||
@@ -306,8 +308,8 @@ func TestMarkItemsRead(t *testing.T) {
|
||||
|
||||
db3 := testDB()
|
||||
scope3 := testItemsSetup(db3)
|
||||
db3.MarkItemsRead(MarkFilter{FeedID: &scope3.feed11.Id})
|
||||
have = getItemGuids(db3.ListItems(ItemFilter{Status: &read}, 10, false, false))
|
||||
db3.MarkItemsRead(model.MarkFilter{FeedID: &scope3.feed11.Id})
|
||||
have = getItemGuids(db3.ListItems(model.ItemFilter{Status: &read}, 10, false, false))
|
||||
want = []string{
|
||||
"item111", "item112", "item122",
|
||||
"item211", "item012",
|
||||
@@ -321,14 +323,14 @@ func TestMarkItemsRead(t *testing.T) {
|
||||
|
||||
func TestDeleteOldItems(t *testing.T) {
|
||||
now := time.Now().UTC()
|
||||
starred := STARRED
|
||||
starred := model.STARRED
|
||||
|
||||
t.Run("keeps at least 50 items", func(t *testing.T) {
|
||||
db := testDB()
|
||||
feed := db.CreateFeed(CreateFeedParams{Title: "f", FeedLink: "http://f.xml"})
|
||||
items := make([]Item, 100)
|
||||
feed := db.CreateFeed(model.CreateFeedParams{Title: "f", FeedLink: "http://f.xml"})
|
||||
items := make([]model.Item, 100)
|
||||
for i := range 100 {
|
||||
items[i] = Item{GUID: strconv.Itoa(i), FeedId: feed.Id, Date: now.Add(time.Duration(i) * time.Hour * 24)}
|
||||
items[i] = model.Item{GUID: strconv.Itoa(i), FeedId: feed.Id, Date: now.Add(time.Duration(i) * time.Hour * 24)}
|
||||
}
|
||||
db.CreateItems(items)
|
||||
|
||||
@@ -346,10 +348,10 @@ func TestDeleteOldItems(t *testing.T) {
|
||||
|
||||
t.Run("keeps all less than 90 days old", func(t *testing.T) {
|
||||
db := testDB()
|
||||
feed := db.CreateFeed(CreateFeedParams{Title: "f", FeedLink: "http://f.xml"})
|
||||
items := make([]Item, 100)
|
||||
feed := db.CreateFeed(model.CreateFeedParams{Title: "f", FeedLink: "http://f.xml"})
|
||||
items := make([]model.Item, 100)
|
||||
for i := 0; i < 100; i++ {
|
||||
items[i] = Item{GUID: strconv.Itoa(i), FeedId: feed.Id, Date: now.Add(time.Duration(i) * time.Second)}
|
||||
items[i] = model.Item{GUID: strconv.Itoa(i), FeedId: feed.Id, Date: now.Add(time.Duration(i) * time.Second)}
|
||||
}
|
||||
db.CreateItems(items)
|
||||
|
||||
@@ -368,10 +370,10 @@ func TestDeleteOldItems(t *testing.T) {
|
||||
|
||||
t.Run("keeps starred", func(t *testing.T) {
|
||||
db := testDB()
|
||||
feed := db.CreateFeed(CreateFeedParams{Title: "f", FeedLink: "http://f.xml"})
|
||||
items := make([]Item, 100)
|
||||
feed := db.CreateFeed(model.CreateFeedParams{Title: "f", FeedLink: "http://f.xml"})
|
||||
items := make([]model.Item, 100)
|
||||
for i := 0; i < 100; i++ {
|
||||
items[i] = Item{GUID: strconv.Itoa(i), FeedId: feed.Id, Date: now.Add(time.Duration(i) * time.Second)}
|
||||
items[i] = model.Item{GUID: strconv.Itoa(i), FeedId: feed.Id, Date: now.Add(time.Duration(i) * time.Second)}
|
||||
}
|
||||
db.CreateItems(items)
|
||||
|
||||
@@ -397,9 +399,9 @@ func TestCreateItemsLastArrived(t *testing.T) {
|
||||
synctest.Test(t, func(t *testing.T) {
|
||||
db := testDB()
|
||||
defer db.db.Close()
|
||||
feed := db.CreateFeed(CreateFeedParams{Title: "test feed", FeedLink: "http://example.com/feed"})
|
||||
feed := db.CreateFeed(model.CreateFeedParams{Title: "test feed", FeedLink: "http://example.com/feed"})
|
||||
|
||||
item := Item{
|
||||
item := model.Item{
|
||||
GUID: "item1",
|
||||
FeedId: feed.Id,
|
||||
Title: "Title 1",
|
||||
@@ -407,7 +409,7 @@ func TestCreateItemsLastArrived(t *testing.T) {
|
||||
}
|
||||
|
||||
// 1. Initial creation
|
||||
db.CreateItems([]Item{item})
|
||||
db.CreateItems([]model.Item{item})
|
||||
|
||||
var lastArrived1 time.Time
|
||||
err := db.db.QueryRow("select last_arrived from items where guid = ?", item.GUID).Scan(&lastArrived1)
|
||||
@@ -418,7 +420,7 @@ func TestCreateItemsLastArrived(t *testing.T) {
|
||||
time.Sleep(time.Second * 10)
|
||||
|
||||
// 2. Update on conflict
|
||||
db.CreateItems([]Item{item})
|
||||
db.CreateItems([]model.Item{item})
|
||||
|
||||
var lastArrived2 time.Time
|
||||
err = db.db.QueryRow("select last_arrived from items where guid = ?", item.GUID).Scan(&lastArrived2)
|
||||
@@ -435,9 +437,9 @@ func TestCreateItemsLastArrived(t *testing.T) {
|
||||
func TestSearch(t *testing.T) {
|
||||
db := testDB()
|
||||
defer db.Close()
|
||||
feed := db.CreateFeed(CreateFeedParams{Title: "f", FeedLink: "http://f.xml"})
|
||||
feed := db.CreateFeed(model.CreateFeedParams{Title: "f", FeedLink: "http://f.xml"})
|
||||
|
||||
db.CreateItems([]Item{
|
||||
db.CreateItems([]model.Item{
|
||||
{
|
||||
GUID: "i1",
|
||||
FeedId: feed.Id,
|
||||
@@ -460,40 +462,40 @@ func TestSearch(t *testing.T) {
|
||||
|
||||
// 1. Basic search
|
||||
s1 := "emergency"
|
||||
have := getItemGuids(db.ListItems(ItemFilter{Search: &s1}, 10, true, false))
|
||||
have := getItemGuids(db.ListItems(model.ItemFilter{Search: &s1}, 10, true, false))
|
||||
if !reflect.DeepEqual(have, []string{"i1"}) {
|
||||
t.Errorf("basic search failed: expected [i1], got %v", have)
|
||||
}
|
||||
|
||||
// 2. HTML stripping: Should find text, but NOT the tags
|
||||
s2 := "test"
|
||||
have = getItemGuids(db.ListItems(ItemFilter{Search: &s2}, 10, true, false))
|
||||
have = getItemGuids(db.ListItems(model.ItemFilter{Search: &s2}, 10, true, false))
|
||||
if !reflect.DeepEqual(have, []string{"i1"}) {
|
||||
t.Errorf("html text search failed: expected [i1], got %v", have)
|
||||
}
|
||||
|
||||
s3 := "secret-class"
|
||||
have = getItemGuids(db.ListItems(ItemFilter{Search: &s3}, 10, true, false))
|
||||
have = getItemGuids(db.ListItems(model.ItemFilter{Search: &s3}, 10, true, false))
|
||||
if len(have) > 0 {
|
||||
t.Errorf("html tag search should have failed but found: %v", have)
|
||||
}
|
||||
|
||||
// 3. Multi-word (AND)
|
||||
s4 := "broadcast system"
|
||||
have = getItemGuids(db.ListItems(ItemFilter{Search: &s4}, 10, true, false))
|
||||
have = getItemGuids(db.ListItems(model.ItemFilter{Search: &s4}, 10, true, false))
|
||||
if !reflect.DeepEqual(have, []string{"i1"}) {
|
||||
t.Errorf("multi-word search failed: expected [i1], got %v", have)
|
||||
}
|
||||
|
||||
// 4. Unicode
|
||||
s5 := "Привет"
|
||||
have = getItemGuids(db.ListItems(ItemFilter{Search: &s5}, 10, true, false))
|
||||
have = getItemGuids(db.ListItems(model.ItemFilter{Search: &s5}, 10, true, false))
|
||||
if !reflect.DeepEqual(have, []string{"i2"}) {
|
||||
t.Errorf("unicode search failed: expected [i2], got %v", have)
|
||||
}
|
||||
|
||||
s6 := "世界"
|
||||
have = getItemGuids(db.ListItems(ItemFilter{Search: &s6}, 10, true, false))
|
||||
have = getItemGuids(db.ListItems(model.ItemFilter{Search: &s6}, 10, true, false))
|
||||
if !reflect.DeepEqual(have, []string{"i2"}) {
|
||||
t.Errorf("unicode search (CJK) failed: expected [i2], got %v", have)
|
||||
}
|
||||
@@ -501,14 +503,14 @@ func TestSearch(t *testing.T) {
|
||||
// 5. Trigger: Update
|
||||
db.db.Exec("update items set title = 'Updated Title' where guid = 'i1'")
|
||||
s7 := "Updated"
|
||||
have = getItemGuids(db.ListItems(ItemFilter{Search: &s7}, 10, true, false))
|
||||
have = getItemGuids(db.ListItems(model.ItemFilter{Search: &s7}, 10, true, false))
|
||||
if !reflect.DeepEqual(have, []string{"i1"}) {
|
||||
t.Errorf("update trigger failed: expected [i1], got %v", have)
|
||||
}
|
||||
|
||||
// 6. Trigger: Delete
|
||||
db.db.Exec("delete from items where guid = 'i1'")
|
||||
have = getItemGuids(db.ListItems(ItemFilter{Search: &s7}, 10, true, false))
|
||||
have = getItemGuids(db.ListItems(model.ItemFilter{Search: &s7}, 10, true, false))
|
||||
if len(have) > 0 {
|
||||
t.Errorf("delete trigger failed: found deleted item: %v", have)
|
||||
}
|
||||
|
||||
@@ -4,10 +4,12 @@ import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"log"
|
||||
|
||||
"github.com/nkanaev/yarr/src/storage/model"
|
||||
)
|
||||
|
||||
func settingsDefaults() Settings {
|
||||
return Settings{
|
||||
func settingsDefaults() model.Settings {
|
||||
return model.Settings{
|
||||
Filter: "",
|
||||
Feed: "",
|
||||
FeedListWidth: 300,
|
||||
@@ -21,7 +23,7 @@ func settingsDefaults() Settings {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SQLiteStorage) GetSettings() Settings {
|
||||
func (s *SQLiteStorage) GetSettings() model.Settings {
|
||||
result := settingsDefaults()
|
||||
rows, err := s.db.Query(`select key, val from settings;`)
|
||||
if err != nil {
|
||||
@@ -61,7 +63,7 @@ func (s *SQLiteStorage) GetSettings() Settings {
|
||||
return result
|
||||
}
|
||||
|
||||
func (s *SQLiteStorage) UpdateSettings(params UpdateSettingsParams) bool {
|
||||
func (s *SQLiteStorage) UpdateSettings(params model.UpdateSettingsParams) bool {
|
||||
tx, err := s.db.Begin()
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/nkanaev/yarr/src/storage/model"
|
||||
)
|
||||
|
||||
func TestSettingsDefaults(t *testing.T) {
|
||||
@@ -22,7 +24,7 @@ func TestUpdateSettings(t *testing.T) {
|
||||
s := testDB()
|
||||
defer s.Close()
|
||||
|
||||
params := UpdateSettingsParams{
|
||||
params := model.UpdateSettingsParams{
|
||||
ThemeName: ptr("night"),
|
||||
FeedListWidth: ptr(400),
|
||||
RefreshRate: ptr(int64(15)),
|
||||
@@ -49,7 +51,7 @@ func TestGetSettings(t *testing.T) {
|
||||
s := testDB()
|
||||
defer s.Close()
|
||||
|
||||
s.UpdateSettings(UpdateSettingsParams{Language: ptr("fr")})
|
||||
s.UpdateSettings(model.UpdateSettingsParams{Language: ptr("fr")})
|
||||
|
||||
settings := s.GetSettings()
|
||||
if settings.Language != "fr" {
|
||||
@@ -64,8 +66,8 @@ func TestSettingsExhaustive(t *testing.T) {
|
||||
s := testDB()
|
||||
defer s.Close()
|
||||
|
||||
settingsType := reflect.TypeOf(Settings{})
|
||||
paramsType := reflect.TypeOf(UpdateSettingsParams{})
|
||||
settingsType := reflect.TypeOf(model.Settings{})
|
||||
paramsType := reflect.TypeOf(model.UpdateSettingsParams{})
|
||||
|
||||
settings := s.GetSettings()
|
||||
m := settings.Map()
|
||||
@@ -125,7 +127,7 @@ func TestSettingsExhaustive(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
if ok := s.UpdateSettings(paramsValue.Interface().(UpdateSettingsParams)); !ok {
|
||||
if ok := s.UpdateSettings(paramsValue.Interface().(model.UpdateSettingsParams)); !ok {
|
||||
t.Errorf("UpdateSettings failed for %q", jsonKey)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user