Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(390)

Unified Diff: server/router/router_test.go

Issue 2043423004: Make HTTP middleware easier to use (Closed) Base URL: https://github.com/luci/luci-go@master
Patch Set: router: Improve documentation style Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« server/router/router.go ('K') | « server/router/router.go ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: server/router/router_test.go
diff --git a/server/router/router_test.go b/server/router/router_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..85406fb131c8aece0a18f98b2a87773bea6318ec
--- /dev/null
+++ b/server/router/router_test.go
@@ -0,0 +1,310 @@
+// Copyright 2016 The LUCI Authors. All rights reserved.
+// Use of this source code is governed under the Apache License, Version 2.0
+// that can be found in the LICENSE file.
+
+package router
+
+import (
+ "io"
+ "io/ioutil"
+ "net/http"
+ "net/http/httptest"
+ "testing"
+ "time"
+
+ "golang.org/x/net/context"
+
+ assert "github.com/smartystreets/assertions"
+ . "github.com/smartystreets/goconvey/convey"
+)
+
+func TestNew(t *testing.T) {
+ t.Parallel()
+ Convey("Initializes non-nil httprouter.Router", t, func() {
+ So(New().hrouter, ShouldNotBeNil)
+ })
+}
+
+func TestUse(t *testing.T) {
+ t.Parallel()
+ a := func(_ *Context, _ Handler) {}
+ b := func(_ *Context, _ Handler) {}
+
+ Convey("Use appends middleware", t, func() {
+ r := New()
+ So(len(r.middleware), ShouldEqual, 0)
+ r.Use(MiddlewareChain{a, b})
+ So(len(r.middleware), ShouldEqual, 2)
+ })
+}
+
+func TestSubrouter(t *testing.T) {
+ t.Parallel()
+ a := func(_ *Context, _ Handler) {}
+ b := func(_ *Context, _ Handler) {}
nodir 2016/06/16 04:20:25 I don't know if you do it intentionally or not, bu
nishanths 2016/06/16 22:11:26 Thanks. Rewritten.
+
+ Convey("Subrouter derives from original router", t, func() {
+ r1 := New()
+ r1.Use(MiddlewareChain{a, b})
+ r2 := r1.Subrouter("/foo")
+ So(r1.hrouter, ShouldPointTo, r2.hrouter)
+ So(&r1.middleware[0], ShouldNotPointTo, &r2.middleware[0])
+ So(len(r1.middleware), ShouldEqual, len(r2.middleware))
+ })
+}
+
+func TestHandle(t *testing.T) {
+ t.Parallel()
+ a := func(_ *Context, _ Handler) {}
+ b := func(_ *Context, _ Handler) {}
+ c := func(_ *Context, _ Handler) {}
+ handler := func(_ *Context) {}
+
+ Convey("No side-effects on r.middleware", t, func() {
+ r := New()
+ So(len(r.middleware), ShouldEqual, 0)
+ r.Handle("GET", "/bar", MiddlewareChain{b, c}, handler)
+ So(len(r.middleware), ShouldEqual, 0)
+ })
+
+ Convey("No side-effects on r.middleware", t, func() {
nodir 2016/06/16 04:20:25 convey usually panics on identical names. make the
nishanths 2016/06/16 22:11:26 Thanks for the tip. Done.
+ r := New()
+ r.Use(MiddlewareChain{a})
+ So(len(r.middleware), ShouldEqual, 1)
+ r.Handle("GET", "/bar", MiddlewareChain{b, c}, handler)
+ So(len(r.middleware), ShouldEqual, 1)
+ })
+}
+
+func TestRunMiddlewareChain(t *testing.T) {
+ t.Parallel()
+ a := func(c *Context, next Handler) {
+ io.WriteString(c, "1")
nodir 2016/06/16 04:20:25 consider replacing digits with something that indi
nishanths 2016/06/16 22:11:26 Done.
+ next(c)
+ io.WriteString(c, "7")
nodir 2016/06/16 04:20:25 hm.. this test violates middleware rule that it sh
nishanths 2016/06/16 22:11:26 Done, using context.Context instead.
+ }
+ b := func(c *Context, next Handler) {
+ io.WriteString(c, "2")
+ next(c)
+ io.WriteString(c, "6")
+ }
+ c := func(c *Context, next Handler) {
+ io.WriteString(c, "3")
+ next(c)
+ }
+ d := func(c *Context, next Handler) {
+ next(c)
+ io.WriteString(c, "5")
+ }
+ stop := func(_ *Context, _ Handler) {}
+ handler := func(c *Context) {
+ io.WriteString(c, "4")
+ }
+
+ Convey("Handles length 0 chains", t, func() {
+ rec := httptest.NewRecorder()
+ var m MiddlewareChain
+ n := MiddlewareChain{}
+ run(&Context{writer: rec}, m, n, handler)
nodir 2016/06/16 04:20:25 you could just pass nils instead as middleware cha
nishanths 2016/06/16 22:11:26 Done. For some reason, I was thinking we would nee
+ So(rec.Body.String(), ShouldResemble, "4")
+ })
+
+ Convey("Executes in order", t, func() {
+ rec := httptest.NewRecorder()
+ m := MiddlewareChain{a, b, c}
+ n := MiddlewareChain{d}
+ run(&Context{writer: rec}, m, n, handler)
+ So(rec.Body.String(), ShouldResemble, "1234567")
+ })
+
+ Convey("Not calling next does not execute upcoming handlers", t, func() {
+ rec := httptest.NewRecorder()
+ mc := MiddlewareChain{a, stop, b}
+ run(&Context{writer: rec}, mc, nil, handler)
+ So(rec.Body.String(), ShouldResemble, "17")
+ })
+}
+
+func TestServeHTTP(t *testing.T) {
+ t.Parallel()
+ setup := func(h http.Handler) (client *http.Client, ts *httptest.Server) {
+ client = &http.Client{}
+ client.Timeout = 10 * time.Second
+ ts = httptest.NewServer(h)
+ return
+ }
+ cleanup := func(ts *httptest.Server) {
+ ts.Close()
+ }
nodir 2016/06/16 04:20:25 setup and cleanup can be inlined if you use hierar
nishanths 2016/06/16 22:11:26 Done.
+ a := func(c *Context, next Handler) {
+ c.Context = context.WithValue(c.Context, "a", "A")
+ next(c)
+ }
+ b := func(c *Context, next Handler) {
+ val := c.Value("a").(string)
+ assert.ShouldEqual(val, "A")
+ c.Context = context.WithValue(c.Context, "a", val+"B")
+ next(c)
+ }
+ handler := func(c *Context) {
+ assert.ShouldEqual(c.Value("b"), "AB")
+ c.WriteHeader(http.StatusOK)
+ }
+ helloHandler := func(c *Context) {
+ c.Write([]byte("Hello, " + c.Params[0].Value))
+ }
+
+ Convey("Middleware registered using Use and Handle should both execute in order", t, func() {
+ r := New()
+ r.Use(MiddlewareChain{a})
+ r.GET("/foo", MiddlewareChain{b}, handler)
+ client, ts := setup(r)
+ defer cleanup(ts)
+
+ res, err := client.Get(ts.URL + "/foo")
+ So(err, ShouldBeNil)
+ res.Body.Close()
+ })
+
+ Convey("Wrong HTTP method for existing path should return method not allowed", t, func() {
+ r := New()
+ r.Use(MiddlewareChain{a})
+ r.GET("/foo", MiddlewareChain{b}, handler)
+ client, ts := setup(r)
+ defer cleanup(ts)
+
+ res, err := client.Post(ts.URL+"/foo", "", nil)
+ So(err, ShouldBeNil)
+ defer res.Body.Close()
+ So(res.StatusCode, ShouldEqual, http.StatusMethodNotAllowed)
+ })
+
+ Convey("Response should match expected value", t, func() {
+ r := New()
+ r.Use(MiddlewareChain{a})
+ r.GET("/hello/:name", MiddlewareChain{b}, helloHandler)
+ client, ts := setup(r)
+ defer cleanup(ts)
+
+ res, err := client.Get(ts.URL + "/hello/世界")
+ So(err, ShouldBeNil)
+ defer res.Body.Close()
+ p, err := ioutil.ReadAll(res.Body)
+ So(res.StatusCode, ShouldEqual, http.StatusOK)
+ So(string(p), ShouldEqual, "Hello, 世界")
+ })
+}
+
+func TestMakeBasePath(t *testing.T) {
+ t.Parallel()
+ cases := []struct{ base, relative, result string }{
+ {"/", "", "/"},
+ {"", "", "/"},
+ {"/foo/", "bar/", "/foo/bar/"},
+ {"/foo/", "bar//", "/foo/bar/"},
+ {"/foo/", "bar", "/foo/bar/"},
+ {"/foo", "//bar", "/foo/bar/"},
+ {"/foo//", "//bar///baz/qux", "/foo/bar/baz/qux/"},
+ }
+
+ Convey("Makes cleaned base path with exactly one trailing '/'", t, func() {
+ for _, c := range cases {
+ So(makeBasePath(c.base, c.relative), ShouldEqual, c.result)
+ }
+ })
+}
+
+func TestWrite(t *testing.T) {
+ t.Parallel()
+ setup := func(h http.Handler) (client *http.Client, ts *httptest.Server) {
+ client = &http.Client{}
+ client.Timeout = 10 * time.Second
+ ts = httptest.NewServer(h)
+ return
+ }
+ cleanup := func(ts *httptest.Server) {
+ ts.Close()
+ }
+
+ Convey("Written() and StatusCode() should change before and after Write", t, func() {
+ a := func(c *Context, next Handler) {
+ assert.ShouldBeFalse(c.Written())
+ assert.ShouldEqual(c.StatusCode(), 0)
+ next(c)
+ assert.ShouldBeTrue(c.Written())
+ assert.ShouldEqual(c.StatusCode(), http.StatusOK)
+ }
+ b := func(c *Context, next Handler) {
+ assert.ShouldBeFalse(c.Written())
+ assert.ShouldEqual(c.StatusCode(), 0)
+ io.WriteString(c, "foo")
+ assert.ShouldBeTrue(c.Written())
+ assert.ShouldEqual(c.StatusCode(), http.StatusOK)
+ next(c)
+ assert.ShouldBeTrue(c.Written())
+ assert.ShouldEqual(c.StatusCode(), http.StatusOK)
+ }
+ c := func(c *Context, next Handler) {
+ assert.ShouldBeTrue(c.Written())
+ assert.ShouldEqual(c.StatusCode(), http.StatusOK)
+ next(c)
+ assert.ShouldBeTrue(c.Written())
+ assert.ShouldEqual(c.StatusCode(), http.StatusOK)
+ }
+ handler := func(c *Context) {
+ assert.ShouldBeTrue(c.Written())
+ assert.ShouldEqual(c.StatusCode(), http.StatusOK)
+ }
+
+ r := New()
+ r.Use(MiddlewareChain{a, b, c})
+ r.GET("/test/write", nil, handler)
+ client, ts := setup(r)
+ defer cleanup(ts)
+
+ res, err := client.Get(ts.URL + "/test/write")
+ So(err, ShouldBeNil)
+ res.Body.Close()
+ })
+
+ Convey("Written() and StatusCode() should change before and after WriteHeader", t, func() {
+ a := func(c *Context, next Handler) {
+ assert.ShouldBeFalse(c.Written())
nodir 2016/06/16 04:20:25 why not So(c.Written(), ShouldBeFalse) this is m
nishanths 2016/06/16 22:11:26 Done. Earlier I used assertions because the tests
+ assert.ShouldEqual(c.StatusCode(), 0)
+ next(c)
+ assert.ShouldBeTrue(c.Written())
+ assert.ShouldEqual(c.StatusCode(), http.StatusTeapot)
+ }
+ b := func(c *Context, next Handler) {
+ assert.ShouldBeFalse(c.Written())
+ assert.ShouldEqual(c.StatusCode(), 0)
+ c.WriteHeader(http.StatusTeapot)
+ assert.ShouldBeTrue(c.Written())
+ assert.ShouldEqual(c.StatusCode(), http.StatusTeapot)
+ next(c)
+ assert.ShouldBeTrue(c.Written())
+ assert.ShouldEqual(c.StatusCode(), http.StatusTeapot)
+ }
+ c := func(c *Context, next Handler) {
+ assert.ShouldBeTrue(c.Written())
+ assert.ShouldEqual(c.StatusCode(), http.StatusTeapot)
+ next(c)
+ assert.ShouldBeTrue(c.Written())
+ assert.ShouldEqual(c.StatusCode(), http.StatusTeapot)
+ }
+ handler := func(c *Context) {
+ assert.ShouldBeTrue(c.Written())
+ assert.ShouldEqual(c.StatusCode(), http.StatusTeapot)
+ }
+
+ r := New()
+ r.Use(MiddlewareChain{a, b, c})
+ r.GET("/test/writeheader", nil, handler)
+ client, ts := setup(r)
+ defer cleanup(ts)
+
+ res, err := client.Get(ts.URL + "/test/writeheader")
+ So(err, ShouldBeNil)
+ res.Body.Close()
+ })
+}
« server/router/router.go ('K') | « server/router/router.go ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698