2016-10-13 16:25:01 +02:00
// Black-box Testing
package iris_test
2016-07-06 20:24:34 +02:00
2016-07-07 00:25:50 +02:00
import (
2016-10-06 22:19:55 +02:00
"encoding/json"
2016-07-07 00:25:50 +02:00
"encoding/xml"
2016-10-06 22:19:55 +02:00
"fmt"
2017-01-04 20:29:58 +01:00
"io/ioutil"
2017-01-02 20:20:17 +01:00
"net/http"
2016-07-07 00:25:50 +02:00
"net/url"
"strconv"
2016-07-07 01:20:04 +02:00
"strings"
2016-07-07 00:25:50 +02:00
"testing"
2016-07-07 16:02:15 +02:00
"time"
2016-12-22 18:07:03 +01:00
"github.com/gavv/httpexpect"
"github.com/kataras/iris"
"github.com/kataras/iris/httptest"
2016-07-07 00:25:50 +02:00
)
2016-07-06 20:24:34 +02:00
2016-10-13 16:25:01 +02:00
// White-box testing *
2016-07-06 20:24:34 +02:00
func TestContextDoNextStop ( t * testing . T ) {
2016-10-13 16:25:01 +02:00
var context iris . Context
2016-07-06 20:24:34 +02:00
ok := false
afterStop := false
2016-10-13 16:25:01 +02:00
context . Middleware = iris . Middleware { iris . HandlerFunc ( func ( * iris . Context ) {
2016-07-06 20:24:34 +02:00
ok = true
2016-10-13 16:25:01 +02:00
} ) , iris . HandlerFunc ( func ( * iris . Context ) {
2016-07-06 20:24:34 +02:00
ok = true
2016-10-13 16:25:01 +02:00
} ) , iris . HandlerFunc ( func ( * iris . Context ) {
2016-07-06 20:24:34 +02:00
// this will never execute
afterStop = true
} ) }
context . Do ( )
2016-10-13 16:25:01 +02:00
if context . Pos != 0 {
t . Fatalf ( "Expecting position 0 for context's middleware but we got: %d" , context . Pos )
2016-07-06 20:24:34 +02:00
}
if ! ok {
t . Fatalf ( "Unexpected behavior, first context's middleware didn't executed" )
}
ok = false
context . Next ( )
2016-10-13 16:25:01 +02:00
if int ( context . Pos ) != 1 {
t . Fatalf ( "Expecting to have position %d but we got: %d" , 1 , context . Pos )
2016-07-06 20:24:34 +02:00
}
if ! ok {
t . Fatalf ( "Next context's middleware didn't executed" )
}
context . StopExecution ( )
2016-10-13 16:25:01 +02:00
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 )
2016-07-06 20:24:34 +02:00
}
if ! context . IsStopped ( ) {
t . Fatalf ( "Should be stopped" )
}
context . Next ( )
if afterStop {
t . Fatalf ( "We stopped the execution but the next handler was executed" )
}
}
2016-10-25 14:58:18 +02:00
type pathParameter struct {
Key string
Value string
}
type pathParameters [ ] pathParameter
2016-10-13 16:25:01 +02:00
// White-box testing *
2016-07-07 00:25:50 +02:00
func TestContextParams ( t * testing . T ) {
2017-01-02 20:20:17 +01:00
context := & iris . Context { }
2016-10-25 14:58:18 +02:00
params := pathParameters {
pathParameter { Key : "testkey" , Value : "testvalue" } ,
pathParameter { Key : "testkey2" , Value : "testvalue2" } ,
pathParameter { Key : "id" , Value : "3" } ,
pathParameter { Key : "bigint" , Value : "548921854390354" } ,
}
for _ , p := range params {
context . Set ( p . Key , p . Value )
2016-07-06 20:24:34 +02:00
}
if v := context . Param ( params [ 0 ] . Key ) ; v != params [ 0 ] . Value {
t . Fatalf ( "Expecting parameter value to be %s but we got %s" , params [ 0 ] . Value , context . Param ( "testkey" ) )
}
if v := context . Param ( params [ 1 ] . Key ) ; v != params [ 1 ] . Value {
t . Fatalf ( "Expecting parameter value to be %s but we got %s" , params [ 1 ] . Value , context . Param ( "testkey2" ) )
}
2016-10-25 14:58:18 +02:00
if context . ParamsLen ( ) != len ( params ) {
t . Fatalf ( "Expecting to have %d parameters but we got %d" , len ( params ) , context . ParamsLen ( ) )
2016-07-06 20:24:34 +02:00
}
if vi , err := context . ParamInt ( params [ 2 ] . Key ) ; err != nil {
t . Fatalf ( "Unexpecting error on context's ParamInt while trying to get the integer of the %s" , params [ 2 ] . Value )
} else if vi != 3 {
t . Fatalf ( "Expecting to receive %d but we got %d" , 3 , vi )
}
if vi , err := context . ParamInt64 ( params [ 3 ] . Key ) ; err != nil {
t . Fatalf ( "Unexpecting error on context's ParamInt while trying to get the integer of the %s" , params [ 2 ] . Value )
} else if vi != 548921854390354 {
t . Fatalf ( "Expecting to receive %d but we got %d" , 548921854390354 , vi )
}
2016-07-07 00:25:50 +02:00
// end-to-end test now, note that we will not test the whole mux here, this happens on http_test.go
2016-10-13 16:25:01 +02:00
iris . ResetDefault ( )
2016-07-07 00:25:50 +02:00
expectedParamsStr := "param1=myparam1,param2=myparam2,param3=myparam3afterstatic,anything=/andhere/anything/you/like"
2016-10-13 16:25:01 +02:00
iris . Get ( "/path/:param1/:param2/staticpath/:param3/*anything" , func ( ctx * iris . Context ) {
2016-10-25 14:58:18 +02:00
paramsStr := ctx . ParamsSentence ( )
2017-01-02 20:20:17 +01:00
ctx . WriteString ( paramsStr )
2016-07-07 00:25:50 +02:00
} )
2016-10-13 16:25:01 +02:00
httptest . New ( iris . Default , t ) . GET ( "/path/myparam1/myparam2/staticpath/myparam3afterstatic/andhere/anything/you/like" ) . Expect ( ) . Status ( iris . StatusOK ) . Body ( ) . Equal ( expectedParamsStr )
2016-07-07 00:25:50 +02:00
}
func TestContextURLParams ( t * testing . T ) {
2016-10-13 16:25:01 +02:00
iris . ResetDefault ( )
2016-07-07 00:25:50 +02:00
passedParams := map [ string ] string { "param1" : "value1" , "param2" : "value2" }
2016-10-13 16:25:01 +02:00
iris . Get ( "/" , func ( ctx * iris . Context ) {
2016-07-07 00:25:50 +02:00
params := ctx . URLParams ( )
2016-10-13 16:25:01 +02:00
ctx . JSON ( iris . StatusOK , params )
2016-07-07 00:25:50 +02:00
} )
2016-10-13 16:25:01 +02:00
e := httptest . New ( iris . Default , t )
2016-07-07 00:25:50 +02:00
2016-10-13 16:25:01 +02:00
e . GET ( "/" ) . WithQueryObject ( passedParams ) . Expect ( ) . Status ( iris . StatusOK ) . JSON ( ) . Equal ( passedParams )
2016-07-07 00:25:50 +02:00
}
// hoststring returns the full host, will return the HOST:IP
func TestContextHostString ( t * testing . T ) {
2016-10-13 16:25:01 +02:00
iris . ResetDefault ( )
iris . Default . Config . VHost = "0.0.0.0:8080"
iris . Get ( "/" , func ( ctx * iris . Context ) {
2017-01-02 20:20:17 +01:00
ctx . WriteString ( ctx . Host ( ) )
2016-07-07 00:25:50 +02:00
} )
2016-10-13 16:25:01 +02:00
iris . Get ( "/wrong" , func ( ctx * iris . Context ) {
2017-01-02 20:20:17 +01:00
ctx . WriteString ( ctx . Host ( ) + "w" )
2016-07-07 00:25:50 +02:00
} )
2016-10-13 16:25:01 +02:00
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 )
2016-07-07 00:25:50 +02:00
}
// 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 ) {
2016-10-13 16:25:01 +02:00
iris . ResetDefault ( )
2016-07-07 00:25:50 +02:00
vhost := "mycustomvirtualname.com"
2016-10-13 16:25:01 +02:00
iris . Default . Config . VHost = vhost + ":8080"
iris . Get ( "/" , func ( ctx * iris . Context ) {
2017-01-02 20:20:17 +01:00
ctx . WriteString ( ctx . VirtualHostname ( ) )
2016-07-07 00:25:50 +02:00
} )
2016-10-13 16:25:01 +02:00
iris . Get ( "/wrong" , func ( ctx * iris . Context ) {
2017-01-02 20:20:17 +01:00
ctx . WriteString ( ctx . VirtualHostname ( ) + "w" )
2016-07-07 00:25:50 +02:00
} )
2016-10-13 16:25:01 +02:00
e := httptest . New ( iris . Default , t )
e . GET ( "/" ) . Expect ( ) . Status ( iris . StatusOK ) . Body ( ) . Equal ( vhost )
2016-07-07 00:25:50 +02:00
e . GET ( "/wrong" ) . Expect ( ) . Body ( ) . NotEqual ( vhost )
}
func TestContextFormValueString ( t * testing . T ) {
2016-10-13 16:25:01 +02:00
iris . ResetDefault ( )
2016-07-07 00:25:50 +02:00
var k , v string
k = "postkey"
v = "postvalue"
2016-10-13 16:25:01 +02:00
iris . Post ( "/" , func ( ctx * iris . Context ) {
2017-01-02 20:20:17 +01:00
ctx . WriteString ( k + "=" + ctx . FormValue ( k ) )
2016-07-07 00:25:50 +02:00
} )
2016-10-13 16:25:01 +02:00
e := httptest . New ( iris . Default , t )
2016-07-07 00:25:50 +02:00
2016-10-13 16:25:01 +02:00
e . POST ( "/" ) . WithFormField ( k , v ) . Expect ( ) . Status ( iris . StatusOK ) . Body ( ) . Equal ( k + "=" + v )
2016-07-07 00:25:50 +02:00
}
func TestContextSubdomain ( t * testing . T ) {
2016-10-13 16:25:01 +02:00
iris . ResetDefault ( )
iris . Default . Config . VHost = "mydomain.com:9999"
2016-09-27 15:28:38 +02:00
//Default.Config.Tester.ListeningAddr = "mydomain.com:9999"
// Default.Config.Tester.ExplicitURL = true
2016-10-13 16:25:01 +02:00
iris . Party ( "mysubdomain." ) . Get ( "/mypath" , func ( ctx * iris . Context ) {
2017-01-02 20:20:17 +01:00
ctx . WriteString ( ctx . Subdomain ( ) )
2016-07-07 00:25:50 +02:00
} )
2016-10-13 16:25:01 +02:00
e := httptest . New ( iris . Default , t )
2016-09-27 15:28:38 +02:00
2016-10-13 16:25:01 +02:00
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" )
2016-07-07 00:25:50 +02:00
//e.GET("http://mysubdomain.mydomain.com:9999").Expect().Status(StatusNotFound)
2016-10-13 16:25:01 +02:00
//e.GET("http://mysubdomain.mydomain.com:9999/mypath").Expect().Status(iris.StatusOK).Body().Equal("mysubdomain")
2016-07-07 00:25:50 +02:00
}
type testBinderData struct {
Username string
Mail string
Data [ ] string ` form:"mydata" json:"mydata" `
}
type testBinderXMLData struct {
XMLName xml . Name ` xml:"info" `
FirstAttr string ` xml:"first,attr" `
SecondAttr string ` xml:"second,attr" `
Name string ` xml:"name",json:"name" `
Birth string ` xml:"birth",json:"birth" `
Stars int ` xml:"stars",json:"stars" `
}
2016-12-13 06:12:58 +01:00
type testBinder struct {
//pointer of testBinderDataJSON or testBinderXMLData
vp interface { }
m iris . Unmarshaler
shouldError bool
}
2016-07-07 00:25:50 +02:00
2016-12-13 06:12:58 +01:00
func ( tj * testBinder ) Decode ( data [ ] byte ) error {
if tj . shouldError {
return fmt . Errorf ( "Should error" )
}
return tj . m . Unmarshal ( data , tj . vp )
}
2016-07-07 00:25:50 +02:00
2016-12-13 06:12:58 +01:00
func testUnmarshaler ( t * testing . T , tb * testBinder ,
write func ( ctx * iris . Context ) ) * httpexpect . Request {
2016-07-07 00:25:50 +02:00
2016-12-13 06:12:58 +01:00
// a very dirty and awful way but here we must test in deep
// the custom object's decoder error with the custom
// unmarshaler result whenever the testUnmarshaler called.
if tb . shouldError == false {
tb . shouldError = true
testUnmarshaler ( t , tb , nil )
tb . shouldError = false
}
2016-07-07 00:25:50 +02:00
2016-10-13 16:25:01 +02:00
iris . ResetDefault ( )
2016-12-13 06:12:58 +01:00
h := func ( ctx * iris . Context ) {
err := ctx . UnmarshalBody ( tb . vp , tb . m )
if tb . shouldError && err == nil {
t . Fatalf ( "Should prompted for error 'Should error' but not error returned from the custom decoder!" )
} else if err != nil {
t . Fatalf ( "Error when parsing the body: %s" , err . Error ( ) )
2016-07-07 00:25:50 +02:00
}
2016-12-13 06:12:58 +01:00
if write != nil {
write ( ctx )
2016-10-06 22:19:55 +02:00
}
2016-12-13 06:12:58 +01:00
}
2016-07-07 00:25:50 +02:00
2016-12-13 06:12:58 +01:00
iris . Post ( "/bind_req_body" , h )
2016-10-06 22:19:55 +02:00
2016-12-13 06:12:58 +01:00
e := httptest . New ( iris . Default , t )
return e . POST ( "/bind_req_body" )
2016-07-07 00:25:50 +02:00
}
2016-12-13 06:12:58 +01:00
// same as DecodeBody
// JSON, XML by DecodeBody passing the default unmarshalers
func TestContextBinders ( t * testing . 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" } }
// JSON
vJSON := & testBinder { & testBinderData { } ,
iris . UnmarshalerFunc ( json . Unmarshal ) , false }
testUnmarshaler (
t ,
vJSON ,
func ( ctx * iris . Context ) {
ctx . JSON ( iris . StatusOK , vJSON . vp )
} ) .
WithJSON ( passed ) .
Expect ( ) .
Status ( iris . StatusOK ) .
JSON ( ) . Object ( ) . Equal ( expectedObject )
// XML
expectedObj := testBinderXMLData {
XMLName : xml . Name { Local : "info" , Space : "info" } ,
FirstAttr : "this is the first attr" ,
SecondAttr : "this is the second attr" ,
Name : "Iris web framework" ,
Birth : "13 March 2016" ,
Stars : 5758 ,
2016-10-06 22:19:55 +02:00
}
2016-12-13 06:12:58 +01:00
expectedAndPassedObjText := ` < ` + expectedObj . XMLName . Local + ` first=" ` +
expectedObj . FirstAttr + ` " second=" ` +
expectedObj . SecondAttr + ` "><name> ` +
expectedObj . Name + ` </name><birth> ` +
expectedObj . Birth + ` </birth><stars> ` +
strconv . Itoa ( expectedObj . Stars ) + ` </stars></info> `
// JSON
vXML := & testBinder { & testBinderXMLData { } ,
iris . UnmarshalerFunc ( xml . Unmarshal ) , false }
testUnmarshaler (
t ,
vXML ,
func ( ctx * iris . Context ) {
ctx . XML ( iris . StatusOK , vXML . vp )
} ) .
WithText ( expectedAndPassedObjText ) .
Expect ( ) .
Status ( iris . StatusOK ) .
Body ( ) . Equal ( expectedAndPassedObjText )
2016-10-06 22:19:55 +02:00
}
2016-12-13 06:12:58 +01:00
func TestContextReadForm ( t * testing . T ) {
2016-10-13 16:25:01 +02:00
iris . ResetDefault ( )
2016-10-06 22:19:55 +02:00
2016-12-13 06:12:58 +01:00
iris . Post ( "/form" , func ( ctx * iris . Context ) {
obj := testBinderData { }
err := ctx . ReadForm ( & obj )
2016-10-06 22:19:55 +02:00
if err != nil {
2016-12-13 06:12:58 +01:00
t . Fatalf ( "Error when parsing the FORM: %s" , err . Error ( ) )
2016-10-06 22:19:55 +02:00
}
2016-10-13 16:25:01 +02:00
ctx . JSON ( iris . StatusOK , obj )
2016-10-06 22:19:55 +02:00
} )
2016-10-13 16:25:01 +02:00
e := httptest . New ( iris . Default , t )
2016-10-06 22:19:55 +02:00
2016-12-13 06:12:58 +01:00
passed := map [ string ] interface { } { "Username" : "myusername" , "Mail" : "mymail@iris-go.com" , "mydata" : url . Values { "[0]" : [ ] string { "mydata1" } ,
"[1]" : [ ] string { "mydata2" } } }
2016-07-07 00:25:50 +02:00
2016-12-13 06:12:58 +01:00
expectedObject := testBinderData { Username : "myusername" , Mail : "mymail@iris-go.com" , Data : [ ] string { "mydata1" , "mydata2" } }
2016-07-07 00:25:50 +02:00
2016-12-13 06:12:58 +01:00
e . POST ( "/form" ) . WithForm ( passed ) . Expect ( ) . Status ( iris . StatusOK ) . JSON ( ) . Object ( ) . Equal ( expectedObject )
2016-07-06 20:24:34 +02:00
}
2016-07-07 01:20:04 +02:00
// TestContextRedirectTo tests the named route redirect action
func TestContextRedirectTo ( t * testing . T ) {
2016-10-13 16:25:01 +02:00
iris . ResetDefault ( )
2017-01-02 20:20:17 +01:00
h := func ( ctx * iris . Context ) { ctx . WriteString ( ctx . Path ( ) ) }
2016-10-13 16:25:01 +02:00
iris . Get ( "/mypath" , h ) ( "my-path" )
iris . Get ( "/mypostpath" , h ) ( "my-post-path" )
iris . Get ( "mypath/with/params/:param1/:param2" , func ( ctx * iris . Context ) {
2016-10-25 14:58:18 +02:00
if l := ctx . ParamsLen ( ) ; l != 2 {
t . Fatalf ( "Strange error, expecting parameters to be two but we got: %d" , l )
2016-07-07 01:20:04 +02:00
}
2017-01-02 20:20:17 +01:00
ctx . WriteString ( ctx . Path ( ) )
2016-07-07 01:20:04 +02:00
} ) ( "my-path-with-params" )
2016-10-13 16:25:01 +02:00
iris . Get ( "/redirect/to/:routeName/*anyparams" , func ( ctx * iris . Context ) {
2016-07-07 01:20:04 +02:00
routeName := ctx . Param ( "routeName" )
var args [ ] interface { }
anyparams := ctx . Param ( "anyparams" )
if anyparams != "" && anyparams != "/" {
params := strings . Split ( anyparams [ 1 : ] , "/" ) // firstparam/secondparam
for _ , s := range params {
args = append ( args , s )
}
}
ctx . RedirectTo ( routeName , args ... )
} )
2016-07-07 01:26:05 +02:00
2016-10-13 16:25:01 +02:00
e := httptest . New ( iris . Default , t )
2016-07-07 00:25:50 +02:00
2016-10-13 16:25:01 +02:00
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" )
2016-07-07 00:25:50 +02:00
}
func TestContextUserValues ( t * testing . T ) {
2016-10-13 16:25:01 +02:00
iris . ResetDefault ( )
2016-07-07 00:25:50 +02:00
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" } }
2016-10-13 16:25:01 +02:00
iris . Get ( "/test" , func ( ctx * iris . Context ) {
2016-07-07 00:25:50 +02:00
for k , v := range values {
ctx . Set ( k , v )
}
2016-10-13 16:25:01 +02:00
} , func ( ctx * iris . Context ) {
2016-07-07 00:25:50 +02:00
for k , v := range values {
userValue := ctx . Get ( k )
if userValue != v {
t . Fatalf ( "Expecting user value: %s to be equal with: %#v but got: %#v" , k , v , userValue )
}
if m , isMap := userValue . ( map [ string ] string ) ; isMap {
if m [ "key" ] != v . ( map [ string ] string ) [ "key" ] {
t . Fatalf ( "Expecting user value: %s to be equal with: %#v but got: %#v" , k , v . ( map [ string ] string ) [ "key" ] , m [ "key" ] )
}
} else {
if userValue != v {
t . Fatalf ( "Expecting user value: %s to be equal with: %#v but got: %#v" , k , v , userValue )
}
}
}
} )
2016-10-13 16:25:01 +02:00
e := httptest . New ( iris . Default , t )
2016-07-07 00:25:50 +02:00
2016-10-13 16:25:01 +02:00
e . GET ( "/test" ) . Expect ( ) . Status ( iris . StatusOK )
2016-07-07 00:25:50 +02:00
}
2016-07-07 16:02:15 +02:00
func TestContextCookieSetGetRemove ( t * testing . T ) {
2016-10-13 16:25:01 +02:00
iris . ResetDefault ( )
2016-07-07 16:02:15 +02:00
key := "mykey"
value := "myvalue"
2016-10-13 16:25:01 +02:00
iris . Get ( "/set" , func ( ctx * iris . Context ) {
2016-07-07 16:02:15 +02:00
ctx . SetCookieKV ( key , value ) // should return non empty cookies
} )
2016-10-13 16:25:01 +02:00
iris . Get ( "/set_advanced" , func ( ctx * iris . Context ) {
2017-01-02 20:20:17 +01:00
c := & http . Cookie { }
c . Name = key
c . Value = value
c . HttpOnly = true
c . Expires = time . Now ( ) . Add ( time . Duration ( ( 60 * 60 * 24 * 7 * 4 ) ) * time . Second )
2016-07-07 16:02:15 +02:00
ctx . SetCookie ( c )
} )
2016-10-13 16:25:01 +02:00
iris . Get ( "/get" , func ( ctx * iris . Context ) {
2017-01-02 20:20:17 +01:00
ctx . WriteString ( ctx . GetCookie ( key ) ) // should return my value
2016-07-07 16:02:15 +02:00
} )
2016-10-13 16:25:01 +02:00
iris . Get ( "/remove" , func ( ctx * iris . Context ) {
2016-07-07 16:02:15 +02:00
ctx . RemoveCookie ( key )
cookieFound := false
ctx . VisitAllCookies ( func ( k , v string ) {
cookieFound = true
} )
if cookieFound {
t . Fatalf ( "Cookie has been found, when it shouldn't!" )
}
2017-01-02 20:20:17 +01:00
ctx . WriteString ( ctx . GetCookie ( key ) ) // should return ""
2016-07-07 16:02:15 +02:00
} )
2016-10-13 16:25:01 +02:00
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 ( "" )
2016-07-07 16:02:15 +02:00
// test again with advanced set
2016-10-13 16:25:01 +02:00
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 ( "" )
2016-07-07 16:02:15 +02:00
}
2016-07-07 00:25:50 +02:00
func TestContextSessions ( t * testing . T ) {
t . Parallel ( )
values := map [ string ] interface { } {
"Name" : "iris" ,
"Months" : "4" ,
"Secret" : "dsads£2132215£%%Ssdsa" ,
}
2016-10-13 16:25:01 +02:00
iris . ResetDefault ( )
iris . Default . Config . Sessions . Cookie = "mycustomsessionid"
2016-07-07 00:25:50 +02:00
2016-10-13 16:25:01 +02:00
writeValues := func ( ctx * iris . Context ) {
2016-07-07 00:25:50 +02:00
sessValues := ctx . Session ( ) . GetAll ( )
2016-10-13 16:25:01 +02:00
ctx . JSON ( iris . StatusOK , sessValues )
2016-07-07 00:25:50 +02:00
}
if testEnableSubdomain {
2016-10-13 16:25:01 +02:00
iris . Party ( testSubdomain + "." ) . Get ( "/get" , func ( ctx * iris . Context ) {
2016-07-07 00:25:50 +02:00
writeValues ( ctx )
} )
}
2016-10-13 16:25:01 +02:00
iris . Post ( "set" , func ( ctx * iris . Context ) {
2016-07-07 00:25:50 +02:00
vals := make ( map [ string ] interface { } , 0 )
if err := ctx . ReadJSON ( & vals ) ; err != nil {
t . Fatalf ( "Cannot readjson. Trace %s" , err . Error ( ) )
}
for k , v := range vals {
ctx . Session ( ) . Set ( k , v )
}
} )
2016-10-13 16:25:01 +02:00
iris . Get ( "/get" , func ( ctx * iris . Context ) {
2016-07-07 00:25:50 +02:00
writeValues ( ctx )
} )
2016-10-13 16:25:01 +02:00
iris . Get ( "/clear" , func ( ctx * iris . Context ) {
2016-07-07 00:25:50 +02:00
ctx . Session ( ) . Clear ( )
writeValues ( ctx )
} )
2016-10-13 16:25:01 +02:00
iris . Get ( "/destroy" , func ( ctx * iris . Context ) {
2016-07-07 00:25:50 +02:00
ctx . SessionDestroy ( )
writeValues ( ctx )
// the cookie and all values should be empty
} )
2016-07-08 22:11:39 +02:00
// request cookie should be empty
2016-10-13 16:25:01 +02:00
iris . Get ( "/after_destroy" , func ( ctx * iris . Context ) {
2016-07-08 22:11:39 +02:00
} )
2016-10-13 16:25:01 +02:00
iris . Default . Config . VHost = "mydomain.com"
e := httptest . New ( iris . Default , t )
2016-07-07 00:25:50 +02:00
2016-10-13 16:25:01 +02:00
e . POST ( "/set" ) . WithJSON ( values ) . Expect ( ) . Status ( iris . StatusOK ) . Cookies ( ) . NotEmpty ( )
e . GET ( "/get" ) . Expect ( ) . Status ( iris . StatusOK ) . JSON ( ) . Object ( ) . Equal ( values )
2016-07-07 00:25:50 +02:00
if testEnableSubdomain {
es := subdomainTester ( e )
2016-10-13 16:25:01 +02:00
es . Request ( "GET" , "/get" ) . Expect ( ) . Status ( iris . StatusOK ) . JSON ( ) . Object ( ) . Equal ( values )
2016-07-07 00:25:50 +02:00
}
2016-07-06 20:24:34 +02:00
2016-09-30 17:48:48 +02:00
// test destroy which also clears first
2016-10-13 16:25:01 +02:00
d := e . GET ( "/destroy" ) . Expect ( ) . Status ( iris . StatusOK )
:rainbow: sessions were re-written, update to 4.0.0-alpha.2, read HISTORY.md
**Sessions were re-written **
- Developers can use more than one 'session database', at the same time,
to store the sessions
- Easy to develop a custom session database (only two functions are
required (Load & Update)), [learn
more](https://github.com/iris-contrib/sessiondb/blob/master/redis/database.go)
- Session databases are located
[here](https://github.com/iris-contrib/sessiondb), contributions are
welcome
- The only frontend deleted 'thing' is the: **config.Sessions.Provider**
- No need to register a database, the sessions works out-of-the-box
- No frontend/API changes except the
`context.Session().Set/Delete/Clear`, they doesn't return errors
anymore, btw they (errors) were always nil :)
- Examples (master branch) were updated.
```sh
$ go get github.com/iris-contrib/sessiondb/$DATABASE
```
```go
db := $DATABASE.New(configurationHere{})
iris.UseSessionDB(db)
```
> Note: Book is not updated yet, examples are up-to-date as always.
2016-07-15 19:50:36 +02:00
d . JSON ( ) . Null ( )
2016-07-08 22:11:39 +02:00
// 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.
2016-10-13 16:25:01 +02:00
e . GET ( "/after_destroy" ) . Expect ( ) . Status ( iris . StatusOK ) . Cookies ( ) . Empty ( )
2016-07-07 00:25:50 +02:00
// set and clear again
2016-10-13 16:25:01 +02:00
e . POST ( "/set" ) . WithJSON ( values ) . Expect ( ) . Status ( iris . StatusOK ) . Cookies ( ) . NotEmpty ( )
e . GET ( "/clear" ) . Expect ( ) . Status ( iris . StatusOK ) . JSON ( ) . Object ( ) . Empty ( )
2016-07-06 20:24:34 +02:00
}
2016-07-07 01:36:38 +02:00
type renderTestInformationType struct {
XMLName xml . Name ` xml:"info" `
FirstAttr string ` xml:"first,attr" `
SecondAttr string ` xml:"second,attr" `
Name string ` xml:"name",json:"name" `
Birth string ` xml:"birth",json:"birth" `
Stars int ` xml:"stars",json:"stars" `
}
func TestContextRenderRest ( t * testing . T ) {
2016-10-13 16:25:01 +02:00
iris . ResetDefault ( )
2016-07-07 01:36:38 +02:00
dataContents := [ ] byte ( "Some binary data here." )
textContents := "Plain text here"
JSONPContents := map [ string ] string { "hello" : "jsonp" }
JSONPCallback := "callbackName"
JSONXMLContents := renderTestInformationType {
XMLName : xml . Name { Local : "info" , Space : "info" } , // only need to verify that later
FirstAttr : "this is the first attr" ,
SecondAttr : "this is the second attr" ,
Name : "Iris web framework" ,
Birth : "13 March 2016" ,
Stars : 4064 ,
}
markdownContents := "# Hello dynamic markdown from Iris"
2016-10-13 16:25:01 +02:00
iris . Get ( "/data" , func ( ctx * iris . Context ) {
ctx . Data ( iris . StatusOK , dataContents )
2016-07-07 01:36:38 +02:00
} )
2016-10-13 16:25:01 +02:00
iris . Get ( "/text" , func ( ctx * iris . Context ) {
ctx . Text ( iris . StatusOK , textContents )
2016-07-07 01:36:38 +02:00
} )
2016-10-13 16:25:01 +02:00
iris . Get ( "/jsonp" , func ( ctx * iris . Context ) {
ctx . JSONP ( iris . StatusOK , JSONPCallback , JSONPContents )
2016-07-07 01:36:38 +02:00
} )
2016-10-13 16:25:01 +02:00
iris . Get ( "/json" , func ( ctx * iris . Context ) {
ctx . JSON ( iris . StatusOK , JSONXMLContents )
2016-07-07 01:36:38 +02:00
} )
2016-10-13 16:25:01 +02:00
iris . Get ( "/xml" , func ( ctx * iris . Context ) {
ctx . XML ( iris . StatusOK , JSONXMLContents )
2016-07-07 01:36:38 +02:00
} )
2016-10-13 16:25:01 +02:00
iris . Get ( "/markdown" , func ( ctx * iris . Context ) {
ctx . Markdown ( iris . StatusOK , markdownContents )
2016-07-07 01:36:38 +02:00
} )
2016-10-13 16:25:01 +02:00
e := httptest . New ( iris . Default , t )
dataT := e . GET ( "/data" ) . Expect ( ) . Status ( iris . StatusOK )
2016-07-07 01:36:38 +02:00
dataT . Header ( "Content-Type" ) . Equal ( "application/octet-stream" )
dataT . Body ( ) . Equal ( string ( dataContents ) )
2016-10-13 16:25:01 +02:00
textT := e . GET ( "/text" ) . Expect ( ) . Status ( iris . StatusOK )
2016-07-07 01:36:38 +02:00
textT . Header ( "Content-Type" ) . Equal ( "text/plain; charset=UTF-8" )
textT . Body ( ) . Equal ( textContents )
2016-10-13 16:25:01 +02:00
JSONPT := e . GET ( "/jsonp" ) . Expect ( ) . Status ( iris . StatusOK )
2016-07-07 01:36:38 +02:00
JSONPT . Header ( "Content-Type" ) . Equal ( "application/javascript; charset=UTF-8" )
JSONPT . Body ( ) . Equal ( JSONPCallback + ` ( { "hello":"jsonp"}); ` )
2016-10-13 16:25:01 +02:00
JSONT := e . GET ( "/json" ) . Expect ( ) . Status ( iris . StatusOK )
2016-07-07 01:36:38 +02:00
JSONT . Header ( "Content-Type" ) . Equal ( "application/json; charset=UTF-8" )
JSONT . JSON ( ) . Object ( ) . Equal ( JSONXMLContents )
2016-10-13 16:25:01 +02:00
XMLT := e . GET ( "/xml" ) . Expect ( ) . Status ( iris . StatusOK )
2016-07-07 01:36:38 +02:00
XMLT . Header ( "Content-Type" ) . Equal ( "text/xml; charset=UTF-8" )
XMLT . Body ( ) . Equal ( ` < ` + JSONXMLContents . XMLName . Local + ` first=" ` + JSONXMLContents . FirstAttr + ` " second=" ` + JSONXMLContents . SecondAttr + ` "><name> ` + JSONXMLContents . Name + ` </name><birth> ` + JSONXMLContents . Birth + ` </birth><stars> ` + strconv . Itoa ( JSONXMLContents . Stars ) + ` </stars></info> ` )
2016-10-13 16:25:01 +02:00
markdownT := e . GET ( "/markdown" ) . Expect ( ) . Status ( iris . StatusOK )
2016-07-07 01:36:38 +02:00
markdownT . Header ( "Content-Type" ) . Equal ( "text/html; charset=UTF-8" )
markdownT . Body ( ) . Equal ( "<h1>" + markdownContents [ 2 : ] + "</h1>\n" )
2016-09-29 16:05:22 +02:00
}
2016-07-07 01:36:38 +02:00
2016-09-29 16:05:22 +02:00
func TestContextPreRender ( t * testing . T ) {
2016-10-13 16:25:01 +02:00
iris . ResetDefault ( )
2016-12-13 06:12:58 +01:00
preRender := func ( errMsg string , shouldContinue bool ) iris . PreRender {
return 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 {
msg := ""
if prevMsg := b [ "Error" ] ; prevMsg != nil {
// we have a previous message
msg += prevMsg . ( string )
}
msg += errMsg
b [ "Error" ] = msg
}
return shouldContinue
2016-09-29 16:05:22 +02:00
}
2016-12-13 06:12:58 +01:00
}
errMsg1 := "thereIsAnError"
2016-09-29 16:05:22 +02:00
errMsg2 := "thereIsASecondError"
errMsg3 := "thereisAThirdError"
2016-12-13 06:12:58 +01:00
// only errMsg1 and errMsg2 should be rendered because
// on errMsg2 we stop the execution
iris . UsePreRender ( preRender ( errMsg1 , true ) )
iris . UsePreRender ( preRender ( errMsg2 , false ) )
iris . UsePreRender ( preRender ( errMsg3 , false ) ) // false doesn't matters here
2016-09-29 16:05:22 +02:00
2016-10-13 16:25:01 +02:00
iris . Get ( "/" , func ( ctx * iris . Context ) {
ctx . RenderTemplateSource ( iris . StatusOK , "<h1>HI {{.Username}}. Error: {{.Error}}</h1>" , map [ string ] interface { } { "Username" : "kataras" } )
2016-09-29 16:05:22 +02:00
} )
2016-10-13 16:25:01 +02:00
e := httptest . New ( iris . Default , t )
2016-09-29 16:05:22 +02:00
expected := "<h1>HI kataras. Error: " + errMsg1 + errMsg2 + "</h1>"
2016-10-13 16:25:01 +02:00
e . GET ( "/" ) . Expect ( ) . Status ( iris . StatusOK ) . Body ( ) . Contains ( expected )
2016-07-07 01:36:38 +02:00
}
2016-10-15 19:48:06 +02:00
func TestTemplatesDisabled ( t * testing . T ) {
iris . ResetDefault ( )
defer iris . Close ( )
iris . Default . Config . DisableTemplateEngines = true
file := "index.html"
2017-01-02 20:20:17 +01:00
errTmpl := "<h2>Template: %s</h2><b>%s</b>"
expctedErrMsg := fmt . Sprintf ( errTmpl , file , "Error: Unable to execute a template. Trace: Templates are disabled '.Config.DisableTemplatesEngines = true' please turn that to false, as defaulted.\n" )
2016-10-15 19:48:06 +02:00
iris . Get ( "/renderErr" , func ( ctx * iris . Context ) {
ctx . MustRender ( file , nil )
} )
e := httptest . New ( iris . Default , t )
e . GET ( "/renderErr" ) . Expect ( ) . Status ( iris . StatusServiceUnavailable ) . Body ( ) . Equal ( expctedErrMsg )
}
2016-12-15 14:14:48 +01:00
2016-12-15 16:16:17 +01:00
func TestTransactions ( t * testing . T ) {
2016-12-15 14:14:48 +01:00
iris . ResetDefault ( )
2016-12-16 09:20:05 +01:00
firstTransactionFailureMessage := "Error: Virtual failure!!!"
secondTransactionSuccessHTMLMessage := "<h1>This will sent at all cases because it lives on different transaction and it doesn't fails</h1>"
2016-12-15 14:14:48 +01:00
persistMessage := "<h1>I persist show this message to the client!</h1>"
2017-01-02 20:20:17 +01:00
maybeFailureTransaction := func ( shouldFail bool , isRequestScoped bool ) func ( t * iris . Transaction ) {
return func ( t * iris . Transaction ) {
// OPTIONAl, the next transactions and the flow will not be skipped if this transaction fails
if isRequestScoped {
t . SetScope ( iris . RequestTransactionScope )
}
2016-12-15 16:16:17 +01:00
2016-12-15 14:14:48 +01:00
// OPTIONAL STEP:
// create a new custom type of error here to keep track of the status code and reason message
2017-01-02 20:20:17 +01:00
err := iris . NewTransactionErrResult ( )
2016-12-15 14:14:48 +01:00
2017-01-02 20:20:17 +01:00
t . Context . Text ( iris . StatusOK , "Blablabla this should not be sent to the client because we will fill the err with a message and status" )
2016-12-15 14:14:48 +01:00
2016-12-15 16:16:17 +01:00
fail := shouldFail
2016-12-15 14:14:48 +01:00
if fail {
2017-01-02 20:20:17 +01:00
err . StatusCode = iris . StatusInternalServerError
err . Reason = firstTransactionFailureMessage
2016-12-15 14:14:48 +01:00
}
// OPTIONAl STEP:
// but useful if we want to post back an error message to the client if the transaction failed.
// if the reason is empty then the transaction completed succesfuly,
2017-01-02 20:20:17 +01:00
// otherwise we rollback the whole response body and cookies and everything lives inside the transaction.Request.
t . Complete ( err )
2016-12-15 16:16:17 +01:00
}
}
2016-12-15 14:14:48 +01:00
2017-01-02 20:20:17 +01:00
successTransaction := func ( scope * iris . Transaction ) {
2017-01-04 14:16:53 +01:00
if scope . Context . Request . RequestURI == "/failAllBecauseOfRequestScopeAndFailure" {
t . Fatalf ( "We are inside successTransaction but the previous REQUEST SCOPED TRANSACTION HAS FAILED SO THiS SHOULD NOT BE RAN AT ALL" )
}
2016-12-15 16:16:17 +01:00
scope . Context . HTML ( iris . StatusOK ,
2016-12-16 09:20:05 +01:00
secondTransactionSuccessHTMLMessage )
2016-12-15 16:16:17 +01:00
// * if we don't have any 'throw error' logic then no need of scope.Complete()
}
2016-12-15 14:14:48 +01:00
2016-12-15 16:16:17 +01:00
persistMessageHandler := func ( ctx * iris . Context ) {
2016-12-15 14:14:48 +01:00
// OPTIONAL, depends on the usage:
// at any case, what ever happens inside the context's transactions send this to the client
ctx . HTML ( iris . StatusOK , persistMessage )
2016-12-15 16:16:17 +01:00
}
iris . Get ( "/failFirsTransactionButSuccessSecondWithPersistMessage" , func ( ctx * iris . Context ) {
ctx . BeginTransaction ( maybeFailureTransaction ( true , false ) )
ctx . BeginTransaction ( successTransaction )
persistMessageHandler ( ctx )
} )
iris . Get ( "/failFirsTransactionButSuccessSecond" , func ( ctx * iris . Context ) {
ctx . BeginTransaction ( maybeFailureTransaction ( true , false ) )
ctx . BeginTransaction ( successTransaction )
} )
iris . Get ( "/failAllBecauseOfRequestScopeAndFailure" , func ( ctx * iris . Context ) {
ctx . BeginTransaction ( maybeFailureTransaction ( true , true ) )
ctx . BeginTransaction ( successTransaction )
2016-12-15 14:14:48 +01:00
} )
2017-01-02 20:20:17 +01:00
customErrorTemplateText := "<h1>custom error</h1>"
iris . OnError ( iris . StatusInternalServerError , func ( ctx * iris . Context ) {
ctx . Text ( iris . StatusInternalServerError , customErrorTemplateText )
} )
failureWithRegisteredErrorHandler := func ( ctx * iris . Context ) {
ctx . BeginTransaction ( func ( transaction * iris . Transaction ) {
transaction . SetScope ( iris . RequestTransactionScope )
err := iris . NewTransactionErrResult ( )
err . StatusCode = iris . StatusInternalServerError // set only the status code in order to execute the registered template
transaction . Complete ( err )
} )
ctx . Text ( iris . StatusOK , "this will not be sent to the client because first is requested scope and it's failed" )
}
iris . Get ( "/failAllBecauseFirstTransactionFailedWithRegisteredErrorTemplate" , failureWithRegisteredErrorHandler )
2016-12-15 14:14:48 +01:00
e := httptest . New ( iris . Default , t )
2016-12-15 16:16:17 +01:00
e . GET ( "/failFirsTransactionButSuccessSecondWithPersistMessage" ) .
Expect ( ) .
Status ( iris . StatusOK ) .
ContentType ( "text/html" , iris . Config . Charset ) .
Body ( ) .
2017-01-02 20:20:17 +01:00
Equal ( secondTransactionSuccessHTMLMessage + persistMessage )
2016-12-15 16:16:17 +01:00
2016-12-15 14:14:48 +01:00
e . GET ( "/failFirsTransactionButSuccessSecond" ) .
Expect ( ) .
Status ( iris . StatusOK ) .
ContentType ( "text/html" , iris . Config . Charset ) .
Body ( ) .
2017-01-02 20:20:17 +01:00
Equal ( secondTransactionSuccessHTMLMessage )
2016-12-15 16:16:17 +01:00
e . GET ( "/failAllBecauseOfRequestScopeAndFailure" ) .
Expect ( ) .
Status ( iris . StatusInternalServerError ) .
Body ( ) .
2016-12-16 09:20:05 +01:00
Equal ( firstTransactionFailureMessage )
2017-01-02 20:20:17 +01:00
e . GET ( "/failAllBecauseFirstTransactionFailedWithRegisteredErrorTemplate" ) .
2016-12-16 09:20:05 +01:00
Expect ( ) .
2017-01-02 20:20:17 +01:00
Status ( iris . StatusInternalServerError ) .
2016-12-18 13:08:28 +01:00
Body ( ) .
2017-01-02 20:20:17 +01:00
Equal ( customErrorTemplateText )
2016-12-15 14:14:48 +01:00
}
2017-01-04 20:29:58 +01:00
func TestLimitRequestBodySize ( t * testing . T ) {
const maxBodySize = 1 << 20
api := iris . New ( )
// or inside handler: ctx.SetMaxRequestBodySize(int64(maxBodySize))
api . Use ( iris . LimitRequestBodySize ( maxBodySize ) )
api . Post ( "/" , func ( ctx * iris . Context ) {
b , err := ioutil . ReadAll ( ctx . Request . Body )
if len ( b ) > maxBodySize {
// this is a fatal error it should never happened.
t . Fatalf ( "body is larger (%d) than maxBodySize (%d) even if we add the LimitRequestBodySize middleware" , len ( b ) , maxBodySize )
}
// if is larger then send a bad request status
if err != nil {
ctx . WriteHeader ( iris . StatusBadRequest )
ctx . Writef ( err . Error ( ) )
return
}
ctx . Write ( b )
} )
// UseGlobal should be called at the end used to prepend handlers
// api.UseGlobal(iris.LimitRequestBodySize(int64(maxBodySize)))
e := httptest . New ( api , t )
// test with small body
e . POST ( "/" ) . WithBytes ( [ ] byte ( "ok" ) ) . Expect ( ) . Status ( iris . StatusOK ) . Body ( ) . Equal ( "ok" )
// test with equal to max body size limit
bsent := make ( [ ] byte , maxBodySize , maxBodySize )
e . POST ( "/" ) . WithBytes ( bsent ) . Expect ( ) . Status ( iris . StatusOK ) . Body ( ) . Length ( ) . Equal ( len ( bsent ) )
// test with larger body sent and wait for the custom response
largerBSent := make ( [ ] byte , maxBodySize + 1 , maxBodySize + 1 )
e . POST ( "/" ) . WithBytes ( largerBSent ) . Expect ( ) . Status ( iris . StatusBadRequest ) . Body ( ) . Equal ( "http: request body too large" )
}