Skip to content

Commit

Permalink
feat: add new list commands
Browse files Browse the repository at this point in the history
- Added `--rename` flag to rename a list
- Added `--merge` flag to merge a list with another list
- Added `--remove` flag to remove a list
  • Loading branch information
radulucut committed Aug 20, 2024
1 parent 75002eb commit 6ad1442
Show file tree
Hide file tree
Showing 5 changed files with 422 additions and 5 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,15 @@ cleed list

# Show all feeds in a list
cleed list mylist

# Rename a list
cleed list mylist --rename newlist

# Merge a list. Move all feeds from anotherlist to mylist and remove anotherlist
cleed list mylist --merge anotherlist

# Remove a list
cleed list mylist --remove
```

#### Configuration
Expand Down
29 changes: 28 additions & 1 deletion cmd/cleed/list.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package cleed

import "github.com/spf13/cobra"
import (
"github.com/spf13/cobra"
)

func (r *Root) initList() {
cmd := &cobra.Command{
Expand All @@ -14,18 +16,43 @@ Examples:
# Show all feeds in a list
cleed list mylist
# Rename a list
cleed list mylist --rename newlist
# Merge a list. Move all feeds from anotherlist to mylist and remove anotherlist
cleed list mylist --merge anotherlist
# Remove a list
cleed list mylist --remove
`,

RunE: r.RunList,
Args: cobra.MaximumNArgs(1),
}

flags := cmd.Flags()
flags.String("rename", "", "rename a list")
flags.String("merge", "", "merge a list")
flags.Bool("remove", false, "remove a list")

r.Cmd.AddCommand(cmd)
}

