1
0
mirror of https://github.com/kataras/iris.git synced 2026-01-25 12:55:57 +00:00

add the stale release

This commit is contained in:
Gerasimos (Makis) Maropoulos
2020-08-12 23:41:20 +03:00
parent 535fb6dc0d
commit 2a4ce876b6
793 changed files with 188 additions and 52415 deletions

View File

@@ -10,11 +10,11 @@ import (
"strings"
"time"
"github.com/kataras/iris/v12/context"
"github.com/kataras/iris/v12/core/errgroup"
"github.com/kataras/iris/v12/hero"
"github.com/kataras/iris/v12/macro"
macroHandler "github.com/kataras/iris/v12/macro/handler"
"github.com/kataras/iris/context"
"github.com/kataras/iris/core/errgroup"
"github.com/kataras/iris/hero"
"github.com/kataras/iris/macro"
macroHandler "github.com/kataras/iris/macro/handler"
)
// MethodNone is a Virtual method

View File

@@ -1,103 +0,0 @@
package router
import (
"bytes"
"math/rand"
"strings"
"testing"
"time"
"github.com/kataras/iris/v12/context"
)
//
// randStringBytesMaskImprSrc helps us to generate random paths for the test,
// the below piece of code is external, as an answer to a stackoverflow question.
//
// START.
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
const (
letterIdxBits = 6 // 6 bits to represent a letter index
letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits
)
var src = rand.NewSource(time.Now().UnixNano())
func randStringBytesMaskImprSrc(n int) string {
b := make([]byte, n)
// A src.Int63() generates 63 random bits, enough for letterIdxMax characters!
for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; {
if remain == 0 {
cache, remain = src.Int63(), letterIdxMax
}
if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
b[i] = letterBytes[idx]
i--
}
cache >>= letterIdxBits
remain--
}
return strings.ToLower(string(b))
}
// END.
func genPaths(routesLength, minCharLength, maxCharLength int) []string {
// b := new(strings.Builder)
b := new(bytes.Buffer)
paths := make([]string, routesLength)
pathStart := '/'
for i := 0; i < routesLength; i++ {
pathSegmentCharsLength := rand.Intn(maxCharLength-minCharLength) + minCharLength
b.WriteRune(pathStart)
b.WriteString(randStringBytesMaskImprSrc(pathSegmentCharsLength))
b.WriteString("/{name:string}/") // sugar.
b.WriteString(randStringBytesMaskImprSrc(pathSegmentCharsLength))
b.WriteString("/{age:int}/end")
paths[i] = b.String()
b.Reset()
}
return paths
}
// Build 1296(=144*9(the available http methods)) routes
// with up to 2*range(15-42)+ 2 named paths lowercase letters
// and 12 request handlers each.
//
// GOCACHE=off && go test -run=XXX -bench=BenchmarkAPIBuilder$ -benchtime=10s
func BenchmarkAPIBuilder(b *testing.B) {
rand.Seed(time.Now().Unix())
noOpHandler := func(ctx *context.Context) {}
handlersPerRoute := make(context.Handlers, 12)
for i := 0; i < cap(handlersPerRoute); i++ {
handlersPerRoute[i] = noOpHandler
}
routesLength := 144
// i.e /gzhyweumidvelqewrvoyqmzopvuxli/{name:string}/bibrkratnrrhvsjwsxygfwmqwhcstc/{age:int}/end
paths := genPaths(routesLength, 15, 42)
api := NewAPIBuilder()
requestHandler := NewDefaultHandler(nil, nil)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < routesLength; i++ {
api.Any(paths[i], handlersPerRoute...)
}
if err := requestHandler.Build(api); err != nil {
b.Fatal(err)
}
b.StopTimer()
b.Logf("%d routes have just builded\n", len(api.GetRoutes()))
}

View File

@@ -3,9 +3,9 @@ package router
import (
"net/http"
"github.com/kataras/iris/v12/context"
"github.com/kataras/iris/v12/hero"
"github.com/kataras/iris/v12/macro"
"github.com/kataras/iris/context"
"github.com/kataras/iris/hero"
"github.com/kataras/iris/macro"
)
// APIContainer is a wrapper of a common `Party` featured by Dependency Injection.

View File

@@ -19,7 +19,7 @@ import (
"sync"
"time"
"github.com/kataras/iris/v12/context"
"github.com/kataras/iris/context"
)
const indexName = "/index.html"

View File

