this is fun

This commit is contained in:
Bel LaPointe
2019-02-28 13:55:20 -07:00
parent c8318d45ac
commit 28310b0dd6
12 changed files with 593 additions and 33 deletions

View File

@@ -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
}

View File

@@ -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)
}
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}
}

View File

@@ -1,7 +1,6 @@
package remote
type loadListsResponse struct {
Total int `json:"total"`
Lists []List `json:"list"`
}

36
mytinytodo/remote/op.go Normal file
View File

@@ -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)
}

View File

@@ -1,7 +1,6 @@
package remote
type loadTasksResponse struct {
Total int `json:"total"`
Tasks []Task `json:"list"`
}