carlos / todomvc-bakeoff Public

Clone
git clone https://amadan.net/carlos/todomvc-bakeoff

Plain git — no account needed to clone.

Download

Download this file

1package ref
2
3import (
4 "io"
5 "net/http"
6 "net/http/cookiejar"
7 "net/http/httptest"
8 "net/url"
9 "regexp"
10 "strings"
11 "testing"
12)
13
14func newTestServer(t *testing.T) *httptest.Server {
15 t.Helper()
16 srv := httptest.NewUnstartedServer(nil)
17 srv.Start()
18 srv.Config.Handler = NewApp(srv.URL, "../../../shared")
19 t.Cleanup(srv.Close)
20 return srv
21}
22
23func client(t *testing.T) *http.Client {
24 t.Helper()
25 jar, _ := cookiejar.New(nil)
26 return &http.Client{
27 Jar: jar,
28 CheckRedirect: func(*http.Request, []*http.Request) error { return http.ErrUseLastResponse },
29 }
30}
31
32func post(t *testing.T, c *http.Client, srv *httptest.Server, path string, form url.Values, origin string) *http.Response {
33 t.Helper()
34 req, _ := http.NewRequest("POST", srv.URL+path, strings.NewReader(form.Encode()))
35 req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
36 if origin != "" {
37 req.Header.Set("Origin", origin)
38 }
39 res, err := c.Do(req)
40 if err != nil {
41 t.Fatal(err)
42 }
43 res.Body.Close()
44 return res
45}
46
47func get(t *testing.T, c *http.Client, srv *httptest.Server, path string) (int, string) {
48 t.Helper()
49 res, err := c.Get(srv.URL + path)
50 if err != nil {
51 t.Fatal(err)
52 }
53 defer res.Body.Close()
54 b, _ := io.ReadAll(res.Body)
55 return res.StatusCode, string(b)
56}
57
58var liRe = regexp.MustCompile(`<li[^>]*data-id="([^"]+)"`)
59
60func ids(body string) []string {
61 var out []string
62 for _, m := range liRe.FindAllStringSubmatch(body, -1) {
63 out = append(out, m[1])
64 }
65 return out
66}
67
68func TestAddRedirectsToFilter(t *testing.T) {
69 srv := newTestServer(t)
70 c := client(t)
71 get(t, c, srv, "/")
72 res := post(t, c, srv, "/todos", url.Values{"title": {" a "}, "filter": {"active"}}, srv.URL)
73 if res.StatusCode != 303 || res.Header.Get("Location") != "/active" {
74 t.Fatalf("got %d %q, want 303 /active", res.StatusCode, res.Header.Get("Location"))
75 }
76 _, body := get(t, c, srv, "/")
77 if n := len(ids(body)); n != 1 {
78 t.Fatalf("want 1 item, got %d", n)
79 }
80 if !strings.Contains(body, "<label>a</label>") {
81 t.Fatalf("want trimmed label 'a'")
82 }
83}
84
85func TestForeignOriginForbidden(t *testing.T) {
86 srv := newTestServer(t)
87 c := client(t)
88 get(t, c, srv, "/")
89 res := post(t, c, srv, "/todos", url.Values{"title": {"x"}}, "https://evil.example")
90 if res.StatusCode != 403 {
91 t.Fatalf("got %d, want 403", res.StatusCode)
92 }
93}
94
95func TestOtherListIDNotFound(t *testing.T) {
96 srv := newTestServer(t)
97 a, b := client(t), client(t)
98 get(t, a, srv, "/")
99 get(t, b, srv, "/")
100 post(t, a, srv, "/todos", url.Values{"title": {"mine"}}, srv.URL)
101 _, body := get(t, a, srv, "/")
102 id := ids(body)[0]
103 res := post(t, b, srv, "/todos/"+id+"/toggle", nil, srv.URL)
104 if res.StatusCode != 404 {
105 t.Fatalf("got %d, want 404", res.StatusCode)
106 }
107 _, body = get(t, a, srv, "/completed")
108 if len(ids(body)) != 0 {
109 t.Fatalf("A's item was toggled by B")
110 }
111}
112
113func TestGarbledCookieGetsEmptyList(t *testing.T) {
114 srv := newTestServer(t)
115 req, _ := http.NewRequest("GET", srv.URL+"/", nil)
116 req.Header.Set("Cookie", "todo=%%%")
117 res, err := http.DefaultClient.Do(req)
118 if err != nil {
119 t.Fatal(err)
120 }
121 b, _ := io.ReadAll(res.Body)
122 res.Body.Close()
123 if res.StatusCode != 200 || len(ids(string(b))) != 0 {
124 t.Fatalf("got %d with %d items", res.StatusCode, len(ids(string(b))))
125 }
126}
127