@@ -7,10 +7,10 @@ import (
"strings"
"time"
"github.com/kataras/iris/v12/context"
"github.com/kataras/iris/v12/core/errgroup"
"github.com/kataras/iris/v12/core/netutil"
macroHandler "github.com/kataras/iris/v12/macro/handler"
"github.com/kataras/iris/context"
"github.com/kataras/iris/core/errgroup"
"github.com/kataras/iris/core/netutil"
macroHandler "github.com/kataras/iris/macro/handler"
"github.com/kataras/golog"
"github.com/kataras/pio"

View File

@@ -1,7 +1,7 @@
package router
import (
"github.com/kataras/iris/v12/context"
"github.com/kataras/iris/context"
)
// ExecutionRules gives control to the execution of the route handlers outside of the handlers themselves.

View File

@@ -1,91 +0,0 @@
package router_test
import (
"testing"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/context"
"github.com/kataras/iris/v12/core/router"
"github.com/kataras/iris/v12/httptest"
)
var (
finalExecutionRulesResponse = "1234"
testExecutionResponse = func(t *testing.T, app *iris.Application, path string) {
e := httptest.New(t, app)
e.GET(path).Expect().Status(httptest.StatusOK).Body().Equal(finalExecutionRulesResponse)
}
)
func writeStringHandler(text string, withNext bool) context.Handler {
return func(ctx *context.Context) {
ctx.WriteString(text)
if withNext {
ctx.Next()
}
}
}
func TestRouterExecutionRulesForceMain(t *testing.T) {
app := iris.New()
begin := app.Party("/")
begin.SetExecutionRules(router.ExecutionRules{Main: router.ExecutionOptions{Force: true}})
// no need of `ctx.Next()` all main handlers should be executed with the Main.Force:True rule.
begin.Get("/", writeStringHandler("12", false), writeStringHandler("3", false), writeStringHandler("4", false))
testExecutionResponse(t, app, "/")
}
func TestRouterExecutionRulesForceBegin(t *testing.T) {
app := iris.New()
begin := app.Party("/begin_force")
begin.SetExecutionRules(router.ExecutionRules{Begin: router.ExecutionOptions{Force: true}})
// should execute, begin rule is to force execute them without `ctx.Next()`.
begin.Use(writeStringHandler("1", false))
begin.Use(writeStringHandler("2", false))
// begin starts with begin and ends to the main handlers but not last, so this done should not be executed.
begin.Done(writeStringHandler("5", false))
begin.Get("/", writeStringHandler("3", false), writeStringHandler("4", false))
testExecutionResponse(t, app, "/begin_force")
}
func TestRouterExecutionRulesForceDone(t *testing.T) {
app := iris.New()
done := app.Party("/done_force")
done.SetExecutionRules(router.ExecutionRules{Done: router.ExecutionOptions{Force: true}})
// these done should be executed without `ctx.Next()`
done.Done(writeStringHandler("3", false), writeStringHandler("4", false))
// first with `ctx.Next()`, because Done.Force:True rule will alter the latest of the main handler(s) only.
done.Get("/", writeStringHandler("1", true), writeStringHandler("2", false))
// rules should be kept in children.
doneChild := done.Party("/child")
// even if only one, it's the latest, Done.Force:True rule should modify it.
doneChild.Get("/", writeStringHandler("12", false))
testExecutionResponse(t, app, "/done_force")
testExecutionResponse(t, app, "/done_force/child")
}
func TestRouterExecutionRulesShouldNotModifyTheCallersHandlerAndChildrenCanResetExecutionRules(t *testing.T) {
app := iris.New()
app.SetExecutionRules(router.ExecutionRules{Done: router.ExecutionOptions{Force: true}})
h := writeStringHandler("4", false)
app.Done(h)
app.Get("/", writeStringHandler("123", false))
// remember: the handler stored in var didn't had a `ctx.Next()`, modified its clone above with adding a `ctx.Next()`
// note the "clone" word, the original handler shouldn't be changed.
app.Party("/c").SetExecutionRules(router.ExecutionRules{}).Get("/", h, writeStringHandler("err caller modified!", false))
testExecutionResponse(t, app, "/")
e := httptest.New(t, app)
e.GET("/c").Expect().Status(httptest.StatusOK).Body().Equal("4") // the "should not" should not be written.
}

View File

@@ -4,7 +4,7 @@ import (
"mime"
"path/filepath"
"github.com/kataras/iris/v12/context"
"github.com/kataras/iris/context"
)
var types = map[string]string{

View File

@@ -3,9 +3,9 @@ package router
import (
"net/http"
"github.com/kataras/iris/v12/context"
"github.com/kataras/iris/v12/core/errgroup"
"github.com/kataras/iris/v12/macro"
"github.com/kataras/iris/context"
"github.com/kataras/iris/core/errgroup"
"github.com/kataras/iris/macro"
)
// Party is just a group joiner of routes which have the same prefix and share same middleware(s) also.

View File

@@ -6,10 +6,10 @@ import (
"strconv"
"strings"
"github.com/kataras/iris/v12/core/netutil"
"github.com/kataras/iris/v12/macro"
"github.com/kataras/iris/v12/macro/interpreter/ast"
"github.com/kataras/iris/v12/macro/interpreter/lexer"
"github.com/kataras/iris/core/netutil"
"github.com/kataras/iris/macro"
"github.com/kataras/iris/macro/interpreter/ast"
"github.com/kataras/iris/macro/interpreter/lexer"
)
// Param receives a parameter name prefixed with the ParamStart symbol.

View File

@@ -1,136 +0,0 @@
package router
import (
"testing"
)
func TestCleanPath(t *testing.T) {
tests := []struct {
path string
expected string
}{
{
"/",
"/",
},
{
"noslashPrefix",
"/noslashPrefix",
},
{
"slashSuffix/",
"/slashSuffix",
},
{
"noSlashPrefixAndslashSuffix/",
"/noSlashPrefixAndslashSuffix",
},
// don't do any clean up inside {},
// fixes #927.
{
"/total/{year:string regexp(\\d{4})}",
"/total/{year:string regexp(\\d{4})}",
},
{
"/total/{year:string regexp(\\d{4})}/more",
"/total/{year:string regexp(\\d{4})}/more",
},
{
"/total/{year:string regexp(\\d{4})}/more/{s:string regexp(\\d{7})}",
"/total/{year:string regexp(\\d{4})}/more/{s:string regexp(\\d{7})}",
},
{
"/single_no_params",
"/single_no_params",
},
{
"/single/{id:uint64}",
"/single/{id:uint64}",
},
}
for i, tt := range tests {
if expected, got := tt.expected, cleanPath(tt.path); expected != got {
t.Fatalf("[%d] - expected path '%s' but got '%s'", i, expected, got)
}
}
}
func TestSplitPath(t *testing.T) {
tests := []struct {
path string
expected []string
}{
{
"/v2/stores/{id:string format(uuid)} /v3",
[]string{"/v2/stores/{id:string format(uuid)}", "/v3"},
},
{
"/user/{id:uint64} /admin/{id:uint64}",
[]string{"/user/{id:uint64}", "/admin/{id:uint64}"},
},
{
"/users/{id:int} /admins/{id:int64}",
[]string{"/users/{id:int}", "/admins/{id:int64}"},
},
{
"/user /admin",
[]string{"/user", "/admin"},
},
{
"/single_no_params",
[]string{"/single_no_params"},
},
{
"/single/{id:int}",
[]string{"/single/{id:int}"},
},
}
equalSlice := func(s1 []string, s2 []string) bool {
if len(s1) != len(s2) {
return false
}
for i := range s1 {
if s2[i] != s1[i] {
return false
}
}
return true
}
for i, tt := range tests {
paths := splitPath(tt.path)
if expected, got := tt.expected, paths; !equalSlice(expected, got) {
t.Fatalf("[%d] - expected paths '%#v' but got '%#v'", i, expected, got)
}
}
}
func TestSplitSubdomainAndPath(t *testing.T) {
tests := []struct {
original string
subdomain string
path string
}{
{"admin./users/42", "admin.", "/users/42"},
{"static.", "static.", "/"},
{"static./" + WildcardFileParam(), "static.", "/" + WildcardFileParam()},
{"//api/users\\42", "", "/api/users/42"},
{"admin./users//42", "admin.", "/users/42"},
{"*./users/42/", "*.", "/users/42"},
}
for i, tt := range tests {
subdomain, path := splitSubdomainAndPath(tt.original)
if expected, got := tt.subdomain, subdomain; expected != got {
t.Fatalf("[%d] - expected subdomain '%s' but got '%s'", i, expected, got)
}
if expected, got := tt.path, path; expected != got {
t.Fatalf("[%d] - expected path '%s' but got '%s'", i, expected, got)
}
}
}

View File

@@ -8,9 +8,9 @@ import (
"strings"
"time"
"github.com/kataras/iris/v12/context"
"github.com/kataras/iris/v12/macro"
"github.com/kataras/iris/v12/macro/handler"
"github.com/kataras/iris/context"
"github.com/kataras/iris/macro"
"github.com/kataras/iris/macro/handler"
"github.com/kataras/pio"
)

View File

@@ -1,111 +0,0 @@
package router_test
import (
"reflect"
"testing"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/core/router"
"github.com/kataras/iris/v12/httptest"
)
func TestRegisterRule(t *testing.T) {
app := iris.New()
v1 := app.Party("/v1")
v1.SetRegisterRule(iris.RouteSkip)
getHandler := func(ctx iris.Context) {
ctx.Writef("[get] %s", ctx.Method())
}
anyHandler := func(ctx iris.Context) {
ctx.Writef("[any] %s", ctx.Method())
}
getRoute := v1.Get("/", getHandler)
v1.Any("/", anyHandler)
if route := v1.Get("/", getHandler); !reflect.DeepEqual(route, getRoute) {
t.Fatalf("expected route to be equal with the original get route")
}
// test RouteSkip.
e := httptest.New(t, app, httptest.LogLevel("error"))
testRegisterRule(e, "[get] GET")
// test RouteOverride (default behavior).
v1.SetRegisterRule(iris.RouteOverride)
v1.Any("/", anyHandler)
app.RefreshRouter()
testRegisterRule(e, "[any] GET")
// test RouteError.
v1.SetRegisterRule(iris.RouteError)
if route := v1.Get("/", getHandler); route != nil {
t.Fatalf("expected duplicated route, with RouteError rule, to be nil but got: %#+v", route)
}
if expected, got := 1, len(v1.GetReporter().Errors); expected != got {
t.Fatalf("expected api builder's errors length to be: %d but got: %d", expected, got)
}
}
func testRegisterRule(e *httptest.Expect, expectedGetBody string) {
for _, method := range router.AllMethods {
tt := e.Request(method, "/v1").Expect().Status(httptest.StatusOK).Body()
if method == iris.MethodGet {
tt.Equal(expectedGetBody)
} else {
tt.Equal("[any] " + method)
}
}
}
func TestRegisterRuleOverlap(t *testing.T) {
app := iris.New()
// TODO(@kataras) the overlapping does not work per-party yet,
// it just checks compares from the total app's routes (which is the best possible action to do
// because MVC applications can be separated into different parties too?).
usersRouter := app.Party("/users")
usersRouter.SetRegisterRule(iris.RouteOverlap)
// second handler will be executed, status will be reset-ed as well,
// stop without data written.
usersRouter.Get("/", func(ctx iris.Context) {
ctx.StopWithStatus(iris.StatusUnauthorized)
})
usersRouter.Get("/", func(ctx iris.Context) {
ctx.WriteString("data")
})
// first handler will be executed, no stop called.
usersRouter.Get("/p1", func(ctx iris.Context) {
ctx.StatusCode(iris.StatusUnauthorized)
})
usersRouter.Get("/p1", func(ctx iris.Context) {
ctx.WriteString("not written")
})
// first handler will be executed, stop but with data sent on default writer
// (body sent cannot be reset-ed here).
usersRouter.Get("/p2", func(ctx iris.Context) {
ctx.StopWithText(iris.StatusUnauthorized, "no access")
})
usersRouter.Get("/p2", func(ctx iris.Context) {
ctx.WriteString("not written")
})
// second will be executed, response can be reset-ed on recording.
usersRouter.Get("/p3", func(ctx iris.Context) {
ctx.Record()
ctx.StopWithText(iris.StatusUnauthorized, "no access")
})
usersRouter.Get("/p3", func(ctx iris.Context) {
ctx.WriteString("p3 data")
})
e := httptest.New(t, app)
e.GET("/users").Expect().Status(httptest.StatusOK).Body().Equal("data")
e.GET("/users/p1").Expect().Status(httptest.StatusUnauthorized).Body().Equal("Unauthorized")
e.GET("/users/p2").Expect().Status(httptest.StatusUnauthorized).Body().Equal("no access")
e.GET("/users/p3").Expect().Status(httptest.StatusOK).Body().Equal("p3 data")
}

View File

@@ -1,56 +0,0 @@
// white-box testing
package router
import (
"testing"
"github.com/kataras/iris/v12/macro"
)
func TestRouteStaticPath(t *testing.T) {
tests := []struct {
tmpl string
static string
}{
{
tmpl: "/files/{file:path}",
static: "/files",
},
{
tmpl: "/path",
static: "/path",
},
{
tmpl: "/path/segment",
static: "/path/segment",
},
{
tmpl: "/path/segment/{n:int}",
static: "/path/segment",
},
{
tmpl: "/path/{n:uint64}/{n:int}",
static: "/path",
},
{
tmpl: "/path/{n:uint64}/static",
static: "/path",
},
{
tmpl: "/{name}",
static: "/",
},
{
tmpl: "/",
static: "/",
},
}
for i, tt := range tests {
route := Route{tmpl: macro.Template{Src: tt.tmpl}}
if expected, got := tt.static, route.StaticPath(); expected != got {
t.Fatalf("[%d:%s] expected static path to be: '%s' but got: '%s'", i, tt.tmpl, expected, got)
}
}
}

View File

@@ -7,7 +7,7 @@ import (
"strings"
"sync"
"github.com/kataras/iris/v12/context"
"github.com/kataras/iris/context"
"github.com/schollz/closestmatch"
)

View File

@@ -1,287 +0,0 @@
// black-box testing
//
// see _examples/routing/main_test.go for the most common router tests that you may want to see,
// this is a test which makes sure that the APIBuilder's `UseGlobal`, `Use` and `Done` functions are
// working as expected.
package router_test
import (
"fmt"
"testing"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/httptest"
)
// test registering of below handlers
// with a different order but the route's final
// response should be the same at all cases.
var (
writeHandler = func(s string) iris.Handler {
return func(ctx iris.Context) {
ctx.WriteString(s)
ctx.Next()
}
}
mainResponse = "main"
mainHandler = writeHandler(mainResponse)
firstUseResponse = "use1"
firstUseHandler = writeHandler(firstUseResponse)
secondUseResponse = "use2"
secondUseHandler = writeHandler(secondUseResponse)
firstUseRouterResponse = "userouter1"
// Use inline handler, no the `writeHandler`,
// because it will be overriden by `secondUseRouterHandler` otherwise,
// look `UseRouter:context.UpsertHandlers` for more.
firstUseRouterHandler = func(ctx iris.Context) {
ctx.WriteString(firstUseRouterResponse)
ctx.Next()
}
secondUseRouterResponse = "userouter2"
secondUseRouterHandler = writeHandler(secondUseRouterResponse)
firstUseGlobalResponse = "useglobal1"
firstUseGlobalHandler = writeHandler(firstUseGlobalResponse)
secondUseGlobalResponse = "useglobal2"
secondUseGlobalHandler = writeHandler(secondUseGlobalResponse)
firstDoneResponse = "done1"
firstDoneHandler = writeHandler(firstDoneResponse)
secondDoneResponse = "done2"
secondDoneHandler = func(ctx iris.Context) {
ctx.WriteString(secondDoneResponse)
}
finalResponse = firstUseRouterResponse + secondUseRouterResponse + firstUseGlobalResponse + secondUseGlobalResponse +
firstUseResponse + secondUseResponse + mainResponse + firstDoneResponse + secondDoneResponse
testResponse = func(t *testing.T, app *iris.Application, path string) {
t.Helper()
e := httptest.New(t, app)
e.GET(path).Expect().Status(httptest.StatusOK).Body().Equal(finalResponse)
}
)
func TestMiddlewareByRouteDef(t *testing.T) {
app := iris.New()
app.UseRouter(firstUseRouterHandler)
app.UseRouter(secondUseRouterHandler)
app.Get("/mypath", firstUseGlobalHandler, secondUseGlobalHandler, firstUseHandler, secondUseHandler,
mainHandler, firstDoneHandler, secondDoneHandler)
testResponse(t, app, "/mypath")
}
func TestMiddlewareByUseAndDoneDef(t *testing.T) {
app := iris.New()
app.UseRouter(firstUseRouterHandler, secondUseRouterHandler)
app.Use(firstUseGlobalHandler, secondUseGlobalHandler, firstUseHandler, secondUseHandler)
app.Done(firstDoneHandler, secondDoneHandler)
app.Get("/mypath", mainHandler)
testResponse(t, app, "/mypath")
}
func TestMiddlewareByUseUseGlobalAndDoneDef(t *testing.T) {
app := iris.New()
app.Use(firstUseHandler, secondUseHandler)
// if failed then UseGlobal didnt' registered these handlers even before the
// existing middleware.
app.UseGlobal(firstUseGlobalHandler, secondUseGlobalHandler)
app.Done(firstDoneHandler, secondDoneHandler)
app.UseRouter(firstUseRouterHandler, secondUseRouterHandler)
app.Get("/mypath", mainHandler)
testResponse(t, app, "/mypath")
}
func TestMiddlewareByUseDoneAndUseGlobalDef(t *testing.T) {
app := iris.New()
app.UseRouter(firstUseRouterHandler, secondUseRouterHandler)
app.Use(firstUseHandler, secondUseHandler)
app.Done(firstDoneHandler, secondDoneHandler)
app.Get("/mypath", mainHandler)
// if failed then UseGlobal was unable to
// prepend these handlers to the route was registered before
// OR
// when order failed because these should be executed in order, first the firstUseGlobalHandler,
// because they are the same type (global begin handlers)
app.UseGlobal(firstUseGlobalHandler)
app.UseGlobal(secondUseGlobalHandler)
testResponse(t, app, "/mypath")
}
func TestMiddlewareByUseGlobalUseAndDoneGlobalDef(t *testing.T) {
app := iris.New()
app.UseRouter(firstUseRouterHandler)
app.UseRouter(secondUseRouterHandler)
app.UseGlobal(firstUseGlobalHandler)
app.UseGlobal(secondUseGlobalHandler)
app.Use(firstUseHandler, secondUseHandler)
app.Get("/mypath", mainHandler)
app.DoneGlobal(firstDoneHandler, secondDoneHandler)
testResponse(t, app, "/mypath")
}
func TestMiddlewareByDoneUseAndUseGlobalDef(t *testing.T) {
app := iris.New()
app.UseRouter(firstUseRouterHandler, secondUseRouterHandler)
app.Done(firstDoneHandler, secondDoneHandler)
app.Use(firstUseHandler, secondUseHandler)
app.Get("/mypath", mainHandler)
app.UseGlobal(firstUseGlobalHandler)
app.UseGlobal(secondUseGlobalHandler)
testResponse(t, app, "/mypath")
}
func TestUseRouterStopExecution(t *testing.T) {
app := iris.New()
app.UseRouter(func(ctx iris.Context) {
ctx.WriteString("stop")
// no ctx.Next, so the router has not even the chance to work.
})
app.Get("/", writeHandler("index"))
e := httptest.New(t, app)
e.GET("/").Expect().Status(iris.StatusOK).Body().Equal("stop")
app = iris.New()
app.OnErrorCode(iris.StatusForbidden, func(ctx iris.Context) {
ctx.Writef("err: %v", ctx.GetErr())
})
app.UseRouter(func(ctx iris.Context) {
ctx.StopWithPlainError(iris.StatusForbidden, fmt.Errorf("custom error"))
// stopped but not data written yet, the error code handler
// should be responsible of it (use StopWithError to write and close).
})
app.Get("/", writeHandler("index"))
e = httptest.New(t, app)
e.GET("/").Expect().Status(iris.StatusForbidden).Body().Equal("err: custom error")
}
func TestUseRouterParentDisallow(t *testing.T) {
const expectedResponse = "no_userouter_allowed"
app := iris.New()
app.UseRouter(func(ctx iris.Context) {
ctx.WriteString("always")
ctx.Next()
})
app.Get("/index", func(ctx iris.Context) {
ctx.WriteString(expectedResponse)
})
app.SetPartyMatcher(func(p iris.Party, ctx iris.Context) bool {
// modifies the PartyMatcher to not match any UseRouter,
// tests should receive the handlers response alone.
return false
})
app.PartyFunc("/", func(p iris.Party) { // it's the same instance of app.
p.UseRouter(func(ctx iris.Context) {
ctx.WriteString("_2")
ctx.Next()
})
p.Get("/", func(ctx iris.Context) {
ctx.WriteString(expectedResponse)
})
})
app.PartyFunc("/user", func(p iris.Party) {
p.UseRouter(func(ctx iris.Context) {
ctx.WriteString("_3")
ctx.Next()
})
p.Get("/", func(ctx iris.Context) {
ctx.WriteString(expectedResponse)
})
})
e := httptest.New(t, app)
e.GET("/index").Expect().Status(iris.StatusOK).Body().Equal(expectedResponse)
e.GET("/").Expect().Status(iris.StatusOK).Body().Equal(expectedResponse)
e.GET("/user").Expect().Status(iris.StatusOK).Body().Equal(expectedResponse)
}
func TestUseRouterSubdomains(t *testing.T) {
app := iris.New()
app.UseRouter(func(ctx iris.Context) {
if ctx.Subdomain() == "old" {
ctx.Next() // call the router, do not write.
return
}
// if we write here, it will always give 200 OK,
// even on not registered routes, that's the point at the end,
// full control here when we need it.
ctx.WriteString("always_")
ctx.Next()
})
adminAPI := app.Subdomain("admin")
adminAPI.UseRouter(func(ctx iris.Context) {
ctx.WriteString("admin always_")
ctx.Next()
})
adminAPI.Get("/", func(ctx iris.Context) {
ctx.WriteString("admin")
})
adminControlAPI := adminAPI.Subdomain("control")
adminControlAPI.UseRouter(func(ctx iris.Context) {
ctx.WriteString("control admin always_")
ctx.Next()
})
adminControlAPI.Get("/", func(ctx iris.Context) {
ctx.WriteString("control admin")
})
oldAPI := app.Subdomain("old")
oldAPI.Get("/", func(ctx iris.Context) {
ctx.WriteString("chat")
})
e := httptest.New(t, app, httptest.URL("http://example.com"))
e.GET("/notfound").Expect().Status(iris.StatusOK).Body().Equal("always_")
e.GET("/").WithURL("http://admin.example.com").Expect().Status(iris.StatusOK).Body().
Equal("always_admin always_admin")
e.GET("/").WithURL("http://control.admin.example.com").Expect().Status(iris.StatusOK).Body().
Equal("always_admin always_control admin always_control admin")
// It has a route, and use router just proceeds to the router.
e.GET("/").WithURL("http://old.example.com").Expect().Status(iris.StatusOK).Body().
Equal("chat")
// this is not a registered path, should fire 404, the UseRouter does not write
// anything to the response writer, so the router has control over it.
e.GET("/notfound").WithURL("http://old.example.com").Expect().Status(iris.StatusNotFound).Body().
Equal("Not Found")
}

View File

@@ -4,8 +4,8 @@ import (
"net/http"
"strings"
"github.com/kataras/iris/v12/context"
"github.com/kataras/iris/v12/core/netutil"
"github.com/kataras/iris/context"
"github.com/kataras/iris/core/netutil"
)
type subdomainRedirectWrapper struct {

View File

@@ -1,74 +0,0 @@
package router_test
import (
"net/http"
"strings"
"testing"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/context"
"github.com/kataras/iris/v12/httptest"
)
func TestRouteExists(t *testing.T) {
// build the api
app := iris.New()
emptyHandler := func(*context.Context) {}
// setup the tested routes
app.Handle("GET", "/route-exists", emptyHandler)
app.Handle("POST", "/route-with-param/{param}", emptyHandler)
// check RouteExists
app.Handle("GET", "/route-test", func(ctx *context.Context) {
if ctx.RouteExists("GET", "/route-not-exists") {
t.Error("Route with path should not exists")
}
if ctx.RouteExists("POST", "/route-exists") {
t.Error("Route with method should not exists")
}
if !ctx.RouteExists("GET", "/route-exists") {
t.Error("Route 1 should exists")
}
if !ctx.RouteExists("POST", "/route-with-param/a-param") {
t.Error("Route 2 should exists")
}
})
// run the tests
httptest.New(t, app, httptest.Debug(false)).Request("GET", "/route-test").Expect().Status(iris.StatusOK)
}
func TestLowercaseRouting(t *testing.T) {
app := iris.New()
app.WrapRouter(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
// test bottom to begin wrapper, the last ones should execute first.
// The ones that are registered at `Build` state, after this `WrapRouter` call.
// So path should be already lowecased.
if expected, got := strings.ToLower(r.URL.Path), r.URL.Path; expected != got {
t.Fatalf("expected path: %s but got: %s", expected, got)
}
next(w, r)
})
h := func(ctx iris.Context) { ctx.WriteString(ctx.Path()) }
// Register routes.
tests := []string{"/", "/lowercase", "/UPPERCASE", "/Title", "/m1xEd2"}
for _, tt := range tests {
app.Get(tt, h)
}
app.Configure(iris.WithLowercaseRouting)
// Test routes.
e := httptest.New(t, app)
for _, tt := range tests {
s := strings.ToLower(tt)
e.GET(tt).Expect().Status(httptest.StatusOK).Body().Equal(s)
e.GET(s).Expect().Status(httptest.StatusOK).Body().Equal(s)
e.GET(strings.ToUpper(tt)).Expect().Status(httptest.StatusOK).Body().Equal(s)
}
}

View File

@@ -1,198 +0,0 @@
// black-box testing
//
// see _examples/routing/main_test.go for the most common router tests that you may want to see,
// this is a test for the new feature that I just coded: wildcard "/{p:path}" on root without conflicts
package router_test
import (
"net/http"
"testing"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/context"
"github.com/kataras/iris/v12/httptest"
)
const (
same_as_request_path = "same"
from_status_code = "from"
staticPathPrefixBody = "from the static path: "
prefix_static_path_following_by_request_path = "prefix_same"
)
type testRouteRequest struct {
method string
subdomain string
path string
expectedStatusCode int
expectedBody string
}
type testRoute struct {
method string
path string
handler context.Handler
requests []testRouteRequest
}
var h = func(ctx *context.Context) {
ctx.WriteString(ctx.Path())
}
var h2 = func(ctx *context.Context) {
ctx.StatusCode(iris.StatusForbidden) // ! 200 but send the body as expected,
// we need that kind of behavior to determinate which handler is executed for routes that
// both having wildcard path but first one is registered on root level.
ctx.WriteString(ctx.Path())
}
func h3(ctx *context.Context) {
ctx.Writef(staticPathPrefixBody + ctx.Path())
}
func TestRouterWildcardDifferentPrefixPath(t *testing.T) {
tt := []testRoute{
{"GET", "/s/{p:path}", h, []testRouteRequest{
{"GET", "", "/s/that/is/wildcard", iris.StatusOK, same_as_request_path},
{"GET", "", "/s/ok", iris.StatusOK, same_as_request_path},
}},
{"GET", "/som/{p:path}", h, []testRouteRequest{
{"GET", "", "/som/that/is/wildcard", iris.StatusOK, same_as_request_path},
{"GET", "", "/som/ok", iris.StatusOK, same_as_request_path},
}},
{"GET", "/some/{p:path}", h, []testRouteRequest{
{"GET", "", "/some/that/is/wildcard", iris.StatusOK, same_as_request_path},
{"GET", "", "/some1/that/is/wildcard", iris.StatusNotFound, from_status_code},
}},
}
testTheRoutes(t, tt, false)
}
func TestRouterWildcardAndStatic(t *testing.T) {
tt := []testRoute{
{"GET", "/some/{p:path}", h2, []testRouteRequest{
{"GET", "", "/some/that/is/wildcard", iris.StatusForbidden, same_as_request_path},
{"GET", "", "/some/did", iris.StatusForbidden, same_as_request_path},
{"GET", "", "/some1/that/is/wildcard", iris.StatusNotFound, from_status_code},
}},
{"GET", "/some/static", h, []testRouteRequest{
{"GET", "", "/some/static", iris.StatusOK, same_as_request_path},
}},
{"GET", "/s/{p:path}", h2, []testRouteRequest{
{"GET", "", "/s/that/is/wildcard", iris.StatusForbidden, same_as_request_path},
{"GET", "", "/s/did", iris.StatusForbidden, same_as_request_path},
{"GET", "", "/s1/that/is/wildcard", iris.StatusNotFound, from_status_code},
}},
{"GET", "/s/static", h, []testRouteRequest{
{"GET", "", "/s/static", iris.StatusOK, same_as_request_path},
}},
}
testTheRoutes(t, tt, false)
}
func TestRouterWildcardRootMany(t *testing.T) {
tt := []testRoute{
// all routes will be handlded by "h" because we added wildcard to root,
// this feature is very important and can remove noumerous of previous hacks on our apps.
{"GET", "/{p:path}", h, []testRouteRequest{
{"GET", "", "/this/is/wildcard/on/root", iris.StatusOK, same_as_request_path},
}}, // mormally, order matters, root should be registered at last
// but we change the front level order algorithm to put last these automatically
// see handler.go
{"GET", "/some/{p:path}", h2, []testRouteRequest{
{"GET", "", "/some/that/is/wildcard", iris.StatusForbidden, same_as_request_path},
{"GET", "", "/some/did", iris.StatusForbidden, same_as_request_path},
}},
{"GET", "/some/static", h, []testRouteRequest{
{"GET", "", "/some/static", iris.StatusOK, same_as_request_path},
}},
{"GET", "/some1", h, []testRouteRequest{
{"GET", "", "/some1", iris.StatusOK, same_as_request_path},
// this will show up because of the first wildcard, as we wanted to do.
{"GET", "", "/some1/that/is/wildcard", iris.StatusOK, same_as_request_path},
}},
}
testTheRoutes(t, tt, false)
}
func TestRouterWildcardRootManyAndRootStatic(t *testing.T) {
tt := []testRoute{
// routes that may return 404 will be handled by the below route ("h" handler) because we added wildcard to root,
// this feature is very important and can remove noumerous of previous hacks on our apps.
//
// Static paths and parameters have priority over wildcard, all three types can be registered in the same path prefix.
//
// Remember, all of those routes are registered don't be tricked by the visual appearance of the below test blocks.
{"GET", "/{p:path}", h, []testRouteRequest{
{"GET", "", "/other2almost/some", iris.StatusOK, same_as_request_path},
}},
{"GET", "/static/{p:path}", h, []testRouteRequest{
{"GET", "", "/static", iris.StatusOK, same_as_request_path}, // HERE<- IF NOT FOUND THEN BACKWARDS TO WILDCARD IF THERE IS ONE, HMM.
{"GET", "", "/static/something/here", iris.StatusOK, same_as_request_path},
}},
{"GET", "/", h, []testRouteRequest{
{"GET", "", "/", iris.StatusOK, same_as_request_path},
}},
{"GET", "/other/{paramother:path}", h2, []testRouteRequest{
// OK and not h2 because of the root wildcard.
{"GET", "", "/other", iris.StatusOK, same_as_request_path},
{"GET", "", "/other/wildcard", iris.StatusForbidden, same_as_request_path},
{"GET", "", "/other/wildcard/here", iris.StatusForbidden, same_as_request_path},
}},
{"GET", "/other2/{paramothersecond:path}", h2, []testRouteRequest{
{"GET", "", "/other2/wildcard", iris.StatusForbidden, same_as_request_path},
{"GET", "", "/other2/more/than/one/path/parts", iris.StatusForbidden, same_as_request_path},
}},
{"GET", "/other2/static", h3, []testRouteRequest{
{"GET", "", "/other2/static", iris.StatusOK, prefix_static_path_following_by_request_path},
// h2(Forbiddenn) instead of h3 OK because it will be handled by the /other2/{paramothersecond:path}'s handler which gives 403.
{"GET", "", "/other2/staticed", iris.StatusForbidden, same_as_request_path},
}},
}
testTheRoutes(t, tt, false)
}
func testTheRoutes(t *testing.T, tests []testRoute, debug bool) {
// build the api
app := iris.New()
for _, tt := range tests {
app.Handle(tt.method, tt.path, tt.handler)
}
// setup the test suite
e := httptest.New(t, app, httptest.Debug(debug))
// run the tests
for _, tt := range tests {
for _, req := range tt.requests {
// t.Logf("req: %s:%s\n", tt.method, tt.path)
method := req.method
if method == "" {
method = tt.method
}
ex := e.Request(method, req.path)
if req.subdomain != "" {
ex.WithURL("http://" + req.subdomain + ".localhost:8080")
}
expectedBody := req.expectedBody
if req.expectedBody == same_as_request_path {
expectedBody = req.path
}
if req.expectedBody == from_status_code {
expectedBody = http.StatusText(req.expectedStatusCode)
}
if req.expectedBody == prefix_static_path_following_by_request_path {
expectedBody = staticPathPrefixBody + req.path
}
ex.Expect().Status(req.expectedStatusCode).Body().Equal(expectedBody)
}
}
}

View File

@@ -1,51 +0,0 @@
package router
import (
"bytes"
"net/http"
"net/http/httptest"
"testing"
)
func TestMakeWrapperFunc(t *testing.T) {
var (
firstBody = []byte("1")
secondBody = []byte("2")
mainBody = []byte("3")
expectedBody = append(firstBody, append(secondBody, mainBody...)...)
)
pre := func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
w.Header().Set("X-Custom", "data")
next(w, r)
}
first := func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
w.Write(firstBody)
next(w, r)
}
second := func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
w.Write(secondBody)
next(w, r)
}
mainHandler := func(w http.ResponseWriter, r *http.Request) {
w.Write(mainBody)
}
wrapper := makeWrapperFunc(second, first)
wrapper = makeWrapperFunc(wrapper, pre)
w := httptest.NewRecorder()
r := httptest.NewRequest("GET", "https://iris-go.com", nil)
wrapper(w, r, mainHandler)
if got := w.Body.Bytes(); !bytes.Equal(expectedBody, got) {
t.Fatalf("expected boy: %s but got: %s", string(expectedBody), string(got))
}
if expected, got := "data", w.Header().Get("X-Custom"); expected != got {
t.Fatalf("expected x-custom header: %s but got: %s", expected, got)
}
}

