mirror of
https://github.com/kataras/iris.git
synced 2026-01-24 04:15:56 +00:00
add the stale release
This commit is contained in:
@@ -1,173 +0,0 @@
|
||||
package errgroup
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestErrorError(t *testing.T) {
|
||||
testErr := errors.New("error")
|
||||
err := &Error{Err: testErr}
|
||||
if expected, got := testErr.Error(), err.Error(); expected != got {
|
||||
t.Fatalf("expected %s but got %s", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrorUnwrap(t *testing.T) {
|
||||
wrapped := errors.New("unwrap")
|
||||
|
||||
err := &Error{Err: fmt.Errorf("this wraps:%w", wrapped)}
|
||||
if expected, got := wrapped, errors.Unwrap(err); expected != got {
|
||||
t.Fatalf("expected %#+v but got %#+v", expected, got)
|
||||
}
|
||||
|
||||
}
|
||||
func TestErrorIs(t *testing.T) {
|
||||
testErr := errors.New("is")
|
||||
err := &Error{Err: fmt.Errorf("this is: %w", testErr)}
|
||||
if expected, got := true, errors.Is(err, testErr); expected != got {
|
||||
t.Fatalf("expected %v but got %v", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrorAs(t *testing.T) {
|
||||
testErr := errors.New("as")
|
||||
err := &Error{Err: testErr}
|
||||
if expected, got := true, errors.As(err, &testErr); expected != got {
|
||||
t.Fatalf("[testErr as err] expected %v but got %v", expected, got)
|
||||
}
|
||||
if expected, got := true, errors.As(testErr, &err); expected != got {
|
||||
t.Fatalf("[err as testErr] expected %v but got %v", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGroupError(t *testing.T) {
|
||||
g := New(0)
|
||||
tests := []string{"error 1", "error 2", "error 3"}
|
||||
for _, tt := range tests {
|
||||
g.Add(errors.New(tt))
|
||||
}
|
||||
|
||||
if expected, got := strings.Join(tests, "\n"), g.Error(); expected != got {
|
||||
t.Fatalf("expected '%s' but got '%s'", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGroup(t *testing.T) {
|
||||
const (
|
||||
apiErrorsType = iota + 1
|
||||
childAPIErrorsType
|
||||
childAPIErrors2Type = "string type 1"
|
||||
childAPIErrors2Type1 = "string type 2"
|
||||
|
||||
apiErrorsText = "apiErrors error 1"
|
||||
childAPIErrorsText = "apiErrors:child error 1"
|
||||
childAPIErrors2Text = "apiErrors:child2 error 1"
|
||||
childAPIErrors2Text1 = "apiErrors:child2_1 error 1"
|
||||
)
|
||||
|
||||
g := New(nil)
|
||||
apiErrorsGroup := g.Group(apiErrorsType)
|
||||
apiErrorsGroup.Errf(apiErrorsText)
|
||||
|
||||
childAPIErrorsGroup := apiErrorsGroup.Group(childAPIErrorsType)
|
||||
childAPIErrorsGroup.Addf(childAPIErrorsText)
|
||||
childAPIErrorsGroup2 := apiErrorsGroup.Group(childAPIErrors2Type)
|
||||
childAPIErrorsGroup2.Addf(childAPIErrors2Text)
|
||||
childAPIErrorsGroup2Group1 := childAPIErrorsGroup2.Group(childAPIErrors2Type1)
|
||||
childAPIErrorsGroup2Group1.Addf(childAPIErrors2Text1)
|
||||
|
||||
if apiErrorsGroup.Type != apiErrorsType {
|
||||
t.Fatal("invalid type")
|
||||
}
|
||||
|
||||
if childAPIErrorsGroup.Type != childAPIErrorsType {
|
||||
t.Fatal("invalid type")
|
||||
}
|
||||
|
||||
if childAPIErrorsGroup2.Type != childAPIErrors2Type {
|
||||
t.Fatal("invalid type")
|
||||
}
|
||||
|
||||
if childAPIErrorsGroup2Group1.Type != childAPIErrors2Type1 {
|
||||
t.Fatal("invalid type")
|
||||
}
|
||||
|
||||
if expected, got := 2, len(apiErrorsGroup.children); expected != got {
|
||||
t.Fatalf("expected %d but got %d", expected, got)
|
||||
}
|
||||
|
||||
if expected, got := 0, len(childAPIErrorsGroup.children); expected != got {
|
||||
t.Fatalf("expected %d but got %d", expected, got)
|
||||
}
|
||||
|
||||
if expected, got := 1, len(childAPIErrorsGroup2.children); expected != got {
|
||||
t.Fatalf("expected %d but got %d", expected, got)
|
||||
}
|
||||
|
||||
if expected, got := 0, len(childAPIErrorsGroup2Group1.children); expected != got {
|
||||
t.Fatalf("expected %d but got %d", expected, got)
|
||||
}
|
||||
|
||||
if expected, got := 1, apiErrorsGroup.index; expected != got {
|
||||
t.Fatalf("expected index %d but got %d", expected, got)
|
||||
}
|
||||
|
||||
if expected, got := 2, childAPIErrorsGroup.index; expected != got {
|
||||
t.Fatalf("expected index %d but got %d", expected, got)
|
||||
}
|
||||
|
||||
if expected, got := 3, childAPIErrorsGroup2.index; expected != got {
|
||||
t.Fatalf("expected index %d but got %d", expected, got)
|
||||
}
|
||||
|
||||
if expected, got := 4, childAPIErrorsGroup2Group1.index; expected != got {
|
||||
t.Fatalf("expected index %d but got %d", expected, got)
|
||||
}
|
||||
|
||||
t.Run("Error", func(t *testing.T) {
|
||||
if expected, got :=
|
||||
strings.Join([]string{apiErrorsText, childAPIErrorsText, childAPIErrors2Text, childAPIErrors2Text1}, delim), g.Error(); expected != got {
|
||||
t.Fatalf("expected '%s' but got '%s'", expected, got)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Walk", func(t *testing.T) {
|
||||
expectedEntries := 4
|
||||
_ = Walk(g, func(typ interface{}, err error) {
|
||||
g.IncludeChildren = false
|
||||
childAPIErrorsGroup.IncludeChildren = false
|
||||
childAPIErrorsGroup2.IncludeChildren = false
|
||||
childAPIErrorsGroup2Group1.IncludeChildren = false
|
||||
|
||||
expectedEntries--
|
||||
var expected string
|
||||
|
||||
switch typ {
|
||||
case apiErrorsType:
|
||||
expected = apiErrorsText
|
||||
case childAPIErrorsType:
|
||||
expected = childAPIErrorsText
|
||||
case childAPIErrors2Type:
|
||||
expected = childAPIErrors2Text
|
||||
case childAPIErrors2Type1:
|
||||
expected = childAPIErrors2Text1
|
||||
}
|
||||
|
||||
if got := err.Error(); expected != got {
|
||||
t.Fatalf("[%v] expected '%s' but got '%s'", typ, expected, got)
|
||||
}
|
||||
})
|
||||
|
||||
if expectedEntries != 0 {
|
||||
t.Fatalf("not valid number of errors [...%d]", expectedEntries)
|
||||
}
|
||||
|
||||
g.IncludeChildren = true
|
||||
childAPIErrorsGroup.IncludeChildren = true
|
||||
childAPIErrorsGroup2.IncludeChildren = true
|
||||
childAPIErrorsGroup2Group1.IncludeChildren = true
|
||||
})
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/kataras/iris/v12/context"
|
||||
"github.com/kataras/iris/context"
|
||||
)
|
||||
|
||||
// FromStd converts native http.Handler & http.HandlerFunc to context.Handler.
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
// black-box testing
|
||||
package handlerconv_test
|
||||
|
||||
import (
|
||||
stdContext "context"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/kataras/iris/v12"
|
||||
"github.com/kataras/iris/v12/context"
|
||||
"github.com/kataras/iris/v12/core/handlerconv"
|
||||
"github.com/kataras/iris/v12/httptest"
|
||||
)
|
||||
|
||||
func TestFromStd(t *testing.T) {
|
||||
expected := "ok"
|
||||
std := func(w http.ResponseWriter, r *http.Request) {
|
||||
_, err := w.Write([]byte(expected))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
h := handlerconv.FromStd(http.HandlerFunc(std))
|
||||
|
||||
hFunc := handlerconv.FromStd(std)
|
||||
|
||||
app := iris.New()
|
||||
app.Get("/handler", h)
|
||||
app.Get("/func", hFunc)
|
||||
|
||||
e := httptest.New(t, app)
|
||||
|
||||
e.GET("/handler").
|
||||
Expect().Status(iris.StatusOK).Body().Equal(expected)
|
||||
|
||||
e.GET("/func").
|
||||
Expect().Status(iris.StatusOK).Body().Equal(expected)
|
||||
}
|
||||
|
||||
func TestFromStdWithNext(t *testing.T) {
|
||||
basicauth := "secret"
|
||||
passed := "ok"
|
||||
|
||||
type contextKey string
|
||||
stdWNext := func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
||||
if username, password, ok := r.BasicAuth(); ok &&
|
||||
username == basicauth && password == basicauth {
|
||||
ctx := stdContext.WithValue(r.Context(), contextKey("key"), "ok")
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
return
|
||||
}
|
||||
w.WriteHeader(iris.StatusForbidden)
|
||||
}
|
||||
|
||||
h := handlerconv.FromStdWithNext(stdWNext)
|
||||
next := func(ctx *context.Context) {
|
||||
ctx.WriteString(ctx.Request().Context().Value(contextKey("key")).(string))
|
||||
}
|
||||
|
||||
app := iris.New()
|
||||
app.Get("/handlerwithnext", h, next)
|
||||
|
||||
e := httptest.New(t, app)
|
||||
|
||||
e.GET("/handlerwithnext").
|
||||
Expect().Status(iris.StatusForbidden)
|
||||
|
||||
e.GET("/handlerwithnext").WithBasicAuth(basicauth, basicauth).
|
||||
Expect().Status(iris.StatusOK).Body().Equal(passed)
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/kataras/iris/v12/core/netutil"
|
||||
"github.com/kataras/iris/core/netutil"
|
||||
)
|
||||
|
||||
func singleJoiningSlash(a, b string) string {
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
// black-box testing
|
||||
package host_test
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/kataras/iris/v12"
|
||||
"github.com/kataras/iris/v12/context"
|
||||
"github.com/kataras/iris/v12/core/host"
|
||||
"github.com/kataras/iris/v12/httptest"
|
||||
)
|
||||
|
||||
func TestProxy(t *testing.T) {
|
||||
expectedIndex := "ok /"
|
||||
expectedAbout := "ok /about"
|
||||
unexpectedRoute := "unexpected"
|
||||
|
||||
// proxySrv := iris.New()
|
||||
u, err := url.Parse("https://localhost:4444")
|
||||
if err != nil {
|
||||
t.Fatalf("%v while parsing url", err)
|
||||
}
|
||||
|
||||
proxy := host.NewProxy("", u)
|
||||
|
||||
addr := &net.TCPAddr{
|
||||
IP: net.IPv4(127, 0, 0, 1),
|
||||
Port: 0,
|
||||
}
|
||||
|
||||
listener, err := net.ListenTCP("tcp", addr)
|
||||
if err != nil {
|
||||
t.Fatalf("%v while creating listener", err)
|
||||
}
|
||||
|
||||
go proxy.Serve(listener) // should be localhost/127.0.0.1:80 but travis throws permission denied.
|
||||
|
||||
t.Log(listener.Addr().String())
|
||||
<-time.After(time.Second)
|
||||
t.Log(listener.Addr().String())
|
||||
|
||||
app := iris.New()
|
||||
app.Get("/", func(ctx *context.Context) {
|
||||
ctx.WriteString(expectedIndex)
|
||||
})
|
||||
|
||||
app.Get("/about", func(ctx *context.Context) {
|
||||
ctx.WriteString(expectedAbout)
|
||||
})
|
||||
|
||||
app.OnErrorCode(iris.StatusNotFound, func(ctx *context.Context) {
|
||||
ctx.WriteString(unexpectedRoute)
|
||||
})
|
||||
|
||||
l, err := net.Listen("tcp", "localhost:4444") // should be localhost/127.0.0.1:443 but travis throws permission denied.
|
||||
if err != nil {
|
||||
t.Fatalf("%v while creating tcp4 listener for new tls local test listener", err)
|
||||
}
|
||||
// main server
|
||||
go app.Run(iris.Listener(httptest.NewLocalTLSListener(l)), iris.WithoutStartupLog) // nolint:errcheck
|
||||
|
||||
e := httptest.NewInsecure(t, httptest.URL("http://"+listener.Addr().String()))
|
||||
e.GET("/").Expect().Status(iris.StatusOK).Body().Equal(expectedIndex)
|
||||
e.GET("/about").Expect().Status(iris.StatusOK).Body().Equal(expectedAbout)
|
||||
e.GET("/notfound").Expect().Status(iris.StatusNotFound).Body().Equal(unexpectedRoute)
|
||||
}
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/kataras/iris/v12/core/netutil"
|
||||
"github.com/kataras/iris/core/netutil"
|
||||
|
||||
"golang.org/x/crypto/acme/autocert"
|
||||
)
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
// white-box testing
|
||||
package host
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
func ExampleSupervisor_RegisterOnError() {
|
||||
su := New(&http.Server{Addr: ":8273", Handler: http.DefaultServeMux})
|
||||
|
||||
su.RegisterOnError(func(err error) {
|
||||
fmt.Println(err.Error())
|
||||
})
|
||||
|
||||
su.RegisterOnError(func(err error) {
|
||||
fmt.Println(err.Error())
|
||||
})
|
||||
|
||||
su.RegisterOnError(func(err error) {
|
||||
fmt.Println(err.Error())
|
||||
})
|
||||
|
||||
go su.ListenAndServe()
|
||||
time.Sleep(1 * time.Second)
|
||||
if err := su.Shutdown(context.TODO()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
// Output:
|
||||
// http: Server closed
|
||||
// http: Server closed
|
||||
// http: Server closed
|
||||
}
|
||||
|
||||
type myTestTask struct {
|
||||
restartEvery time.Duration
|
||||
maxRestarts int
|
||||
logger *log.Logger
|
||||
}
|
||||
|
||||
func (m myTestTask) OnServe(host TaskHost) {
|
||||
host.Supervisor.DeferFlow() // don't exit on underline server's Shutdown.
|
||||
|
||||
ticker := time.NewTicker(m.restartEvery)
|
||||
defer ticker.Stop()
|
||||
rans := 0
|
||||
for range ticker.C {
|
||||
exitAfterXRestarts := m.maxRestarts
|
||||
if rans == exitAfterXRestarts {
|
||||
m.logger.Println("exit")
|
||||
ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Second)
|
||||
defer cancel()
|
||||
_ = host.Supervisor.Shutdown(ctx) // total shutdown
|
||||
host.Supervisor.RestoreFlow() // free to exit (if shutdown)
|
||||
return
|
||||
}
|
||||
|
||||
rans++
|
||||
|
||||
m.logger.Println(fmt.Sprintf("closed %d times", rans))
|
||||
host.Shutdown(context.TODO())
|
||||
|
||||
startDelay := 2 * time.Second
|
||||
time.AfterFunc(startDelay, func() {
|
||||
m.logger.Println("restart")
|
||||
if err := host.Serve(); err != nil { // restart
|
||||
panic(err)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleSupervisor_RegisterOnServe() {
|
||||
h := New(&http.Server{
|
||||
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
}),
|
||||
})
|
||||
|
||||
logger := log.New(os.Stdout, "Supervisor: ", 0)
|
||||
|
||||
mytask := myTestTask{
|
||||
restartEvery: 3 * time.Second,
|
||||
maxRestarts: 2,
|
||||
logger: logger,
|
||||
}
|
||||
|
||||
h.RegisterOnServe(mytask.OnServe)
|
||||
|
||||
ln, err := net.Listen("tcp4", ":9394")
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
|
||||
logger.Println("server started...")
|
||||
h.Serve(ln)
|
||||
|
||||
// Output:
|
||||
// Supervisor: server started...
|
||||
// Supervisor: closed 1 times
|
||||
// Supervisor: restart
|
||||
// Supervisor: closed 2 times
|
||||
// Supervisor: restart
|
||||
// Supervisor: exit
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
// white-box testing
|
||||
|
||||
package host
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/iris-contrib/httpexpect/v2"
|
||||
)
|
||||
|
||||
const (
|
||||
debug = false
|
||||
)
|
||||
|
||||
func newTester(t *testing.T, baseURL string, handler http.Handler) *httpexpect.Expect {
|
||||
var transporter http.RoundTripper
|
||||
|
||||
if strings.HasPrefix(baseURL, "http") { // means we are testing real serve time
|
||||
transporter = &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}
|
||||
} else { // means we are testing the handler itself
|
||||
transporter = httpexpect.NewBinder(handler)
|
||||
}
|
||||
|
||||
testConfiguration := httpexpect.Config{
|
||||
BaseURL: baseURL,
|
||||
Client: &http.Client{
|
||||
Transport: transporter,
|
||||
Jar: httpexpect.NewJar(),
|
||||
},
|
||||
Reporter: httpexpect.NewAssertReporter(t),
|
||||
}
|
||||
|
||||
if debug {
|
||||
testConfiguration.Printers = []httpexpect.Printer{
|
||||
httpexpect.NewDebugPrinter(t, true),
|
||||
}
|
||||
}
|
||||
|
||||
return httpexpect.WithConfig(testConfiguration)
|
||||
}
|
||||
|
||||
func testSupervisor(t *testing.T, creator func(*http.Server, []func(TaskHost)) *Supervisor) {
|
||||
loggerOutput := &bytes.Buffer{}
|
||||
logger := log.New(loggerOutput, "", 0)
|
||||
mu := new(sync.RWMutex)
|
||||
const (
|
||||
expectedHelloMessage = "Hello\n"
|
||||
)
|
||||
|
||||
// http routing
|
||||
|
||||
expectedBody := "this is the response body\n"
|
||||
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
_, err := w.Write([]byte(expectedBody))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
|
||||
// host (server wrapper and adapter) construction
|
||||
|
||||
srv := &http.Server{Handler: mux, ErrorLog: logger}
|
||||
addr := "localhost:5525"
|
||||
// serving
|
||||
ln, err := net.Listen("tcp4", addr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
helloMe := func(_ TaskHost) {
|
||||
mu.Lock()
|
||||
logger.Print(expectedHelloMessage)
|
||||
mu.Unlock()
|
||||
}
|
||||
|
||||
host := creator(srv, []func(TaskHost){helloMe})
|
||||
defer host.Shutdown(context.TODO())
|
||||
|
||||
go host.Serve(ln)
|
||||
|
||||
// http testsing and various calls
|
||||
// no need for time sleep because the following will take some time by theirselves
|
||||
tester := newTester(t, "http://"+addr, mux)
|
||||
tester.Request("GET", "/").Expect().Status(http.StatusOK).Body().Equal(expectedBody)
|
||||
|
||||
// WARNING: Data Race here because we try to read the logs
|
||||
// but it's "safe" here.
|
||||
|
||||
// testing Task (recorded) message:
|
||||
mu.RLock()
|
||||
got := loggerOutput.String()
|
||||
mu.RUnlock()
|
||||
if expectedHelloMessage != got {
|
||||
t.Fatalf("expected hello Task's message to be '%s' but got '%s'", expectedHelloMessage, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSupervisor(t *testing.T) {
|
||||
testSupervisor(t, func(srv *http.Server, tasks []func(TaskHost)) *Supervisor {
|
||||
su := New(srv)
|
||||
for _, t := range tasks {
|
||||
su.RegisterOnServe(t)
|
||||
}
|
||||
|
||||
return su
|
||||
})
|
||||
}
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/kataras/iris/v12/core/netutil"
|
||||
"github.com/kataras/iris/core/netutil"
|
||||
)
|
||||
|
||||
// WriteStartupLogOnServe is a task which accepts a logger(io.Writer)
|
||||
|
||||
@@ -1,167 +0,0 @@
|
||||
// white-box testing
|
||||
package memstore
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type myTestObject struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
func TestMuttable(t *testing.T) {
|
||||
var p Store
|
||||
|
||||
// slice
|
||||
p.Set("slice", []myTestObject{{"value 1"}, {"value 2"}})
|
||||
v := p.Get("slice").([]myTestObject)
|
||||
v[0].Name = "modified"
|
||||
|
||||
vv := p.Get("slice").([]myTestObject)
|
||||
if vv[0].Name != "modified" {
|
||||
t.Fatalf("expected slice to be muttable but caller was not able to change its value")
|
||||
}
|
||||
|
||||
// map
|
||||
|
||||
p.Set("map", map[string]myTestObject{"key 1": {"value 1"}, "key 2": {"value 2"}})
|
||||
vMap := p.Get("map").(map[string]myTestObject)
|
||||
vMap["key 1"] = myTestObject{"modified"}
|
||||
|
||||
vvMap := p.Get("map").(map[string]myTestObject)
|
||||
if vvMap["key 1"].Name != "modified" {
|
||||
t.Fatalf("expected map to be muttable but caller was not able to change its value")
|
||||
}
|
||||
|
||||
// object pointer of a value, it can change like maps or slices and arrays.
|
||||
p.Set("objp", &myTestObject{"value"})
|
||||
// we expect pointer here, as we set it.
|
||||
vObjP := p.Get("objp").(*myTestObject)
|
||||
|
||||
vObjP.Name = "modified"
|
||||
|
||||
vvObjP := p.Get("objp").(*myTestObject)
|
||||
if vvObjP.Name != "modified" {
|
||||
t.Fatalf("expected objp to be muttable but caller was able to change its value")
|
||||
}
|
||||
}
|
||||
|
||||
func TestImmutable(t *testing.T) {
|
||||
var p Store
|
||||
|
||||
// slice
|
||||
p.SetImmutable("slice", []myTestObject{{"value 1"}, {"value 2"}})
|
||||
v := p.Get("slice").([]myTestObject)
|
||||
v[0].Name = "modified"
|
||||
|
||||
vv := p.Get("slice").([]myTestObject)
|
||||
if vv[0].Name == "modified" {
|
||||
t.Fatalf("expected slice to be immutable but caller was able to change its value")
|
||||
}
|
||||
|
||||
// map
|
||||
p.SetImmutable("map", map[string]myTestObject{"key 1": {"value 1"}, "key 2": {"value 2"}})
|
||||
vMap := p.Get("map").(map[string]myTestObject)
|
||||
vMap["key 1"] = myTestObject{"modified"}
|
||||
|
||||
vvMap := p.Get("map").(map[string]myTestObject)
|
||||
if vvMap["key 1"].Name == "modified" {
|
||||
t.Fatalf("expected map to be immutable but caller was able to change its value")
|
||||
}
|
||||
|
||||
// object value, it's immutable at all cases.
|
||||
p.SetImmutable("obj", myTestObject{"value"})
|
||||
vObj := p.Get("obj").(myTestObject)
|
||||
vObj.Name = "modified"
|
||||
|
||||
vvObj := p.Get("obj").(myTestObject)
|
||||
if vvObj.Name == "modified" {
|
||||
t.Fatalf("expected obj to be immutable but caller was able to change its value")
|
||||
}
|
||||
|
||||
// object pointer of a value, it's immutable at all cases.
|
||||
p.SetImmutable("objp", &myTestObject{"value"})
|
||||
// we expect no pointer here if SetImmutable.
|
||||
// so it can't be changed by-design
|
||||
vObjP := p.Get("objp").(myTestObject)
|
||||
|
||||
vObjP.Name = "modified"
|
||||
|
||||
vvObjP := p.Get("objp").(myTestObject)
|
||||
if vvObjP.Name == "modified" {
|
||||
t.Fatalf("expected objp to be immutable but caller was able to change its value")
|
||||
}
|
||||
}
|
||||
|
||||
func TestImmutableSetOnlyWithSetImmutable(t *testing.T) {
|
||||
var p Store
|
||||
|
||||
p.SetImmutable("objp", &myTestObject{"value"})
|
||||
|
||||
p.Set("objp", &myTestObject{"modified"})
|
||||
vObjP := p.Get("objp").(myTestObject)
|
||||
if vObjP.Name == "modified" {
|
||||
t.Fatalf("caller should not be able to change the immutable entry with a simple `Set`")
|
||||
}
|
||||
|
||||
p.SetImmutable("objp", &myTestObject{"value with SetImmutable"})
|
||||
vvObjP := p.Get("objp").(myTestObject)
|
||||
if vvObjP.Name != "value with SetImmutable" {
|
||||
t.Fatalf("caller should be able to change the immutable entry with a `SetImmutable`")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetInt64Default(t *testing.T) {
|
||||
var p Store
|
||||
|
||||
p.Set("a uint16", uint16(2))
|
||||
if v := p.GetInt64Default("a uint16", 0); v != 2 {
|
||||
t.Fatalf("unexpected value of %d", v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJSON(t *testing.T) {
|
||||
var p Store
|
||||
|
||||
p.Set("key1", "value1")
|
||||
p.Set("key2", 2)
|
||||
p.Set("key3", myTestObject{Name: "makis"})
|
||||
|
||||
b, err := json.Marshal(p)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expectedJSON := []byte(`[{"key":"key1","value":"value1"},{"key":"key2","value":2},{"key":"key3","value":{"name":"makis"}}]`)
|
||||
|
||||
if !bytes.Equal(b, expectedJSON) {
|
||||
t.Fatalf("expected: %s but got: %s", string(expectedJSON), string(b))
|
||||
}
|
||||
|
||||
var newStore Store
|
||||
if err = json.Unmarshal(b, &newStore); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for i, v := range newStore {
|
||||
expected, got := p.Get(v.Key), v.ValueRaw
|
||||
|
||||
if ex, g := fmt.Sprintf("%v", expected), fmt.Sprintf("%v", got); ex != g {
|
||||
if _, isMap := got.(map[string]interface{}); isMap {
|
||||
// was struct but converted into map (as expected).
|
||||
b1, _ := json.Marshal(expected)
|
||||
b2, _ := json.Marshal(got)
|
||||
|
||||
if !bytes.Equal(b1, b2) {
|
||||
t.Fatalf("[%d] JSON expected: %s but got: %s", i, string(b1), string(b2))
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
t.Fatalf("[%d] expected: %s but got: %s", i, ex, g)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package netutil
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIsLoopbackHost(t *testing.T) {
|
||||
tests := []struct {
|
||||
host string
|
||||
valid bool
|
||||
}{
|
||||
{"subdomain.127.0.0.1:8080", true},
|
||||
{"subdomain.127.0.0.1", true},
|
||||
{"subdomain.localhost:8080", true},
|
||||
{"subdomain.localhost", true},
|
||||
{"subdomain.127.0000.0000.1:8080", true},
|
||||
{"subdomain.127.0000.0000.1", true},
|
||||
{"subdomain.127.255.255.254:8080", true},
|
||||
{"subdomain.127.255.255.254", true},
|
||||
|
||||
{"subdomain.0000:0:0000::01.1:8080", false},
|
||||
{"subdomain.0000:0:0000::01", false},
|
||||
{"subdomain.0000:0:0000::01.1:8080", false},
|
||||
{"subdomain.0000:0:0000::01", false},
|
||||
{"subdomain.0000:0000:0000:0000:0000:0000:0000:0001:8080", true},
|
||||
{"subdomain.0000:0000:0000:0000:0000:0000:0000:0001", false},
|
||||
|
||||
{"subdomain.example:8080", false},
|
||||
{"subdomain.example", false},
|
||||
{"subdomain.example.com:8080", false},
|
||||
{"subdomain.example.com", false},
|
||||
{"subdomain.com", false},
|
||||
{"subdomain", false},
|
||||
{".subdomain", false},
|
||||
{"127.0.0.1.com", false},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
if expected, got := tt.valid, IsLoopbackHost(tt.host); expected != got {
|
||||
t.Fatalf("[%d] expected %t but got %t for %s", i, expected, got, tt.host)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
package netutil
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIP(t *testing.T) {
|
||||
privateRanges := []IPRange{
|
||||
{
|
||||
Start: net.ParseIP("10.0.0.0"),
|
||||
End: net.ParseIP("10.255.255.255"),
|
||||
},
|
||||
{
|
||||
Start: net.ParseIP("100.64.0.0"),
|
||||
End: net.ParseIP("100.127.255.255"),
|
||||
},
|
||||
{
|
||||
Start: net.ParseIP("172.16.0.0"),
|
||||
End: net.ParseIP("172.31.255.255"),
|
||||
},
|
||||
{
|
||||
Start: net.ParseIP("192.0.0.0"),
|
||||
End: net.ParseIP("192.0.0.255"),
|
||||
},
|
||||
{
|
||||
Start: net.ParseIP("192.168.0.0"),
|
||||
End: net.ParseIP("192.168.255.255"),
|
||||
},
|
||||
{
|
||||
Start: net.ParseIP("198.18.0.0"),
|
||||
End: net.ParseIP("198.19.255.255"),
|
||||
},
|
||||
}
|
||||
|
||||
addresses := []string{
|
||||
"201.37.138.59",
|
||||
"159.117.3.153",
|
||||
"166.192.97.84",
|
||||
"225.181.213.210",
|
||||
"124.50.84.134",
|
||||
"87.53.250.102",
|
||||
"106.79.33.62",
|
||||
"242.120.17.144",
|
||||
"131.179.101.254",
|
||||
"103.11.11.174",
|
||||
"115.97.0.114",
|
||||
"219.202.120.251",
|
||||
"37.72.123.120",
|
||||
"154.94.78.101",
|
||||
"126.105.144.250",
|
||||
}
|
||||
|
||||
got, ok := GetIPAddress(addresses, privateRanges)
|
||||
if !ok {
|
||||
t.Logf("expected addr to be matched")
|
||||
}
|
||||
|
||||
if expected := "126.105.144.250"; expected != got {
|
||||
t.Logf("expected addr to be found: %s but got: %s", expected, got)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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()))
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -19,7 +19,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/kataras/iris/v12/context"
|
||||
"github.com/kataras/iris/context"
|
||||
)
|
||||
|
||||
const indexName = "/index.html"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"mime"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/kataras/iris/v12/context"
|
||||
"github.com/kataras/iris/context"
|
||||
)
|
||||
|
||||
var types = map[string]string{
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/kataras/iris/v12/context"
|
||||
"github.com/kataras/iris/context"
|
||||
|
||||
"github.com/schollz/closestmatch"
|
||||
)
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
@@ -3,7 +3,7 @@ package router
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/kataras/iris/v12/context"
|
||||
"github.com/kataras/iris/context"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
Reference in New Issue
Block a user