From e08b3f78e8c9f8b8d0ebbc26b43dcd1c2c8f2411 Mon Sep 17 00:00:00 2001 From: Vishal Rana Date: Sun, 29 Mar 2015 23:35:08 -0700 Subject: [PATCH] Coverage for handler Signed-off-by: Vishal Rana --- README.md | 9 ++-- context.go | 7 +++ context_test.go | 2 - echo.go | 125 ++++++++++++++++++++++++++++-------------------- echo_test.go | 116 ++++++++++++++++++++++++++++++++------------ 5 files changed, 170 insertions(+), 89 deletions(-) diff --git a/README.md b/README.md index 6ccd698d..f164a846 100644 --- a/README.md +++ b/README.md @@ -5,14 +5,17 @@ Echo is a fast HTTP router (zero memory allocation) + micro web framework in Go. - Zippy router. - Extensible middleware / handler, supports: - Middleware + - `func(*echo.Context)` - `func(echo.HandlerFunc) echo.HandlerFunc` - - `http.HandlerFunc` - - `http.Handler` - `func(http.Handler) http.Handler` + - `http.Handler` + - `http.HandlerFunc` + - `func(http.ResponseWriter, *http.Request)` - Handler - `func(*echo.Context)` - - `http.HandlerFunc` - `http.Handler` + - `http.HandlerFunc` + - `func(http.ResponseWriter, *http.Request)` - Serve static files, including index. ### Installatioin diff --git a/context.go b/context.go index 3819d19f..9ad8a1b2 100644 --- a/context.go +++ b/context.go @@ -47,6 +47,13 @@ func (c *Context) Bind(i interface{}) bool { return true } +// String writes status and string to the response. +func (c *Context) String(n int, s string) { + c.Response.Header().Set(HeaderContentType, MIMEText+"; charset=utf-8") + c.Response.WriteHeader(n) + c.Response.Write([]byte(s)) +} + // JSON writes status and JSON to the response. func (c *Context) JSON(n int, i interface{}) { enc := json.NewEncoder(c.Response) diff --git a/context_test.go b/context_test.go index b9e2ac4f..29a7ec0d 100644 --- a/context_test.go +++ b/context_test.go @@ -3,9 +3,7 @@ package echo import "testing" func TestContextBind(t *testing.T) { - } func TestContextJSON(t *testing.T) { - } diff --git a/echo.go b/echo.go index 8bad0190..6087d925 100644 --- a/echo.go +++ b/echo.go @@ -24,6 +24,7 @@ type ( const ( MIMEJSON = "application/json" + MIMEText = "text/plain" HeaderAccept = "Accept" HeaderContentDisposition = "Content-Disposition" @@ -32,8 +33,8 @@ const ( ) // New creates a echo instance. -func New() (b *Echo) { - b = &Echo{ +func New() (e *Echo) { + e = &Echo{ maxParam: 5, notFoundHandler: func(c *Context) { http.Error(c.Response, http.StatusText(http.StatusNotFound), http.StatusNotFound) @@ -45,13 +46,13 @@ func New() (b *Echo) { http.Error(c.Response, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) }, } - b.Router = NewRouter(b) - b.pool.New = func() interface{} { + e.Router = NewRouter(e) + e.pool.New = func() interface{} { return &Context{ Response: &response{}, - params: make(Params, b.maxParam), + params: make(Params, e.maxParam), store: make(store), - echo: b, + echo: e, } } return @@ -70,137 +71,151 @@ func (h HandlerFunc) ServeHTTP(r http.ResponseWriter, w *http.Request) { // MaxParam sets the maximum allowed path parameters. Default is 5, good enough // for many users. -func (b *Echo) MaxParam(n uint8) { - b.maxParam = n +func (e *Echo) MaxParam(n uint8) { + e.maxParam = n } // NotFoundHandler sets a custom NotFound handler. -func (b *Echo) NotFoundHandler(h Handler) { - b.notFoundHandler = wrapH(h) +func (e *Echo) NotFoundHandler(h Handler) { + e.notFoundHandler = wrapH(h) } // MethodNotAllowedHandler sets a custom MethodNotAllowed handler. -func (b *Echo) MethodNotAllowedHandler(h Handler) { - b.methodNotAllowedHandler = wrapH(h) +func (e *Echo) MethodNotAllowedHandler(h Handler) { + e.methodNotAllowedHandler = wrapH(h) } // InternalServerErrorHandler sets a custom InternalServerError handler. -func (b *Echo) InternalServerErrorHandler(h Handler) { - b.internalServerErrorHandler = wrapH(h) +func (e *Echo) InternalServerErrorHandler(h Handler) { + e.internalServerErrorHandler = wrapH(h) } // Use adds handler to the middleware chain. -func (b *Echo) Use(m ...Middleware) { +func (e *Echo) Use(m ...Middleware) { for _, h := range m { - b.middleware = append(b.middleware, wrapM(h)) + e.middleware = append(e.middleware, wrapM(h)) } } // Connect adds a CONNECT route > handler to the router. -func (b *Echo) Connect(path string, h Handler) { - b.Router.Add("CONNECT", path, wrapH(h)) +func (e *Echo) Connect(path string, h Handler) { + e.Router.Add("CONNECT", path, wrapH(h)) } // Delete adds a DELETE route > handler to the router. -func (b *Echo) Delete(path string, h Handler) { - b.Router.Add("DELETE", path, wrapH(h)) +func (e *Echo) Delete(path string, h Handler) { + e.Router.Add("DELETE", path, wrapH(h)) } // Get adds a GET route > handler to the router. -func (b *Echo) Get(path string, h Handler) { - b.Router.Add("GET", path, wrapH(h)) +func (e *Echo) Get(path string, h Handler) { + e.Router.Add("GET", path, wrapH(h)) } // Head adds a HEAD route > handler to the router. -func (b *Echo) Head(path string, h Handler) { - b.Router.Add("HEAD", path, wrapH(h)) +func (e *Echo) Head(path string, h Handler) { + e.Router.Add("HEAD", path, wrapH(h)) } // Options adds an OPTIONS route > handler to the router. -func (b *Echo) Options(path string, h Handler) { - b.Router.Add("OPTIONS", path, wrapH(h)) +func (e *Echo) Options(path string, h Handler) { + e.Router.Add("OPTIONS", path, wrapH(h)) } // Patch adds a PATCH route > handler to the router. -func (b *Echo) Patch(path string, h Handler) { - b.Router.Add("PATCH", path, wrapH(h)) +func (e *Echo) Patch(path string, h Handler) { + e.Router.Add("PATCH", path, wrapH(h)) } // Post adds a POST route > handler to the router. -func (b *Echo) Post(path string, h Handler) { - b.Router.Add("POST", path, wrapH(h)) +func (e *Echo) Post(path string, h Handler) { + e.Router.Add("POST", path, wrapH(h)) } // Put adds a PUT route > handler to the router. -func (b *Echo) Put(path string, h Handler) { - b.Router.Add("PUT", path, wrapH(h)) +func (e *Echo) Put(path string, h Handler) { + e.Router.Add("PUT", path, wrapH(h)) } // Trace adds a TRACE route > handler to the router. -func (b *Echo) Trace(path string, h Handler) { - b.Router.Add("TRACE", path, wrapH(h)) +func (e *Echo) Trace(path string, h Handler) { + e.Router.Add("TRACE", path, wrapH(h)) } // Static serves static files. -func (b *Echo) Static(path, root string) { +func (e *Echo) Static(path, root string) { fs := http.StripPrefix(path, http.FileServer(http.Dir(root))) - b.Get(path+"/*", func(c *Context) { + e.Get(path+"/*", func(c *Context) { fs.ServeHTTP(c.Response, c.Request) }) } // ServeFile serves a file. -func (b *Echo) ServeFile(path, file string) { - b.Get(path, func(c *Context) { +func (e *Echo) ServeFile(path, file string) { + e.Get(path, func(c *Context) { http.ServeFile(c.Response, c.Request, file) }) } // Index serves index file. -func (b *Echo) Index(file string) { - b.ServeFile("/", file) +func (e *Echo) Index(file string) { + e.ServeFile("/", file) } -func (b *Echo) ServeHTTP(rw http.ResponseWriter, r *http.Request) { - h, c, s := b.Router.Find(r.Method, r.URL.Path) +func (e *Echo) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + h, c, s := e.Router.Find(r.Method, r.URL.Path) c.reset(rw, r) if h != nil { // Middleware - for i := len(b.middleware) - 1; i >= 0; i-- { - h = b.middleware[i](h) + for i := len(e.middleware) - 1; i >= 0; i-- { + h = e.middleware[i](h) } // Handler h(c) } else { if s == NotFound { - b.notFoundHandler(c) + e.notFoundHandler(c) } else if s == NotAllowed { - b.methodNotAllowedHandler(c) + e.methodNotAllowedHandler(c) } } - b.pool.Put(c) + e.pool.Put(c) } -func (b *Echo) Run(addr string) { - log.Fatal(http.ListenAndServe(addr, b)) +func (e *Echo) Run(addr string) { + log.Fatal(http.ListenAndServe(addr, e)) } // wraps Middleware func wrapM(m Middleware) MiddlewareFunc { switch m := m.(type) { + case func(*Context): + return func(h HandlerFunc) HandlerFunc { + return func(c *Context) { + m(c) + h(c) + } + } case func(HandlerFunc) HandlerFunc: return MiddlewareFunc(m) - case http.HandlerFunc, http.Handler: + case func(http.Handler) http.Handler: + return func(h HandlerFunc) HandlerFunc { + return func(c *Context) { + m(h).ServeHTTP(c.Response, c.Request) + h(c) + } + } + case http.Handler, http.HandlerFunc: return func(h HandlerFunc) HandlerFunc { return func(c *Context) { m.(http.Handler).ServeHTTP(c.Response, c.Request) h(c) } } - case func(http.Handler) http.Handler: + case func(http.ResponseWriter, *http.Request): return func(h HandlerFunc) HandlerFunc { return func(c *Context) { - m(h).ServeHTTP(c.Response, c.Request) + m(c.Response, c.Request) h(c) } } @@ -214,10 +229,14 @@ func wrapH(h Handler) HandlerFunc { switch h := h.(type) { case func(*Context): return HandlerFunc(h) - case http.HandlerFunc, func(http.ResponseWriter, *http.Request), http.Handler: + case http.Handler, http.HandlerFunc: return func(c *Context) { h.(http.Handler).ServeHTTP(c.Response, c.Request) } + case func(http.ResponseWriter, *http.Request): + return func(c *Context) { + h(c.Response, c.Request) + } default: panic("echo: unknown handler") } diff --git a/echo_test.go b/echo_test.go index bd216e40..bddd163a 100644 --- a/echo_test.go +++ b/echo_test.go @@ -21,41 +21,47 @@ var u = user{ Name: "Joe", } +// TODO: Fix me func TestEchoMaxParam(t *testing.T) { - b := New() - b.MaxParam(8) - if b.maxParam != 8 { - t.Errorf("max param should be 8, found %d", b.maxParam) + e := New() + e.MaxParam(8) + if e.maxParam != 8 { + t.Errorf("max param should be 8, found %d", e.maxParam) } } func TestEchoIndex(t *testing.T) { - b := New() - b.Index("example/public/index.html") + e := New() + e.Index("example/public/index.html") w := httptest.NewRecorder() r, _ := http.NewRequest("GET", "/", nil) - b.ServeHTTP(w, r) + e.ServeHTTP(w, r) if w.Code != 200 { t.Errorf("status code should be 200, found %d", w.Code) } } func TestEchoStatic(t *testing.T) { - b := New() - b.Static("/js", "example/public/js") + e := New() + e.Static("/js", "example/public/js") w := httptest.NewRecorder() r, _ := http.NewRequest("GET", "/js/main.js", nil) - b.ServeHTTP(w, r) + e.ServeHTTP(w, r) if w.Code != 200 { t.Errorf("status code should be 200, found %d", w.Code) } } func TestEchoMiddleware(t *testing.T) { - b := New() + e := New() - // func(HandlerFunc) HandlerFunc - b.Use(func(h HandlerFunc) HandlerFunc { + // func(*echo.Context) + e.Use(func(c *Context) { + c.Request.Header.Set("e", "5") + }) + + // func(echo.HandlerFunc) echo.HandlerFunc + e.Use(func(h HandlerFunc) HandlerFunc { return HandlerFunc(func(c *Context) { c.Request.Header.Set("a", "1") h(c) @@ -63,45 +69,93 @@ func TestEchoMiddleware(t *testing.T) { }) // http.HandlerFunc - b.Use(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + e.Use(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { r.Header.Set("b", "2") })) // http.Handler - b.Use(http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + e.Use(http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { r.Header.Set("c", "3") }))) // func(http.Handler) http.Handler - b.Use(func(http.Handler) http.Handler { + e.Use(func(http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { r.Header.Set("d", "4") }) }) + // func(http.ResponseWriter, *http.Request) + e.Use(func(w http.ResponseWriter, r *http.Request) { + r.Header.Set("f", "6") + }) + // Route - b.Get("/users", func(c *Context) { - h := c.Request.Header.Get("a") - if h != "1" { - t.Errorf("header a should be 1, found %s", h) + e.Get("/hello", func(c *Context) { + if c.Request.Header.Get("a") != "1" { + t.Error("header a should be 1") } - h = c.Request.Header.Get("b") - if h != "2" { - t.Errorf("header b should be 2, found %s", h) + if c.Request.Header.Get("b") != "2" { + t.Error("header b should be 2") } - h = c.Request.Header.Get("c") - if h != "3" { - t.Errorf("header c should be 3, found %s", h) + if c.Request.Header.Get("c") != "3" { + t.Error("header c should be 3") } - h = c.Request.Header.Get("d") - if h != "4" { - t.Errorf("header d should be 4, found %s", h) + if c.Request.Header.Get("d") != "4" { + t.Error("header d should be 4") } + if c.Request.Header.Get("e") != "5" { + t.Error("header e should be 5") + } + if c.Request.Header.Get("f") != "6" { + t.Error("header f should be 6") + } + c.String(200, "world") }) w := httptest.NewRecorder() - r, _ := http.NewRequest("GET", "/users", nil) - b.ServeHTTP(w, r) + r, _ := http.NewRequest("GET", "/hello", nil) + e.ServeHTTP(w, r) + if w.Body.String() != "world" { + t.Errorf("body should be world") + } +} + +func TestEchoHandler(t *testing.T) { + e := New() + + // func(*echo.Context) + e.Get("/1", func(c *Context) { + c.String(http.StatusOK, "1") + }) + w := httptest.NewRecorder() + r, _ := http.NewRequest("GET", "/1", nil) + e.ServeHTTP(w, r) + if w.Body.String() != "1" { + t.Errorf("body should be 1") + } + + // http.Handler / http.HandlerFunc + e.Get("/2", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("2")) + })) + w = httptest.NewRecorder() + r, _ = http.NewRequest("GET", "/2", nil) + e.ServeHTTP(w, r) + if w.Body.String() != "2" { + t.Errorf("body should be 2") + } + + // func(http.ResponseWriter, *http.Request) + e.Get("/3", func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("3")) + }) + w = httptest.NewRecorder() + r, _ = http.NewRequest("GET", "/3", nil) + e.ServeHTTP(w, r) + if w.Body.String() != "3" { + t.Errorf("body should be 3") + } } func verifyUser(rd io.Reader, t *testing.T) {