View File

@@ -1,160 +0,0 @@
// black-box testing
package router_test
import (
"bytes"
"net/http"
"testing"
"github.com/kataras/iris/v12"
"github.com/kataras/iris/v12/context"
"github.com/kataras/iris/v12/httptest"
)
var defaultErrHandler = func(ctx *context.Context) {
text := http.StatusText(ctx.GetStatusCode())
ctx.WriteString(text)
}
func TestOnAnyErrorCode(t *testing.T) {
app := iris.New()
app.Configure(iris.WithFireMethodNotAllowed)
buff := &bytes.Buffer{}
expectedPrintBeforeExecuteErr := "printed before error"
// with a middleware
app.OnAnyErrorCode(func(ctx *context.Context) {
buff.WriteString(expectedPrintBeforeExecuteErr)
ctx.Next()
}, defaultErrHandler)
expectedFoundResponse := "found"
app.Get("/found", func(ctx *context.Context) {
ctx.WriteString(expectedFoundResponse)
})
expected407 := "this should be sent, we manage the response response by ourselves"
app.Get("/407", func(ctx *context.Context) {
ctx.Record()
ctx.WriteString(expected407)
ctx.StatusCode(iris.StatusProxyAuthRequired)
})
e := httptest.New(t, app)
e.GET("/found").Expect().Status(iris.StatusOK).
Body().Equal(expectedFoundResponse)
e.GET("/notfound").Expect().Status(iris.StatusNotFound).
Body().Equal(http.StatusText(iris.StatusNotFound))
checkAndClearBuf(t, buff, expectedPrintBeforeExecuteErr)
e.POST("/found").Expect().Status(iris.StatusMethodNotAllowed).
Body().Equal(http.StatusText(iris.StatusMethodNotAllowed))
checkAndClearBuf(t, buff, expectedPrintBeforeExecuteErr)
e.GET("/407").Expect().Status(iris.StatusProxyAuthRequired).
Body().Equal(expected407)
// Test Configuration.ResetOnFireErrorCode.
app2 := iris.New()
app2.Configure(iris.WithResetOnFireErrorCode)
app2.OnAnyErrorCode(func(ctx *context.Context) {
buff.WriteString(expectedPrintBeforeExecuteErr)
ctx.Next()
}, defaultErrHandler)
app2.Get("/406", func(ctx *context.Context) {
ctx.Record()
ctx.WriteString("this should not be sent, only status text will be sent")
ctx.WriteString("the handler can handle 'rollback' of the text when error code fired because of the recorder")
ctx.StatusCode(iris.StatusNotAcceptable)
})
httptest.New(t, app2).GET("/406").Expect().Status(iris.StatusNotAcceptable).
Body().Equal(http.StatusText(iris.StatusNotAcceptable))
checkAndClearBuf(t, buff, expectedPrintBeforeExecuteErr)
}
func checkAndClearBuf(t *testing.T, buff *bytes.Buffer, expected string) {
t.Helper()
if got := buff.String(); got != expected {
t.Fatalf("expected middleware to run before the error handler, expected: '%s' but got: '%s'", expected, got)
}
buff.Reset()
}
func TestPartyOnErrorCode(t *testing.T) {
app := iris.New()
app.Configure(iris.WithFireMethodNotAllowed)
globalNotFoundResponse := "custom not found"
app.OnErrorCode(iris.StatusNotFound, func(ctx iris.Context) {
ctx.WriteString(globalNotFoundResponse)
})
globalMethodNotAllowedResponse := "global: method not allowed"
app.OnErrorCode(iris.StatusMethodNotAllowed, func(ctx iris.Context) {
ctx.WriteString(globalMethodNotAllowedResponse)
})
app.Get("/path", h)
usersResponse := "users: method allowed"
users := app.Party("/users")
users.OnErrorCode(iris.StatusMethodNotAllowed, func(ctx iris.Context) {
ctx.WriteString(usersResponse)
})
users.Get("/", h)
write400 := func(ctx iris.Context) { ctx.StatusCode(iris.StatusBadRequest) }
// test setting the error code from a handler.
users.Get("/badrequest", write400)
usersuserResponse := "users:user: method allowed"
user := users.Party("/{id:int}")
user.OnErrorCode(iris.StatusMethodNotAllowed, func(ctx iris.Context) {
ctx.WriteString(usersuserResponse)
})
usersuserNotFoundResponse := "users:user: not found"
user.OnErrorCode(iris.StatusNotFound, func(ctx iris.Context) {
ctx.WriteString(usersuserNotFoundResponse)
})
user.Get("/", h)
user.Get("/ab/badrequest", write400)
friends := users.Party("/friends")
friends.Get("/{id:int}", h)
e := httptest.New(t, app)
e.GET("/notfound").Expect().Status(iris.StatusNotFound).Body().Equal(globalNotFoundResponse)
e.POST("/path").Expect().Status(iris.StatusMethodNotAllowed).Body().Equal(globalMethodNotAllowedResponse)
e.GET("/path").Expect().Status(iris.StatusOK).Body().Equal("/path")
e.POST("/users").Expect().Status(iris.StatusMethodNotAllowed).
Body().Equal(usersResponse)
e.POST("/users/42").Expect().Status(iris.StatusMethodNotAllowed).
Body().Equal(usersuserResponse)
e.GET("/users/42").Expect().Status(iris.StatusOK).
Body().Equal("/users/42")
e.GET("/users/ab").Expect().Status(iris.StatusNotFound).Body().Equal(usersuserNotFoundResponse)
// inherit the parent.
e.GET("/users/42/friends/dsa").Expect().Status(iris.StatusNotFound).Body().Equal(usersuserNotFoundResponse)
// if not registered to the party, then the root is taking action.
e.GET("/users/42/ab/badrequest").Expect().Status(iris.StatusBadRequest).Body().Equal(http.StatusText(iris.StatusBadRequest))
// if not registered to the party, and not in root, then just write the status text (fallback behavior)
e.GET("/users/badrequest").Expect().Status(iris.StatusBadRequest).
Body().Equal(http.StatusText(iris.StatusBadRequest))
}

View File

@@ -3,7 +3,7 @@ package router
import (
"strings"
"github.com/kataras/iris/v12/context"
"github.com/kataras/iris/context"
)
const (