package ref import ( "io" "net/http" "net/http/cookiejar" "net/http/httptest" "net/url" "regexp" "strings" "testing" ) func newTestServer(t *testing.T) *httptest.Server { t.Helper() srv := httptest.NewUnstartedServer(nil) srv.Start() srv.Config.Handler = NewApp(srv.URL, "../../../shared") t.Cleanup(srv.Close) return srv } func client(t *testing.T) *http.Client { t.Helper() jar, _ := cookiejar.New(nil) return &http.Client{ Jar: jar, CheckRedirect: func(*http.Request, []*http.Request) error { return http.ErrUseLastResponse }, } } func post(t *testing.T, c *http.Client, srv *httptest.Server, path string, form url.Values, origin string) *http.Response { t.Helper() req, _ := http.NewRequest("POST", srv.URL+path, strings.NewReader(form.Encode())) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") if origin != "" { req.Header.Set("Origin", origin) } res, err := c.Do(req) if err != nil { t.Fatal(err) } res.Body.Close() return res } func get(t *testing.T, c *http.Client, srv *httptest.Server, path string) (int, string) { t.Helper() res, err := c.Get(srv.URL + path) if err != nil { t.Fatal(err) } defer res.Body.Close() b, _ := io.ReadAll(res.Body) return res.StatusCode, string(b) } var liRe = regexp.MustCompile(`]*data-id="([^"]+)"`) func ids(body string) []string { var out []string for _, m := range liRe.FindAllStringSubmatch(body, -1) { out = append(out, m[1]) } return out } func TestAddRedirectsToFilter(t *testing.T) { srv := newTestServer(t) c := client(t) get(t, c, srv, "/") res := post(t, c, srv, "/todos", url.Values{"title": {" a "}, "filter": {"active"}}, srv.URL) if res.StatusCode != 303 || res.Header.Get("Location") != "/active" { t.Fatalf("got %d %q, want 303 /active", res.StatusCode, res.Header.Get("Location")) } _, body := get(t, c, srv, "/") if n := len(ids(body)); n != 1 { t.Fatalf("want 1 item, got %d", n) } if !strings.Contains(body, "") { t.Fatalf("want trimmed label 'a'") } } func TestForeignOriginForbidden(t *testing.T) { srv := newTestServer(t) c := client(t) get(t, c, srv, "/") res := post(t, c, srv, "/todos", url.Values{"title": {"x"}}, "https://evil.example") if res.StatusCode != 403 { t.Fatalf("got %d, want 403", res.StatusCode) } } func TestOtherListIDNotFound(t *testing.T) { srv := newTestServer(t) a, b := client(t), client(t) get(t, a, srv, "/") get(t, b, srv, "/") post(t, a, srv, "/todos", url.Values{"title": {"mine"}}, srv.URL) _, body := get(t, a, srv, "/") id := ids(body)[0] res := post(t, b, srv, "/todos/"+id+"/toggle", nil, srv.URL) if res.StatusCode != 404 { t.Fatalf("got %d, want 404", res.StatusCode) } _, body = get(t, a, srv, "/completed") if len(ids(body)) != 0 { t.Fatalf("A's item was toggled by B") } } func TestGarbledCookieGetsEmptyList(t *testing.T) { srv := newTestServer(t) req, _ := http.NewRequest("GET", srv.URL+"/", nil) req.Header.Set("Cookie", "todo=%%%") res, err := http.DefaultClient.Do(req) if err != nil { t.Fatal(err) } b, _ := io.ReadAll(res.Body) res.Body.Close() if res.StatusCode != 200 || len(ids(string(b))) != 0 { t.Fatalf("got %d with %d items", res.StatusCode, len(ids(string(b)))) } }