this is fun
parent
c8318d45ac
commit
28310b0dd6
|
|
@ -3,21 +3,121 @@ package mytinytodo
|
|||
import (
|
||||
"local/mytinytodoclient/mytinytodo/remote"
|
||||
"local/rproxy3/storage"
|
||||
"log"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
const nsQueue = "delta"
|
||||
const keyQueue = "queue"
|
||||
const nsTasks = "delta"
|
||||
|
||||
type Buffer struct {
|
||||
remote *remote.Client
|
||||
db storage.DB
|
||||
config *Config
|
||||
db storage.DB
|
||||
dbLock *sync.RWMutex
|
||||
done chan struct{}
|
||||
interval time.Duration
|
||||
}
|
||||
|
||||
func NewBuffer(config *remote.Config) (*Buffer, error) {
|
||||
remote, err := remote.NewClient(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
func NewBuffer(config *Config) (*Buffer, error) {
|
||||
db := storage.NewMap()
|
||||
return &Buffer{
|
||||
remote: remote,
|
||||
db: db,
|
||||
}, nil
|
||||
b := &Buffer{
|
||||
config: config,
|
||||
db: db,
|
||||
done: make(chan struct{}),
|
||||
dbLock: &sync.RWMutex{},
|
||||
interval: time.Second * 10,
|
||||
}
|
||||
go b.Dequeue()
|
||||
go b.RefreshLocal()
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (buffer *Buffer) Close() {
|
||||
close(buffer.done)
|
||||
}
|
||||
|
||||
func (buffer *Buffer) Enqueue(op remote.Op, listID, taskName string, taskTags ...string) error {
|
||||
buffer.dbLock.Lock()
|
||||
defer buffer.dbLock.Unlock()
|
||||
qop := &QueuedOp{
|
||||
Op: op,
|
||||
ListID: listID,
|
||||
TaskName: taskName,
|
||||
TaskTags: taskTags,
|
||||
}
|
||||
uuid, _ := uuid.NewRandom()
|
||||
key := uuid.String()
|
||||
todo := NewStringArray()
|
||||
if err := buffer.db.Get(nsQueue, keyQueue, todo); err != nil {
|
||||
return err
|
||||
}
|
||||
sa := todo.StringArray()
|
||||
sa = append(sa, key)
|
||||
todo = NewStringArray(sa...)
|
||||
if err := buffer.db.Set(nsTasks, key, qop); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := buffer.db.Set(nsQueue, keyQueue, todo); err != nil {
|
||||
return err
|
||||
}
|
||||
log.Printf("enqueued task %v as %v, %vth in line", qop, key, len(sa))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (buffer *Buffer) Dequeue() {
|
||||
buffer.notDoneCallback(func() {
|
||||
client, err := remote.NewClient(buffer.config.Config)
|
||||
if err != nil {
|
||||
log.Printf("cannot create client: %v", err)
|
||||
return
|
||||
}
|
||||
if _, err := client.Lists(); err != nil {
|
||||
log.Printf("cannot client.lists: %v", err)
|
||||
return
|
||||
}
|
||||
buffer.dbLock.Lock()
|
||||
defer buffer.dbLock.Unlock()
|
||||
todo := NewStringArray()
|
||||
if err := buffer.db.Get(nsQueue, keyQueue, todo); err != nil {
|
||||
log.Printf("cannot get %v.%v: %v", nsQueue, keyQueue, err)
|
||||
return
|
||||
}
|
||||
sa := todo.StringArray()
|
||||
nsa := []string{}
|
||||
for i := range sa {
|
||||
qop := &QueuedOp{}
|
||||
if err := buffer.db.Get(nsTasks, sa[i], qop); err != nil {
|
||||
log.Printf("cannot get %v.%v: %v", nsTasks, sa[i], err)
|
||||
nsa = append(nsa, sa[i])
|
||||
continue
|
||||
}
|
||||
log.Printf("UPSERT %v", qop)
|
||||
}
|
||||
if err := buffer.db.Set(nsQueue, keyQueue, NewStringArray(nsa...)); err != nil {
|
||||
log.Printf("cannot update queue: %v", err)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (buffer *Buffer) RefreshLocal() {
|
||||
buffer.notDoneCallback(func() {
|
||||
buffer.dbLock.Lock()
|
||||
defer buffer.dbLock.Unlock()
|
||||
})
|
||||
}
|
||||
|
||||
func (buffer *Buffer) notDoneCallback(foo func()) {
|
||||
for {
|
||||
select {
|
||||
case <-buffer.done:
|
||||
return
|
||||
case <-time.After(buffer.interval):
|
||||
foo()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
package mytinytodo
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestNewBuffer(t *testing.T) {
|
||||
b, s := mockBuffer(t)
|
||||
defer b.Close()
|
||||
defer s.Close()
|
||||
}
|
||||
|
||||
func TestBufferEnqueue(t *testing.T) {
|
||||
t.Fatal("not implemented")
|
||||
}
|
||||
|
||||
func TestBufferDequeue(t *testing.T) {
|
||||
t.Fatal("not implemented")
|
||||
}
|
||||
|
||||
func TestBufferRefreshLocal(t *testing.T) {
|
||||
t.Fatal("not implemented")
|
||||
}
|
||||
|
||||
func TestBufferNotDoneCallback(t *testing.T) {
|
||||
b := &Buffer{
|
||||
done: make(chan struct{}),
|
||||
interval: time.Millisecond * 70,
|
||||
}
|
||||
called := make(chan struct{})
|
||||
go b.notDoneCallback(func() {
|
||||
called <- struct{}{}
|
||||
})
|
||||
<-called
|
||||
close(b.done)
|
||||
}
|
||||
|
||||
func mockBuffer(t *testing.T) (*Buffer, *httptest.Server) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
}))
|
||||
osArgsWas := os.Args[:]
|
||||
os.Args = []string{"skip", "-remote", srv.URL, "-p", ""}
|
||||
defer func() {
|
||||
os.Args = osArgsWas[:]
|
||||
}()
|
||||
config, err := NewConfig()
|
||||
if err != nil {
|
||||
t.Fatalf("cannot get new config: %v", err)
|
||||
}
|
||||
buff, err := NewBuffer(config)
|
||||
if err != nil {
|
||||
t.Fatalf("cannot get new buffer: %v", err)
|
||||
}
|
||||
return buff, srv
|
||||
}
|
||||
|
|
@ -2,6 +2,13 @@ package mytinytodo
|
|||
|
||||
import "local/mytinytodoclient/mytinytodo/remote"
|
||||
|
||||
func NewConfig() (*remote.Config, error) {
|
||||
return remote.NewConfig()
|
||||
type Config struct {
|
||||
*remote.Config
|
||||
}
|
||||
|
||||
func NewConfig() (*Config, error) {
|
||||
c, err := remote.NewConfig()
|
||||
return &Config{
|
||||
Config: c,
|
||||
}, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,56 @@
|
|||
package mytinytodo
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"local/mytinytodoclient/mytinytodo/remote"
|
||||
)
|
||||
|
||||
type StringArray []string
|
||||
|
||||
func NewStringArray(s ...string) *StringArray {
|
||||
arr := s[:]
|
||||
var sa StringArray = arr
|
||||
return &sa
|
||||
}
|
||||
|
||||
func (sa *StringArray) StringArray() []string {
|
||||
return []string(*sa)
|
||||
}
|
||||
|
||||
func (sa *StringArray) Encode() ([]byte, error) {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
enc := gob.NewEncoder(buf)
|
||||
err := enc.Encode(sa.StringArray())
|
||||
return buf.Bytes(), err
|
||||
}
|
||||
|
||||
func (sa *StringArray) Decode(b []byte) error {
|
||||
buf := bytes.NewBuffer(b)
|
||||
enc := gob.NewDecoder(buf)
|
||||
var s []string
|
||||
err := enc.Decode(&s)
|
||||
*sa = StringArray(s)
|
||||
return err
|
||||
}
|
||||
|
||||
type QueuedOp struct {
|
||||
Op remote.Op
|
||||
ListID string
|
||||
TaskName string
|
||||
TaskTags []string
|
||||
}
|
||||
|
||||
func (qop *QueuedOp) Encode() ([]byte, error) {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
enc := gob.NewEncoder(buf)
|
||||
err := enc.Encode(*qop)
|
||||
return buf.Bytes(), err
|
||||
}
|
||||
|
||||
func (qop *QueuedOp) Decode(b []byte) error {
|
||||
buf := bytes.NewBuffer(b)
|
||||
enc := gob.NewDecoder(buf)
|
||||
err := enc.Decode(qop)
|
||||
return err
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
package mytinytodo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"local/mytinytodoclient/mytinytodo/remote"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPackableStringArray(t *testing.T) {
|
||||
cases := [][]string{
|
||||
nil,
|
||||
[]string{},
|
||||
[]string{"a"},
|
||||
[]string{"a", "b"},
|
||||
}
|
||||
|
||||
for i, c := range cases {
|
||||
sa := NewStringArray(c...)
|
||||
if fmt.Sprintf("%v", sa.StringArray()) != fmt.Sprintf("%v", c) {
|
||||
t.Errorf("[%d]: NewStringArray() != input: got %v, want %v", i, sa, c)
|
||||
}
|
||||
encoded, err := sa.Encode()
|
||||
if err != nil {
|
||||
t.Errorf("[%d]: sa.Encode() error: %v", i, err)
|
||||
}
|
||||
sa2 := NewStringArray()
|
||||
if err := sa2.Decode(encoded); err != nil {
|
||||
t.Errorf("[%d]: sa2.Decode() error: %v", i, err)
|
||||
}
|
||||
if fmt.Sprintf("%v", sa2.StringArray()) != fmt.Sprintf("%v", c) {
|
||||
t.Errorf("[%d]: Decoded != input: got %v, want %v", i, sa2, c)
|
||||
}
|
||||
t.Logf("[%d] sa: %v vs %v", i, *sa, *sa2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPackableQueuedOp(t *testing.T) {
|
||||
cases := []*QueuedOp{
|
||||
&QueuedOp{
|
||||
Op: remote.NEW,
|
||||
ListID: "1",
|
||||
TaskName: "name",
|
||||
TaskTags: []string{"some", "tags"},
|
||||
},
|
||||
&QueuedOp{},
|
||||
}
|
||||
|
||||
for i, c := range cases {
|
||||
encoded, err := c.Encode()
|
||||
if err != nil {
|
||||
t.Errorf("[%d]: qop.Encode() error: %v", i, err)
|
||||
}
|
||||
qop := &QueuedOp{}
|
||||
if err := qop.Decode(encoded); err != nil {
|
||||
t.Errorf("[%d]: qop.Decode() error: %v", i, err)
|
||||
}
|
||||
if fmt.Sprintf("%v", *qop) != fmt.Sprintf("%v", *c) {
|
||||
t.Errorf("[%d]: Decoded != input: got %v, want %v", i, *qop, *c)
|
||||
}
|
||||
t.Logf("[%d] qop: %v vs %v", i, *qop, *c)
|
||||
}
|
||||
}
|
||||
|
|
@ -9,9 +9,8 @@ import (
|
|||
)
|
||||
|
||||
type Client struct {
|
||||
config *Config
|
||||
http *http.Client
|
||||
session *http.Cookie
|
||||
config *Config
|
||||
http *http.Client
|
||||
}
|
||||
|
||||
func NewClient(config *Config) (*Client, error) {
|
||||
|
|
@ -23,14 +22,14 @@ func NewClient(config *Config) (*Client, error) {
|
|||
func (c *Client) ParseArgs() error {
|
||||
for i := 0; i < len(c.config.args); i++ {
|
||||
arg := c.config.args[i]
|
||||
switch arg {
|
||||
case "list":
|
||||
switch fromString(arg) {
|
||||
case LISTS:
|
||||
log.Printf("lists: %v", fmt.Sprint(c.Lists()))
|
||||
case "tasks":
|
||||
case TASKS:
|
||||
listID := c.config.args[i+1]
|
||||
i += 1
|
||||
log.Printf("tasks: %v", fmt.Sprint(c.Tasks(List{ID: listID})))
|
||||
case "new":
|
||||
case NEW:
|
||||
listID := c.config.args[i+1]
|
||||
i += 1
|
||||
taskTitle := c.config.args[i+1]
|
||||
|
|
@ -38,14 +37,14 @@ func (c *Client) ParseArgs() error {
|
|||
tagsCSV := c.config.args[i+1]
|
||||
i += 1
|
||||
log.Printf("new: %v", fmt.Sprint(c.NewTask(List{ID: listID}, Task{Title: taskTitle}, tagsCSV)))
|
||||
case "close":
|
||||
taskID := c.config.args[i+1]
|
||||
i += 1
|
||||
log.Printf("close: %v", fmt.Sprint(c.CloseTask(Task{ID: taskID})))
|
||||
case "open":
|
||||
case OPEN:
|
||||
taskID := c.config.args[i+1]
|
||||
i += 1
|
||||
log.Printf("open: %v", fmt.Sprint(c.OpenTask(Task{ID: taskID})))
|
||||
case CLOSE:
|
||||
taskID := c.config.args[i+1]
|
||||
i += 1
|
||||
log.Printf("close: %v", fmt.Sprint(c.CloseTask(Task{ID: taskID})))
|
||||
default:
|
||||
log.Printf("unknown arg %q", arg)
|
||||
}
|
||||
|
|
@ -82,7 +81,6 @@ func (c *Client) Tasks(list List) ([]Task, error) {
|
|||
}
|
||||
|
||||
func (c *Client) NewTask(list List, task Task, tags string) error {
|
||||
log.Printf("new: %v < %v", list, task.Title)
|
||||
client, err := NewHTTP(c.config.remote, c.config.password)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -96,13 +94,13 @@ func (c *Client) NewTask(list List, task Task, tags string) error {
|
|||
return err
|
||||
} else if err := json.NewDecoder(resp.Body).Decode(&lists); err != nil {
|
||||
return fmt.Errorf("cannot make task: %v", err)
|
||||
} else if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("non 200 status on new task: %v", resp.StatusCode)
|
||||
}
|
||||
log.Print(lists)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) CloseTask(task Task) error {
|
||||
log.Printf("close: %v", task.ID)
|
||||
client, err := NewHTTP(c.config.remote, c.config.password)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -115,13 +113,13 @@ func (c *Client) CloseTask(task Task) error {
|
|||
return err
|
||||
} else if err := json.NewDecoder(resp.Body).Decode(&lists); err != nil {
|
||||
return fmt.Errorf("cannot close task: %v", err)
|
||||
} else if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("non 200 status on close task: %v", resp.StatusCode)
|
||||
}
|
||||
log.Print(lists)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) OpenTask(task Task) error {
|
||||
log.Printf("open: %v", task.ID)
|
||||
client, err := NewHTTP(c.config.remote, c.config.password)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -134,7 +132,8 @@ func (c *Client) OpenTask(task Task) error {
|
|||
return err
|
||||
} else if err := json.NewDecoder(resp.Body).Decode(&lists); err != nil {
|
||||
return fmt.Errorf("cannot close task: %v", err)
|
||||
} else if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("non 200 status on open task: %v", resp.StatusCode)
|
||||
}
|
||||
log.Print(lists)
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,123 @@
|
|||
package remote
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func mockClient(code int, body string) (*Client, *httptest.Server) {
|
||||
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(code)
|
||||
w.Write([]byte(body))
|
||||
}))
|
||||
return &Client{
|
||||
config: &Config{
|
||||
remote: s.URL,
|
||||
password: "",
|
||||
args: []string{},
|
||||
},
|
||||
http: &http.Client{},
|
||||
}, s
|
||||
}
|
||||
|
||||
func TestClientLists(t *testing.T) {
|
||||
c, srv := mockClient(200, `{"list":[{"id":"1", "name":"name"}]}`)
|
||||
defer srv.Close()
|
||||
|
||||
if l, err := c.Lists(); err != nil {
|
||||
t.Fatalf("cannot lists(): %v", err)
|
||||
} else if l[0] != (List{ID: "1", Name: "name"}) {
|
||||
t.Fatalf("cannot parse lists(): %v", l[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientTasks(t *testing.T) {
|
||||
c, srv := mockClient(200, `{"list":[{"id":"1", "title":"title", "compl": 1}]}`)
|
||||
defer srv.Close()
|
||||
|
||||
if l, err := c.Tasks(List{}); err != nil {
|
||||
t.Fatalf("cannot tasks(): %v", err)
|
||||
} else if l[0] != (Task{ID: "1", Title: "title", Complete: 1}) {
|
||||
t.Fatalf("cannot parse tasks(): %v", l[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientNewTask(t *testing.T) {
|
||||
cases := []struct {
|
||||
status int
|
||||
err error
|
||||
}{
|
||||
{
|
||||
status: http.StatusOK,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
status: http.StatusNotFound,
|
||||
err: errors.New("non 200 status on new task: 404"),
|
||||
},
|
||||
}
|
||||
|
||||
for i, c := range cases {
|
||||
cli, srv := mockClient(c.status, `{}`)
|
||||
defer srv.Close()
|
||||
|
||||
err := cli.NewTask(List{ID: "1"}, Task{Title: "a"}, "a,tag")
|
||||
if (err != nil && c.err == nil) || (err == nil && c.err != nil) {
|
||||
t.Errorf("[%d] new task unexpected error: got %v, want %v", i, err, c.err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientCloseTask(t *testing.T) {
|
||||
cases := []struct {
|
||||
status int
|
||||
err error
|
||||
}{
|
||||
{
|
||||
status: http.StatusOK,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
status: http.StatusNotFound,
|
||||
err: errors.New("non 200 status on close task: 404"),
|
||||
},
|
||||
}
|
||||
|
||||
for i, c := range cases {
|
||||
cli, srv := mockClient(c.status, `{}`)
|
||||
defer srv.Close()
|
||||
|
||||
err := cli.CloseTask(Task{ID: "1"})
|
||||
if (err != nil && c.err == nil) || (err == nil && c.err != nil) {
|
||||
t.Errorf("[%d] new task unexpected error: got %v, want %v", i, err, c.err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientOpenTask(t *testing.T) {
|
||||
cases := []struct {
|
||||
status int
|
||||
err error
|
||||
}{
|
||||
{
|
||||
status: http.StatusOK,
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
status: http.StatusNotFound,
|
||||
err: errors.New("non 200 status on open task: 404"),
|
||||
},
|
||||
}
|
||||
|
||||
for i, c := range cases {
|
||||
cli, srv := mockClient(c.status, `{}`)
|
||||
defer srv.Close()
|
||||
|
||||
err := cli.OpenTask(Task{ID: "1"})
|
||||
if (err != nil && c.err == nil) || (err == nil && c.err != nil) {
|
||||
t.Errorf("[%d] new task unexpected error: got %v, want %v", i, err, c.err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package remote
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewConfig(t *testing.T) {
|
||||
osArgsWas := os.Args[:]
|
||||
defer func() {
|
||||
os.Args = osArgsWas[:]
|
||||
}()
|
||||
|
||||
os.Args = []string{"ignore", "-remote", "remotename", "-p", "password", "other"}
|
||||
|
||||
if conf, err := NewConfig(); err != nil {
|
||||
t.Errorf("new config failed: %v", err)
|
||||
} else if conf.remote != "remotename" {
|
||||
t.Errorf("new config no remote flag: %v", conf.remote)
|
||||
} else if conf.password != "password" {
|
||||
t.Errorf("new config no password flag: %v", conf.password)
|
||||
} else if conf.args[0] != "other" {
|
||||
t.Errorf("new config no other args: %v", conf.args)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
package remote
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func mockHTTP(status int, response string) (*HTTP, *httptest.Server) {
|
||||
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(status)
|
||||
w.Write([]byte(response))
|
||||
}))
|
||||
return &HTTP{
|
||||
password: "",
|
||||
domain: s.URL,
|
||||
client: &http.Client{},
|
||||
}, s
|
||||
}
|
||||
|
||||
func TestNewHTTP(t *testing.T) {
|
||||
h, srv := mockHTTP(http.StatusOK, "")
|
||||
defer srv.Close()
|
||||
if _, err := NewHTTP(h.domain, ""); err != nil {
|
||||
t.Fatalf("cannot make new HTTP: %v", err)
|
||||
}
|
||||
|
||||
h, srv = mockHTTP(http.StatusNotFound, "")
|
||||
defer srv.Close()
|
||||
if _, err := NewHTTP(h.domain, ""); err == nil {
|
||||
t.Fatalf("can make new HTTP on bad status: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPGet(t *testing.T) {
|
||||
validBody := "body"
|
||||
h, srv := mockHTTP(http.StatusOK, validBody)
|
||||
defer srv.Close()
|
||||
resp, err := h.Get("/anything")
|
||||
if err != nil {
|
||||
t.Fatalf("cannot use HTTP GET: %v", err)
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
t.Errorf("cannot use HTTP GET: %v", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPPost(t *testing.T) {
|
||||
validBody := "body"
|
||||
h, srv := mockHTTP(http.StatusOK, validBody)
|
||||
defer srv.Close()
|
||||
resp, err := h.Post("/anything", validBody)
|
||||
if err != nil {
|
||||
t.Fatalf("cannot use HTTP GET: %v", err)
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
t.Errorf("cannot use HTTP GET: %v", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPNewReq(t *testing.T) {
|
||||
cases := []struct {
|
||||
method string
|
||||
path string
|
||||
body string
|
||||
}{
|
||||
{
|
||||
method: "GET",
|
||||
path: "/",
|
||||
body: "",
|
||||
},
|
||||
{
|
||||
method: "POST",
|
||||
path: "/asdf/asdf",
|
||||
body: "bod",
|
||||
},
|
||||
}
|
||||
|
||||
h := &HTTP{}
|
||||
for i, c := range cases {
|
||||
r, _ := h.NewReq(c.method, c.path, c.body)
|
||||
if r.Method != c.method {
|
||||
t.Errorf("[%d] wrong method on newreq: got %v, want %v", i, r.Method, c.method)
|
||||
}
|
||||
if r.URL.Path != c.path {
|
||||
t.Errorf("[%d] wrong path on newreq: got %v, want %v", i, r.URL.Path, c.path)
|
||||
}
|
||||
if b, err := ioutil.ReadAll(r.Body); err != nil {
|
||||
t.Errorf("[%d] bad body on newreq: %v", i, err)
|
||||
} else if string(b) != c.body {
|
||||
t.Errorf("[%d] wrong body on newreq: got %v, want %v", i, string(b), c.body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
package remote
|
||||
|
||||
type loadListsResponse struct {
|
||||
Total int `json:"total"`
|
||||
Lists []List `json:"list"`
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
package remote
|
||||
|
||||
type Op int
|
||||
|
||||
const (
|
||||
NEW Op = iota
|
||||
CLOSE Op = iota
|
||||
OPEN Op = iota
|
||||
LISTS Op = iota
|
||||
TASKS Op = iota
|
||||
)
|
||||
|
||||
func (op Op) String() string {
|
||||
switch op {
|
||||
case LISTS:
|
||||
return "lists"
|
||||
case TASKS:
|
||||
return "tasks"
|
||||
case NEW:
|
||||
return "new"
|
||||
case CLOSE:
|
||||
return "close"
|
||||
case OPEN:
|
||||
return "open"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func fromString(s string) Op {
|
||||
for i := 0; i < 20; i++ {
|
||||
if Op(i).String() == s {
|
||||
return Op(i)
|
||||
}
|
||||
}
|
||||
return Op(-1)
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
package remote
|
||||
|
||||
type loadTasksResponse struct {
|
||||
Total int `json:"total"`
|
||||
Tasks []Task `json:"list"`
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue