diff --git a/HISTORY.md b/HISTORY.md index 6741a1e9..c157797f 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -3,6 +3,79 @@ **How to upgrade**: remove your `$GOPATH/src/github.com/kataras` folder, open your command-line and execute this command: `go get -u github.com/kataras/iris/iris`. +## 4.5.2/.3 -> 4.6.0 + +### This update affects only testers who used `iris.Tester` at the past. + +- **FIX**: httptest flags caused by httpexpect which used to help you with tests inside **old** func `iris.Tester` as reported [here]( https://github.com/kataras/iris/issues/337#issuecomment-253429976) + +- **NEW**: `iris.ResetDefault()` func which resets the default iris instance which is the station for the most part of the public/package API + +- **NEW**: package `httptest` with configuration which can be passed per 'tester' instead of iris instance( this is very helpful for testers) + +- **CHANGED**: All tests are now converted for 'white-box' testing, means that tests now have package named: `iris_test` instead of `iris` in the same main directory. + +- **CHANGED**: `iris.Tester` moved to `httptest.New` which lives inside the new `/kataras/iris/httptest` package, so: + + +**old** +```go +import ( + "github.com/kataras/iris" + "testing" +) + +func MyTest(t *testing.T) { + iris.Get("/mypath", func(ctx *iris.Context){ + ctx.Write("my body") + }) + // with configs: iris.Config.Tester.ExplicitURL/Debug = true + e:= iris.Tester(t) + e.GET("/mypath").Expect().Status(iris.StatusOK).Body().Equal("my body") +} +``` +**used that instead/new** +```go +import ( + "github.com/kataras/iris/httptest" + "github.com/kataras/iris" + "testing" +) + +func MyTest(t *testing.T) { + // make sure that you reset your default station if you don't use the form of app := iris.New() + iris.ResetDefault() + + iris.Get("/mypath", func(ctx *iris.Context){ + ctx.Write("my body") + }) + + e:= httptest.New(iris.Default, t) + // with configs: e:= httptest.New(iris.Default, t, httptest.ExplicitURL(true), httptest.Debug(true)) + e.GET("/mypath").Expect().Status(iris.StatusOK).Body().Equal("my body") +} +``` + + +Finally, some plugins container's additions: + +- **NEW**: `iris.Plugins.Len()` func which returns the length of the current activated plugins in the default station + +- **NEW**: `iris.Plugins.Fired("event") int` func which returns how much times and from how many plugins a particular event type is fired, event types are: `"prelookup", "prebuild", "prelisten", "postlisten", "preclose", "predownload"` + +- **NEW**: `iris.Plugins.PreLookupFired() bool` func which returns true if `PreLookup` fired at least one time + +- **NEW**: `iris.Plugins.PreBuildFired() bool` func which returns true if `PreBuild` fired at least one time + +- **NEW**: `iris.Plugins.PreListenFired() bool` func which returns true if `PreListen/PreListenParallel` fired at least one time + +- **NEW**: `iris.Plugins.PostListenFired() bool` func which returns true if `PostListen` fired at least one time + +- **NEW**: `iris.Plugins.PreCloseFired() bool` func which returns true if `PreClose` fired at least one time + +- **NEW**: `iris.Plugins.PreDownloadFired() bool` func which returns true if `PreDownload` fired at least one time + + ## 4.5.1 -> 4.5.2 - **Feature request**: I never though that it will be easier for users to catch 405 instead of simple 404, I though that will make your life harder, but it's requested by the Community [here](https://github.com/kataras/iris/issues/469), so I did my duty. Enable firing Status Method Not Allowed (405) with a simple configuration field: `iris.Config.FireMethodNotAllowed=true` or `iris.Set(iris.OptionFireMethodNotAllowed(true))` or `app := iris.New(iris.Configuration{FireMethodNotAllowed:true})`. A trivial, test example can be shown here: diff --git a/configuration.go b/configuration.go index e4dd9d29..fa6fbaa3 100644 --- a/configuration.go +++ b/configuration.go @@ -207,10 +207,6 @@ type Configuration struct { // Websocket contains the configs for Websocket's server integration Websocket WebsocketConfiguration - // Tester contains the configs for the test framework, so far we have only one because all test framework's configs are setted by the iris itself - // You can find example on the https://github.com/kataras/iris/glob/master/context_test.go - Tester TesterConfiguration - // Other are the custom, dynamic options, can be empty // this fill used only by you to set any app's options you want // for each of an Iris instance @@ -547,7 +543,6 @@ func DefaultConfiguration() Configuration { Gzip: false, Sessions: DefaultSessionsConfiguration(), Websocket: DefaultWebsocketConfiguration(), - Tester: DefaultTesterConfiguration(), Other: options.Options{}, } } @@ -820,38 +815,6 @@ func DefaultWebsocketConfiguration() WebsocketConfiguration { } } -// TesterConfiguration configuration used inside main config field 'Tester' -type TesterConfiguration struct { - // ExplicitURL If true then the url (should) be prepended manually, useful when want to test subdomains - // Default is false - ExplicitURL bool - // Debug if true then debug messages from the httpexpect will be shown when a test runs - // Default is false - Debug bool -} - -var ( - // OptionTesterExplicitURL If true then the url (should) be prepended manually, useful when want to test subdomains - // Default is false - OptionTesterExplicitURL = func(val bool) OptionSet { - return func(c *Configuration) { - c.Tester.ExplicitURL = val - } - } - // OptionTesterDebug if true then debug messages from the httpexpect will be shown when a test runs - // Default is false - OptionTesterDebug = func(val bool) OptionSet { - return func(c *Configuration) { - c.Tester.Debug = val - } - } -) - -// DefaultTesterConfiguration returns the default configuration for a tester -func DefaultTesterConfiguration() TesterConfiguration { - return TesterConfiguration{ExplicitURL: false, Debug: false} -} - // Default values for base Server conf const ( // DefaultServerHostname returns the default hostname which is 0.0.0.0 diff --git a/configuration_test.go b/configuration_test.go index 7d892e6c..5b613802 100644 --- a/configuration_test.go +++ b/configuration_test.go @@ -1,6 +1,8 @@ -package iris +// Black-box Testing +package iris_test import ( + "github.com/kataras/iris" "reflect" "testing" ) @@ -8,9 +10,9 @@ import ( // go test -v -run TestConfig* func TestConfigStatic(t *testing.T) { - def := DefaultConfiguration() + def := iris.DefaultConfiguration() - api := New(def) + api := iris.New(def) afterNew := *api.Config if !reflect.DeepEqual(def, afterNew) { @@ -23,7 +25,7 @@ func TestConfigStatic(t *testing.T) { t.Fatalf("Configuration should be not equal, got: %#v", afterNew) } - api = New(Configuration{IsDevelopment: true}) + api = iris.New(iris.Configuration{IsDevelopment: true}) afterNew = *api.Config @@ -31,7 +33,7 @@ func TestConfigStatic(t *testing.T) { t.Fatalf("Passing a Configuration field as Option fails, expected IsDevelopment to be true but was false") } - api = New() // empty , means defaults so + api = iris.New() // empty , means defaults so if !reflect.DeepEqual(def, *api.Config) { t.Fatalf("Default configuration is not the same after NewFromConfig expected:\n %#v \ngot:\n %#v", def, *api.Config) } @@ -41,7 +43,7 @@ func TestConfigOptions(t *testing.T) { charset := "MYCHARSET" dev := true - api := New(OptionCharset(charset), OptionIsDevelopment(dev)) + api := iris.New(iris.OptionCharset(charset), iris.OptionIsDevelopment(dev)) if got := api.Config.Charset; got != charset { t.Fatalf("Expected configuration Charset to be: %s but got: %s", charset, got) @@ -53,7 +55,7 @@ func TestConfigOptions(t *testing.T) { // now check if other default values are setted (should be setted automatically) - expected := DefaultConfiguration() + expected := iris.DefaultConfiguration() expected.Charset = charset expected.IsDevelopment = dev @@ -69,9 +71,9 @@ func TestConfigOptionsDeep(t *testing.T) { dev := true vhost := "mydomain.com" // first session, after charset,dev and profilepath, no canonical order. - api := New(OptionSessionsCookie(cookiename), OptionCharset(charset), OptionIsDevelopment(dev), OptionVHost(vhost)) + api := iris.New(iris.OptionSessionsCookie(cookiename), iris.OptionCharset(charset), iris.OptionIsDevelopment(dev), iris.OptionVHost(vhost)) - expected := DefaultConfiguration() + expected := iris.DefaultConfiguration() expected.Sessions.Cookie = cookiename expected.Charset = charset expected.IsDevelopment = dev diff --git a/context.go b/context.go index 03c5f113..bdc0b99f 100644 --- a/context.go +++ b/context.go @@ -96,10 +96,10 @@ type ( Params PathParameters framework *Framework //keep track all registed middleware (handlers) - middleware Middleware + Middleware Middleware // exported because is useful for debugging session sessions.Session - // pos is the position number of the Context, look .Next to understand - pos uint8 + // Pos is the position number of the Context, look .Next to understand + Pos uint8 // exported because is useful for debugging } ) @@ -110,37 +110,37 @@ func (ctx *Context) GetRequestCtx() *fasthttp.RequestCtx { // Do calls the first handler only, it's like Next with negative pos, used only on Router&MemoryRouter func (ctx *Context) Do() { - ctx.pos = 0 - ctx.middleware[0].Serve(ctx) + ctx.Pos = 0 + ctx.Middleware[0].Serve(ctx) } // Next calls all the next handler from the middleware stack, it used inside a middleware func (ctx *Context) Next() { //set position to the next - ctx.pos++ - midLen := uint8(len(ctx.middleware)) + ctx.Pos++ + midLen := uint8(len(ctx.Middleware)) //run the next - if ctx.pos < midLen { - ctx.middleware[ctx.pos].Serve(ctx) + if ctx.Pos < midLen { + ctx.Middleware[ctx.Pos].Serve(ctx) } } // StopExecution just sets the .pos to 255 in order to not move to the next middlewares(if any) func (ctx *Context) StopExecution() { - ctx.pos = stopExecutionPosition + ctx.Pos = stopExecutionPosition } // // IsStopped checks and returns true if the current position of the Context is 255, means that the StopExecution has called func (ctx *Context) IsStopped() bool { - return ctx.pos == stopExecutionPosition + return ctx.Pos == stopExecutionPosition } // GetHandlerName as requested returns the stack-name of the function which the Middleware is setted from func (ctx *Context) GetHandlerName() string { - return runtime.FuncForPC(reflect.ValueOf(ctx.middleware[len(ctx.middleware)-1]).Pointer()).Name() + return runtime.FuncForPC(reflect.ValueOf(ctx.Middleware[len(ctx.Middleware)-1]).Pointer()).Name() } /* Request */ diff --git a/context_test.go b/context_test.go index b27a7265..c40a0907 100644 --- a/context_test.go +++ b/context_test.go @@ -1,4 +1,5 @@ -package iris +// Black-box Testing +package iris_test /* The most part of the context covered, @@ -14,30 +15,32 @@ import ( "encoding/json" "encoding/xml" "fmt" + "github.com/kataras/iris" + "github.com/kataras/iris/httptest" + "github.com/valyala/fasthttp" "net/url" "strconv" "strings" "testing" "time" - - "github.com/valyala/fasthttp" ) +// White-box testing * func TestContextDoNextStop(t *testing.T) { - var context Context + var context iris.Context ok := false afterStop := false - context.middleware = Middleware{HandlerFunc(func(*Context) { + context.Middleware = iris.Middleware{iris.HandlerFunc(func(*iris.Context) { ok = true - }), HandlerFunc(func(*Context) { + }), iris.HandlerFunc(func(*iris.Context) { ok = true - }), HandlerFunc(func(*Context) { + }), iris.HandlerFunc(func(*iris.Context) { // this will never execute afterStop = true })} context.Do() - if context.pos != 0 { - t.Fatalf("Expecting position 0 for context's middleware but we got: %d", context.pos) + if context.Pos != 0 { + t.Fatalf("Expecting position 0 for context's middleware but we got: %d", context.Pos) } if !ok { t.Fatalf("Unexpected behavior, first context's middleware didn't executed") @@ -46,16 +49,16 @@ func TestContextDoNextStop(t *testing.T) { context.Next() - if int(context.pos) != 1 { - t.Fatalf("Expecting to have position %d but we got: %d", 1, context.pos) + if int(context.Pos) != 1 { + t.Fatalf("Expecting to have position %d but we got: %d", 1, context.Pos) } if !ok { t.Fatalf("Next context's middleware didn't executed") } context.StopExecution() - if context.pos != stopExecutionPosition { - t.Fatalf("Context's StopExecution didn't worked, we expected to have position %d but we got %d", stopExecutionPosition, context.pos) + if context.Pos != 255 { + t.Fatalf("Context's StopExecution didn't worked, we expected to have position %d but we got %d", 255, context.Pos) } if !context.IsStopped() { @@ -69,13 +72,14 @@ func TestContextDoNextStop(t *testing.T) { } } +// White-box testing * func TestContextParams(t *testing.T) { - var context Context - params := PathParameters{ - PathParameter{Key: "testkey", Value: "testvalue"}, - PathParameter{Key: "testkey2", Value: "testvalue2"}, - PathParameter{Key: "id", Value: "3"}, - PathParameter{Key: "bigint", Value: "548921854390354"}, + var context iris.Context + params := iris.PathParameters{ + iris.PathParameter{Key: "testkey", Value: "testvalue"}, + iris.PathParameter{Key: "testkey2", Value: "testvalue2"}, + iris.PathParameter{Key: "id", Value: "3"}, + iris.PathParameter{Key: "bigint", Value: "548921854390354"}, } context.Params = params @@ -104,94 +108,94 @@ func TestContextParams(t *testing.T) { // end-to-end test now, note that we will not test the whole mux here, this happens on http_test.go - initDefault() + iris.ResetDefault() expectedParamsStr := "param1=myparam1,param2=myparam2,param3=myparam3afterstatic,anything=/andhere/anything/you/like" - Get("/path/:param1/:param2/staticpath/:param3/*anything", func(ctx *Context) { + iris.Get("/path/:param1/:param2/staticpath/:param3/*anything", func(ctx *iris.Context) { paramsStr := ctx.Params.String() ctx.Write(paramsStr) }) - Tester(t).GET("/path/myparam1/myparam2/staticpath/myparam3afterstatic/andhere/anything/you/like").Expect().Status(StatusOK).Body().Equal(expectedParamsStr) + httptest.New(iris.Default, t).GET("/path/myparam1/myparam2/staticpath/myparam3afterstatic/andhere/anything/you/like").Expect().Status(iris.StatusOK).Body().Equal(expectedParamsStr) } func TestContextURLParams(t *testing.T) { - initDefault() + iris.ResetDefault() passedParams := map[string]string{"param1": "value1", "param2": "value2"} - Get("/", func(ctx *Context) { + iris.Get("/", func(ctx *iris.Context) { params := ctx.URLParams() - ctx.JSON(StatusOK, params) + ctx.JSON(iris.StatusOK, params) }) - e := Tester(t) + e := httptest.New(iris.Default, t) - e.GET("/").WithQueryObject(passedParams).Expect().Status(StatusOK).JSON().Equal(passedParams) + e.GET("/").WithQueryObject(passedParams).Expect().Status(iris.StatusOK).JSON().Equal(passedParams) } // hoststring returns the full host, will return the HOST:IP func TestContextHostString(t *testing.T) { - initDefault() - Default.Config.VHost = "0.0.0.0:8080" - Get("/", func(ctx *Context) { + iris.ResetDefault() + iris.Default.Config.VHost = "0.0.0.0:8080" + iris.Get("/", func(ctx *iris.Context) { ctx.Write(ctx.HostString()) }) - Get("/wrong", func(ctx *Context) { + iris.Get("/wrong", func(ctx *iris.Context) { ctx.Write(ctx.HostString() + "w") }) - e := Tester(t) - e.GET("/").Expect().Status(StatusOK).Body().Equal(Default.Config.VHost) - e.GET("/wrong").Expect().Body().NotEqual(Default.Config.VHost) + e := httptest.New(iris.Default, t) + e.GET("/").Expect().Status(iris.StatusOK).Body().Equal(iris.Default.Config.VHost) + e.GET("/wrong").Expect().Body().NotEqual(iris.Default.Config.VHost) } // VirtualHostname returns the hostname only, // if the host starts with 127.0.0.1 or localhost it gives the registered hostname part of the listening addr func TestContextVirtualHostName(t *testing.T) { - initDefault() + iris.ResetDefault() vhost := "mycustomvirtualname.com" - Default.Config.VHost = vhost + ":8080" - Get("/", func(ctx *Context) { + iris.Default.Config.VHost = vhost + ":8080" + iris.Get("/", func(ctx *iris.Context) { ctx.Write(ctx.VirtualHostname()) }) - Get("/wrong", func(ctx *Context) { + iris.Get("/wrong", func(ctx *iris.Context) { ctx.Write(ctx.VirtualHostname() + "w") }) - e := Tester(t) - e.GET("/").Expect().Status(StatusOK).Body().Equal(vhost) + e := httptest.New(iris.Default, t) + e.GET("/").Expect().Status(iris.StatusOK).Body().Equal(vhost) e.GET("/wrong").Expect().Body().NotEqual(vhost) } func TestContextFormValueString(t *testing.T) { - initDefault() + iris.ResetDefault() var k, v string k = "postkey" v = "postvalue" - Post("/", func(ctx *Context) { + iris.Post("/", func(ctx *iris.Context) { ctx.Write(k + "=" + ctx.FormValueString(k)) }) - e := Tester(t) + e := httptest.New(iris.Default, t) - e.POST("/").WithFormField(k, v).Expect().Status(StatusOK).Body().Equal(k + "=" + v) + e.POST("/").WithFormField(k, v).Expect().Status(iris.StatusOK).Body().Equal(k + "=" + v) } func TestContextSubdomain(t *testing.T) { - initDefault() - Default.Config.VHost = "mydomain.com:9999" + iris.ResetDefault() + iris.Default.Config.VHost = "mydomain.com:9999" //Default.Config.Tester.ListeningAddr = "mydomain.com:9999" // Default.Config.Tester.ExplicitURL = true - Party("mysubdomain.").Get("/mypath", func(ctx *Context) { + iris.Party("mysubdomain.").Get("/mypath", func(ctx *iris.Context) { ctx.Write(ctx.Subdomain()) }) - e := Tester(t) + e := httptest.New(iris.Default, t) - e.GET("/").WithURL("http://mysubdomain.mydomain.com:9999").Expect().Status(StatusNotFound) - e.GET("/mypath").WithURL("http://mysubdomain.mydomain.com:9999").Expect().Status(StatusOK).Body().Equal("mysubdomain") + e.GET("/").WithURL("http://mysubdomain.mydomain.com:9999").Expect().Status(iris.StatusNotFound) + e.GET("/mypath").WithURL("http://mysubdomain.mydomain.com:9999").Expect().Status(iris.StatusOK).Body().Equal("mysubdomain") //e.GET("http://mysubdomain.mydomain.com:9999").Expect().Status(StatusNotFound) - //e.GET("http://mysubdomain.mydomain.com:9999/mypath").Expect().Status(StatusOK).Body().Equal("mysubdomain") + //e.GET("http://mysubdomain.mydomain.com:9999/mypath").Expect().Status(iris.StatusOK).Body().Equal("mysubdomain") } type testBinderData struct { @@ -210,53 +214,53 @@ type testBinderXMLData struct { } func TestContextReadForm(t *testing.T) { - initDefault() + iris.ResetDefault() - Post("/form", func(ctx *Context) { + iris.Post("/form", func(ctx *iris.Context) { obj := testBinderData{} err := ctx.ReadForm(&obj) if err != nil { t.Fatalf("Error when parsing the FORM: %s", err.Error()) } - ctx.JSON(StatusOK, obj) + ctx.JSON(iris.StatusOK, obj) }) - e := Tester(t) + e := httptest.New(iris.Default, t) passed := map[string]interface{}{"Username": "myusername", "Mail": "mymail@iris-go.com", "mydata": url.Values{"[0]": []string{"mydata1"}, "[1]": []string{"mydata2"}}} expectedObject := testBinderData{Username: "myusername", Mail: "mymail@iris-go.com", Data: []string{"mydata1", "mydata2"}} - e.POST("/form").WithForm(passed).Expect().Status(StatusOK).JSON().Object().Equal(expectedObject) + e.POST("/form").WithForm(passed).Expect().Status(iris.StatusOK).JSON().Object().Equal(expectedObject) } func TestContextReadJSON(t *testing.T) { - initDefault() - Post("/json", func(ctx *Context) { + iris.ResetDefault() + iris.Post("/json", func(ctx *iris.Context) { obj := testBinderData{} err := ctx.ReadJSON(&obj) if err != nil { t.Fatalf("Error when parsing the JSON body: %s", err.Error()) } - ctx.JSON(StatusOK, obj) + ctx.JSON(iris.StatusOK, obj) }) - Post("/json_pointer", func(ctx *Context) { + iris.Post("/json_pointer", func(ctx *iris.Context) { obj := &testBinderData{} err := ctx.ReadJSON(obj) if err != nil { t.Fatalf("Error when parsing the JSON body: %s", err.Error()) } - ctx.JSON(StatusOK, obj) + ctx.JSON(iris.StatusOK, obj) }) - e := Tester(t) + e := httptest.New(iris.Default, t) passed := map[string]interface{}{"Username": "myusername", "Mail": "mymail@iris-go.com", "mydata": []string{"mydata1", "mydata2"}} expectedObject := testBinderData{Username: "myusername", Mail: "mymail@iris-go.com", Data: []string{"mydata1", "mydata2"}} - e.POST("/json").WithJSON(passed).Expect().Status(StatusOK).JSON().Object().Equal(expectedObject) - e.POST("/json_pointer").WithJSON(passed).Expect().Status(StatusOK).JSON().Object().Equal(expectedObject) + e.POST("/json").WithJSON(passed).Expect().Status(iris.StatusOK).JSON().Object().Equal(expectedObject) + e.POST("/json_pointer").WithJSON(passed).Expect().Status(iris.StatusOK).JSON().Object().Equal(expectedObject) } type testJSONBinderDataWithDecoder struct { @@ -274,57 +278,57 @@ func (tj *testJSONBinderDataWithDecoder) Decode(data []byte) error { } func TestContextReadJSONWithDecoder(t *testing.T) { - initDefault() - Post("/json_should_error", func(ctx *Context) { + iris.ResetDefault() + iris.Post("/json_should_error", func(ctx *iris.Context) { obj := testJSONBinderDataWithDecoder{shouldError: true} err := ctx.ReadJSON(&obj) if err == nil { t.Fatalf("Should prompted for error 'Should error' but not error returned from the custom decoder!") } ctx.Write(err.Error()) - ctx.SetStatusCode(StatusOK) + ctx.SetStatusCode(iris.StatusOK) }) - Post("/json", func(ctx *Context) { + iris.Post("/json", func(ctx *iris.Context) { obj := testJSONBinderDataWithDecoder{} err := ctx.ReadJSON(&obj) if err != nil { t.Fatalf("Error when parsing the JSON body: %s", err.Error()) } - ctx.JSON(StatusOK, obj) + ctx.JSON(iris.StatusOK, obj) }) - Post("/json_pointer", func(ctx *Context) { + iris.Post("/json_pointer", func(ctx *iris.Context) { obj := &testJSONBinderDataWithDecoder{} err := ctx.ReadJSON(obj) if err != nil { t.Fatalf("Error when parsing the JSON body: %s", err.Error()) } - ctx.JSON(StatusOK, obj) + ctx.JSON(iris.StatusOK, obj) }) - e := Tester(t) + e := httptest.New(iris.Default, t) passed := map[string]interface{}{"Username": "kataras", "Mail": "mymail@iris-go.com", "mydata": []string{"mydata1", "mydata2"}} expectedObject := testJSONBinderDataWithDecoder{Username: "kataras", Mail: "mymail@iris-go.com", Data: []string{"mydata1", "mydata2"}} - e.POST("/json_should_error").WithJSON(passed).Expect().Status(StatusOK).Body().Equal("Should error") - e.POST("/json").WithJSON(passed).Expect().Status(StatusOK).JSON().Object().Equal(expectedObject) - e.POST("/json_pointer").WithJSON(passed).Expect().Status(StatusOK).JSON().Object().Equal(expectedObject) + e.POST("/json_should_error").WithJSON(passed).Expect().Status(iris.StatusOK).Body().Equal("Should error") + e.POST("/json").WithJSON(passed).Expect().Status(iris.StatusOK).JSON().Object().Equal(expectedObject) + e.POST("/json_pointer").WithJSON(passed).Expect().Status(iris.StatusOK).JSON().Object().Equal(expectedObject) } // no need for xml, it's exact the same. func TestContextReadXML(t *testing.T) { - initDefault() + iris.ResetDefault() - Post("/xml", func(ctx *Context) { + iris.Post("/xml", func(ctx *iris.Context) { obj := testBinderXMLData{} err := ctx.ReadXML(&obj) if err != nil { t.Fatalf("Error when parsing the XML body: %s", err.Error()) } - ctx.XML(StatusOK, obj) + ctx.XML(iris.StatusOK, obj) }) - e := Tester(t) + e := httptest.New(iris.Default, t) expectedObj := testBinderXMLData{ XMLName: xml.Name{Local: "info", Space: "info"}, FirstAttr: "this is the first attr", @@ -335,23 +339,23 @@ func TestContextReadXML(t *testing.T) { } // so far no WithXML or .XML like WithJSON and .JSON on httpexpect I added a feature request as post issue and we're waiting expectedBody := `<` + expectedObj.XMLName.Local + ` first="` + expectedObj.FirstAttr + `" second="` + expectedObj.SecondAttr + `">` + expectedObj.Name + `` + expectedObj.Birth + `` + strconv.Itoa(expectedObj.Stars) + `` - e.POST("/xml").WithText(expectedBody).Expect().Status(StatusOK).Body().Equal(expectedBody) + e.POST("/xml").WithText(expectedBody).Expect().Status(iris.StatusOK).Body().Equal(expectedBody) } // TestContextRedirectTo tests the named route redirect action func TestContextRedirectTo(t *testing.T) { - initDefault() - h := func(ctx *Context) { ctx.Write(ctx.PathString()) } - Get("/mypath", h)("my-path") - Get("/mypostpath", h)("my-post-path") - Get("mypath/with/params/:param1/:param2", func(ctx *Context) { + iris.ResetDefault() + h := func(ctx *iris.Context) { ctx.Write(ctx.PathString()) } + iris.Get("/mypath", h)("my-path") + iris.Get("/mypostpath", h)("my-post-path") + iris.Get("mypath/with/params/:param1/:param2", func(ctx *iris.Context) { if len(ctx.Params) != 2 { t.Fatalf("Strange error, expecting parameters to be two but we got: %d", len(ctx.Params)) } ctx.Write(ctx.PathString()) })("my-path-with-params") - Get("/redirect/to/:routeName/*anyparams", func(ctx *Context) { + iris.Get("/redirect/to/:routeName/*anyparams", func(ctx *iris.Context) { routeName := ctx.Param("routeName") var args []interface{} anyparams := ctx.Param("anyparams") @@ -365,25 +369,25 @@ func TestContextRedirectTo(t *testing.T) { ctx.RedirectTo(routeName, args...) }) - e := Tester(t) + e := httptest.New(iris.Default, t) - e.GET("/redirect/to/my-path/").Expect().Status(StatusOK).Body().Equal("/mypath") - e.GET("/redirect/to/my-post-path/").Expect().Status(StatusOK).Body().Equal("/mypostpath") - e.GET("/redirect/to/my-path-with-params/firstparam/secondparam").Expect().Status(StatusOK).Body().Equal("/mypath/with/params/firstparam/secondparam") + e.GET("/redirect/to/my-path/").Expect().Status(iris.StatusOK).Body().Equal("/mypath") + e.GET("/redirect/to/my-post-path/").Expect().Status(iris.StatusOK).Body().Equal("/mypostpath") + e.GET("/redirect/to/my-path-with-params/firstparam/secondparam").Expect().Status(iris.StatusOK).Body().Equal("/mypath/with/params/firstparam/secondparam") } func TestContextUserValues(t *testing.T) { - initDefault() + iris.ResetDefault() testCustomObjUserValue := struct{ Name string }{Name: "a name"} values := map[string]interface{}{"key1": "value1", "key2": "value2", "key3": 3, "key4": testCustomObjUserValue, "key5": map[string]string{"key": "value"}} - Get("/test", func(ctx *Context) { + iris.Get("/test", func(ctx *iris.Context) { for k, v := range values { ctx.Set(k, v) } - }, func(ctx *Context) { + }, func(ctx *iris.Context) { for k, v := range values { userValue := ctx.Get(k) if userValue != v { @@ -403,21 +407,21 @@ func TestContextUserValues(t *testing.T) { } }) - e := Tester(t) + e := httptest.New(iris.Default, t) - e.GET("/test").Expect().Status(StatusOK) + e.GET("/test").Expect().Status(iris.StatusOK) } func TestContextCookieSetGetRemove(t *testing.T) { - initDefault() + iris.ResetDefault() key := "mykey" value := "myvalue" - Get("/set", func(ctx *Context) { + iris.Get("/set", func(ctx *iris.Context) { ctx.SetCookieKV(key, value) // should return non empty cookies }) - Get("/set_advanced", func(ctx *Context) { + iris.Get("/set_advanced", func(ctx *iris.Context) { c := fasthttp.AcquireCookie() c.SetKey(key) c.SetValue(value) @@ -427,11 +431,11 @@ func TestContextCookieSetGetRemove(t *testing.T) { fasthttp.ReleaseCookie(c) }) - Get("/get", func(ctx *Context) { + iris.Get("/get", func(ctx *iris.Context) { ctx.Write(ctx.GetCookie(key)) // should return my value }) - Get("/remove", func(ctx *Context) { + iris.Get("/remove", func(ctx *iris.Context) { ctx.RemoveCookie(key) cookieFound := false ctx.VisitAllCookies(func(k, v string) { @@ -443,38 +447,38 @@ func TestContextCookieSetGetRemove(t *testing.T) { ctx.Write(ctx.GetCookie(key)) // should return "" }) - e := Tester(t) - e.GET("/set").Expect().Status(StatusOK).Cookies().NotEmpty() - e.GET("/get").Expect().Status(StatusOK).Body().Equal(value) - e.GET("/remove").Expect().Status(StatusOK).Body().Equal("") + e := httptest.New(iris.Default, t) + e.GET("/set").Expect().Status(iris.StatusOK).Cookies().NotEmpty() + e.GET("/get").Expect().Status(iris.StatusOK).Body().Equal(value) + e.GET("/remove").Expect().Status(iris.StatusOK).Body().Equal("") // test again with advanced set - e.GET("/set_advanced").Expect().Status(StatusOK).Cookies().NotEmpty() - e.GET("/get").Expect().Status(StatusOK).Body().Equal(value) - e.GET("/remove").Expect().Status(StatusOK).Body().Equal("") + e.GET("/set_advanced").Expect().Status(iris.StatusOK).Cookies().NotEmpty() + e.GET("/get").Expect().Status(iris.StatusOK).Body().Equal(value) + e.GET("/remove").Expect().Status(iris.StatusOK).Body().Equal("") } func TestContextFlashMessages(t *testing.T) { - initDefault() + iris.ResetDefault() firstKey := "name" lastKey := "package" - values := PathParameters{PathParameter{Key: firstKey, Value: "kataras"}, PathParameter{Key: lastKey, Value: "iris"}} + values := iris.PathParameters{iris.PathParameter{Key: firstKey, Value: "kataras"}, iris.PathParameter{Key: lastKey, Value: "iris"}} jsonExpected := map[string]string{firstKey: "kataras", lastKey: "iris"} // set the flashes, the cookies are filled - Put("/set", func(ctx *Context) { + iris.Put("/set", func(ctx *iris.Context) { for _, v := range values { ctx.SetFlash(v.Key, v.Value) } }) // get the first flash, the next should be available to the next requess - Get("/get_first_flash", func(ctx *Context) { + iris.Get("/get_first_flash", func(ctx *iris.Context) { for _, v := range values { val, err := ctx.GetFlash(v.Key) if err == nil { - ctx.JSON(StatusOK, map[string]string{v.Key: val}) + ctx.JSON(iris.StatusOK, map[string]string{v.Key: val}) } else { - ctx.JSON(StatusOK, nil) // return nil + ctx.JSON(iris.StatusOK, nil) // return nil } break @@ -483,30 +487,30 @@ func TestContextFlashMessages(t *testing.T) { }) // just an empty handler to test if the flashes should remeain to the next if GetFlash/GetFlashes used - Get("/get_no_getflash", func(ctx *Context) { + iris.Get("/get_no_getflash", func(ctx *iris.Context) { }) // get the last flash, the next should be available to the next requess - Get("/get_last_flash", func(ctx *Context) { + iris.Get("/get_last_flash", func(ctx *iris.Context) { for i, v := range values { if i == len(values)-1 { val, err := ctx.GetFlash(v.Key) if err == nil { - ctx.JSON(StatusOK, map[string]string{v.Key: val}) + ctx.JSON(iris.StatusOK, map[string]string{v.Key: val}) } else { - ctx.JSON(StatusOK, nil) // return nil + ctx.JSON(iris.StatusOK, nil) // return nil } } } }) - Get("/get_zero_flashes", func(ctx *Context) { - ctx.JSON(StatusOK, ctx.GetFlashes()) // should return nil + iris.Get("/get_zero_flashes", func(ctx *iris.Context) { + ctx.JSON(iris.StatusOK, ctx.GetFlashes()) // should return nil }) // we use the GetFlash to get the flash messages, the messages and the cookies should be empty after that - Get("/get_flash", func(ctx *Context) { + iris.Get("/get_flash", func(ctx *iris.Context) { kv := make(map[string]string) for _, v := range values { val, err := ctx.GetFlash(v.Key) @@ -514,15 +518,15 @@ func TestContextFlashMessages(t *testing.T) { kv[v.Key] = val } } - ctx.JSON(StatusOK, kv) - }, func(ctx *Context) { + ctx.JSON(iris.StatusOK, kv) + }, func(ctx *iris.Context) { // at the same request, flashes should be available if len(ctx.GetFlashes()) == 0 { t.Fatalf("Flashes should be remeain to the whole request lifetime") } }) - Get("/get_flashes", func(ctx *Context) { + iris.Get("/get_flashes", func(ctx *iris.Context) { // one time one handler, using GetFlashes kv := make(map[string]string) flashes := ctx.GetFlashes() @@ -531,48 +535,48 @@ func TestContextFlashMessages(t *testing.T) { kv[k], _ = ctx.GetFlash(k) } if len(flashes) != len(kv) { - ctx.SetStatusCode(StatusNoContent) + ctx.SetStatusCode(iris.StatusNoContent) return } ctx.Next() - }, func(ctx *Context) { + }, func(ctx *iris.Context) { // third time on a next handler // test the if next handler has access to them(must) because flash are request lifetime now. // print them to the client for test the response also - ctx.JSON(StatusOK, ctx.GetFlashes()) + ctx.JSON(iris.StatusOK, ctx.GetFlashes()) }) - e := Tester(t) - e.PUT("/set").Expect().Status(StatusOK).Cookies().NotEmpty() - e.GET("/get_first_flash").Expect().Status(StatusOK).JSON().Object().ContainsKey(firstKey).NotContainsKey(lastKey) + e := httptest.New(iris.Default, t) + e.PUT("/set").Expect().Status(iris.StatusOK).Cookies().NotEmpty() + e.GET("/get_first_flash").Expect().Status(iris.StatusOK).JSON().Object().ContainsKey(firstKey).NotContainsKey(lastKey) // just a request which does not use the flash message, so flash messages should be available on the next request - e.GET("/get_no_getflash").Expect().Status(StatusOK) - e.GET("/get_last_flash").Expect().Status(StatusOK).JSON().Object().ContainsKey(lastKey).NotContainsKey(firstKey) - g := e.GET("/get_zero_flashes").Expect().Status(StatusOK) + e.GET("/get_no_getflash").Expect().Status(iris.StatusOK) + e.GET("/get_last_flash").Expect().Status(iris.StatusOK).JSON().Object().ContainsKey(lastKey).NotContainsKey(firstKey) + g := e.GET("/get_zero_flashes").Expect().Status(iris.StatusOK) g.JSON().Null() g.Cookies().Empty() // set the magain - e.PUT("/set").Expect().Status(StatusOK).Cookies().NotEmpty() + e.PUT("/set").Expect().Status(iris.StatusOK).Cookies().NotEmpty() // get them again using GetFlash - e.GET("/get_flash").Expect().Status(StatusOK).JSON().Object().Equal(jsonExpected) + e.GET("/get_flash").Expect().Status(iris.StatusOK).JSON().Object().Equal(jsonExpected) // this should be empty again - g = e.GET("/get_zero_flashes").Expect().Status(StatusOK) + g = e.GET("/get_zero_flashes").Expect().Status(iris.StatusOK) g.JSON().Null() g.Cookies().Empty() //set them again - e.PUT("/set").Expect().Status(StatusOK).Cookies().NotEmpty() + e.PUT("/set").Expect().Status(iris.StatusOK).Cookies().NotEmpty() // get them again using GetFlashes - e.GET("/get_flashes").Expect().Status(StatusOK).JSON().Object().Equal(jsonExpected) + e.GET("/get_flashes").Expect().Status(iris.StatusOK).JSON().Object().Equal(jsonExpected) // this should be empty again - g = e.GET("/get_zero_flashes").Expect().Status(StatusOK) + g = e.GET("/get_zero_flashes").Expect().Status(iris.StatusOK) g.JSON().Null() g.Cookies().Empty() // test Get, and get again should return nothing - e.PUT("/set").Expect().Status(StatusOK).Cookies().NotEmpty() - e.GET("/get_first_flash").Expect().Status(StatusOK).JSON().Object().ContainsKey(firstKey).NotContainsKey(lastKey) - g = e.GET("/get_first_flash").Expect().Status(StatusOK) + e.PUT("/set").Expect().Status(iris.StatusOK).Cookies().NotEmpty() + e.GET("/get_first_flash").Expect().Status(iris.StatusOK).JSON().Object().ContainsKey(firstKey).NotContainsKey(lastKey) + g = e.GET("/get_first_flash").Expect().Status(iris.StatusOK) g.JSON().Null() g.Cookies().Empty() } @@ -585,21 +589,21 @@ func TestContextSessions(t *testing.T) { "Secret": "dsads£2132215£%%Ssdsa", } - initDefault() - Config.Sessions.Cookie = "mycustomsessionid" + iris.ResetDefault() + iris.Default.Config.Sessions.Cookie = "mycustomsessionid" - writeValues := func(ctx *Context) { + writeValues := func(ctx *iris.Context) { sessValues := ctx.Session().GetAll() - ctx.JSON(StatusOK, sessValues) + ctx.JSON(iris.StatusOK, sessValues) } if testEnableSubdomain { - Party(testSubdomain+".").Get("/get", func(ctx *Context) { + iris.Party(testSubdomain+".").Get("/get", func(ctx *iris.Context) { writeValues(ctx) }) } - Post("set", func(ctx *Context) { + iris.Post("set", func(ctx *iris.Context) { vals := make(map[string]interface{}, 0) if err := ctx.ReadJSON(&vals); err != nil { t.Fatalf("Cannot readjson. Trace %s", err.Error()) @@ -609,44 +613,44 @@ func TestContextSessions(t *testing.T) { } }) - Get("/get", func(ctx *Context) { + iris.Get("/get", func(ctx *iris.Context) { writeValues(ctx) }) - Get("/clear", func(ctx *Context) { + iris.Get("/clear", func(ctx *iris.Context) { ctx.Session().Clear() writeValues(ctx) }) - Get("/destroy", func(ctx *Context) { + iris.Get("/destroy", func(ctx *iris.Context) { ctx.SessionDestroy() writeValues(ctx) // the cookie and all values should be empty }) // request cookie should be empty - Get("/after_destroy", func(ctx *Context) { + iris.Get("/after_destroy", func(ctx *iris.Context) { }) - Default.Config.VHost = "mydomain.com" - e := Tester(t) + iris.Default.Config.VHost = "mydomain.com" + e := httptest.New(iris.Default, t) - e.POST("/set").WithJSON(values).Expect().Status(StatusOK).Cookies().NotEmpty() - e.GET("/get").Expect().Status(StatusOK).JSON().Object().Equal(values) + e.POST("/set").WithJSON(values).Expect().Status(iris.StatusOK).Cookies().NotEmpty() + e.GET("/get").Expect().Status(iris.StatusOK).JSON().Object().Equal(values) if testEnableSubdomain { es := subdomainTester(e) - es.Request("GET", "/get").Expect().Status(StatusOK).JSON().Object().Equal(values) + es.Request("GET", "/get").Expect().Status(iris.StatusOK).JSON().Object().Equal(values) } // test destroy which also clears first - d := e.GET("/destroy").Expect().Status(StatusOK) + d := e.GET("/destroy").Expect().Status(iris.StatusOK) d.JSON().Null() // This removed: d.Cookies().Empty(). Reason: // httpexpect counts the cookies setted or deleted at the response time, but cookie is not removed, to be really removed needs to SetExpire(now-1second) so, // test if the cookies removed on the next request, like the browser's behavior. - e.GET("/after_destroy").Expect().Status(StatusOK).Cookies().Empty() + e.GET("/after_destroy").Expect().Status(iris.StatusOK).Cookies().Empty() // set and clear again - e.POST("/set").WithJSON(values).Expect().Status(StatusOK).Cookies().NotEmpty() - e.GET("/clear").Expect().Status(StatusOK).JSON().Object().Empty() + e.POST("/set").WithJSON(values).Expect().Status(iris.StatusOK).Cookies().NotEmpty() + e.GET("/clear").Expect().Status(iris.StatusOK).JSON().Object().Empty() } type renderTestInformationType struct { @@ -659,7 +663,7 @@ type renderTestInformationType struct { } func TestContextRenderRest(t *testing.T) { - initDefault() + iris.ResetDefault() dataContents := []byte("Some binary data here.") textContents := "Plain text here" @@ -675,59 +679,59 @@ func TestContextRenderRest(t *testing.T) { } markdownContents := "# Hello dynamic markdown from Iris" - Get("/data", func(ctx *Context) { - ctx.Data(StatusOK, dataContents) + iris.Get("/data", func(ctx *iris.Context) { + ctx.Data(iris.StatusOK, dataContents) }) - Get("/text", func(ctx *Context) { - ctx.Text(StatusOK, textContents) + iris.Get("/text", func(ctx *iris.Context) { + ctx.Text(iris.StatusOK, textContents) }) - Get("/jsonp", func(ctx *Context) { - ctx.JSONP(StatusOK, JSONPCallback, JSONPContents) + iris.Get("/jsonp", func(ctx *iris.Context) { + ctx.JSONP(iris.StatusOK, JSONPCallback, JSONPContents) }) - Get("/json", func(ctx *Context) { - ctx.JSON(StatusOK, JSONXMLContents) + iris.Get("/json", func(ctx *iris.Context) { + ctx.JSON(iris.StatusOK, JSONXMLContents) }) - Get("/xml", func(ctx *Context) { - ctx.XML(StatusOK, JSONXMLContents) + iris.Get("/xml", func(ctx *iris.Context) { + ctx.XML(iris.StatusOK, JSONXMLContents) }) - Get("/markdown", func(ctx *Context) { - ctx.Markdown(StatusOK, markdownContents) + iris.Get("/markdown", func(ctx *iris.Context) { + ctx.Markdown(iris.StatusOK, markdownContents) }) - e := Tester(t) - dataT := e.GET("/data").Expect().Status(StatusOK) + e := httptest.New(iris.Default, t) + dataT := e.GET("/data").Expect().Status(iris.StatusOK) dataT.Header("Content-Type").Equal("application/octet-stream") dataT.Body().Equal(string(dataContents)) - textT := e.GET("/text").Expect().Status(StatusOK) + textT := e.GET("/text").Expect().Status(iris.StatusOK) textT.Header("Content-Type").Equal("text/plain; charset=UTF-8") textT.Body().Equal(textContents) - JSONPT := e.GET("/jsonp").Expect().Status(StatusOK) + JSONPT := e.GET("/jsonp").Expect().Status(iris.StatusOK) JSONPT.Header("Content-Type").Equal("application/javascript; charset=UTF-8") JSONPT.Body().Equal(JSONPCallback + `({"hello":"jsonp"});`) - JSONT := e.GET("/json").Expect().Status(StatusOK) + JSONT := e.GET("/json").Expect().Status(iris.StatusOK) JSONT.Header("Content-Type").Equal("application/json; charset=UTF-8") JSONT.JSON().Object().Equal(JSONXMLContents) - XMLT := e.GET("/xml").Expect().Status(StatusOK) + XMLT := e.GET("/xml").Expect().Status(iris.StatusOK) XMLT.Header("Content-Type").Equal("text/xml; charset=UTF-8") XMLT.Body().Equal(`<` + JSONXMLContents.XMLName.Local + ` first="` + JSONXMLContents.FirstAttr + `" second="` + JSONXMLContents.SecondAttr + `">` + JSONXMLContents.Name + `` + JSONXMLContents.Birth + `` + strconv.Itoa(JSONXMLContents.Stars) + ``) - markdownT := e.GET("/markdown").Expect().Status(StatusOK) + markdownT := e.GET("/markdown").Expect().Status(iris.StatusOK) markdownT.Header("Content-Type").Equal("text/html; charset=UTF-8") markdownT.Body().Equal("