func (r *Root) RunList(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return r.feed.Lists()
}
rename := cmd.Flag("rename").Value.String()
if rename != "" {
return r.feed.RenameList(args[0], rename)
}
merge := cmd.Flag("merge").Value.String()
if merge != "" {
return r.feed.MergeLists(args[0], merge)
}
if cmd.Flag("remove").Changed {
return r.feed.RemoveList(args[0])
}
return r.feed.ListFeeds(args[0])
}
279 changes: 275 additions & 4 deletions cmd/cleed/list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ package cleed
import (
"bytes"
"fmt"
"net/url"
"os"
"path"
"testing"
"time"

"github.com/radulucut/cleed/internal"
"github.com/radulucut/cleed/internal/storage"
_storage "github.com/radulucut/cleed/internal/storage"
"github.com/radulucut/cleed/mocks"
"github.com/stretchr/testify/assert"
"go.uber.org/mock/gomock"
Expand All @@ -23,7 +25,7 @@ func Test_List(t *testing.T) {

out := new(bytes.Buffer)
printer := internal.NewPrinter(nil, out, out)
storage := storage.NewLocalStorage("cleed_test", timeMock)
storage := _storage.NewLocalStorage("cleed_test", timeMock)
defer localStorageCleanup(t, storage)

configDir, err := os.UserConfigDir()
Expand Down Expand Up @@ -72,7 +74,7 @@ func Test_List_No_List(t *testing.T) {

out := new(bytes.Buffer)
printer := internal.NewPrinter(nil, out, out)
storage := storage.NewLocalStorage("cleed_test", timeMock)
storage := _storage.NewLocalStorage("cleed_test", timeMock)
defer localStorageCleanup(t, storage)

feed := internal.NewTerminalFeed(timeMock, printer, storage)
Expand All @@ -97,7 +99,7 @@ func Test_List_Feeds(t *testing.T) {

out := new(bytes.Buffer)
printer := internal.NewPrinter(nil, out, out)
storage := storage.NewLocalStorage("cleed_test", timeMock)
storage := _storage.NewLocalStorage("cleed_test", timeMock)
defer localStorageCleanup(t, storage)

configDir, err := os.UserConfigDir()
Expand Down Expand Up @@ -139,3 +141,272 @@ func Test_List_Feeds(t *testing.T) {
"Total: 2 feeds",
), out.String())
}

func Test_List_Rename(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

timeMock := mocks.NewMockTime(ctrl)
timeMock.EXPECT().Now().Return(defaultCurrentTime).AnyTimes()

out := new(bytes.Buffer)
printer := internal.NewPrinter(nil, out, out)
storage := _storage.NewLocalStorage("cleed_test", timeMock)
defer localStorageCleanup(t, storage)

configDir, err := os.UserConfigDir()
if err != nil {
t.Fatal(err)
}

listsDir := path.Join(configDir, "cleed_test", "lists")
err = os.MkdirAll(listsDir, 0700)
if err != nil {
t.Fatal(err)
}

err = os.WriteFile(path.Join(listsDir, "test"),
[]byte(fmt.Sprintf("%d %s\n%d %s\n",
defaultCurrentTime.Unix(), "https://example.com",
defaultCurrentTime.Unix()+300, "https://test.com",
),
), 0600)
if err != nil {
t.Fatal(err)
}

feed := internal.NewTerminalFeed(timeMock, printer, storage)
feed.SetAgent("cleed/test")

root, err := NewRoot("0.1.0", timeMock, printer, storage, feed)
assert.NoError(t, err)

os.Args = []string{"cleed", "list", "test", "--rename", "newlist"}

err = root.Cmd.Execute()
assert.NoError(t, err)
assert.Equal(t, "list test was renamed to newlist\n", out.String())

lists, err := storage.LoadLists()
assert.NoError(t, err)
assert.Equal(t, []string{"newlist"}, lists)

items, err := storage.GetFeedsFromList("newlist")
assert.NoError(t, err)
assert.Equal(t, []*_storage.ListItem{
{AddedAt: time.Unix(defaultCurrentTime.Unix(), 0), Address: "https://example.com"},
{AddedAt: time.Unix(defaultCurrentTime.Unix()+300, 0), Address: "https://test.com"},
}, items)
}

func Test_List_Merge(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

timeMock := mocks.NewMockTime(ctrl)
timeMock.EXPECT().Now().Return(defaultCurrentTime).AnyTimes()

out := new(bytes.Buffer)
printer := internal.NewPrinter(nil, out, out)
storage := _storage.NewLocalStorage("cleed_test", timeMock)
defer localStorageCleanup(t, storage)

configDir, err := os.UserConfigDir()
if err != nil {
t.Fatal(err)
}

listsDir := path.Join(configDir, "cleed_test", "lists")
err = os.MkdirAll(listsDir, 0700)
if err != nil {
t.Fatal(err)
}

err = os.WriteFile(path.Join(listsDir, "test"),
[]byte(fmt.Sprintf("%d %s\n%d %s\n",
defaultCurrentTime.Unix(), "https://example.com",
defaultCurrentTime.Unix()+300, "https://test.com",
),
), 0600)
if err != nil {
t.Fatal(err)
}

err = os.WriteFile(path.Join(listsDir, "test2"),
[]byte(fmt.Sprintf("%d %s\n%d %s\n%d %s\n",
defaultCurrentTime.Unix()-300, "https://example0.com",
defaultCurrentTime.Unix()+400, "https://test.com",
defaultCurrentTime.Unix()+500, "https://example2.com",
),
), 0600)
if err != nil {
t.Fatal(err)
}

feed := internal.NewTerminalFeed(timeMock, printer, storage)
feed.SetAgent("cleed/test")

root, err := NewRoot("0.1.0", timeMock, printer, storage, feed)
assert.NoError(t, err)

os.Args = []string{"cleed", "list", "test", "--merge", "test2"}

err = root.Cmd.Execute()
assert.NoError(t, err)
assert.Equal(t, "list test was merged with test2. test2 was removed\n", out.String())

lists, err := storage.LoadLists()
assert.NoError(t, err)
assert.Equal(t, []string{"test"}, lists)

items, err := storage.GetFeedsFromList("test")
assert.NoError(t, err)
assert.Equal(t, []*_storage.ListItem{
{AddedAt: time.Unix(defaultCurrentTime.Unix()-300, 0), Address: "https://example0.com"},
{AddedAt: time.Unix(defaultCurrentTime.Unix(), 0), Address: "https://example.com"},
{AddedAt: time.Unix(defaultCurrentTime.Unix()+300, 0), Address: "https://test.com"},
{AddedAt: time.Unix(defaultCurrentTime.Unix()+500, 0), Address: "https://example2.com"},
}, items)
}

func Test_List_Remove(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

timeMock := mocks.NewMockTime(ctrl)
timeMock.EXPECT().Now().Return(defaultCurrentTime).AnyTimes()

out := new(bytes.Buffer)
printer := internal.NewPrinter(nil, out, out)
storage := _storage.NewLocalStorage("cleed_test", timeMock)
defer localStorageCleanup(t, storage)

configDir, err := os.UserConfigDir()
if err != nil {
t.Fatal(err)
}
listsDir := path.Join(configDir, "cleed_test", "lists")
err = os.MkdirAll(listsDir, 0700)
if err != nil {
t.Fatal(err)
}

err = os.WriteFile(path.Join(listsDir, "default"),
[]byte(fmt.Sprintf("%d %s\n%d %s\n",
defaultCurrentTime.Unix(), "https://example.com",
defaultCurrentTime.Unix(), "https://test.com",
),
), 0600)
if err != nil {
t.Fatal(err)
}

err = os.WriteFile(path.Join(listsDir, "test"),
[]byte(fmt.Sprintf("%d %s\n%d %s\n",
defaultCurrentTime.Unix(), "https://test.com",
defaultCurrentTime.Unix(), "https://example2.com",
),
), 0600)
if err != nil {
t.Fatal(err)
}

cacheDir, err := os.UserCacheDir()
if err != nil {
t.Fatal(err)
}
cacheDir = path.Join(cacheDir, "cleed_test")
err = os.MkdirAll(cacheDir, 0700)
if err != nil {
t.Fatal(err)
}

err = storage.SaveCacheInfo(map[string]*_storage.CacheInfoItem{
"https://example.com": {
URL: "https://example.com",
LastCheck: defaultCurrentTime,
ETag: "etag",
FetchAfter: time.Unix(0, 0),
},
"https://test.com": {
URL: "https://test.com",
LastCheck: defaultCurrentTime,
ETag: "etag",
FetchAfter: time.Unix(0, 0),
},
"https://example2.com": {
URL: "https://example2.com",
LastCheck: defaultCurrentTime,
ETag: "etag",
FetchAfter: time.Unix(0, 0),
},
})
if err != nil {
t.Fatal(err)
}

err = storage.SaveFeedCache(bytes.NewBuffer([]byte("example")), "https://example.com")
if err != nil {
t.Fatal(err)
}

err = storage.SaveFeedCache(bytes.NewBuffer([]byte("test")), "https://test.com")
if err != nil {
t.Fatal(err)
}

err = storage.SaveFeedCache(bytes.NewBuffer([]byte("example2")), "https://example2.com")
if err != nil {
t.Fatal(err)
}

feed := internal.NewTerminalFeed(timeMock, printer, storage)
feed.SetAgent("cleed/test")

root, err := NewRoot("0.1.0", timeMock, printer, storage, feed)
assert.NoError(t, err)

os.Args = []string{"cleed", "list", "test", "--remove"}

err = root.Cmd.Execute()
assert.NoError(t, err)
assert.Equal(t, "list test was removed\n", out.String())

files, err := os.ReadDir(cacheDir)
if err != nil {
t.Fatal(err)
}
assert.Len(t, files, 3)

cacheInfo, err := storage.LoadCacheInfo()
if err != nil {
t.Fatal(err)
}
assert.Len(t, cacheInfo, 2)
assert.Equal(t, &_storage.CacheInfoItem{
URL: "https://example.com",
LastCheck: time.Unix(defaultCurrentTime.Unix(), 0),
ETag: "etag",
FetchAfter: time.Unix(0, 0),
}, cacheInfo["https://example.com"])
assert.Equal(t, &_storage.CacheInfoItem{
URL: "https://test.com",
LastCheck: time.Unix(defaultCurrentTime.Unix(), 0),
ETag: "etag",
FetchAfter: time.Unix(0, 0),
}, cacheInfo["https://test.com"])

assert.NoFileExists(t, path.Join(cacheDir, "feed_"+url.QueryEscape("https://example2.com")))

b, err := os.ReadFile(path.Join(cacheDir, "feed_"+url.QueryEscape("https://example.com")))
if err != nil {
t.Fatal(err)
}
assert.Equal(t, "example", string(b))

b, err = os.ReadFile(path.Join(cacheDir, "feed_"+url.QueryEscape("https://test.com")))
if err != nil {
t.Fatal(err)
}
assert.Equal(t, "test", string(b))
}
Loading

0 comments on commit 6ad1442

Please sign in to comment.