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