" + markdownContents[2:] + "

\n") } func TestContextPreRender(t *testing.T) { - initDefault() + iris.ResetDefault() errMsg1 := "thereIsAnError" - UsePreRender(func(ctx *Context, src string, binding interface{}, options ...map[string]interface{}) bool { + iris.UsePreRender(func(ctx *iris.Context, src string, binding interface{}, options ...map[string]interface{}) bool { // put the 'Error' binding here, for the shake of the test if b, isMap := binding.(map[string]interface{}); isMap { b["Error"] = errMsg1 @@ -736,7 +740,7 @@ func TestContextPreRender(t *testing.T) { return true }) errMsg2 := "thereIsASecondError" - UsePreRender(func(ctx *Context, src string, binding interface{}, options ...map[string]interface{}) bool { + iris.UsePreRender(func(ctx *iris.Context, src string, binding interface{}, options ...map[string]interface{}) bool { // put the 'Error' binding here, for the shake of the test if b, isMap := binding.(map[string]interface{}); isMap { prev := b["Error"].(string) @@ -748,7 +752,7 @@ func TestContextPreRender(t *testing.T) { }) errMsg3 := "thereisAThirdError" - UsePreRender(func(ctx *Context, src string, binding interface{}, options ...map[string]interface{}) bool { + iris.UsePreRender(func(ctx *iris.Context, src string, binding interface{}, options ...map[string]interface{}) bool { // put the 'Error' binding here, for the shake of the test if b, isMap := binding.(map[string]interface{}); isMap { prev := b["Error"].(string) @@ -759,11 +763,11 @@ func TestContextPreRender(t *testing.T) { return true }) - Get("/", func(ctx *Context) { - ctx.RenderTemplateSource(StatusOK, "

