// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package tsweb import ( "bytes" "encoding/json" "errors" "net/http" "net/http/httptest" "strings" "testing" ) type Data struct { Name string Price int } type Response struct { Status string Error string Data *Data } func TestNewJSONHandler(t *testing.T) { checkStatus := func(w *httptest.ResponseRecorder, status string) *Response { d := &Response{ Data: &Data{}, } t.Logf("%s", w.Body.Bytes()) err := json.Unmarshal(w.Body.Bytes(), d) if err != nil { t.Logf(err.Error()) return nil } if d.Status == status { t.Logf("ok: %s", d.Status) } else { t.Fatalf("wrong status: %s %s", d.Status, status) } return d } // 2 1 h21 := JSONHandler(func(w http.ResponseWriter, r *http.Request) error { return nil }) t.Run("2 1 simple", func(t *testing.T) { w := httptest.NewRecorder() r := httptest.NewRequest("GET", "/", nil) h21.ServeHTTP(w, r) checkStatus(w, "success") }) t.Run("2 1 HTTPError", func(t *testing.T) { h := JSONHandler(func(w http.ResponseWriter, r *http.Request) HTTPError { return Error(http.StatusForbidden, "forbidden", nil) }) w := httptest.NewRecorder() r := httptest.NewRequest("GET", "/", nil) h.ServeHTTP(w, r) if w.Code != http.StatusForbidden { t.Fatalf("wrong code: %d %d", w.Code, http.StatusForbidden) } }) // 2 2 h22 := JSONHandler(func(w http.ResponseWriter, r *http.Request) (*Data, error) { return &Data{Name: "tailscale"}, nil }) t.Run("2 2 get data", func(t *testing.T) { w := httptest.NewRecorder() r := httptest.NewRequest("GET", "/", nil) h22.ServeHTTP(w, r) checkStatus(w, "success") }) // 3 1 h31 := JSONHandler(func(w http.ResponseWriter, r *http.Request, d *Data) error { if d.Name == "" { return errors.New("name is empty") } return nil }) t.Run("3 1 post data", func(t *testing.T) { w := httptest.NewRecorder() r := httptest.NewRequest("POST", "/", strings.NewReader(`{"Name": "tailscale"}`)) h31.ServeHTTP(w, r) checkStatus(w, "success") }) t.Run("3 1 bad json", func(t *testing.T) { w := httptest.NewRecorder() r := httptest.NewRequest("POST", "/", strings.NewReader(`{`)) h31.ServeHTTP(w, r) checkStatus(w, "error") }) t.Run("3 1 post data error", func(t *testing.T) { w := httptest.NewRecorder() r := httptest.NewRequest("POST", "/", strings.NewReader(`{}`)) h31.ServeHTTP(w, r) resp := checkStatus(w, "error") if resp.Error != "name is empty" { t.Fatalf("wrong error") } }) // 3 2 h32 := JSONHandler(func(w http.ResponseWriter, r *http.Request, d *Data) (*Data, error) { if d.Price == 0 { return nil, errors.New("price is empty") } return &Data{Price: d.Price * 2}, nil }) t.Run("3 2 post data", func(t *testing.T) { w := httptest.NewRecorder() r := httptest.NewRequest("POST", "/", strings.NewReader(`{"Price": 10}`)) h32.ServeHTTP(w, r) resp := checkStatus(w, "success") t.Log(resp.Data) if resp.Data.Price != 20 { t.Fatalf("wrong price: %d %d", resp.Data.Price, 10) } }) t.Run("3 2 post data error", func(t *testing.T) { w := httptest.NewRecorder() r := httptest.NewRequest("POST", "/", strings.NewReader(`{}`)) h32.ServeHTTP(w, r) resp := checkStatus(w, "error") if resp.Error != "price is empty" { t.Fatalf("wrong error") } }) // fn check shouldPanic := func() { r := recover() if r == nil { t.Fatalf("should panic") } t.Log(r) } t.Run("2 0 panic", func(t *testing.T) { defer shouldPanic() JSONHandler(func(w http.ResponseWriter, r *http.Request) {}) }) t.Run("2 1 panic return value", func(t *testing.T) { defer shouldPanic() JSONHandler(func(w http.ResponseWriter, r *http.Request) string { return "" }) }) t.Run("2 1 panic arguments", func(t *testing.T) { defer shouldPanic() JSONHandler(func(r *http.Request, w http.ResponseWriter) error { return nil }) }) t.Run("3 1 panic arguments", func(t *testing.T) { defer shouldPanic() JSONHandler(func(name string, r *http.Request, w http.ResponseWriter) error { return nil }) }) t.Run("3 2 panic return value", func(t *testing.T) { defer shouldPanic() //lint:ignore ST1008 intentional JSONHandler(func(name string, r *http.Request, w http.ResponseWriter) (error, string) { return nil, "panic" }) }) t.Run("2 2 forbidden", func(t *testing.T) { code := http.StatusForbidden body := []byte("forbidden") h := JSONHandler(func(w http.ResponseWriter, r *http.Request) (*Data, error) { w.WriteHeader(code) w.Write(body) return nil, nil }) w := httptest.NewRecorder() r := httptest.NewRequest("GET", "/", nil) h.ServeHTTP(w, r) if w.Code != http.StatusForbidden { t.Fatalf("wrong code: %d %d", w.Code, code) } if !bytes.Equal(w.Body.Bytes(), []byte("forbidden")) { t.Fatalf("wrong body: %s %s", w.Body.Bytes(), body) } }) }