Unittesting begins
This commit is contained in:
73
server/ajax/ajax.go
Normal file
73
server/ajax/ajax.go
Normal file
@@ -0,0 +1,73 @@
|
||||
package ajax
|
||||
|
||||
import (
|
||||
"local/storage"
|
||||
"local/todo-server/config"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type Ajax struct {
|
||||
DB storage.DB
|
||||
}
|
||||
|
||||
func New() (*Ajax, error) {
|
||||
db, err := storage.New(storage.TypeFromString(config.StoreType), config.StoreAddr, config.StoreUser, config.StorePass)
|
||||
return &Ajax{
|
||||
DB: db,
|
||||
}, err
|
||||
}
|
||||
|
||||
func (a *Ajax) HandleAjax(w http.ResponseWriter, r *http.Request) {
|
||||
params := r.URL.Query()
|
||||
var foo func(http.ResponseWriter, *http.Request) error
|
||||
if v := params.Get("loadLists"); v != "" {
|
||||
foo = a.loadLists
|
||||
} else if v := params.Get("loadTasks"); v != "" {
|
||||
foo = a.loadTasks
|
||||
} else if v := params.Get("newTask"); v != "" {
|
||||
foo = a.newTask
|
||||
} else if v := params.Get("fullNewTask"); v != "" {
|
||||
foo = a.newTask
|
||||
} else if v := params.Get("deleteTask"); v != "" {
|
||||
foo = a.deleteTask
|
||||
} else if v := params.Get("completeTask"); v != "" {
|
||||
foo = a.completeTask
|
||||
} else if v := params.Get("editNote"); v != "" {
|
||||
foo = a.editNote
|
||||
} else if v := params.Get("editTask"); v != "" {
|
||||
foo = a.editTask
|
||||
} else if v := params.Get("changeOrder"); v != "" {
|
||||
foo = a.changeOrder
|
||||
} else if v := params.Get("suggestTags"); v != "" {
|
||||
foo = a.suggestTags
|
||||
} else if v := params.Get("setPrio"); v != "" {
|
||||
foo = a.setPrio
|
||||
} else if v := params.Get("tagCloud"); v != "" {
|
||||
foo = a.tagCloud
|
||||
} else if v := params.Get("addList"); v != "" {
|
||||
foo = a.addList
|
||||
} else if v := params.Get("renameList"); v != "" {
|
||||
foo = a.renameList
|
||||
} else if v := params.Get("deleteList"); v != "" {
|
||||
foo = a.deleteList
|
||||
} else if v := params.Get("setSort"); v != "" {
|
||||
foo = a.setSort
|
||||
} else if v := params.Get("publishList"); v != "" {
|
||||
foo = a.publishList
|
||||
} else if v := params.Get("moveTask"); v != "" {
|
||||
foo = a.moveTask
|
||||
} else if v := params.Get("changeListOrder"); v != "" {
|
||||
foo = a.changeListOrder
|
||||
} else if v := params.Get("parseTaskStr"); v != "" {
|
||||
foo = a.parseTaskStr
|
||||
} else if v := params.Get("clearCompletedInList"); v != "" {
|
||||
foo = a.clearCompletedInList
|
||||
} else if v := params.Get("setShowNotesInList"); v != "" {
|
||||
foo = a.setShowNotesInList
|
||||
} else if v := params.Get("setHideList"); v != "" {
|
||||
foo = a.setHideList
|
||||
}
|
||||
if err := foo(w, r); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
20
server/ajax/ajax_test.go
Normal file
20
server/ajax/ajax_test.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package ajax
|
||||
|
||||
import (
|
||||
"local/todo-server/config"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
mockAjax()
|
||||
}
|
||||
|
||||
func mockAjax() *Ajax {
|
||||
config.StoreType = "map"
|
||||
ajax, err := New()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
ajax.storageSetCur("list")
|
||||
return ajax
|
||||
}
|
||||
60
server/ajax/form/form.go
Normal file
60
server/ajax/form/form.go
Normal file
@@ -0,0 +1,60 @@
|
||||
package form
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"html"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type readCloser struct {
|
||||
io.Reader
|
||||
}
|
||||
|
||||
func (rc readCloser) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func Get(r *http.Request, k string) string {
|
||||
s := r.FormValue(k)
|
||||
if s == "" {
|
||||
b, _ := ioutil.ReadAll(r.Body)
|
||||
var m map[string]json.RawMessage
|
||||
if err := json.Unmarshal(b, &m); err != nil {
|
||||
return ""
|
||||
}
|
||||
v, _ := m[k]
|
||||
s = strings.TrimPrefix(strings.TrimSuffix(string(v), `"`), `"`)
|
||||
r.Body = readCloser{bytes.NewReader(b)}
|
||||
}
|
||||
s = html.UnescapeString(s)
|
||||
s = strings.ReplaceAll(s, "\r", "")
|
||||
return s
|
||||
}
|
||||
|
||||
func ToInt(s string) int {
|
||||
v, _ := strconv.Atoi(s)
|
||||
return v
|
||||
}
|
||||
|
||||
func ToStrArr(k string) []string {
|
||||
arr := strings.Split(k, ",")
|
||||
outArr := []string{}
|
||||
for i := range arr {
|
||||
s := strings.TrimSpace(arr[i])
|
||||
if len(s) > 0 {
|
||||
outArr = append(outArr, s)
|
||||
}
|
||||
}
|
||||
return outArr
|
||||
}
|
||||
|
||||
func ToTime(s string) time.Time {
|
||||
v, _ := time.Parse("2006-01-02 15:04:05", s)
|
||||
return v
|
||||
}
|
||||
132
server/ajax/form/form_test.go
Normal file
132
server/ajax/form/form_test.go
Normal file
@@ -0,0 +1,132 @@
|
||||
package form
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
r := testReq()
|
||||
if v := Get(r, "a"); v != "b" {
|
||||
t.Error(v)
|
||||
}
|
||||
if v := Get(r, "c"); v != "4" {
|
||||
t.Error(v)
|
||||
}
|
||||
if v := Get(r, "d"); v != "e, f,g" {
|
||||
t.Error(v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestToStrArr(t *testing.T) {
|
||||
cases := []struct {
|
||||
in string
|
||||
out int
|
||||
}{
|
||||
{
|
||||
in: "4",
|
||||
out: 1,
|
||||
},
|
||||
{
|
||||
in: "a,b,c",
|
||||
out: 3,
|
||||
},
|
||||
{
|
||||
in: " a, b, c ",
|
||||
out: 3,
|
||||
},
|
||||
{
|
||||
in: "a,b, c",
|
||||
out: 3,
|
||||
},
|
||||
{
|
||||
in: "a,b",
|
||||
out: 2,
|
||||
},
|
||||
{
|
||||
in: "a,",
|
||||
out: 1,
|
||||
},
|
||||
{
|
||||
in: "a",
|
||||
out: 1,
|
||||
},
|
||||
{
|
||||
in: "",
|
||||
out: 0,
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
if len(ToStrArr(c.in)) != c.out {
|
||||
t.Error(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestToTime(t *testing.T) {
|
||||
cases := []struct {
|
||||
in string
|
||||
out string
|
||||
}{
|
||||
{
|
||||
in: "2001-02-03 04:05:06",
|
||||
out: "2001-02-03 04:05:06",
|
||||
},
|
||||
{
|
||||
in: "5",
|
||||
out: "0001-01-01 00:00:00",
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
time := ToTime(c.in)
|
||||
if v := time.Format("2006-01-02 15:04:05"); v != c.out {
|
||||
t.Error(c, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestToInt(t *testing.T) {
|
||||
cases := []struct {
|
||||
in string
|
||||
out int
|
||||
}{
|
||||
{
|
||||
in: "4",
|
||||
out: 4,
|
||||
},
|
||||
{
|
||||
in: "a",
|
||||
out: 0,
|
||||
},
|
||||
{
|
||||
in: "",
|
||||
out: 0,
|
||||
},
|
||||
{
|
||||
in: "-1",
|
||||
out: -1,
|
||||
},
|
||||
{
|
||||
in: "5",
|
||||
out: 5,
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
if ToInt(c.in) != c.out {
|
||||
t.Error(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testReq() *http.Request {
|
||||
return httptest.NewRequest("POST", "/path/to", strings.NewReader(`{
|
||||
"a": "b",
|
||||
"c": 4,
|
||||
"d": "e, f,g"
|
||||
}`))
|
||||
}
|
||||
52
server/ajax/list.go
Normal file
52
server/ajax/list.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package ajax
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type List struct{}
|
||||
|
||||
func (a *Ajax) loadLists(w http.ResponseWriter, r *http.Request) error {
|
||||
return errors.New("not impl")
|
||||
}
|
||||
|
||||
func (a *Ajax) changeOrder(w http.ResponseWriter, r *http.Request) error {
|
||||
return errors.New("not impl")
|
||||
}
|
||||
|
||||
func (a *Ajax) addList(w http.ResponseWriter, r *http.Request) error {
|
||||
return errors.New("not impl")
|
||||
}
|
||||
|
||||
func (a *Ajax) renameList(w http.ResponseWriter, r *http.Request) error {
|
||||
return errors.New("not impl")
|
||||
}
|
||||
|
||||
func (a *Ajax) deleteList(w http.ResponseWriter, r *http.Request) error {
|
||||
return errors.New("not impl")
|
||||
}
|
||||
|
||||
func (a *Ajax) setSort(w http.ResponseWriter, r *http.Request) error {
|
||||
return errors.New("not impl")
|
||||
}
|
||||
|
||||
func (a *Ajax) publishList(w http.ResponseWriter, r *http.Request) error {
|
||||
return errors.New("not impl")
|
||||
}
|
||||
|
||||
func (a *Ajax) changeListOrder(w http.ResponseWriter, r *http.Request) error {
|
||||
return errors.New("not impl")
|
||||
}
|
||||
|
||||
func (a *Ajax) clearCompletedInList(w http.ResponseWriter, r *http.Request) error {
|
||||
return errors.New("not impl")
|
||||
}
|
||||
|
||||
func (a *Ajax) setShowNotesInList(w http.ResponseWriter, r *http.Request) error {
|
||||
return errors.New("not impl")
|
||||
}
|
||||
|
||||
func (a *Ajax) setHideList(w http.ResponseWriter, r *http.Request) error {
|
||||
return errors.New("not impl")
|
||||
}
|
||||
56
server/ajax/list/list.go
Normal file
56
server/ajax/list/list.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package ajax
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type List struct{}
|
||||
|
||||
func New(r *http.Request) (*List, error) {
|
||||
return &List{}, errors.New("not impl")
|
||||
}
|
||||
|
||||
func (l *List) loadLists() error {
|
||||
return errors.New("not impl")
|
||||
}
|
||||
|
||||
func (l *List) changeOrder() error {
|
||||
return errors.New("not impl")
|
||||
}
|
||||
|
||||
func (l *List) addList() error {
|
||||
return errors.New("not impl")
|
||||
}
|
||||
|
||||
func (l *List) renameList() error {
|
||||
return errors.New("not impl")
|
||||
}
|
||||
|
||||
func (l *List) deleteList() error {
|
||||
return errors.New("not impl")
|
||||
}
|
||||
|
||||
func (l *List) setSort() error {
|
||||
return errors.New("not impl")
|
||||
}
|
||||
|
||||
func (l *List) publishList() error {
|
||||
return errors.New("not impl")
|
||||
}
|
||||
|
||||
func (l *List) changeListOrder() error {
|
||||
return errors.New("not impl")
|
||||
}
|
||||
|
||||
func (l *List) clearCompletedInList() error {
|
||||
return errors.New("not impl")
|
||||
}
|
||||
|
||||
func (l *List) setShowNotesInList() error {
|
||||
return errors.New("not impl")
|
||||
}
|
||||
|
||||
func (l *List) setHideList() error {
|
||||
return errors.New("not impl")
|
||||
}
|
||||
115
server/ajax/storage.go
Normal file
115
server/ajax/storage.go
Normal file
@@ -0,0 +1,115 @@
|
||||
package ajax
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"local/todo-server/server/ajax/form"
|
||||
"local/todo-server/server/ajax/task"
|
||||
"net/http"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (a *Ajax) Cur(r *http.Request) (string, string, []string) {
|
||||
listID, _ := a.storageGetCur()
|
||||
taskID := form.Get(r, "id")
|
||||
tags, _ := a.storageGetCurTags()
|
||||
return listID, taskID, tags
|
||||
}
|
||||
|
||||
func (a *Ajax) storageListTasks(listID string, filters ...func(t *task.Task) bool) ([]*task.Task, error) {
|
||||
results, err := a.DB.List(nil, listID+"/", listID+"/}")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tasks := []*task.Task{}
|
||||
for _, result := range results {
|
||||
taskID := strings.TrimPrefix(result, listID+"/")
|
||||
task, err := a.storageGetTask(listID, taskID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
filtered := true
|
||||
for _, f := range filters {
|
||||
if !f(task) {
|
||||
filtered = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if filtered {
|
||||
tasks = append(tasks, task)
|
||||
}
|
||||
}
|
||||
return tasks, nil
|
||||
}
|
||||
|
||||
func (a *Ajax) storageGetTask(listID, taskID string) (*task.Task, error) {
|
||||
var task task.Task
|
||||
err := a.storageGet(path.Join(listID, taskID), &task)
|
||||
return &task, err
|
||||
}
|
||||
|
||||
func (a *Ajax) storageSetTask(listID, taskID string, task *task.Task) error {
|
||||
return a.storageSet(path.Join(listID, taskID), *task)
|
||||
}
|
||||
|
||||
func (a *Ajax) storageDelTask(listID, taskID string) error {
|
||||
return a.storageDel(path.Join(listID, taskID))
|
||||
}
|
||||
|
||||
func (a *Ajax) storageGetList(listID string) (*List, error) {
|
||||
var list List
|
||||
err := a.storageGet(listID, &list)
|
||||
return &list, err
|
||||
}
|
||||
|
||||
func (a *Ajax) storageSetList(listID string, list *List) error {
|
||||
return a.storageSet(listID, *list)
|
||||
}
|
||||
|
||||
func (a *Ajax) storageDelList(listID string) error {
|
||||
return a.storageDel(listID)
|
||||
}
|
||||
|
||||
func (a *Ajax) storageSetCurTags(tags []string) error {
|
||||
return a.storageSet("currentTags", tags)
|
||||
}
|
||||
|
||||
func (a *Ajax) storageGetCurTags() ([]string, error) {
|
||||
var tags []string
|
||||
err := a.storageGet("currentTags", &tags)
|
||||
return tags, err
|
||||
}
|
||||
|
||||
func (a *Ajax) storageSetCur(listID string) error {
|
||||
return a.storageSet("currentList", listID)
|
||||
}
|
||||
|
||||
func (a *Ajax) storageGetCur() (string, error) {
|
||||
var listID string
|
||||
err := a.storageGet("currentList", &listID)
|
||||
return listID, err
|
||||
}
|
||||
|
||||
func (a *Ajax) storageSet(key string, value interface{}) error {
|
||||
buff := bytes.NewBuffer(nil)
|
||||
encoder := gob.NewEncoder(buff)
|
||||
if err := encoder.Encode(value); err != nil {
|
||||
return err
|
||||
}
|
||||
return a.DB.Set(key, buff.Bytes())
|
||||
}
|
||||
|
||||
func (a *Ajax) storageGet(key string, value interface{}) error {
|
||||
b, err := a.DB.Get(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
buff := bytes.NewBuffer(b)
|
||||
decoder := gob.NewDecoder(buff)
|
||||
return decoder.Decode(value)
|
||||
}
|
||||
|
||||
func (a *Ajax) storageDel(key string) error {
|
||||
return a.DB.Set(key, nil)
|
||||
}
|
||||
14
server/ajax/tag.go
Normal file
14
server/ajax/tag.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package ajax
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func (a *Ajax) suggestTags(w http.ResponseWriter, r *http.Request) error {
|
||||
return errors.New("not impl")
|
||||
}
|
||||
|
||||
func (a *Ajax) tagCloud(w http.ResponseWriter, r *http.Request) error {
|
||||
return errors.New("not impl")
|
||||
}
|
||||
128
server/ajax/task.go
Normal file
128
server/ajax/task.go
Normal file
@@ -0,0 +1,128 @@
|
||||
package ajax
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"local/todo-server/server/ajax/form"
|
||||
"local/todo-server/server/ajax/task"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (a *Ajax) loadTasks(w http.ResponseWriter, r *http.Request) error {
|
||||
listID, _, _ := a.Cur(r)
|
||||
filterComplete := func(t *task.Task) bool {
|
||||
if form.Get(r, "compl") == "" {
|
||||
return true
|
||||
}
|
||||
return t.Complete == (form.Get(r, "compl") != "0")
|
||||
}
|
||||
filterTags := func(t *task.Task) bool {
|
||||
if form.Get(r, "t") == "" {
|
||||
return true
|
||||
}
|
||||
whitelistTags := form.ToStrArr(form.Get(r, "t"))
|
||||
if len(whitelistTags) == 0 {
|
||||
return true
|
||||
}
|
||||
whitelistTagMap := make(map[string]struct{})
|
||||
for _, tag := range whitelistTags {
|
||||
whitelistTagMap[tag] = struct{}{}
|
||||
}
|
||||
for _, tag := range t.Tags {
|
||||
if _, ok := whitelistTagMap[tag]; ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
filterSubstr := func(t *task.Task) bool {
|
||||
substr := form.Get(r, "s")
|
||||
return substr == "" || strings.Contains(fmt.Sprintf("%+v", t), substr)
|
||||
}
|
||||
tasks, err := a.storageListTasks(listID, filterComplete, filterTags, filterSubstr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return json.NewEncoder(w).Encode(map[string]interface{}{"list": tasks})
|
||||
}
|
||||
|
||||
func (a *Ajax) newTask(w http.ResponseWriter, r *http.Request) error {
|
||||
listID, task, err := a.makeTask(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return a.storageSetTask(listID, task.UUID, task)
|
||||
}
|
||||
|
||||
func (a *Ajax) makeTask(r *http.Request) (string, *task.Task, error) {
|
||||
listID, _, tags := a.Cur(r)
|
||||
task, err := task.New(r)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
task.AppendTags(tags)
|
||||
return listID, task, nil
|
||||
}
|
||||
|
||||
func (a *Ajax) deleteTask(w http.ResponseWriter, r *http.Request) error {
|
||||
listID, taskID, _ := a.Cur(r)
|
||||
return a.storageDelTask(listID, taskID)
|
||||
}
|
||||
|
||||
func (a *Ajax) completeTask(w http.ResponseWriter, r *http.Request) error {
|
||||
listID, taskID, _ := a.Cur(r)
|
||||
task, err := a.storageGetTask(listID, taskID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
task.SetComplete(form.Get(r, "compl") == "1")
|
||||
return a.storageSetTask(listID, taskID, task)
|
||||
}
|
||||
|
||||
func (a *Ajax) editNote(w http.ResponseWriter, r *http.Request) error {
|
||||
listID, taskID, _ := a.Cur(r)
|
||||
task, err := a.storageGetTask(listID, taskID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
task.SetNote(form.Get(r, "note"))
|
||||
return a.storageSetTask(listID, taskID, task)
|
||||
}
|
||||
|
||||
func (a *Ajax) editTask(w http.ResponseWriter, r *http.Request) error {
|
||||
listID, task, err := a.makeTask(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, taskID, _ := a.Cur(r)
|
||||
task.UUID = taskID
|
||||
task.ID = task.UUID
|
||||
return a.storageSetTask(listID, task.UUID, task)
|
||||
}
|
||||
|
||||
func (a *Ajax) setPrio(w http.ResponseWriter, r *http.Request) error {
|
||||
listID, taskID, _ := a.Cur(r)
|
||||
task, err := a.storageGetTask(listID, taskID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
task.SetPrio(form.ToInt(form.Get(r, "prio")))
|
||||
return a.storageSetTask(listID, taskID, task)
|
||||
}
|
||||
|
||||
func (a *Ajax) moveTask(w http.ResponseWriter, r *http.Request) error {
|
||||
listID, taskID, _ := a.Cur(r)
|
||||
toList := form.Get(r, "to")
|
||||
task, err := a.storageGetTask(listID, taskID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.storageSetTask(listID, taskID, nil)
|
||||
return a.storageSetTask(toList, taskID, task)
|
||||
}
|
||||
|
||||
func (a *Ajax) parseTaskStr(w http.ResponseWriter, r *http.Request) error {
|
||||
return errors.New("not impl")
|
||||
}
|
||||
100
server/ajax/task/task.go
Normal file
100
server/ajax/task/task.go
Normal file
@@ -0,0 +1,100 @@
|
||||
package task
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"local/todo-server/server/ajax/form"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type Task struct {
|
||||
ID string
|
||||
UUID string
|
||||
Title string
|
||||
Priority int
|
||||
Tags []string
|
||||
Created time.Time
|
||||
Edited time.Time
|
||||
|
||||
Completed time.Time
|
||||
Complete bool
|
||||
Note []string
|
||||
Due time.Time
|
||||
}
|
||||
|
||||
func New(r *http.Request) (*Task, error) {
|
||||
task := &Task{
|
||||
UUID: uuid.New().String(),
|
||||
Title: form.Get(r, "title"),
|
||||
Priority: form.ToInt(form.Get(r, "prio")),
|
||||
Tags: form.ToStrArr(form.Get(r, "tags")),
|
||||
Created: time.Now(),
|
||||
Edited: time.Now(),
|
||||
|
||||
Due: form.ToTime(form.Get(r, "duedate")),
|
||||
}
|
||||
task.ID = task.UUID
|
||||
task.SetNote(form.Get(r, "note"))
|
||||
return task, task.validate()
|
||||
}
|
||||
|
||||
func (t *Task) AppendTags(tags []string) {
|
||||
t.touch()
|
||||
t.Tags = append(t.Tags, tags...)
|
||||
}
|
||||
|
||||
func (t *Task) SetComplete(state bool) {
|
||||
t.touch()
|
||||
t.Complete = state
|
||||
if t.Complete {
|
||||
t.Completed = time.Now()
|
||||
} else {
|
||||
t.Completed = time.Time{}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Task) SetPrio(prio int) {
|
||||
t.touch()
|
||||
t.Priority = prio
|
||||
}
|
||||
|
||||
func (t *Task) SetNote(note string) {
|
||||
t.touch()
|
||||
t.Note = strings.Split(note, "\n")
|
||||
}
|
||||
|
||||
func (t *Task) touch() {
|
||||
t.Edited = time.Now()
|
||||
}
|
||||
|
||||
func (t *Task) validate() error {
|
||||
if t.Title == "" {
|
||||
return errors.New("task cannot have nil title")
|
||||
}
|
||||
if err := t.smartSyntax(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Task) smartSyntax() error {
|
||||
re := regexp.MustCompile(`^(/([+-]{0,1}\d+)?/)?(.*?)(\s+/([^/]*)/$)?$|`)
|
||||
matches := re.FindAllStringSubmatch(t.Title, 1)[0]
|
||||
if len(matches) != 6 {
|
||||
return nil
|
||||
}
|
||||
if matches[1] != "" {
|
||||
t.Priority = form.ToInt(matches[1])
|
||||
}
|
||||
if matches[3] != "" {
|
||||
t.Title = matches[3]
|
||||
}
|
||||
if matches[5] != "" {
|
||||
t.Tags = form.ToStrArr(matches[5])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
59
server/ajax/task/task_test.go
Normal file
59
server/ajax/task/task_test.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package task
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
if _, err := New(toReq(map[string]interface{}{})); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if _, err := New(toReq(map[string]interface{}{"title": ""})); err == nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if task, err := New(toReq(map[string]interface{}{"title": "i want dogs /a,b,c/"})); err != nil {
|
||||
t.Error(err)
|
||||
} else if task.Title != "i want dogs" {
|
||||
t.Error(task.Title)
|
||||
} else if fmt.Sprint(task.Tags) != "[a b c]" {
|
||||
t.Error(task.Tags)
|
||||
} else {
|
||||
was := task.Edited
|
||||
task.touch()
|
||||
if was == task.Edited {
|
||||
t.Error(was)
|
||||
}
|
||||
was = task.Edited
|
||||
task.SetNote("hell\nno")
|
||||
if was == task.Edited {
|
||||
t.Error(was)
|
||||
} else if len(task.Note) != 2 {
|
||||
t.Error(task.Note)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func toReq(m map[string]interface{}) *http.Request {
|
||||
if m == nil {
|
||||
m = map[string]interface{}{}
|
||||
}
|
||||
els := map[string]interface{}{
|
||||
"title": "title",
|
||||
"prio": 1,
|
||||
"tags": "a, b,c",
|
||||
"duedate": "2010-02-03 05:06:07",
|
||||
"note": "hello\nworld\ni\nam a note\nand\ni have\nlots\nof\nlines",
|
||||
}
|
||||
for k := range els {
|
||||
if _, ok := m[k]; !ok {
|
||||
m[k] = els[k]
|
||||
}
|
||||
}
|
||||
b, _ := json.Marshal(m)
|
||||
return httptest.NewRequest("POST", "/paht", bytes.NewReader(b))
|
||||
}
|
||||
47
server/ajax/task_test.go
Normal file
47
server/ajax/task_test.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package ajax
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"local/todo-server/server/ajax/task"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAjaxLoadTasks(t *testing.T) {
|
||||
a := mockAjax()
|
||||
|
||||
func() {
|
||||
w := httptest.NewRecorder()
|
||||
r := httptest.NewRequest("GET", "/", nil)
|
||||
a.loadTasks(w, r)
|
||||
var result struct {
|
||||
List []string `json:"list"`
|
||||
}
|
||||
if v := w.Code; v != http.StatusOK {
|
||||
t.Error(v)
|
||||
} else if err := json.NewDecoder(w.Body).Decode(&result); err != nil {
|
||||
t.Error(err)
|
||||
} else if len(result.List) != 0 {
|
||||
t.Error(result)
|
||||
}
|
||||
}()
|
||||
|
||||
a.storageSetTask("list", "task", &task.Task{Title: "hi"})
|
||||
|
||||
func() {
|
||||
w := httptest.NewRecorder()
|
||||
r := httptest.NewRequest("GET", "/", nil)
|
||||
a.loadTasks(w, r)
|
||||
var result struct {
|
||||
List []task.Task `json:"list"`
|
||||
}
|
||||
if v := w.Code; v != http.StatusOK {
|
||||
t.Error(v)
|
||||
} else if err := json.NewDecoder(w.Body).Decode(&result); err != nil {
|
||||
t.Error(err)
|
||||
} else if len(result.List) != 1 {
|
||||
t.Error(result)
|
||||
}
|
||||
}()
|
||||
}
|
||||
Reference in New Issue
Block a user