HI {{.Username}}. Error: {{.Error}}

", map[string]interface{}{"Username": "kataras"}) + iris.Get("/", func(ctx *iris.Context) { + ctx.RenderTemplateSource(iris.StatusOK, "

HI {{.Username}}. Error: {{.Error}}

", map[string]interface{}{"Username": "kataras"}) }) - e := Tester(t) + e := httptest.New(iris.Default, t) expected := "

HI kataras. Error: " + errMsg1 + errMsg2 + "

" - e.GET("/").Expect().Status(StatusOK).Body().Contains(expected) + e.GET("/").Expect().Status(iris.StatusOK).Body().Contains(expected) } diff --git a/http.go b/http.go index c929ce5d..5422b8a6 100644 --- a/http.go +++ b/http.go @@ -1173,7 +1173,7 @@ func (mux *serveMux) BuildHandler() HandlerFunc { if middleware != nil { // ok we found the correct route, serve it and exit entirely from here context.Params = params - context.middleware = middleware + context.Middleware = middleware //ctx.Request.Header.SetUserAgentBytes(DefaultUserAgent) context.Do() return @@ -1420,7 +1420,8 @@ func ParseScheme(domain string) string { return SchemeHTTP } -var proxyHandler = func(proxyAddr string, redirectSchemeAndHost string) fasthttp.RequestHandler { +// ProxyHandler returns a new fasthttp handler which works as 'proxy', maybe doesn't suits you look its code before using that in production +var ProxyHandler = func(proxyAddr string, redirectSchemeAndHost string) fasthttp.RequestHandler { return func(reqCtx *fasthttp.RequestCtx) { // override the handler and redirect all requests to this addr redirectTo := redirectSchemeAndHost @@ -1458,7 +1459,7 @@ func Proxy(proxyAddr string, redirectSchemeAndHost string) func() error { proxyAddr = ParseHost(proxyAddr) // override the handler and redirect all requests to this addr - h := proxyHandler(proxyAddr, redirectSchemeAndHost) + h := ProxyHandler(proxyAddr, redirectSchemeAndHost) prx := New(OptionDisableBanner(true)) prx.Router = h diff --git a/http_test.go b/http_test.go index 1127b1fb..5a6dd83b 100644 --- a/http_test.go +++ b/http_test.go @@ -1,4 +1,5 @@ -package iris +// Black-box Testing +package iris_test /* This is the part we only care, these are end-to-end tests for the mux(router) and the server, the whole http file is made for these reasons only, so these tests are enough I think. @@ -13,9 +14,10 @@ import ( "testing" "time" - "github.com/valyala/fasthttp" - "github.com/gavv/httpexpect" + "github.com/kataras/iris" + "github.com/kataras/iris/httptest" + "github.com/valyala/fasthttp" ) const ( @@ -75,13 +77,13 @@ func TestParseAddr(t *testing.T) { // test hosts expectedHost1 := "mydomain.com:1993" expectedHost2 := "mydomain.com" - expectedHost3 := DefaultServerHostname + ":9090" + expectedHost3 := iris.DefaultServerHostname + ":9090" expectedHost4 := "mydomain.com:443" - host1 := ParseHost(expectedHost1) - host2 := ParseHost(expectedHost2) - host3 := ParseHost(":9090") - host4 := ParseHost(expectedHost4) + host1 := iris.ParseHost(expectedHost1) + host2 := iris.ParseHost(expectedHost2) + host3 := iris.ParseHost(":9090") + host4 := iris.ParseHost(expectedHost4) if host1 != expectedHost1 { t.Fatalf("Expecting server 1's host to be %s but we got %s", expectedHost1, host1) @@ -99,13 +101,13 @@ func TestParseAddr(t *testing.T) { // test hostname expectedHostname1 := "mydomain.com" expectedHostname2 := "mydomain.com" - expectedHostname3 := DefaultServerHostname + expectedHostname3 := iris.DefaultServerHostname expectedHostname4 := "mydomain.com" - hostname1 := ParseHostname(host1) - hostname2 := ParseHostname(host2) - hostname3 := ParseHostname(host3) - hostname4 := ParseHostname(host4) + hostname1 := iris.ParseHostname(host1) + hostname2 := iris.ParseHostname(host2) + hostname3 := iris.ParseHostname(host3) + hostname4 := iris.ParseHostname(host4) if hostname1 != expectedHostname1 { t.Fatalf("Expecting server 1's hostname to be %s but we got %s", expectedHostname1, hostname1) } @@ -123,14 +125,14 @@ func TestParseAddr(t *testing.T) { } // test scheme, no need to test fullhost(scheme+host) - expectedScheme1 := SchemeHTTP - expectedScheme2 := SchemeHTTP - expectedScheme3 := SchemeHTTP - expectedScheme4 := SchemeHTTPS - scheme1 := ParseScheme(host1) - scheme2 := ParseScheme(host2) - scheme3 := ParseScheme(host3) - scheme4 := ParseScheme(host4) + expectedScheme1 := iris.SchemeHTTP + expectedScheme2 := iris.SchemeHTTP + expectedScheme3 := iris.SchemeHTTP + expectedScheme4 := iris.SchemeHTTPS + scheme1 := iris.ParseScheme(host1) + scheme2 := iris.ParseScheme(host2) + scheme3 := iris.ParseScheme(host3) + scheme4 := iris.ParseScheme(host4) if scheme1 != expectedScheme1 { t.Fatalf("Expecting server 1's hostname to be %s but we got %s", expectedScheme1, scheme1) } @@ -150,13 +152,13 @@ func TestParseAddr(t *testing.T) { // Contains the server test for multi running servers func TestMultiRunningServers_v1_PROXY(t *testing.T) { - defer Close() + defer iris.Close() host := "localhost" // you have to add it to your hosts file( for windows, as 127.0.0.1 mydomain.com) hostTLS := "localhost:9999" - Close() - defer Close() - initDefault() - Default.Config.DisableBanner = true + iris.Close() + defer iris.Close() + iris.ResetDefault() + iris.Default.Config.DisableBanner = true // create the key and cert files on the fly, and delete them when this test finished certFile, ferr := ioutil.TempFile("", "cert") @@ -182,34 +184,33 @@ func TestMultiRunningServers_v1_PROXY(t *testing.T) { certFile.WriteString(testTLSCert) keyFile.WriteString(testTLSKey) - Get("/", func(ctx *Context) { + iris.Get("/", func(ctx *iris.Context) { ctx.Write("Hello from %s", hostTLS) }) - go ListenTLS(hostTLS, certFile.Name(), keyFile.Name()) - if ok := <-Default.Available; !ok { + go iris.ListenTLS(hostTLS, certFile.Name(), keyFile.Name()) + if ok := <-iris.Default.Available; !ok { t.Fatal("Unexpected error: server cannot start, please report this as bug!!") } - closeProxy := Proxy("localhost:8080", "https://"+hostTLS) + closeProxy := iris.Proxy("localhost:8080", "https://"+hostTLS) defer closeProxy() - Default.Config.Tester.ExplicitURL = true - e := Tester(t) + e := httptest.New(iris.Default, t, httptest.ExplicitURL(true)) - e.Request("GET", "http://"+host+":8080").Expect().Status(StatusOK).Body().Equal("Hello from " + hostTLS) - e.Request("GET", "https://"+hostTLS).Expect().Status(StatusOK).Body().Equal("Hello from " + hostTLS) + e.Request("GET", "http://"+host+":8080").Expect().Status(iris.StatusOK).Body().Equal("Hello from " + hostTLS) + e.Request("GET", "https://"+hostTLS).Expect().Status(iris.StatusOK).Body().Equal("Hello from " + hostTLS) } // Contains the server test for multi running servers func TestMultiRunningServers_v2(t *testing.T) { - defer Close() + defer iris.Close() domain := "localhost" hostTLS := "localhost:9999" - initDefault() - Default.Config.DisableBanner = true + iris.ResetDefault() + iris.Default.Config.DisableBanner = true // create the key and cert files on the fly, and delete them when this test finished certFile, ferr := ioutil.TempFile("", "cert") @@ -236,7 +237,7 @@ func TestMultiRunningServers_v2(t *testing.T) { os.Remove(keyFile.Name()) }() - Get("/", func(ctx *Context) { + iris.Get("/", func(ctx *iris.Context) { ctx.Write("Hello from %s", hostTLS) }) @@ -248,24 +249,23 @@ func TestMultiRunningServers_v2(t *testing.T) { //go Go() // using the proxy handler - fsrv1 := &fasthttp.Server{Handler: proxyHandler(domain+":8080", "https://"+hostTLS)} + fsrv1 := &fasthttp.Server{Handler: iris.ProxyHandler(domain+":8080", "https://"+hostTLS)} go fsrv1.ListenAndServe(domain + ":8080") // using the same iris' handler but not as proxy, just the same handler - fsrv2 := &fasthttp.Server{Handler: Default.Router} + fsrv2 := &fasthttp.Server{Handler: iris.Default.Router} go fsrv2.ListenAndServe(domain + ":8888") - go ListenTLS(hostTLS, certFile.Name(), keyFile.Name()) + go iris.ListenTLS(hostTLS, certFile.Name(), keyFile.Name()) - if ok := <-Available; !ok { + if ok := <-iris.Default.Available; !ok { t.Fatal("Unexpected error: server cannot start, please report this as bug!!") } - Default.Config.Tester.ExplicitURL = true - e := Tester(t) + e := httptest.New(iris.Default, t, httptest.ExplicitURL(true)) - e.Request("GET", "http://"+domain+":8080").Expect().Status(StatusOK).Body().Equal("Hello from " + hostTLS) - e.Request("GET", "http://localhost:8888").Expect().Status(StatusOK).Body().Equal("Hello from " + hostTLS) - e.Request("GET", "https://"+hostTLS).Expect().Status(StatusOK).Body().Equal("Hello from " + hostTLS) + e.Request("GET", "http://"+domain+":8080").Expect().Status(iris.StatusOK).Body().Equal("Hello from " + hostTLS) + e.Request("GET", "http://localhost:8888").Expect().Status(iris.StatusOK).Body().Equal("Hello from " + hostTLS) + e.Request("GET", "https://"+hostTLS).Expect().Status(iris.StatusOK).Body().Equal("Hello from " + hostTLS) } @@ -275,13 +275,13 @@ const ( ) func testSubdomainHost() string { - s := testSubdomain + "." + Default.Config.VHost + s := testSubdomain + "." + iris.Default.Config.VHost return s } func testSubdomainURL() string { subdomainHost := testSubdomainHost() - return Default.Config.VScheme + subdomainHost + return iris.Default.Config.VScheme + subdomainHost } func subdomainTester(e *httpexpect.Expect) *httpexpect.Expect { @@ -339,13 +339,13 @@ func TestMuxSimple(t *testing.T) { {"GET", "/test_get_urlparameter2/second", "/test_get_urlparameter2/second", "name=irisurl&something=anything", "name=irisurl,something=anything", 200, true, nil, []param{{"name", "irisurl"}, {"something", "anything"}}}, {"GET", "/test_get_urlparameter2/first/second/third", "/test_get_urlparameter2/first/second/third", "name=irisurl&something=anything&else=elsehere", "name=irisurl,something=anything,else=elsehere", 200, true, nil, []param{{"name", "irisurl"}, {"something", "anything"}, {"else", "elsehere"}}}, } - defer Close() - initDefault() + defer iris.Close() + iris.ResetDefault() for idx := range testRoutes { r := testRoutes[idx] if r.Register { - HandleFunc(r.Method, r.Path, func(ctx *Context) { + iris.HandleFunc(r.Method, r.Path, func(ctx *iris.Context) { ctx.SetStatusCode(r.Status) if r.Params != nil && len(r.Params) > 0 { ctx.SetBodyString(ctx.Params.String()) @@ -370,7 +370,7 @@ func TestMuxSimple(t *testing.T) { } } - e := Tester(t) + e := httptest.New(iris.Default, t) // run the tests (1) for idx := range testRoutes { @@ -383,12 +383,12 @@ func TestMuxSimple(t *testing.T) { } func TestMuxSimpleParty(t *testing.T) { - initDefault() + iris.ResetDefault() - h := func(c *Context) { c.WriteString(c.HostString() + c.PathString()) } + h := func(c *iris.Context) { c.WriteString(c.HostString() + c.PathString()) } if testEnableSubdomain { - subdomainParty := Party(testSubdomain + ".") + subdomainParty := iris.Party(testSubdomain + ".") { subdomainParty.Get("/", h) subdomainParty.Get("/path1", h) @@ -399,7 +399,7 @@ func TestMuxSimpleParty(t *testing.T) { } // simple - p := Party("/party1") + p := iris.Party("/party1") { p.Get("/", h) p.Get("/path1", h) @@ -408,16 +408,16 @@ func TestMuxSimpleParty(t *testing.T) { p.Get("/namedpath/:param1/something/:param2/else", h) } - Default.Config.VHost = "0.0.0.0:8080" - // Default.Config.Tester.Debug = true - // Default.Config.Tester.ExplicitURL = true - e := Tester(t) + iris.Default.Config.VHost = "0.0.0.0:8080" + // iris.Default.Config.Tester.Debug = true + // iris.Default.Config.Tester.ExplicitURL = true + e := httptest.New(iris.Default, t) request := func(reqPath string) { e.Request("GET", reqPath). Expect(). - Status(StatusOK).Body().Equal(Default.Config.VHost + reqPath) + Status(iris.StatusOK).Body().Equal(iris.Default.Config.VHost + reqPath) } // run the tests @@ -432,7 +432,7 @@ func TestMuxSimpleParty(t *testing.T) { subdomainRequest := func(reqPath string) { es.Request("GET", reqPath). Expect(). - Status(StatusOK).Body().Equal(testSubdomainHost() + reqPath) + Status(iris.StatusOK).Body().Equal(testSubdomainHost() + reqPath) } subdomainRequest("/") @@ -444,33 +444,33 @@ func TestMuxSimpleParty(t *testing.T) { } func TestMuxPathEscape(t *testing.T) { - initDefault() + iris.ResetDefault() - Get("/details/:name", func(ctx *Context) { + iris.Get("/details/:name", func(ctx *iris.Context) { name := ctx.Param("name") highlight := ctx.URLParam("highlight") - ctx.Text(StatusOK, fmt.Sprintf("name=%s,highlight=%s", name, highlight)) + ctx.Text(iris.StatusOK, fmt.Sprintf("name=%s,highlight=%s", name, highlight)) }) - e := Tester(t) + e := httptest.New(iris.Default, t) e.GET("/details/Sakamoto desu ga"). WithQuery("highlight", "text"). - Expect().Status(StatusOK).Body().Equal("name=Sakamoto desu ga,highlight=text") + Expect().Status(iris.StatusOK).Body().Equal("name=Sakamoto desu ga,highlight=text") } func TestMuxDecodeURL(t *testing.T) { - initDefault() + iris.ResetDefault() - Get("/encoding/:url", func(ctx *Context) { - url := DecodeURL(ctx.Param("url")) - ctx.SetStatusCode(StatusOK) + iris.Get("/encoding/:url", func(ctx *iris.Context) { + url := iris.DecodeURL(ctx.Param("url")) + ctx.SetStatusCode(iris.StatusOK) ctx.Write(url) }) - e := Tester(t) + e := httptest.New(iris.Default, t) - e.GET("/encoding/http%3A%2F%2Fsome-url.com").Expect().Status(StatusOK).Body().Equal("http://some-url.com") + e.GET("/encoding/http%3A%2F%2Fsome-url.com").Expect().Status(iris.StatusOK).Body().Equal("http://some-url.com") } func TestMuxCustomErrors(t *testing.T) { @@ -500,27 +500,27 @@ func TestMuxCustomErrors(t *testing.T) { {"TRACE", "/test_trace_panic_custom", "/test_trace_panic_custom", "", internalServerMessage, 500, true, nil, nil}, } ) - initDefault() + iris.ResetDefault() // first register the testRoutes needed for _, r := range testRoutesCustomErrors { if r.Register { - HandleFunc(r.Method, r.Path, func(ctx *Context) { + iris.HandleFunc(r.Method, r.Path, func(ctx *iris.Context) { ctx.EmitError(r.Status) }) } } // register the custom errors - OnError(404, func(ctx *Context) { + iris.OnError(iris.StatusNotFound, func(ctx *iris.Context) { ctx.Write("%s", notFoundMessage) }) - OnError(500, func(ctx *Context) { + iris.OnError(iris.StatusInternalServerError, func(ctx *iris.Context) { ctx.Write("%s", internalServerMessage) }) // create httpexpect instance that will call fasthtpp.RequestHandler directly - e := Tester(t) + e := httptest.New(iris.Default, t) // run the tests for _, r := range testRoutesCustomErrors { @@ -531,7 +531,7 @@ func TestMuxCustomErrors(t *testing.T) { } type testUserAPI struct { - *Context + *iris.Context } // GET /users @@ -560,62 +560,62 @@ func (u testUserAPI) DeleteBy(id string) { } func TestMuxAPI(t *testing.T) { - initDefault() + iris.ResetDefault() middlewareResponseText := "I assume that you are authenticated\n" - API("/users", testUserAPI{}, func(ctx *Context) { // optional middleware for .API + iris.API("/users", testUserAPI{}, func(ctx *iris.Context) { // optional middleware for .API // do your work here, or render a login window if not logged in, get the user and send it to the next middleware, or do all here ctx.Set("user", "username") ctx.Next() - }, func(ctx *Context) { + }, func(ctx *iris.Context) { if ctx.Get("user") == "username" { ctx.Write(middlewareResponseText) ctx.Next() } else { - ctx.SetStatusCode(StatusUnauthorized) + ctx.SetStatusCode(iris.StatusUnauthorized) } }) - e := Tester(t) + e := httptest.New(iris.Default, t) userID := "4077" formname := "kataras" - e.GET("/users").Expect().Status(StatusOK).Body().Equal(middlewareResponseText + "Get Users\n") - e.GET("/users/" + userID).Expect().Status(StatusOK).Body().Equal(middlewareResponseText + "Get By " + userID + "\n") - e.PUT("/users").WithFormField("name", formname).Expect().Status(StatusOK).Body().Equal(middlewareResponseText + "Put, name: " + formname + "\n") - e.POST("/users/"+userID).WithFormField("name", formname).Expect().Status(StatusOK).Body().Equal(middlewareResponseText + "Post By " + userID + ", name: " + formname + "\n") - e.DELETE("/users/" + userID).Expect().Status(StatusOK).Body().Equal(middlewareResponseText + "Delete By " + userID + "\n") + e.GET("/users").Expect().Status(iris.StatusOK).Body().Equal(middlewareResponseText + "Get Users\n") + e.GET("/users/" + userID).Expect().Status(iris.StatusOK).Body().Equal(middlewareResponseText + "Get By " + userID + "\n") + e.PUT("/users").WithFormField("name", formname).Expect().Status(iris.StatusOK).Body().Equal(middlewareResponseText + "Put, name: " + formname + "\n") + e.POST("/users/"+userID).WithFormField("name", formname).Expect().Status(iris.StatusOK).Body().Equal(middlewareResponseText + "Post By " + userID + ", name: " + formname + "\n") + e.DELETE("/users/" + userID).Expect().Status(iris.StatusOK).Body().Equal(middlewareResponseText + "Delete By " + userID + "\n") } func TestMuxAPIWithParty(t *testing.T) { - initDefault() - siteParty := Party("sites/:site") + iris.ResetDefault() + siteParty := iris.Party("sites/:site") middlewareResponseText := "I assume that you are authenticated\n" - siteParty.API("/users", testUserAPI{}, func(ctx *Context) { + siteParty.API("/users", testUserAPI{}, func(ctx *iris.Context) { ctx.Set("user", "username") ctx.Next() - }, func(ctx *Context) { + }, func(ctx *iris.Context) { if ctx.Get("user") == "username" { ctx.Write(middlewareResponseText) ctx.Next() } else { - ctx.SetStatusCode(StatusUnauthorized) + ctx.SetStatusCode(iris.StatusUnauthorized) } }) - e := Tester(t) + e := httptest.New(iris.Default, t) siteID := "1" apiPath := "/sites/" + siteID + "/users" userID := "4077" formname := "kataras" - e.GET(apiPath).Expect().Status(StatusOK).Body().Equal(middlewareResponseText + "Get Users\n") - e.GET(apiPath + "/" + userID).Expect().Status(StatusOK).Body().Equal(middlewareResponseText + "Get By " + userID + "\n") - e.PUT(apiPath).WithFormField("name", formname).Expect().Status(StatusOK).Body().Equal(middlewareResponseText + "Put, name: " + formname + "\n") - e.POST(apiPath+"/"+userID).WithFormField("name", formname).Expect().Status(StatusOK).Body().Equal(middlewareResponseText + "Post By " + userID + ", name: " + formname + "\n") - e.DELETE(apiPath + "/" + userID).Expect().Status(StatusOK).Body().Equal(middlewareResponseText + "Delete By " + userID + "\n") + e.GET(apiPath).Expect().Status(iris.StatusOK).Body().Equal(middlewareResponseText + "Get Users\n") + e.GET(apiPath + "/" + userID).Expect().Status(iris.StatusOK).Body().Equal(middlewareResponseText + "Get By " + userID + "\n") + e.PUT(apiPath).WithFormField("name", formname).Expect().Status(iris.StatusOK).Body().Equal(middlewareResponseText + "Put, name: " + formname + "\n") + e.POST(apiPath+"/"+userID).WithFormField("name", formname).Expect().Status(iris.StatusOK).Body().Equal(middlewareResponseText + "Post By " + userID + ", name: " + formname + "\n") + e.DELETE(apiPath + "/" + userID).Expect().Status(iris.StatusOK).Body().Equal(middlewareResponseText + "Delete By " + userID + "\n") } type myTestHandlerData struct { @@ -628,64 +628,64 @@ type myTestCustomHandler struct { data myTestHandlerData } -func (m *myTestCustomHandler) Serve(ctx *Context) { +func (m *myTestCustomHandler) Serve(ctx *iris.Context) { data := &m.data data.DynamicPathParameter = ctx.Param("myparam") - ctx.JSON(StatusOK, data) + ctx.JSON(iris.StatusOK, data) } func TestMuxCustomHandler(t *testing.T) { - initDefault() + iris.ResetDefault() myData := myTestHandlerData{ Sysname: "Redhat", Version: 1, } - Handle("GET", "/custom_handler_1/:myparam", &myTestCustomHandler{myData}) - Handle("GET", "/custom_handler_2/:myparam", &myTestCustomHandler{myData}) + iris.Handle("GET", "/custom_handler_1/:myparam", &myTestCustomHandler{myData}) + iris.Handle("GET", "/custom_handler_2/:myparam", &myTestCustomHandler{myData}) - e := Tester(t) + e := httptest.New(iris.Default, t) // two times per testRoute param1 := "thisimyparam1" expectedData1 := myData expectedData1.DynamicPathParameter = param1 - e.GET("/custom_handler_1/" + param1).Expect().Status(StatusOK).JSON().Equal(expectedData1) + e.GET("/custom_handler_1/" + param1).Expect().Status(iris.StatusOK).JSON().Equal(expectedData1) param2 := "thisimyparam2" expectedData2 := myData expectedData2.DynamicPathParameter = param2 - e.GET("/custom_handler_1/" + param2).Expect().Status(StatusOK).JSON().Equal(expectedData2) + e.GET("/custom_handler_1/" + param2).Expect().Status(iris.StatusOK).JSON().Equal(expectedData2) param3 := "thisimyparam3" expectedData3 := myData expectedData3.DynamicPathParameter = param3 - e.GET("/custom_handler_2/" + param3).Expect().Status(StatusOK).JSON().Equal(expectedData3) + e.GET("/custom_handler_2/" + param3).Expect().Status(iris.StatusOK).JSON().Equal(expectedData3) param4 := "thisimyparam4" expectedData4 := myData expectedData4.DynamicPathParameter = param4 - e.GET("/custom_handler_2/" + param4).Expect().Status(StatusOK).JSON().Equal(expectedData4) + e.GET("/custom_handler_2/" + param4).Expect().Status(iris.StatusOK).JSON().Equal(expectedData4) } func TestMuxFireMethodNotAllowed(t *testing.T) { - initDefault() - Default.Config.FireMethodNotAllowed = true - h := func(ctx *Context) { + iris.ResetDefault() + iris.Default.Config.FireMethodNotAllowed = true + h := func(ctx *iris.Context) { ctx.Write("%s", ctx.MethodString()) } - Default.OnError(StatusMethodNotAllowed, func(ctx *Context) { + iris.Default.OnError(iris.StatusMethodNotAllowed, func(ctx *iris.Context) { ctx.Write("Hello from my custom 405 page") }) - Get("/mypath", h) - Put("/mypath", h) + iris.Get("/mypath", h) + iris.Put("/mypath", h) - e := Tester(t) + e := httptest.New(iris.Default, t) - e.GET("/mypath").Expect().Status(StatusOK).Body().Equal("GET") - e.PUT("/mypath").Expect().Status(StatusOK).Body().Equal("PUT") + e.GET("/mypath").Expect().Status(iris.StatusOK).Body().Equal("GET") + e.PUT("/mypath").Expect().Status(iris.StatusOK).Body().Equal("PUT") // this should fail with 405 and catch by the custom http error - e.POST("/mypath").Expect().Status(StatusMethodNotAllowed).Body().Equal("Hello from my custom 405 page") - Close() + e.POST("/mypath").Expect().Status(iris.StatusMethodNotAllowed).Body().Equal("Hello from my custom 405 page") + iris.Close() } diff --git a/httptest/httptest.go b/httptest/httptest.go new file mode 100644 index 00000000..d9c59bba --- /dev/null +++ b/httptest/httptest.go @@ -0,0 +1,110 @@ +package httptest + +import ( + "github.com/gavv/httpexpect" + "github.com/kataras/iris" + "net/http" + "testing" +) + +type ( + // OptionSetter sets a configuration field to the configuration + OptionSetter interface { + // Set receives a pointer to the Configuration type and does the job of filling it + Set(c *Configuration) + } + // OptionSet implements the OptionSetter + OptionSet func(c *Configuration) +) + +// Set is the func which makes the OptionSet an OptionSetter, this is used mostly +func (o OptionSet) Set(c *Configuration) { + o(c) +} + +// Configuration httptest configuration +type Configuration struct { + // ExplicitURL If true then the url (should) be prepended manually, useful when want to test subdomains + // Default is false + ExplicitURL bool + // Debug if true then debug messages from the httpexpect will be shown when a test runs + // Default is false + Debug bool +} + +// Set implements the OptionSetter for the Configuration itself +func (c Configuration) Set(main *Configuration) { + main.ExplicitURL = c.ExplicitURL + main.Debug = c.Debug +} + +var ( + // ExplicitURL If true then the url (should) be prepended manually, useful when want to test subdomains + // Default is false + ExplicitURL = func(val bool) OptionSet { + return func(c *Configuration) { + c.ExplicitURL = val + } + } + // Debug if true then debug messages from the httpexpect will be shown when a test runs + // Default is false + Debug = func(val bool) OptionSet { + return func(c *Configuration) { + c.Debug = val + } + } +) + +// DefaultConfiguration returns the default configuration for the httptest +// all values are defaulted to false for clarity +func DefaultConfiguration() *Configuration { + return &Configuration{ExplicitURL: false, Debug: false} +} + +// New Prepares and returns a new test framework based on the api +// is useful when you need to have more than one test framework for the same iris instance +// usage: +// iris.Get("/mypath", func(ctx *iris.Context){ctx.Write("my body")}) +// ... +// e := httptest.New(iris.Default, t) +// e.GET("/mypath").Expect().Status(iris.StatusOK).Body().Equal("my body") +// +// You can find example on the https://github.com/kataras/iris/glob/master/context_test.go +func New(api *iris.Framework, t *testing.T, setters ...OptionSetter) *httpexpect.Expect { + conf := DefaultConfiguration() + for _, setter := range setters { + setter.Set(conf) + } + + api.Set(iris.OptionDisableBanner(true)) + + baseURL := "" + if !api.Plugins.PreBuildFired() { + api.Build() + } + if !conf.ExplicitURL { + baseURL = api.Config.VScheme + api.Config.VHost + // if it's still empty then set it to the default server addr + if baseURL == "" { + baseURL = iris.SchemeHTTP + iris.DefaultServerAddr + } + + } + + testConfiguration := httpexpect.Config{ + BaseURL: baseURL, + Client: &http.Client{ + Transport: httpexpect.NewFastBinder(api.Router), + Jar: httpexpect.NewJar(), + }, + Reporter: httpexpect.NewAssertReporter(t), + } + + if conf.Debug { + testConfiguration.Printers = []httpexpect.Printer{ + httpexpect.NewDebugPrinter(t, true), + } + } + + return httpexpect.WithConfig(testConfiguration) +} diff --git a/iris.go b/iris.go index 2f94540f..d7e43238 100644 --- a/iris.go +++ b/iris.go @@ -54,7 +54,6 @@ import ( "fmt" "log" "net" - "net/http" "net/url" "os" "os/signal" @@ -63,7 +62,6 @@ import ( "strconv" "strings" "sync" - "testing" "time" "github.com/gavv/httpexpect" @@ -108,7 +106,16 @@ var ( Available chan bool ) -func initDefault() { +// ResetDefault resets the iris.Default which is the instance which is used on the default iris station for +// iris.Get(all api functions) +// iris.Config +// iris.Logger +// iris.Plugins +// iris.Router +// iris.Websocket +// iris.SSH and iris.Available channel +// useful mostly when you are not using the form of app := iris.New() inside your tests, to make sure that you're using a new iris instance +func ResetDefault() { Default = New() Config = Default.Config Logger = Default.Logger @@ -120,7 +127,7 @@ func initDefault() { } func init() { - initDefault() + ResetDefault() } // ------------------------------------------------------------------------------------- @@ -159,7 +166,6 @@ type ( TemplateString(string, interface{}, ...map[string]interface{}) string TemplateSourceString(string, interface{}) string SerializeToString(string, interface{}, ...map[string]interface{}) string - Tester(*testing.T) *httpexpect.Expect } // Framework is our God |\| Google.Search('Greek mythology Iris') @@ -678,7 +684,7 @@ func ReleaseCtx(ctx *Context) { // see .AcquireCtx & .Serve func (s *Framework) ReleaseCtx(ctx *Context) { ctx.Params = ctx.Params[0:0] - ctx.middleware = nil + ctx.Middleware = nil ctx.session = nil s.contextPool.Put(ctx) } @@ -1112,55 +1118,6 @@ func (s *Framework) SerializeToString(keyOrContentType string, obj interface{}, return res } -// NewTester Prepares and returns a new test framework based on the api -// is useful when you need to have more than one test framework for the same iris insttance, otherwise you can use the iris.Tester(t *testing.T)/variable.Tester(t *testing.T) -func NewTester(api *Framework, t *testing.T) *httpexpect.Expect { - api.Set(OptionDisableBanner(true)) - - baseURL := "" - if api.fsrv == nil { - api.Build() - } - if !api.Config.Tester.ExplicitURL { - baseURL = api.Config.VScheme + api.Config.VHost - // if it's still empty then set it to the default server addr - if baseURL == "" { - baseURL = SchemeHTTP + DefaultServerAddr - } - - } - - testConfiguration := httpexpect.Config{ - BaseURL: baseURL, - Client: &http.Client{ - Transport: httpexpect.NewFastBinder(api.Router), - Jar: httpexpect.NewJar(), - }, - Reporter: httpexpect.NewAssertReporter(t), - } - - if api.Config.Tester.Debug { - testConfiguration.Printers = []httpexpect.Printer{ - httpexpect.NewDebugPrinter(t, true), - } - } - - return httpexpect.WithConfig(testConfiguration) -} - -// Tester returns the test framework for this default insance -func Tester(t *testing.T) *httpexpect.Expect { - return Default.Tester(t) -} - -// Tester returns the test framework for this iris insance -func (s *Framework) Tester(t *testing.T) *httpexpect.Expect { - if s.testFramework == nil { - s.testFramework = NewTester(s, t) - } - return s.testFramework -} - // ------------------------------------------------------------------------------------- // ------------------------------------------------------------------------------------- // ----------------------------------MuxAPI implementation------------------------------ @@ -1732,7 +1689,7 @@ func (api *muxAPI) StaticHandler(systemPath string, stripSlashes int, compress b if errCode == StatusNotFound || errCode == StatusBadRequest || errCode == StatusInternalServerError { api.mux.fireError(errCode, ctx) } - if ctx.pos < uint8(len(ctx.middleware))-1 { + if ctx.Pos < uint8(len(ctx.Middleware))-1 { ctx.Next() // for any case } diff --git a/plugin.go b/plugin.go index 79df305b..430b4245 100644 --- a/plugin.go +++ b/plugin.go @@ -119,23 +119,31 @@ type ( PluginContainer interface { Add(...Plugin) error Remove(string) error + Len() int GetName(Plugin) string GetDescription(Plugin) string GetByName(string) Plugin Printf(string, ...interface{}) + Fired(string) int PreLookup(PreLookupFunc) DoPreLookup(Route) + PreLookupFired() bool PreBuild(PreBuildFunc) DoPreBuild(*Framework) + PreBuildFired() bool PreListen(PreListenFunc) DoPreListen(*Framework) DoPreListenParallel(*Framework) + PreListenFired() bool PostListen(PostListenFunc) DoPostListen(*Framework) + PostListenFired() bool PreClose(PreCloseFunc) DoPreClose(*Framework) + PreCloseFired() bool PreDownload(PreDownloadFunc) DoPreDownload(Plugin, string) + PreDownloadFired() bool // GetAll() []Plugin // GetDownloader is the only one module that is used and fire listeners at the same time in this file @@ -248,11 +256,12 @@ type pluginContainer struct { downloader *pluginDownloadManager logger *log.Logger mu sync.Mutex + fired map[string]int // event/plugin type name and the times fired } // newPluginContainer receives a logger and returns a new PluginContainer func newPluginContainer(l *log.Logger) PluginContainer { - return &pluginContainer{logger: l} + return &pluginContainer{logger: l, fired: make(map[string]int, 0)} } // Add activates the plugins and if succeed then adds it to the activated plugins list @@ -291,10 +300,6 @@ func (p *pluginContainer) Add(plugins ...Plugin) error { return nil } -func (p *pluginContainer) Reset() { - -} - // Remove removes a plugin by it's name, if pluginName is empty "" or no plugin found with this name, then nothing is removed and a specific error is returned. // This doesn't calls the PreClose method func (p *pluginContainer) Remove(pluginName string) error { @@ -322,6 +327,11 @@ func (p *pluginContainer) Remove(pluginName string) error { return nil } +// Len returns the number of activate plugins +func (p *pluginContainer) Len() int { + return len(p.activatedPlugins) +} + // GetName returns the name of a plugin, if no GetName() implemented it returns an empty string "" func (p *pluginContainer) GetName(plugin Plugin) string { if pluginObj, ok := plugin.(pluginGetName); ok { @@ -378,6 +388,29 @@ func (p *pluginContainer) Printf(format string, a ...interface{}) { } +// fire adds a fired event on the (statically type named) map and returns the new times +func (p *pluginContainer) fire(name string) int { + p.mu.Lock() + var times int + // maybe unnessecary but for clarity reasons + if t, found := p.fired[name]; found { + times = t + } + times++ + p.fired[name] = times + p.mu.Unlock() + return times +} + +// Fired receives an event name/plugin type and returns the times which this event is fired and how many plugins are fired this event, +// if zero then it's not fired at all +func (p *pluginContainer) Fired(name string) (times int) { + if t, found := p.fired[name]; found { + times = t + } + return +} + // PreLookup adds a PreLookup plugin-function to the plugin flow container func (p *pluginContainer) PreLookup(fn PreLookupFunc) { p.Add(fn) @@ -388,11 +421,18 @@ func (p *pluginContainer) DoPreLookup(r Route) { for i := range p.activatedPlugins { // check if this method exists on our plugin obj, these are optionaly and call it if pluginObj, ok := p.activatedPlugins[i].(pluginPreLookup); ok { + // fire will add times to the number of events fired this event + p.fire("prelookup") pluginObj.PreLookup(r) } } } +// PreLookupFired returns true if PreLookup event/ plugin type is fired at least one time +func (p *pluginContainer) PreLookupFired() bool { + return p.Fired("prelookup") > 0 +} + // PreBuild adds a PreBuild plugin-function to the plugin flow container func (p *pluginContainer) PreBuild(fn PreBuildFunc) { p.Add(fn) @@ -404,10 +444,16 @@ func (p *pluginContainer) DoPreBuild(station *Framework) { // check if this method exists on our plugin obj, these are optionaly and call it if pluginObj, ok := p.activatedPlugins[i].(pluginPreBuild); ok { pluginObj.PreBuild(station) + p.fire("prebuild") } } } +// PreBuildFired returns true if PreBuild event/ plugin type is fired at least one time +func (p *pluginContainer) PreBuildFired() bool { + return p.Fired("prebuild") > 0 +} + // PreListen adds a PreListen plugin-function to the plugin flow container func (p *pluginContainer) PreListen(fn PreListenFunc) { p.Add(fn) @@ -419,6 +465,7 @@ func (p *pluginContainer) DoPreListen(station *Framework) { // check if this method exists on our plugin obj, these are optionaly and call it if pluginObj, ok := p.activatedPlugins[i].(pluginPreListen); ok { pluginObj.PreListen(station) + p.fire("prelisten") } } } @@ -433,6 +480,7 @@ func (p *pluginContainer) DoPreListenParallel(station *Framework) { go func(plugin Plugin) { if pluginObj, ok := plugin.(pluginPreListen); ok { pluginObj.PreListen(station) + p.fire("prelisten") } wg.Done() @@ -444,6 +492,11 @@ func (p *pluginContainer) DoPreListenParallel(station *Framework) { } +// PreListenFired returns true if PreListen or PreListenParallel event/ plugin type is fired at least one time +func (p *pluginContainer) PreListenFired() bool { + return p.Fired("prelisten") > 0 +} + // PostListen adds a PostListen plugin-function to the plugin flow container func (p *pluginContainer) PostListen(fn PostListenFunc) { p.Add(fn) @@ -455,10 +508,16 @@ func (p *pluginContainer) DoPostListen(station *Framework) { // check if this method exists on our plugin obj, these are optionaly and call it if pluginObj, ok := p.activatedPlugins[i].(pluginPostListen); ok { pluginObj.PostListen(station) + p.fire("postlisten") } } } +// PostListenFired returns true if PostListen event/ plugin type is fired at least one time +func (p *pluginContainer) PostListenFired() bool { + return p.Fired("postlisten") > 0 +} + // PreClose adds a PreClose plugin-function to the plugin flow container func (p *pluginContainer) PreClose(fn PreCloseFunc) { p.Add(fn) @@ -470,10 +529,16 @@ func (p *pluginContainer) DoPreClose(station *Framework) { // check if this method exists on our plugin obj, these are optionaly and call it if pluginObj, ok := p.activatedPlugins[i].(pluginPreClose); ok { pluginObj.PreClose(station) + p.fire("preclose") } } } +// PreCloseFired returns true if PreCLose event/ plugin type is fired at least one time +func (p *pluginContainer) PreCloseFired() bool { + return p.Fired("preclose") > 0 +} + // PreDownload adds a PreDownload plugin-function to the plugin flow container func (p *pluginContainer) PreDownload(fn PreDownloadFunc) { p.Add(fn) @@ -485,6 +550,12 @@ func (p *pluginContainer) DoPreDownload(pluginTryToDownload Plugin, downloadURL // check if this method exists on our plugin obj, these are optionaly and call it if pluginObj, ok := p.activatedPlugins[i].(pluginPreDownload); ok { pluginObj.PreDownload(pluginTryToDownload, downloadURL) + p.fire("predownload") } } } + +// PreDownloadFired returns true if PreDownload event/ plugin type is fired at least one time +func (p *pluginContainer) PreDownloadFired() bool { + return p.Fired("predownload") > 0 +} diff --git a/plugin_test.go b/plugin_test.go index 22eafa3f..a8f68066 100644 --- a/plugin_test.go +++ b/plugin_test.go @@ -1,4 +1,5 @@ -package iris +// Black-box Testing +package iris_test /* Contains tests for plugin, no end-to-end, just local-object tests, these are enoguh for now. @@ -8,6 +9,7 @@ CONTRIBUTE & DISCUSSION ABOUT TESTS TO: https://github.com/iris-contrib/tests import ( "fmt" + "github.com/kataras/iris" "testing" ) @@ -33,52 +35,52 @@ func (t *testPluginEx) GetDescription() string { return testPluginExDescription } -func (t *testPluginEx) Activate(p PluginContainer) error { +func (t *testPluginEx) Activate(p iris.PluginContainer) error { fmt.Println("Activate Struct") t.activated = true return nil } -func (t *testPluginEx) PreListen(*Framework) { +func (t *testPluginEx) PreListen(*iris.Framework) { fmt.Println("PreListen Struct") t.prelistenran = true } -func (t *testPluginEx) PostListen(*Framework) { +func (t *testPluginEx) PostListen(*iris.Framework) { fmt.Println("PostListen Struct") t.postlistenran = true } -func (t *testPluginEx) PreClose(*Framework) { +func (t *testPluginEx) PreClose(*iris.Framework) { fmt.Println("PreClose Struct") t.precloseran = true } func ExamplePlugins_Add() { - initDefault() - Default.Set(OptionDisableBanner(true)) - Plugins.Add(PreListenFunc(func(*Framework) { + iris.ResetDefault() + iris.Default.Set(iris.OptionDisableBanner(true)) + iris.Plugins.Add(iris.PreListenFunc(func(*iris.Framework) { fmt.Println("PreListen Func") })) - Plugins.Add(PostListenFunc(func(*Framework) { + iris.Plugins.Add(iris.PostListenFunc(func(*iris.Framework) { fmt.Println("PostListen Func") })) - Plugins.Add(PreCloseFunc(func(*Framework) { + iris.Plugins.Add(iris.PreCloseFunc(func(*iris.Framework) { fmt.Println("PreClose Func") })) myplugin := &testPluginEx{} - Plugins.Add(myplugin) - desc := Plugins.GetDescription(myplugin) + iris.Plugins.Add(myplugin) + desc := iris.Plugins.GetDescription(myplugin) fmt.Println(desc) // travis have problems if I do that using // Listen(":8080") and Close() - Plugins.DoPreListen(Default) - Plugins.DoPostListen(Default) - Plugins.DoPreClose(Default) + iris.Plugins.DoPreListen(iris.Default) + iris.Plugins.DoPostListen(iris.Default) + iris.Plugins.DoPreClose(iris.Default) // Output: // GetName Struct @@ -95,7 +97,8 @@ func ExamplePlugins_Add() { // if a plugin has GetName, then it should be registered only one time, the name exists for that reason, it's like unique ID func TestPluginDublicateName(t *testing.T) { - var plugins pluginContainer + iris.ResetDefault() + var plugins = iris.Default.Plugins firstNamedPlugin := &testPluginEx{} sameNamedPlugin := &testPluginEx{} // err := plugins.Add(firstNamedPlugin, sameNamedPlugin) or @@ -107,8 +110,8 @@ func TestPluginDublicateName(t *testing.T) { if err == nil { t.Fatalf("Expected an error because of dublicate named plugin!") } - if len(plugins.activatedPlugins) != 1 { - t.Fatalf("Expected: %d activated plugin but we got: %d", 1, len(plugins.activatedPlugins)) + if plugins.Len() != 1 { + t.Fatalf("Expected: %d activated plugin but we got: %d", 1, plugins.Len()) } } @@ -116,7 +119,7 @@ type testPluginActivationType struct { shouldError bool } -func (t testPluginActivationType) Activate(p PluginContainer) error { +func (t testPluginActivationType) Activate(p iris.PluginContainer) error { p.Add(&testPluginEx{}) if t.shouldError { return fmt.Errorf("An error happens, this plugin and the added plugins by this plugin should not be registered") @@ -125,46 +128,49 @@ func (t testPluginActivationType) Activate(p PluginContainer) error { } func TestPluginActivate(t *testing.T) { - var plugins pluginContainer + iris.ResetDefault() + var plugins = iris.Default.Plugins myplugin := testPluginActivationType{shouldError: false} plugins.Add(myplugin) - if len(plugins.activatedPlugins) != 2 { // 2 because it registeres a second plugin also - t.Fatalf("Expected activated plugins to be: %d but we got: %d", 0, len(plugins.activatedPlugins)) + if plugins.Len() != 2 { // 2 because it registeres a second plugin also + t.Fatalf("Expected activated plugins to be: %d but we got: %d", 0, plugins.Len()) } } // if any error returned from the Activate plugin's method, then this plugin and the plugins it registers should not be registered at all func TestPluginActivationError(t *testing.T) { - var plugins pluginContainer + iris.ResetDefault() + var plugins = iris.Default.Plugins myplugin := testPluginActivationType{shouldError: true} plugins.Add(myplugin) - if len(plugins.activatedPlugins) > 0 { - t.Fatalf("Expected activated plugins to be: %d but we got: %d", 0, len(plugins.activatedPlugins)) + if plugins.Len() > 0 { + t.Fatalf("Expected activated plugins to be: %d but we got: %d", 0, plugins.Len()) } } func TestPluginEvents(t *testing.T) { - var plugins pluginContainer + iris.ResetDefault() + var plugins = iris.Default.Plugins var prelistenran, postlistenran, precloseran bool - plugins.Add(PreListenFunc(func(*Framework) { + plugins.Add(iris.PreListenFunc(func(*iris.Framework) { prelistenran = true })) - plugins.Add(PostListenFunc(func(*Framework) { + plugins.Add(iris.PostListenFunc(func(*iris.Framework) { postlistenran = true })) - plugins.Add(PreCloseFunc(func(*Framework) { + plugins.Add(iris.PreCloseFunc(func(*iris.Framework) { precloseran = true })) myplugin := &testPluginEx{} plugins.Add(myplugin) - if len(plugins.activatedPlugins) != 4 { - t.Fatalf("Expected: %d plugins to be registed but we got: %d", 4, len(plugins.activatedPlugins)) + if plugins.Len() != 4 { + t.Fatalf("Expected: %d plugins to be registed but we got: %d", 4, plugins.Len()) } desc := plugins.GetDescription(myplugin) if desc != testPluginExDescription {