some date and repo tests
This commit is contained in:
parent
6701b64863
commit
9092336bf9
|
@ -1,9 +1,57 @@
|
||||||
package task
|
package task
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
type Date time.Time
|
type Date struct {
|
||||||
|
t time.Time
|
||||||
func (d *Date) Weekday() Weekday {
|
}
|
||||||
return d.Weekday()
|
|
||||||
|
func NewDate(year, month, day int) *Date {
|
||||||
|
var m time.Month
|
||||||
|
switch month {
|
||||||
|
case 1:
|
||||||
|
m = time.January
|
||||||
|
case 2:
|
||||||
|
m = time.February
|
||||||
|
case 3:
|
||||||
|
m = time.March
|
||||||
|
case 4:
|
||||||
|
m = time.April
|
||||||
|
case 5:
|
||||||
|
m = time.May
|
||||||
|
case 6:
|
||||||
|
m = time.June
|
||||||
|
case 7:
|
||||||
|
m = time.July
|
||||||
|
case 8:
|
||||||
|
m = time.August
|
||||||
|
case 9:
|
||||||
|
m = time.September
|
||||||
|
case 10:
|
||||||
|
m = time.October
|
||||||
|
case 11:
|
||||||
|
m = time.November
|
||||||
|
case 12:
|
||||||
|
m = time.December
|
||||||
|
}
|
||||||
|
|
||||||
|
t := time.Date(year, m, day, 10, 0, 0, 0, time.UTC)
|
||||||
|
|
||||||
|
if year == 0 && month == 0 && day == 0 {
|
||||||
|
t = time.Time{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Date{
|
||||||
|
t: t,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Date) String() string {
|
||||||
|
if d.t.IsZero() {
|
||||||
|
return "no date"
|
||||||
|
}
|
||||||
|
|
||||||
|
return d.t.Format("2006-01-02")
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
package task_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.sr.ht/~ewintr/go-kit/test"
|
||||||
|
"git.sr.ht/~ewintr/gte/internal/task"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDateString(t *testing.T) {
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
date *task.Date
|
||||||
|
exp string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "zero",
|
||||||
|
date: task.NewDate(0, 0, 0),
|
||||||
|
exp: "no date",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "normal",
|
||||||
|
date: task.NewDate(2021, 1, 30),
|
||||||
|
exp: "2021-01-30",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "normalize",
|
||||||
|
date: task.NewDate(2021, 1, 32),
|
||||||
|
exp: "2021-02-01",
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
test.Equals(t, tc.exp, tc.date.String())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,7 +8,9 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrMStoreError = errors.New("mstore gave error response")
|
ErrMStoreError = errors.New("mstore gave error response")
|
||||||
|
ErrInvalidTask = errors.New("invalid task")
|
||||||
|
ErrInvalidMessage = errors.New("task contains invalid message")
|
||||||
)
|
)
|
||||||
|
|
||||||
type TaskRepo struct {
|
type TaskRepo struct {
|
||||||
|
@ -24,7 +26,7 @@ func NewRepository(ms mstore.MStorer) *TaskRepo {
|
||||||
func (tr *TaskRepo) FindAll(folder string) ([]*Task, error) {
|
func (tr *TaskRepo) FindAll(folder string) ([]*Task, error) {
|
||||||
msgs, err := tr.mstore.Messages(folder)
|
msgs, err := tr.mstore.Messages(folder)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []*Task{}, err
|
return []*Task{}, fmt.Errorf("%w: %v", ErrMStoreError, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks := []*Task{}
|
tasks := []*Task{}
|
||||||
|
@ -38,6 +40,9 @@ func (tr *TaskRepo) FindAll(folder string) ([]*Task, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tr *TaskRepo) Update(t *Task) error {
|
func (tr *TaskRepo) Update(t *Task) error {
|
||||||
|
if t == nil {
|
||||||
|
return ErrInvalidTask
|
||||||
|
}
|
||||||
if !t.Current {
|
if !t.Current {
|
||||||
return ErrOutdatedTask
|
return ErrOutdatedTask
|
||||||
}
|
}
|
||||||
|
@ -82,22 +87,20 @@ func (tr *TaskRepo) CleanUp() error {
|
||||||
// determine which ones need to be gone
|
// determine which ones need to be gone
|
||||||
var tobeRemoved []*Task
|
var tobeRemoved []*Task
|
||||||
for _, tasks := range taskSet {
|
for _, tasks := range taskSet {
|
||||||
maxUid := uint32(0)
|
maxVersion := 0
|
||||||
for _, t := range tasks {
|
for _, t := range tasks {
|
||||||
if t.Message.Uid > maxUid {
|
if t.Version > maxVersion {
|
||||||
maxUid = t.Message.Uid
|
maxVersion = t.Version
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, t := range tasks {
|
for _, t := range tasks {
|
||||||
if t.Message.Uid < maxUid {
|
if t.Version < maxVersion {
|
||||||
tobeRemoved = append(tobeRemoved, t)
|
tobeRemoved = append(tobeRemoved, t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//fmt.Printf("removing: %+v\n", tobeRemoved)
|
|
||||||
|
|
||||||
// remove them
|
// remove them
|
||||||
for _, t := range tobeRemoved {
|
for _, t := range tobeRemoved {
|
||||||
if err := tr.mstore.Remove(t.Message); err != nil {
|
if err := tr.mstore.Remove(t.Message); err != nil {
|
||||||
|
|
|
@ -0,0 +1,206 @@
|
||||||
|
package task_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.sr.ht/~ewintr/go-kit/test"
|
||||||
|
"git.sr.ht/~ewintr/gte/internal/task"
|
||||||
|
"git.sr.ht/~ewintr/gte/pkg/mstore"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRepoFindAll(t *testing.T) {
|
||||||
|
folderA := "folderA"
|
||||||
|
folderB := "folderB"
|
||||||
|
|
||||||
|
type msgs struct {
|
||||||
|
Folder string
|
||||||
|
Subject string
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
tasks []msgs
|
||||||
|
folder string
|
||||||
|
expTasks int
|
||||||
|
expErr error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "empty",
|
||||||
|
folder: folderA,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "unknown folder",
|
||||||
|
folder: "unknown",
|
||||||
|
expErr: task.ErrMStoreError,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "not empty",
|
||||||
|
folder: folderA,
|
||||||
|
tasks: []msgs{
|
||||||
|
{Folder: folderA, Subject: "sub-1"},
|
||||||
|
{Folder: folderA, Subject: "sub-2"},
|
||||||
|
{Folder: folderB, Subject: "sub-3"},
|
||||||
|
{Folder: folderA, Subject: "sub-4"},
|
||||||
|
},
|
||||||
|
expTasks: 3,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
store, err := mstore.NewMemory([]string{folderA, folderB})
|
||||||
|
test.OK(t, err)
|
||||||
|
for _, task := range tc.tasks {
|
||||||
|
test.OK(t, store.Add(task.Folder, task.Subject, "body"))
|
||||||
|
}
|
||||||
|
repo := task.NewRepository(store)
|
||||||
|
actTasks, err := repo.FindAll(tc.folder)
|
||||||
|
test.Equals(t, true, errors.Is(err, tc.expErr))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
test.Equals(t, tc.expTasks, len(actTasks))
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRepoUpdate(t *testing.T) {
|
||||||
|
id := "id"
|
||||||
|
oldFolder := "old folder"
|
||||||
|
folder := "folder"
|
||||||
|
action := "action"
|
||||||
|
|
||||||
|
oldMsg := &mstore.Message{
|
||||||
|
Uid: 1,
|
||||||
|
Folder: oldFolder,
|
||||||
|
Subject: "old subject",
|
||||||
|
Body: "old body",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
task *task.Task
|
||||||
|
expErr error
|
||||||
|
expMsgs []*mstore.Message
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "nil task",
|
||||||
|
expErr: task.ErrInvalidTask,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "task without message",
|
||||||
|
task: &task.Task{
|
||||||
|
Id: id,
|
||||||
|
Folder: folder,
|
||||||
|
Action: action,
|
||||||
|
Current: true,
|
||||||
|
Dirty: true,
|
||||||
|
},
|
||||||
|
expErr: task.ErrMStoreError,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "outdated task",
|
||||||
|
task: &task.Task{
|
||||||
|
Id: id,
|
||||||
|
Folder: folder,
|
||||||
|
Action: action,
|
||||||
|
Dirty: true,
|
||||||
|
},
|
||||||
|
expErr: task.ErrOutdatedTask,
|
||||||
|
},
|
||||||
|
/*
|
||||||
|
{
|
||||||
|
name: "unchanged task",
|
||||||
|
task: &task.Task{
|
||||||
|
Id: id,
|
||||||
|
Folder: folder,
|
||||||
|
Action: action,
|
||||||
|
Current: true,
|
||||||
|
},
|
||||||
|
expMsgs: []*mstore.Message{},
|
||||||
|
},
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
name: "changed task",
|
||||||
|
task: &task.Task{
|
||||||
|
Id: id,
|
||||||
|
Folder: folder,
|
||||||
|
Action: action,
|
||||||
|
Current: true,
|
||||||
|
Dirty: true,
|
||||||
|
Message: oldMsg,
|
||||||
|
},
|
||||||
|
expMsgs: []*mstore.Message{
|
||||||
|
{Uid: 2, Folder: folder, Subject: action},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
mem, err := mstore.NewMemory([]string{folder, oldFolder})
|
||||||
|
test.OK(t, err)
|
||||||
|
test.OK(t, mem.Add(oldMsg.Folder, oldMsg.Subject, oldMsg.Body))
|
||||||
|
|
||||||
|
repo := task.NewRepository(mem)
|
||||||
|
|
||||||
|
actErr := repo.Update(tc.task)
|
||||||
|
test.Equals(t, true, errors.Is(actErr, tc.expErr))
|
||||||
|
if tc.expErr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
actMsgs, err := mem.Messages(folder)
|
||||||
|
test.OK(t, err)
|
||||||
|
for i, _ := range actMsgs {
|
||||||
|
actMsgs[i].Body = ""
|
||||||
|
}
|
||||||
|
test.Equals(t, tc.expMsgs, actMsgs)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRepoCleanUp(t *testing.T) {
|
||||||
|
folderNew := "New"
|
||||||
|
folderPlanned := "Planned"
|
||||||
|
folders := []string{"INBOX", folderNew, "Recurring",
|
||||||
|
folderPlanned, "Unplanned",
|
||||||
|
}
|
||||||
|
id := "id"
|
||||||
|
subject := "subject"
|
||||||
|
|
||||||
|
mem, err := mstore.NewMemory(folders)
|
||||||
|
test.OK(t, err)
|
||||||
|
|
||||||
|
for v := 1; v <= 3; v++ {
|
||||||
|
body := fmt.Sprintf(`
|
||||||
|
id: %s
|
||||||
|
version: %d
|
||||||
|
`, id, v)
|
||||||
|
folder := folderNew
|
||||||
|
if v%2 == 1 {
|
||||||
|
folder = folderPlanned
|
||||||
|
}
|
||||||
|
test.OK(t, mem.Add(folder, subject, body))
|
||||||
|
}
|
||||||
|
|
||||||
|
repo := task.NewRepository(mem)
|
||||||
|
test.OK(t, repo.CleanUp())
|
||||||
|
|
||||||
|
expNew := []*mstore.Message{}
|
||||||
|
actNew, err := mem.Messages(folderNew)
|
||||||
|
test.OK(t, err)
|
||||||
|
test.Equals(t, expNew, actNew)
|
||||||
|
expPlanned := []*mstore.Message{{
|
||||||
|
Uid: 3,
|
||||||
|
Folder: folderPlanned,
|
||||||
|
Subject: subject,
|
||||||
|
Body: `
|
||||||
|
id: id
|
||||||
|
version: 3
|
||||||
|
`,
|
||||||
|
}}
|
||||||
|
actPlanned, err := mem.Messages(folderPlanned)
|
||||||
|
test.OK(t, err)
|
||||||
|
fmt.Printf("act: %+v\n", actPlanned[0])
|
||||||
|
test.Equals(t, expPlanned, actPlanned)
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ package task
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.sr.ht/~ewintr/gte/pkg/mstore"
|
"git.sr.ht/~ewintr/gte/pkg/mstore"
|
||||||
|
@ -14,8 +15,11 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
FOLDER_INBOX = "INBOX"
|
FOLDER_INBOX = "INBOX"
|
||||||
FOLDER_NEW = "New"
|
FOLDER_NEW = "New"
|
||||||
|
FOLDER_RECURRING = "Recurring"
|
||||||
|
FOLDER_PLANNED = "Planned"
|
||||||
|
FOLDER_UNPLANNED = "Unplanned"
|
||||||
|
|
||||||
QUOTE_PREFIX = ">"
|
QUOTE_PREFIX = ">"
|
||||||
PREVIOUS_SEPARATOR = "Previous version:"
|
PREVIOUS_SEPARATOR = "Previous version:"
|
||||||
|
@ -24,13 +28,20 @@ const (
|
||||||
SUBJECT_SEPARATOR = " - "
|
SUBJECT_SEPARATOR = " - "
|
||||||
|
|
||||||
FIELD_ID = "id"
|
FIELD_ID = "id"
|
||||||
|
FIELD_VERSION = "version"
|
||||||
FIELD_ACTION = "action"
|
FIELD_ACTION = "action"
|
||||||
FIELD_PROJECT = "project"
|
FIELD_PROJECT = "project"
|
||||||
FIELD_DUE = "date"
|
FIELD_DUE = "due"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
knownFolders = []string{FOLDER_INBOX, FOLDER_NEW}
|
knownFolders = []string{
|
||||||
|
FOLDER_INBOX,
|
||||||
|
FOLDER_NEW,
|
||||||
|
FOLDER_RECURRING,
|
||||||
|
FOLDER_PLANNED,
|
||||||
|
FOLDER_UNPLANNED,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Task reperesents a task based on the data stored in a message
|
// Task reperesents a task based on the data stored in a message
|
||||||
|
@ -38,6 +49,8 @@ type Task struct {
|
||||||
|
|
||||||
// Id is a UUID that gets carried over when a new message is constructed
|
// Id is a UUID that gets carried over when a new message is constructed
|
||||||
Id string
|
Id string
|
||||||
|
// Version is a method to determine the latest version for cleanup
|
||||||
|
Version int
|
||||||
|
|
||||||
// Folder is the same name as the mstore folder
|
// Folder is the same name as the mstore folder
|
||||||
Folder string
|
Folder string
|
||||||
|
@ -74,6 +87,13 @@ func New(msg *mstore.Message) *Task {
|
||||||
dirty = true
|
dirty = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Version, cannot manually be incremented from body
|
||||||
|
versionStr, _ := FieldFromBody(FIELD_VERSION, msg.Body)
|
||||||
|
version, _ := strconv.Atoi(versionStr)
|
||||||
|
if version == 0 {
|
||||||
|
dirty = true
|
||||||
|
}
|
||||||
|
|
||||||
// Action
|
// Action
|
||||||
action, d := FieldFromBody(FIELD_ACTION, msg.Body)
|
action, d := FieldFromBody(FIELD_ACTION, msg.Body)
|
||||||
if action == "" {
|
if action == "" {
|
||||||
|
@ -99,8 +119,13 @@ func New(msg *mstore.Message) *Task {
|
||||||
dirty = true
|
dirty = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if dirty {
|
||||||
|
version++
|
||||||
|
}
|
||||||
|
|
||||||
return &Task{
|
return &Task{
|
||||||
Id: id,
|
Id: id,
|
||||||
|
Version: version,
|
||||||
Folder: folder,
|
Folder: folder,
|
||||||
Action: action,
|
Action: action,
|
||||||
Project: project,
|
Project: project,
|
||||||
|
@ -129,9 +154,10 @@ func (t *Task) FormatSubject() string {
|
||||||
|
|
||||||
func (t *Task) FormatBody() string {
|
func (t *Task) FormatBody() string {
|
||||||
body := fmt.Sprintf("\n")
|
body := fmt.Sprintf("\n")
|
||||||
order := []string{FIELD_ID, FIELD_PROJECT, FIELD_ACTION}
|
order := []string{FIELD_ID, FIELD_VERSION, FIELD_PROJECT, FIELD_ACTION}
|
||||||
fields := map[string]string{
|
fields := map[string]string{
|
||||||
FIELD_ID: t.Id,
|
FIELD_ID: t.Id,
|
||||||
|
FIELD_VERSION: strconv.Itoa(t.Version),
|
||||||
FIELD_PROJECT: t.Project,
|
FIELD_PROJECT: t.Project,
|
||||||
FIELD_ACTION: t.Action,
|
FIELD_ACTION: t.Action,
|
||||||
}
|
}
|
||||||
|
@ -195,3 +221,9 @@ func FieldFromSubject(field, subject string) string {
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func increment(s string) string {
|
||||||
|
i, _ := strconv.Atoi(s)
|
||||||
|
i++
|
||||||
|
return strconv.Itoa(i)
|
||||||
|
}
|
||||||
|
|
|
@ -11,15 +11,17 @@ import (
|
||||||
|
|
||||||
func TestNewFromMessage(t *testing.T) {
|
func TestNewFromMessage(t *testing.T) {
|
||||||
id := "an id"
|
id := "an id"
|
||||||
|
version := 2
|
||||||
action := "some action"
|
action := "some action"
|
||||||
project := "project"
|
project := "project"
|
||||||
folder := task.FOLDER_NEW
|
folder := task.FOLDER_NEW
|
||||||
|
|
||||||
for _, tc := range []struct {
|
for _, tc := range []struct {
|
||||||
name string
|
name string
|
||||||
message *mstore.Message
|
message *mstore.Message
|
||||||
hasId bool
|
hasId bool
|
||||||
exp *task.Task
|
hasVersion bool
|
||||||
|
exp *task.Task
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "empty",
|
name: "empty",
|
||||||
|
@ -34,13 +36,16 @@ func TestNewFromMessage(t *testing.T) {
|
||||||
Folder: folder,
|
Folder: folder,
|
||||||
Body: fmt.Sprintf(`
|
Body: fmt.Sprintf(`
|
||||||
id: %s
|
id: %s
|
||||||
|
version: %d
|
||||||
action: %s
|
action: %s
|
||||||
project: %s
|
project: %s
|
||||||
`, id, action, project),
|
`, id, version, action, project),
|
||||||
},
|
},
|
||||||
hasId: true,
|
hasId: true,
|
||||||
|
hasVersion: true,
|
||||||
exp: &task.Task{
|
exp: &task.Task{
|
||||||
Id: id,
|
Id: id,
|
||||||
|
Version: version,
|
||||||
Folder: folder,
|
Folder: folder,
|
||||||
Action: action,
|
Action: action,
|
||||||
Project: project,
|
Project: project,
|
||||||
|
@ -70,14 +75,17 @@ action: %s
|
||||||
Subject: "some other action",
|
Subject: "some other action",
|
||||||
Body: fmt.Sprintf(`
|
Body: fmt.Sprintf(`
|
||||||
id: %s
|
id: %s
|
||||||
|
version: %d
|
||||||
action: %s
|
action: %s
|
||||||
`, id, action),
|
`, id, version, action),
|
||||||
},
|
},
|
||||||
hasId: true,
|
hasId: true,
|
||||||
|
hasVersion: true,
|
||||||
exp: &task.Task{
|
exp: &task.Task{
|
||||||
Id: id,
|
Id: id,
|
||||||
Folder: folder,
|
Version: version,
|
||||||
Action: action,
|
Folder: folder,
|
||||||
|
Action: action,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -122,6 +130,9 @@ Forwarded message:
|
||||||
test.Equals(t, false, "" == act.Id)
|
test.Equals(t, false, "" == act.Id)
|
||||||
tc.exp.Id = act.Id
|
tc.exp.Id = act.Id
|
||||||
}
|
}
|
||||||
|
if !tc.hasVersion {
|
||||||
|
tc.exp.Version = 1
|
||||||
|
}
|
||||||
tc.exp.Message = tc.message
|
tc.exp.Message = tc.message
|
||||||
tc.exp.Current = true
|
tc.exp.Current = true
|
||||||
test.Equals(t, tc.exp, act)
|
test.Equals(t, tc.exp, act)
|
||||||
|
@ -166,6 +177,7 @@ func TestFormatSubject(t *testing.T) {
|
||||||
|
|
||||||
func TestFormatBody(t *testing.T) {
|
func TestFormatBody(t *testing.T) {
|
||||||
id := "an id"
|
id := "an id"
|
||||||
|
version := 6
|
||||||
action := "an action"
|
action := "an action"
|
||||||
project := "project"
|
project := "project"
|
||||||
|
|
||||||
|
@ -179,6 +191,7 @@ func TestFormatBody(t *testing.T) {
|
||||||
task: &task.Task{},
|
task: &task.Task{},
|
||||||
exp: `
|
exp: `
|
||||||
id:
|
id:
|
||||||
|
version: 0
|
||||||
project:
|
project:
|
||||||
action:
|
action:
|
||||||
`,
|
`,
|
||||||
|
@ -187,6 +200,7 @@ action:
|
||||||
name: "filled",
|
name: "filled",
|
||||||
task: &task.Task{
|
task: &task.Task{
|
||||||
Id: id,
|
Id: id,
|
||||||
|
Version: version,
|
||||||
Action: action,
|
Action: action,
|
||||||
Project: project,
|
Project: project,
|
||||||
Message: &mstore.Message{
|
Message: &mstore.Message{
|
||||||
|
@ -195,6 +209,7 @@ action:
|
||||||
},
|
},
|
||||||
exp: `
|
exp: `
|
||||||
id: an id
|
id: an id
|
||||||
|
version: 6
|
||||||
project: project
|
project: project
|
||||||
action: an action
|
action: an action
|
||||||
|
|
||||||
|
|
|
@ -187,7 +187,7 @@ Subject: %s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (es *Imap) Remove(msg *Message) error {
|
func (es *Imap) Remove(msg *Message) error {
|
||||||
if !msg.Valid() {
|
if msg == nil || !msg.Valid() {
|
||||||
return ErrInvalidMessage
|
return ErrInvalidMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,8 +11,8 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Memory struct {
|
type Memory struct {
|
||||||
Selected string
|
|
||||||
nextUid uint32
|
nextUid uint32
|
||||||
|
folders []string
|
||||||
messages map[string][]*Message
|
messages map[string][]*Message
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ func NewMemory(folders []string) (*Memory, error) {
|
||||||
if len(folders) == 0 {
|
if len(folders) == 0 {
|
||||||
return &Memory{}, ErrInvalidFolderSet
|
return &Memory{}, ErrInvalidFolderSet
|
||||||
}
|
}
|
||||||
|
sort.Strings(folders)
|
||||||
|
|
||||||
msg := make(map[string][]*Message)
|
msg := make(map[string][]*Message)
|
||||||
for _, f := range folders {
|
for _, f := range folders {
|
||||||
|
@ -31,29 +32,13 @@ func NewMemory(folders []string) (*Memory, error) {
|
||||||
|
|
||||||
return &Memory{
|
return &Memory{
|
||||||
messages: msg,
|
messages: msg,
|
||||||
|
folders: folders,
|
||||||
nextUid: uint32(1),
|
nextUid: uint32(1),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mem *Memory) Folders() ([]string, error) {
|
func (mem *Memory) Folders() ([]string, error) {
|
||||||
folders := []string{}
|
return mem.folders, nil
|
||||||
for f := range mem.messages {
|
|
||||||
folders = append(folders, f)
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Strings(folders)
|
|
||||||
|
|
||||||
return folders, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mem *Memory) Select(folder string) error {
|
|
||||||
if _, ok := mem.messages[folder]; !ok {
|
|
||||||
return ErrFolderDoesNotExist
|
|
||||||
}
|
|
||||||
|
|
||||||
mem.Selected = folder
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mem *Memory) Add(folder, subject, body string) error {
|
func (mem *Memory) Add(folder, subject, body string) error {
|
||||||
|
@ -66,6 +51,7 @@ func (mem *Memory) Add(folder, subject, body string) error {
|
||||||
|
|
||||||
mem.messages[folder] = append(mem.messages[folder], &Message{
|
mem.messages[folder] = append(mem.messages[folder], &Message{
|
||||||
Uid: mem.nextUid,
|
Uid: mem.nextUid,
|
||||||
|
Folder: folder,
|
||||||
Subject: subject,
|
Subject: subject,
|
||||||
Body: body,
|
Body: body,
|
||||||
})
|
})
|
||||||
|
@ -74,28 +60,29 @@ func (mem *Memory) Add(folder, subject, body string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mem *Memory) Messages() ([]*Message, error) {
|
func (mem *Memory) Messages(folder string) ([]*Message, error) {
|
||||||
if mem.Selected == "" {
|
if _, ok := mem.messages[folder]; !ok {
|
||||||
return []*Message{}, ErrNoFolderSelected
|
return []*Message{}, ErrFolderDoesNotExist
|
||||||
}
|
}
|
||||||
|
|
||||||
return mem.messages[mem.Selected], nil
|
return mem.messages[folder], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mem *Memory) Remove(uid uint32) error {
|
func (mem *Memory) Remove(msg *Message) error {
|
||||||
if uid == uint32(0) {
|
if msg == nil || !msg.Valid() {
|
||||||
return ErrInvalidUid
|
return ErrInvalidMessage
|
||||||
}
|
}
|
||||||
if mem.Selected == "" {
|
if _, ok := mem.messages[msg.Folder]; !ok {
|
||||||
return ErrNoFolderSelected
|
return ErrFolderDoesNotExist
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, m := range mem.messages[mem.Selected] {
|
for i, m := range mem.messages[msg.Folder] {
|
||||||
if m.Uid == uid {
|
if m.Uid == msg.Uid {
|
||||||
mem.messages[mem.Selected] = append(mem.messages[mem.Selected][:i], mem.messages[mem.Selected][i+1:]...)
|
mem.messages[msg.Folder] = append(mem.messages[msg.Folder][:i], mem.messages[msg.Folder][i+1:]...)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ErrMessageDoesNotExist
|
return ErrMessageDoesNotExist
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,58 +67,41 @@ func TestMemoryFolders(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMemorySelect(t *testing.T) {
|
|
||||||
mem, err := mstore.NewMemory([]string{"one", "two", "three"})
|
|
||||||
test.OK(t, err)
|
|
||||||
for _, tc := range []struct {
|
|
||||||
name string
|
|
||||||
folder string
|
|
||||||
exp error
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "empty",
|
|
||||||
exp: mstore.ErrFolderDoesNotExist,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "not present",
|
|
||||||
folder: "four",
|
|
||||||
exp: mstore.ErrFolderDoesNotExist,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "present",
|
|
||||||
folder: "two",
|
|
||||||
},
|
|
||||||
} {
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
|
||||||
test.Equals(t, tc.exp, mem.Select(tc.folder))
|
|
||||||
if tc.exp == nil {
|
|
||||||
test.Equals(t, tc.folder, mem.Selected)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMemoryAdd(t *testing.T) {
|
func TestMemoryAdd(t *testing.T) {
|
||||||
folder := "folder"
|
folder := "folder"
|
||||||
|
subject := "subject"
|
||||||
|
|
||||||
for _, tc := range []struct {
|
for _, tc := range []struct {
|
||||||
name string
|
name string
|
||||||
|
folder string
|
||||||
subject string
|
subject string
|
||||||
exp error
|
expMsgs []*mstore.Message
|
||||||
|
expErr error
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "empty",
|
name: "empty",
|
||||||
exp: mstore.ErrInvalidMessage,
|
folder: folder,
|
||||||
|
expErr: mstore.ErrInvalidMessage,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid folder",
|
||||||
|
folder: "not there",
|
||||||
|
subject: subject,
|
||||||
|
expErr: mstore.ErrFolderDoesNotExist,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "valid",
|
name: "valid",
|
||||||
subject: "subject",
|
folder: folder,
|
||||||
|
subject: subject,
|
||||||
|
expMsgs: []*mstore.Message{
|
||||||
|
{Uid: 1, Folder: folder, Subject: subject},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
mem, err := mstore.NewMemory([]string{folder})
|
mem, err := mstore.NewMemory([]string{folder})
|
||||||
test.OK(t, err)
|
test.OK(t, err)
|
||||||
test.Equals(t, tc.exp, mem.Add(folder, tc.subject, ""))
|
test.Equals(t, tc.expErr, mem.Add(tc.folder, tc.subject, ""))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -127,19 +110,17 @@ func TestMemoryMessages(t *testing.T) {
|
||||||
folderA := "folderA"
|
folderA := "folderA"
|
||||||
folderB := "folderB"
|
folderB := "folderB"
|
||||||
|
|
||||||
t.Run("no folder selected", func(t *testing.T) {
|
|
||||||
mem, err := mstore.NewMemory([]string{folderA})
|
|
||||||
test.OK(t, err)
|
|
||||||
_, err = mem.Messages()
|
|
||||||
test.Equals(t, mstore.ErrNoFolderSelected, err)
|
|
||||||
})
|
|
||||||
|
|
||||||
for _, tc := range []struct {
|
for _, tc := range []struct {
|
||||||
name string
|
name string
|
||||||
folder string
|
folder string
|
||||||
amount int
|
amount int
|
||||||
expErr error
|
expErr error
|
||||||
}{
|
}{
|
||||||
|
{
|
||||||
|
name: "unknown folder",
|
||||||
|
folder: "not there",
|
||||||
|
expErr: mstore.ErrFolderDoesNotExist,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "empty folder",
|
name: "empty folder",
|
||||||
folder: folderB,
|
folder: folderB,
|
||||||
|
@ -164,6 +145,7 @@ func TestMemoryMessages(t *testing.T) {
|
||||||
for i := 1; i <= tc.amount; i++ {
|
for i := 1; i <= tc.amount; i++ {
|
||||||
m := &mstore.Message{
|
m := &mstore.Message{
|
||||||
Uid: uint32(i),
|
Uid: uint32(i),
|
||||||
|
Folder: folderA,
|
||||||
Subject: fmt.Sprintf("subject-%d", i),
|
Subject: fmt.Sprintf("subject-%d", i),
|
||||||
Body: fmt.Sprintf("body-%d", i),
|
Body: fmt.Sprintf("body-%d", i),
|
||||||
}
|
}
|
||||||
|
@ -173,8 +155,7 @@ func TestMemoryMessages(t *testing.T) {
|
||||||
test.OK(t, mem.Add(folderA, m.Subject, m.Body))
|
test.OK(t, mem.Add(folderA, m.Subject, m.Body))
|
||||||
}
|
}
|
||||||
|
|
||||||
test.OK(t, mem.Select(tc.folder))
|
actMessages, err := mem.Messages(tc.folder)
|
||||||
actMessages, err := mem.Messages()
|
|
||||||
test.Equals(t, tc.expErr, err)
|
test.Equals(t, tc.expErr, err)
|
||||||
test.Equals(t, expMessages, actMessages)
|
test.Equals(t, expMessages, actMessages)
|
||||||
})
|
})
|
||||||
|
@ -183,12 +164,7 @@ func TestMemoryMessages(t *testing.T) {
|
||||||
|
|
||||||
func TestMemoryRemove(t *testing.T) {
|
func TestMemoryRemove(t *testing.T) {
|
||||||
folderA, folderB := "folderA", "folderB"
|
folderA, folderB := "folderA", "folderB"
|
||||||
|
subject := "subject"
|
||||||
t.Run("no folder selected", func(t *testing.T) {
|
|
||||||
mem, err := mstore.NewMemory([]string{folderA})
|
|
||||||
test.OK(t, err)
|
|
||||||
test.Equals(t, mstore.ErrNoFolderSelected, mem.Remove(uint32(3)))
|
|
||||||
})
|
|
||||||
|
|
||||||
mem, err := mstore.NewMemory([]string{folderA, folderB})
|
mem, err := mstore.NewMemory([]string{folderA, folderB})
|
||||||
test.OK(t, err)
|
test.OK(t, err)
|
||||||
|
@ -197,38 +173,49 @@ func TestMemoryRemove(t *testing.T) {
|
||||||
}
|
}
|
||||||
for _, tc := range []struct {
|
for _, tc := range []struct {
|
||||||
name string
|
name string
|
||||||
folder string
|
msg *mstore.Message
|
||||||
uid uint32
|
|
||||||
expUids []uint32
|
expUids []uint32
|
||||||
expErr error
|
expErr error
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "invalid uid",
|
name: "empty",
|
||||||
folder: folderA,
|
msg: &mstore.Message{
|
||||||
uid: uint32(0),
|
Uid: 1,
|
||||||
expErr: mstore.ErrInvalidUid,
|
Folder: folderB,
|
||||||
},
|
Subject: subject,
|
||||||
{
|
},
|
||||||
name: "empty",
|
|
||||||
folder: folderB,
|
|
||||||
uid: uint32(1),
|
|
||||||
expErr: mstore.ErrMessageDoesNotExist,
|
expErr: mstore.ErrMessageDoesNotExist,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "valid",
|
name: "nil message",
|
||||||
folder: folderA,
|
expErr: mstore.ErrInvalidMessage,
|
||||||
uid: uint32(2),
|
},
|
||||||
|
{
|
||||||
|
name: "unknown folder",
|
||||||
|
msg: &mstore.Message{
|
||||||
|
Uid: 1,
|
||||||
|
Folder: "unknown",
|
||||||
|
Subject: subject,
|
||||||
|
},
|
||||||
|
expErr: mstore.ErrFolderDoesNotExist,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid",
|
||||||
|
msg: &mstore.Message{
|
||||||
|
Uid: 2,
|
||||||
|
Folder: folderA,
|
||||||
|
Subject: subject,
|
||||||
|
},
|
||||||
expUids: []uint32{1, 3},
|
expUids: []uint32{1, 3},
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
test.OK(t, mem.Select(tc.folder))
|
test.Equals(t, tc.expErr, mem.Remove(tc.msg))
|
||||||
test.Equals(t, tc.expErr, mem.Remove(tc.uid))
|
|
||||||
if tc.expErr != nil {
|
if tc.expErr != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
actUids := []uint32{}
|
actUids := []uint32{}
|
||||||
actMsgs, err := mem.Messages()
|
actMsgs, err := mem.Messages(tc.msg.Folder)
|
||||||
test.OK(t, err)
|
test.OK(t, err)
|
||||||
for _, m := range actMsgs {
|
for _, m := range actMsgs {
|
||||||
actUids = append(actUids, m.Uid)
|
actUids = append(actUids, m.Uid)
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package mstore
|
package mstore
|
||||||
|
|
||||||
import "errors"
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrFolderDoesNotExist = errors.New("folder does not exist")
|
ErrFolderDoesNotExist = errors.New("folder does not exist")
|
||||||
|
|
Loading…
Reference in New Issue