func TestContentTypeCheckerMiddleware(t *testing.T) { api := NewApi() // the middleware to test api.Use(&ContentTypeCheckerMiddleware{}) // a simple app api.SetApp(AppSimple(func(w ResponseWriter, r *Request) { w.WriteJson(map[string]string{"Id": "123"}) })) // wrap all handler := api.MakeHandler() // no payload, no content length, no check recorded := test.RunRequest(t, handler, test.MakeSimpleRequest("GET", "http://localhost/", nil)) recorded.CodeIs(200) // JSON payload with correct content type recorded = test.RunRequest(t, handler, test.MakeSimpleRequest("POST", "http://localhost/", map[string]string{"Id": "123"})) recorded.CodeIs(200) // JSON payload with correct content type specifying the utf-8 charset req := test.MakeSimpleRequest("POST", "http://localhost/", map[string]string{"Id": "123"}) req.Header.Set("Content-Type", "application/json; charset=utf-8") recorded = test.RunRequest(t, handler, req) recorded.CodeIs(200) // JSON payload with incorrect content type req = test.MakeSimpleRequest("POST", "http://localhost/", map[string]string{"Id": "123"}) req.Header.Set("Content-Type", "text/x-json") recorded = test.RunRequest(t, handler, req) recorded.CodeIs(415) }
func TestAccessLogApacheMiddlewareMissingData(t *testing.T) { api := NewApi() // the uncomplete middlewares stack buffer := bytes.NewBufferString("") api.Use(&AccessLogApacheMiddleware{ Logger: log.New(buffer, "", 0), Format: CommonLogFormat, textTemplate: nil, }) // a simple app api.SetApp(AppSimple(func(w ResponseWriter, r *Request) { w.WriteJson(map[string]string{"Id": "123"}) })) // wrap all handler := api.MakeHandler() req := test.MakeSimpleRequest("GET", "http://localhost/", nil) recorded := test.RunRequest(t, handler, req) recorded.CodeIs(200) recorded.ContentTypeIsJson() // not much to log when the Env data is missing, but this should still work apacheCommon := regexp.MustCompile(` - - "GET / HTTP/1.1" 0 -`) if !apacheCommon.Match(buffer.Bytes()) { t.Errorf("Got: %s", buffer.String()) } }
func TestRecoverMiddleware(t *testing.T) { api := NewApi() // the middleware to test api.Use(&RecoverMiddleware{ Logger: log.New(ioutil.Discard, "", 0), EnableLogAsJson: false, EnableResponseStackTrace: true, }) // a simple app that fails api.SetApp(AppSimple(func(w ResponseWriter, r *Request) { panic("test") })) // wrap all handler := api.MakeHandler() req := test.MakeSimpleRequest("GET", "http://localhost/", nil) recorded := test.RunRequest(t, handler, req) recorded.CodeIs(500) recorded.ContentTypeIsJson() // payload payload := map[string]string{} err := recorded.DecodeJsonPayload(&payload) if err != nil { t.Fatal(err) } if payload["Error"] == "" { t.Errorf("Expected an error message, got: %v", payload) } }
func TestAccessLogApacheMiddleware(t *testing.T) { api := NewApi() // the middlewares stack buffer := bytes.NewBufferString("") api.Use(&AccessLogApacheMiddleware{ Logger: log.New(buffer, "", 0), Format: CommonLogFormat, textTemplate: nil, }) api.Use(&TimerMiddleware{}) api.Use(&RecorderMiddleware{}) // a simple app api.SetApp(AppSimple(func(w ResponseWriter, r *Request) { w.WriteJson(map[string]string{"Id": "123"}) })) // wrap all handler := api.MakeHandler() req := test.MakeSimpleRequest("GET", "http://localhost/", nil) req.RemoteAddr = "127.0.0.1:1234" recorded := test.RunRequest(t, handler, req) recorded.CodeIs(200) recorded.ContentTypeIsJson() // log tests, eg: '127.0.0.1 - - 29/Nov/2014:22:28:34 +0000 "GET / HTTP/1.1" 200 12' apacheCommon := regexp.MustCompile(`127.0.0.1 - - \d{2}/\w{3}/\d{4}:\d{2}:\d{2}:\d{2} [+\-]\d{4}\ "GET / HTTP/1.1" 200 12`) if !apacheCommon.Match(buffer.Bytes()) { t.Errorf("Got: %s", buffer.String()) } }
func TestStatusMiddleware(t *testing.T) { api := NewApi() // the middlewares status := &StatusMiddleware{} api.Use(status) api.Use(&TimerMiddleware{}) api.Use(&RecorderMiddleware{}) // an app that return the Status api.SetApp(AppSimple(func(w ResponseWriter, r *Request) { w.WriteJson(status.GetStatus()) })) // wrap all handler := api.MakeHandler() // one request recorded := test.RunRequest(t, handler, test.MakeSimpleRequest("GET", "http://localhost/1", nil)) recorded.CodeIs(200) recorded.ContentTypeIsJson() // another request recorded = test.RunRequest(t, handler, test.MakeSimpleRequest("GET", "http://localhost/2", nil)) recorded.CodeIs(200) recorded.ContentTypeIsJson() // payload payload := map[string]interface{}{} err := recorded.DecodeJsonPayload(&payload) if err != nil { t.Fatal(err) } if payload["Pid"] == nil { t.Error("Expected a non nil Pid") } if payload["TotalCount"].(float64) != 1 { t.Errorf("TotalCount 1 Expected, got: %f", payload["TotalCount"].(float64)) } if payload["StatusCodeCount"].(map[string]interface{})["200"].(float64) != 1 { t.Errorf("StatusCodeCount 200 1 Expected, got: %f", payload["StatusCodeCount"].(map[string]interface{})["200"].(float64)) } }
func TestIfMiddleware(t *testing.T) { api := NewApi() // the middleware to test api.Use(&IfMiddleware{ Condition: func(r *Request) bool { if r.URL.Path == "/true" { return true } return false }, IfTrue: MiddlewareSimple(func(handler HandlerFunc) HandlerFunc { return func(w ResponseWriter, r *Request) { r.Env["TRUE_MIDDLEWARE"] = true handler(w, r) } }), IfFalse: MiddlewareSimple(func(handler HandlerFunc) HandlerFunc { return func(w ResponseWriter, r *Request) { r.Env["FALSE_MIDDLEWARE"] = true handler(w, r) } }), }) // a simple app api.SetApp(AppSimple(func(w ResponseWriter, r *Request) { w.WriteJson(r.Env) })) // wrap all handler := api.MakeHandler() recorded := test.RunRequest(t, handler, test.MakeSimpleRequest("GET", "http://localhost/", nil)) recorded.CodeIs(200) recorded.ContentTypeIsJson() recorded.BodyIs("{\"FALSE_MIDDLEWARE\":true}") recorded = test.RunRequest(t, handler, test.MakeSimpleRequest("GET", "http://localhost/true", nil)) recorded.CodeIs(200) recorded.ContentTypeIsJson() recorded.BodyIs("{\"TRUE_MIDDLEWARE\":true}") }
func TestJsonpMiddleware(t *testing.T) { api := NewApi() // the middleware to test api.Use(&JsonpMiddleware{}) // router app with success and error paths router, err := MakeRouter( Get("/ok", func(w ResponseWriter, r *Request) { w.WriteJson(map[string]string{"Id": "123"}) }), Get("/error", func(w ResponseWriter, r *Request) { Error(w, "jsonp error", 500) }), ) if err != nil { t.Fatal(err) } api.SetApp(router) // wrap all handler := api.MakeHandler() recorded := test.RunRequest(t, handler, test.MakeSimpleRequest("GET", "http://localhost/ok?callback=parseResponse", nil)) recorded.CodeIs(200) recorded.HeaderIs("Content-Type", "text/javascript") recorded.HeaderIs("Content-Disposition", "filename=f.txt") recorded.HeaderIs("X-Content-Type-Options", "nosniff") recorded.BodyIs("/**/parseResponse({\"Id\":\"123\"})") recorded = test.RunRequest(t, handler, test.MakeSimpleRequest("GET", "http://localhost/error?callback=parseResponse", nil)) recorded.CodeIs(500) recorded.HeaderIs("Content-Type", "text/javascript") recorded.HeaderIs("Content-Disposition", "filename=f.txt") recorded.HeaderIs("X-Content-Type-Options", "nosniff") recorded.BodyIs("/**/parseResponse({\"Error\":\"jsonp error\"})") }
func TestTimerMiddleware(t *testing.T) { api := NewApi() // a middleware carrying the Env tests api.Use(MiddlewareSimple(func(handler HandlerFunc) HandlerFunc { return func(w ResponseWriter, r *Request) { handler(w, r) if r.Env["ELAPSED_TIME"] == nil { t.Error("ELAPSED_TIME is nil") } elapsedTime := r.Env["ELAPSED_TIME"].(*time.Duration) if elapsedTime.Nanoseconds() <= 0 { t.Errorf( "ELAPSED_TIME is expected to be at least 1 nanosecond %d", elapsedTime.Nanoseconds(), ) } if r.Env["START_TIME"] == nil { t.Error("START_TIME is nil") } start := r.Env["START_TIME"].(*time.Time) if start.After(time.Now()) { t.Errorf( "START_TIME is expected to be in the past %s", start.String(), ) } } })) // the middleware to test api.Use(&TimerMiddleware{}) // a simple app api.SetApp(AppSimple(func(w ResponseWriter, r *Request) { w.WriteJson(map[string]string{"Id": "123"}) })) // wrap all handler := api.MakeHandler() req := test.MakeSimpleRequest("GET", "http://localhost/", nil) recorded := test.RunRequest(t, handler, req) recorded.CodeIs(200) recorded.ContentTypeIsJson() }
func TestGzipEnabled(t *testing.T) { api := NewApi() // the middleware to test api.Use(&GzipMiddleware{}) // router app with success and error paths router, err := MakeRouter( Get("/ok", func(w ResponseWriter, r *Request) { w.WriteJson(map[string]string{"Id": "123"}) }), Get("/error", func(w ResponseWriter, r *Request) { Error(w, "gzipped error", 500) }), ) if err != nil { t.Fatal(err) } api.SetApp(router) // wrap all handler := api.MakeHandler() recorded := test.RunRequest(t, handler, test.MakeSimpleRequest("GET", "http://localhost/ok", nil)) recorded.CodeIs(200) recorded.ContentTypeIsJson() recorded.ContentEncodingIsGzip() recorded.HeaderIs("Vary", "Accept-Encoding") recorded = test.RunRequest(t, handler, test.MakeSimpleRequest("GET", "http://localhost/error", nil)) recorded.CodeIs(500) recorded.ContentTypeIsJson() recorded.ContentEncodingIsGzip() recorded.HeaderIs("Vary", "Accept-Encoding") }
func TestApiNoAppNoMiddleware(t *testing.T) { api := NewApi() if api == nil { t.Fatal("Api object must be instantiated") } handler := api.MakeHandler() if handler == nil { t.Fatal("the http.Handler must be have been create") } recorded := test.RunRequest(t, handler, test.MakeSimpleRequest("GET", "http://localhost/", nil)) recorded.CodeIs(200) }
func TestApiSimpleAppNoMiddleware(t *testing.T) { api := NewApi() api.SetApp(AppSimple(func(w ResponseWriter, r *Request) { w.WriteJson(map[string]string{"Id": "123"}) })) handler := api.MakeHandler() if handler == nil { t.Fatal("the http.Handler must be have been create") } recorded := test.RunRequest(t, handler, test.MakeSimpleRequest("GET", "http://localhost/", nil)) recorded.CodeIs(200) recorded.ContentTypeIsJson() recorded.BodyIs(`{"Id":"123"}`) }
func TestRecorderMiddleware(t *testing.T) { api := NewApi() // a middleware carrying the Env tests api.Use(MiddlewareSimple(func(handler HandlerFunc) HandlerFunc { return func(w ResponseWriter, r *Request) { handler(w, r) if r.Env["STATUS_CODE"] == nil { t.Error("STATUS_CODE is nil") } statusCode := r.Env["STATUS_CODE"].(int) if statusCode != 200 { t.Errorf("STATUS_CODE = 200 expected, got %d", statusCode) } if r.Env["BYTES_WRITTEN"] == nil { t.Error("BYTES_WRITTEN is nil") } bytesWritten := r.Env["BYTES_WRITTEN"].(int64) // '{"Id":"123"}' => 12 chars if bytesWritten != 12 { t.Errorf("BYTES_WRITTEN 12 expected, got %d", bytesWritten) } } })) // the middleware to test api.Use(&RecorderMiddleware{}) // a simple app api.SetApp(AppSimple(func(w ResponseWriter, r *Request) { w.WriteJson(map[string]string{"Id": "123"}) })) // wrap all handler := api.MakeHandler() req := test.MakeSimpleRequest("GET", "http://localhost/", nil) recorded := test.RunRequest(t, handler, req) recorded.CodeIs(200) recorded.ContentTypeIsJson() }
func TestProdStack(t *testing.T) { api := NewApi() api.Use(DefaultProdStack...) api.SetApp(AppSimple(func(w ResponseWriter, r *Request) { w.WriteJson(map[string]string{"Id": "123"}) })) handler := api.MakeHandler() if handler == nil { t.Fatal("the http.Handler must be have been create") } recorded := test.RunRequest(t, handler, test.MakeSimpleRequest("GET", "http://localhost/", nil)) recorded.CodeIs(200) recorded.ContentTypeIsJson() recorded.ContentEncodingIsGzip() }
func TestAccessLogJsonMiddleware(t *testing.T) { api := NewApi() // the middlewares stack buffer := bytes.NewBufferString("") api.Use(&AccessLogJsonMiddleware{ Logger: log.New(buffer, "", 0), }) api.Use(&TimerMiddleware{}) api.Use(&RecorderMiddleware{}) // a simple app api.SetApp(AppSimple(func(w ResponseWriter, r *Request) { w.WriteJson(map[string]string{"Id": "123"}) })) // wrap all handler := api.MakeHandler() req := test.MakeSimpleRequest("GET", "http://localhost/", nil) req.RemoteAddr = "127.0.0.1:1234" recorded := test.RunRequest(t, handler, req) recorded.CodeIs(200) recorded.ContentTypeIsJson() // log tests decoded := &AccessLogJsonRecord{} err := json.Unmarshal(buffer.Bytes(), decoded) if err != nil { t.Fatal(err) } if decoded.StatusCode != 200 { t.Errorf("StatusCode 200 expected, got %d", decoded.StatusCode) } if decoded.RequestURI != "/" { t.Errorf("RequestURI / expected, got %s", decoded.RequestURI) } if decoded.HttpMethod != "GET" { t.Errorf("HttpMethod GET expected, got %s", decoded.HttpMethod) } }
func TestJsonIndentMiddleware(t *testing.T) { api := NewApi() // the middleware to test api.Use(&JsonIndentMiddleware{}) // a simple app api.SetApp(AppSimple(func(w ResponseWriter, r *Request) { w.WriteJson(map[string]string{"Id": "123"}) })) // wrap all handler := api.MakeHandler() req := test.MakeSimpleRequest("GET", "http://localhost/", nil) recorded := test.RunRequest(t, handler, req) recorded.CodeIs(200) recorded.ContentTypeIsJson() recorded.BodyIs("{\n \"Id\": \"123\"\n}") }
// See how many bytes are written when gzipping func TestRecorderAndGzipMiddleware(t *testing.T) { api := NewApi() // a middleware carrying the Env tests api.Use(MiddlewareSimple(func(handler HandlerFunc) HandlerFunc { return func(w ResponseWriter, r *Request) { handler(w, r) if r.Env["BYTES_WRITTEN"] == nil { t.Error("BYTES_WRITTEN is nil") } bytesWritten := r.Env["BYTES_WRITTEN"].(int64) // Yes, the gzipped version actually takes more space. if bytesWritten != 28 { t.Errorf("BYTES_WRITTEN 28 expected, got %d", bytesWritten) } } })) // the middlewares to test api.Use(&RecorderMiddleware{}) api.Use(&GzipMiddleware{}) // a simple app api.SetApp(AppSimple(func(w ResponseWriter, r *Request) { w.WriteJson(map[string]string{"Id": "123"}) })) // wrap all handler := api.MakeHandler() req := test.MakeSimpleRequest("GET", "http://localhost/", nil) // "Accept-Encoding", "gzip" is set by test.MakeSimpleRequest recorded := test.RunRequest(t, handler, req) recorded.CodeIs(200) recorded.ContentTypeIsJson() }
func TestGzipDisabled(t *testing.T) { api := NewApi() // router app with success and error paths router, err := MakeRouter( Get("/ok", func(w ResponseWriter, r *Request) { w.WriteJson(map[string]string{"Id": "123"}) }), ) if err != nil { t.Fatal(err) } api.SetApp(router) handler := api.MakeHandler() recorded := test.RunRequest(t, handler, test.MakeSimpleRequest("GET", "http://localhost/ok", nil)) recorded.CodeIs(200) recorded.ContentTypeIsJson() recorded.HeaderIs("Content-Encoding", "") recorded.HeaderIs("Vary", "") }
func TestPoweredByMiddleware(t *testing.T) { api := NewApi() // the middleware to test api.Use(&PoweredByMiddleware{ XPoweredBy: "test", }) // a simple app api.SetApp(AppSimple(func(w ResponseWriter, r *Request) { w.WriteJson(map[string]string{"Id": "123"}) })) // wrap all handler := api.MakeHandler() req := test.MakeSimpleRequest("GET", "http://localhost/", nil) recorded := test.RunRequest(t, handler, req) recorded.CodeIs(200) recorded.ContentTypeIsJson() recorded.HeaderIs("X-Powered-By", "test") }
func TestAuthBasic(t *testing.T) { // the middleware to test authMiddleware := &AuthBasicMiddleware{ Realm: "test zone", Authenticator: func(userId string, password string) bool { if userId == "admin" && password == "admin" { return true } return false }, Authorizator: func(userId string, request *Request) bool { if request.Method == "GET" { return true } return false }, } // api for testing failure apiFailure := NewApi() apiFailure.Use(authMiddleware) apiFailure.SetApp(AppSimple(func(w ResponseWriter, r *Request) { t.Error("Should never be executed") })) handler := apiFailure.MakeHandler() // simple request fails recorded := test.RunRequest(t, handler, test.MakeSimpleRequest("GET", "http://localhost/", nil)) recorded.CodeIs(401) recorded.ContentTypeIsJson() // auth with wrong cred and right method fails wrongCredReq := test.MakeSimpleRequest("GET", "http://localhost/", nil) encoded := base64.StdEncoding.EncodeToString([]byte("admin:AdmIn")) wrongCredReq.Header.Set("Authorization", "Basic "+encoded) recorded = test.RunRequest(t, handler, wrongCredReq) recorded.CodeIs(401) recorded.ContentTypeIsJson() // auth with right cred and wrong method fails rightCredReq := test.MakeSimpleRequest("POST", "http://localhost/", nil) encoded = base64.StdEncoding.EncodeToString([]byte("admin:admin")) rightCredReq.Header.Set("Authorization", "Basic "+encoded) recorded = test.RunRequest(t, handler, rightCredReq) recorded.CodeIs(401) recorded.ContentTypeIsJson() // api for testing success apiSuccess := NewApi() apiSuccess.Use(authMiddleware) apiSuccess.SetApp(AppSimple(func(w ResponseWriter, r *Request) { if r.Env["REMOTE_USER"] == nil { t.Error("REMOTE_USER is nil") } user := r.Env["REMOTE_USER"].(string) if user != "admin" { t.Error("REMOTE_USER is expected to be 'admin'") } w.WriteJson(map[string]string{"Id": "123"}) })) // auth with right cred and right method succeeds rightCredReq = test.MakeSimpleRequest("GET", "http://localhost/", nil) encoded = base64.StdEncoding.EncodeToString([]byte("admin:admin")) rightCredReq.Header.Set("Authorization", "Basic "+encoded) recorded = test.RunRequest(t, apiSuccess.MakeHandler(), rightCredReq) recorded.CodeIs(200) recorded.ContentTypeIsJson() }
func TestHandler(t *testing.T) { handler := ResourceHandler{ DisableJsonIndent: true, // make the test output less verbose by discarding the error log ErrorLogger: log.New(ioutil.Discard, "", 0), } handler.SetRoutes( Get("/r/:id", func(w ResponseWriter, r *Request) { id := r.PathParam("id") w.WriteJson(map[string]string{"Id": id}) }), Post("/r/:id", func(w ResponseWriter, r *Request) { // JSON echo data := map[string]string{} err := r.DecodeJsonPayload(&data) if err != nil { t.Fatal(err) } w.WriteJson(data) }), Get("/auto-fails", func(w ResponseWriter, r *Request) { a := []int{} _ = a[0] }), Get("/user-error", func(w ResponseWriter, r *Request) { Error(w, "My error", 500) }), Get("/user-notfound", func(w ResponseWriter, r *Request) { NotFound(w, r) }), ) // valid get resource recorded := test.RunRequest(t, &handler, test.MakeSimpleRequest("GET", "http://1.2.3.4/r/123", nil)) recorded.CodeIs(200) recorded.ContentTypeIsJson() recorded.BodyIs(`{"Id":"123"}`) // valid post resource recorded = test.RunRequest(t, &handler, test.MakeSimpleRequest( "POST", "http://1.2.3.4/r/123", &map[string]string{"Test": "Test"})) recorded.CodeIs(200) recorded.ContentTypeIsJson() recorded.BodyIs(`{"Test":"Test"}`) // broken Content-Type post resource request := test.MakeSimpleRequest("POST", "http://1.2.3.4/r/123", &map[string]string{"Test": "Test"}) request.Header.Set("Content-Type", "text/html") recorded = test.RunRequest(t, &handler, request) recorded.CodeIs(415) recorded.ContentTypeIsJson() recorded.BodyIs(`{"Error":"Bad Content-Type or charset, expected 'application/json'"}`) // broken Content-Type post resource request = test.MakeSimpleRequest("POST", "http://1.2.3.4/r/123", &map[string]string{"Test": "Test"}) request.Header.Set("Content-Type", "application/json; charset=ISO-8859-1") recorded = test.RunRequest(t, &handler, request) recorded.CodeIs(415) recorded.ContentTypeIsJson() recorded.BodyIs(`{"Error":"Bad Content-Type or charset, expected 'application/json'"}`) // Content-Type post resource with charset request = test.MakeSimpleRequest("POST", "http://1.2.3.4/r/123", &map[string]string{"Test": "Test"}) request.Header.Set("Content-Type", "application/json;charset=UTF-8") recorded = test.RunRequest(t, &handler, request) recorded.CodeIs(200) recorded.ContentTypeIsJson() recorded.BodyIs(`{"Test":"Test"}`) // auto 405 on undefined route (wrong method) recorded = test.RunRequest(t, &handler, test.MakeSimpleRequest("DELETE", "http://1.2.3.4/r/123", nil)) recorded.CodeIs(405) recorded.ContentTypeIsJson() recorded.BodyIs(`{"Error":"Method not allowed"}`) // auto 404 on undefined route (wrong path) recorded = test.RunRequest(t, &handler, test.MakeSimpleRequest("GET", "http://1.2.3.4/s/123", nil)) recorded.CodeIs(404) recorded.ContentTypeIsJson() recorded.BodyIs(`{"Error":"Resource not found"}`) // auto 500 on unhandled userecorder error recorded = test.RunRequest(t, &handler, test.MakeSimpleRequest("GET", "http://1.2.3.4/auto-fails", nil)) recorded.CodeIs(500) recorded.ContentTypeIsJson() recorded.BodyIs(`{"Error":"Internal Server Error"}`) // userecorder error recorded = test.RunRequest(t, &handler, test.MakeSimpleRequest("GET", "http://1.2.3.4/user-error", nil)) recorded.CodeIs(500) recorded.ContentTypeIsJson() recorded.BodyIs(`{"Error":"My error"}`) // userecorder notfound recorded = test.RunRequest(t, &handler, test.MakeSimpleRequest("GET", "http://1.2.3.4/user-notfound", nil)) recorded.CodeIs(404) recorded.ContentTypeIsJson() recorded.BodyIs(`{"Error":"Resource not found"}`) }