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:
@@ -5,7 +5,7 @@ import (
|
||||
"reflect"
|
||||
"sort"
|
||||
|
||||
"github.com/kataras/iris/v12/context"
|
||||
"github.com/kataras/iris/context"
|
||||
)
|
||||
|
||||
// binding contains the Dependency and the Input, it's the result of a function or struct + dependencies.
|
||||
|
||||
@@ -1,548 +0,0 @@
|
||||
package hero
|
||||
|
||||
import (
|
||||
stdContext "context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/kataras/golog"
|
||||
"github.com/kataras/iris/v12/context"
|
||||
"github.com/kataras/iris/v12/sessions"
|
||||
)
|
||||
|
||||
var (
|
||||
stdContextTyp = reflect.TypeOf((*stdContext.Context)(nil)).Elem()
|
||||
sessionTyp = reflect.TypeOf((*sessions.Session)(nil))
|
||||
timeTyp = reflect.TypeOf((*time.Time)(nil)).Elem()
|
||||
mapStringsTyp = reflect.TypeOf(map[string][]string{})
|
||||
)
|
||||
|
||||
func contextBinding(index int) *binding {
|
||||
return &binding{
|
||||
Dependency: BuiltinDependencies[0],
|
||||
Input: &Input{Type: BuiltinDependencies[0].DestType, Index: index},
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetBindingsForFunc(t *testing.T) {
|
||||
type (
|
||||
testResponse struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
testRequest struct {
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
testRequest2 struct {
|
||||
// normally a body can't have two requests but let's test it.
|
||||
Age int `json:"age"`
|
||||
}
|
||||
)
|
||||
|
||||
var testRequestTyp = reflect.TypeOf(testRequest{})
|
||||
|
||||
var deps = []*Dependency{
|
||||
NewDependency(func(ctx *context.Context) testRequest { return testRequest{Email: "should be ignored"} }),
|
||||
NewDependency(42),
|
||||
NewDependency(func(ctx *context.Context) (v testRequest, err error) {
|
||||
err = ctx.ReadJSON(&v)
|
||||
return
|
||||
}),
|
||||
NewDependency("if two strings requested this should be the last one"),
|
||||
NewDependency("should not be ignored when requested"),
|
||||
|
||||
// Dependencies like these should always be registered last.
|
||||
NewDependency(func(ctx *context.Context, input *Input) (newValue reflect.Value, err error) {
|
||||
wasPtr := input.Type.Kind() == reflect.Ptr
|
||||
|
||||
newValue = reflect.New(indirectType(input.Type))
|
||||
ptr := newValue.Interface()
|
||||
err = ctx.ReadJSON(ptr)
|
||||
|
||||
if !wasPtr {
|
||||
newValue = newValue.Elem()
|
||||
}
|
||||
|
||||
return newValue, err
|
||||
}),
|
||||
}
|
||||
|
||||
var tests = []struct {
|
||||
Func interface{}
|
||||
Expected []*binding
|
||||
}{
|
||||
{ // 0
|
||||
Func: func(ctx *context.Context) {
|
||||
ctx.WriteString("t1")
|
||||
},
|
||||
Expected: []*binding{contextBinding(0)},
|
||||
},
|
||||
{ // 1
|
||||
Func: func(ctx *context.Context) error {
|
||||
return fmt.Errorf("err1")
|
||||
},
|
||||
Expected: []*binding{contextBinding(0)},
|
||||
},
|
||||
{ // 2
|
||||
Func: func(ctx *context.Context) testResponse {
|
||||
return testResponse{Name: "name"}
|
||||
},
|
||||
Expected: []*binding{contextBinding(0)},
|
||||
},
|
||||
{ // 3
|
||||
Func: func(in testRequest) (testResponse, error) {
|
||||
return testResponse{Name: "email of " + in.Email}, nil
|
||||
},
|
||||
Expected: []*binding{{Dependency: deps[2], Input: &Input{Index: 0, Type: testRequestTyp}}},
|
||||
},
|
||||
{ // 4
|
||||
Func: func(in testRequest) (testResponse, error) {
|
||||
return testResponse{Name: "not valid "}, fmt.Errorf("invalid")
|
||||
},
|
||||
Expected: []*binding{{Dependency: deps[2], Input: &Input{Index: 0, Type: testRequestTyp}}},
|
||||
},
|
||||
{ // 5
|
||||
Func: func(ctx *context.Context, in testRequest) testResponse {
|
||||
return testResponse{Name: "(with ctx) email of " + in.Email}
|
||||
},
|
||||
Expected: []*binding{contextBinding(0), {Dependency: deps[2], Input: &Input{Index: 1, Type: testRequestTyp}}},
|
||||
},
|
||||
{ // 6
|
||||
Func: func(in testRequest, ctx *context.Context) testResponse { // reversed.
|
||||
return testResponse{Name: "(with ctx) email of " + in.Email}
|
||||
},
|
||||
Expected: []*binding{{Dependency: deps[2], Input: &Input{Index: 0, Type: testRequestTyp}}, contextBinding(1)},
|
||||
},
|
||||
{ // 7
|
||||
Func: func(in testRequest, ctx *context.Context, in2 string) testResponse { // reversed.
|
||||
return testResponse{Name: "(with ctx) email of " + in.Email + "and in2: " + in2}
|
||||
},
|
||||
Expected: []*binding{
|
||||
{
|
||||
Dependency: deps[2],
|
||||
Input: &Input{Index: 0, Type: testRequestTyp},
|
||||
},
|
||||
contextBinding(1),
|
||||
{
|
||||
Dependency: deps[4],
|
||||
Input: &Input{Index: 2, Type: reflect.TypeOf("")},
|
||||
},
|
||||
},
|
||||
},
|
||||
{ // 8
|
||||
Func: func(in testRequest, ctx *context.Context, in2, in3 string) testResponse { // reversed.
|
||||
return testResponse{Name: "(with ctx) email of " + in.Email + " | in2: " + in2 + " in3: " + in3}
|
||||
},
|
||||
Expected: []*binding{
|
||||
{
|
||||
Dependency: deps[2],
|
||||
Input: &Input{Index: 0, Type: testRequestTyp},
|
||||
},
|
||||
contextBinding(1),
|
||||
{
|
||||
Dependency: deps[len(deps)-3],
|
||||
Input: &Input{Index: 2, Type: reflect.TypeOf("")},
|
||||
},
|
||||
{
|
||||
Dependency: deps[len(deps)-2],
|
||||
Input: &Input{Index: 3, Type: reflect.TypeOf("")},
|
||||
},
|
||||
},
|
||||
},
|
||||
{ // 9
|
||||
Func: func(ctx *context.Context, in testRequest, in2 testRequest2) testResponse {
|
||||
return testResponse{Name: fmt.Sprintf("(with ctx) email of %s and in2.Age %d", in.Email, in2.Age)}
|
||||
},
|
||||
Expected: []*binding{
|
||||
contextBinding(0),
|
||||
{
|
||||
Dependency: deps[2],
|
||||
Input: &Input{Index: 1, Type: testRequestTyp},
|
||||
},
|
||||
{
|
||||
Dependency: deps[len(deps)-1],
|
||||
Input: &Input{Index: 2, Type: reflect.TypeOf(testRequest2{})},
|
||||
},
|
||||
},
|
||||
},
|
||||
{ // 10
|
||||
Func: func() testResponse {
|
||||
return testResponse{Name: "empty in, one out"}
|
||||
},
|
||||
Expected: nil,
|
||||
},
|
||||
{ // 1
|
||||
Func: func(userID string, age int) testResponse {
|
||||
return testResponse{Name: "in from path parameters"}
|
||||
},
|
||||
Expected: []*binding{
|
||||
paramBinding(0, 0, reflect.TypeOf("")),
|
||||
paramBinding(1, 1, reflect.TypeOf(0)),
|
||||
},
|
||||
},
|
||||
// test std context, session, time, request, response writer and headers bindings.
|
||||
{ // 12
|
||||
Func: func(stdContext.Context, *sessions.Session, *golog.Logger, time.Time, *http.Request, http.ResponseWriter, http.Header) testResponse {
|
||||
return testResponse{"builtin deps"}
|
||||
},
|
||||
Expected: []*binding{
|
||||
{
|
||||
Dependency: NewDependency(BuiltinDependencies[1]),
|
||||
Input: &Input{Index: 0, Type: stdContextTyp},
|
||||
},
|
||||
{
|
||||
Dependency: NewDependency(BuiltinDependencies[2]),
|
||||
Input: &Input{Index: 1, Type: sessionTyp},
|
||||
},
|
||||
{
|
||||
Dependency: NewDependency(BuiltinDependencies[3]),
|
||||
Input: &Input{Index: 2, Type: BuiltinDependencies[3].DestType},
|
||||
},
|
||||
{
|
||||
Dependency: NewDependency(BuiltinDependencies[4]),
|
||||
Input: &Input{Index: 3, Type: timeTyp},
|
||||
},
|
||||
{
|
||||
Dependency: NewDependency(BuiltinDependencies[5]),
|
||||
Input: &Input{Index: 4, Type: BuiltinDependencies[5].DestType},
|
||||
},
|
||||
{
|
||||
Dependency: NewDependency(BuiltinDependencies[6]),
|
||||
Input: &Input{Index: 5, Type: BuiltinDependencies[6].DestType},
|
||||
},
|
||||
{
|
||||
Dependency: NewDependency(BuiltinDependencies[7]),
|
||||
Input: &Input{Index: 6, Type: BuiltinDependencies[7].DestType},
|
||||
},
|
||||
},
|
||||
},
|
||||
// test explicitly of http.Header and its underline type map[string][]string which
|
||||
// but shouldn't be binded to request headers because of the (.Explicitly()), instead
|
||||
// the map should be binded to our last of "deps" which is is a dynamic functions reads from request body's JSON
|
||||
// (it's a builtin dependency as well but we declared it to test user dynamic dependencies too).
|
||||
{ // 13
|
||||
Func: func(http.Header) testResponse {
|
||||
return testResponse{"builtin http.Header dep"}
|
||||
},
|
||||
Expected: []*binding{
|
||||
{
|
||||
Dependency: NewDependency(BuiltinDependencies[7]),
|
||||
Input: &Input{Index: 0, Type: BuiltinDependencies[7].DestType},
|
||||
},
|
||||
},
|
||||
},
|
||||
{ // 14
|
||||
Func: func(map[string][]string) testResponse {
|
||||
return testResponse{"not dep registered except the dynamic one"}
|
||||
},
|
||||
Expected: []*binding{
|
||||
{
|
||||
Dependency: deps[len(deps)-1],
|
||||
Input: &Input{Index: 0, Type: mapStringsTyp},
|
||||
},
|
||||
},
|
||||
},
|
||||
{ // 15
|
||||
Func: func(http.Header, map[string][]string) testResponse {
|
||||
return testResponse{}
|
||||
},
|
||||
Expected: []*binding{ // only http.Header should be binded, we don't have map[string][]string registered.
|
||||
{
|
||||
Dependency: NewDependency(BuiltinDependencies[7]),
|
||||
Input: &Input{Index: 0, Type: BuiltinDependencies[7].DestType},
|
||||
},
|
||||
{
|
||||
Dependency: deps[len(deps)-1],
|
||||
Input: &Input{Index: 1, Type: mapStringsTyp},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
c := New()
|
||||
for _, dependency := range deps {
|
||||
c.Register(dependency)
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
bindings := getBindingsForFunc(reflect.ValueOf(tt.Func), c.Dependencies, 0)
|
||||
|
||||
if expected, got := len(tt.Expected), len(bindings); expected != got {
|
||||
t.Fatalf("[%d] expected bindings length to be: %d but got: %d of: %s", i, expected, got, bindings)
|
||||
}
|
||||
|
||||
for j, b := range bindings {
|
||||
if b == nil {
|
||||
t.Fatalf("[%d:%d] binding is nil!", i, j)
|
||||
}
|
||||
|
||||
if tt.Expected[j] == nil {
|
||||
t.Fatalf("[%d:%d] expected dependency was not found!", i, j)
|
||||
}
|
||||
|
||||
// if expected := tt.Expected[j]; !expected.Equal(b) {
|
||||
// t.Fatalf("[%d:%d] got unexpected binding:\n%s", i, j, spew.Sdump(expected, b))
|
||||
// }
|
||||
|
||||
if expected := tt.Expected[j]; !expected.Equal(b) {
|
||||
t.Fatalf("[%d:%d] expected binding:\n%s\nbut got:\n%s", i, j, expected, b)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type (
|
||||
service interface {
|
||||
String() string
|
||||
}
|
||||
serviceImpl struct{}
|
||||
)
|
||||
|
||||
var serviceTyp = reflect.TypeOf((*service)(nil)).Elem()
|
||||
|
||||
func (s *serviceImpl) String() string {
|
||||
return "service"
|
||||
}
|
||||
|
||||
func TestBindingsForStruct(t *testing.T) {
|
||||
type (
|
||||
controller struct {
|
||||
Name string
|
||||
Service service
|
||||
}
|
||||
|
||||
embedded1 struct {
|
||||
Age int
|
||||
}
|
||||
|
||||
embedded2 struct {
|
||||
Now time.Time
|
||||
}
|
||||
|
||||
Embedded3 struct {
|
||||
Age int
|
||||
}
|
||||
|
||||
Embedded4 struct {
|
||||
Now time.Time
|
||||
}
|
||||
|
||||
controllerEmbeddingExported struct {
|
||||
Embedded3
|
||||
Embedded4
|
||||
}
|
||||
|
||||
controllerEmbeddingUnexported struct {
|
||||
embedded1
|
||||
embedded2
|
||||
}
|
||||
|
||||
controller2 struct {
|
||||
Emb1 embedded1
|
||||
Emb2 embedded2
|
||||
}
|
||||
|
||||
controller3 struct {
|
||||
Emb1 embedded1
|
||||
emb2 embedded2 // unused
|
||||
}
|
||||
)
|
||||
|
||||
var deps = []*Dependency{
|
||||
NewDependency("name"),
|
||||
NewDependency(new(serviceImpl)),
|
||||
}
|
||||
|
||||
var depsForAnonymousEmbedded = []*Dependency{
|
||||
NewDependency(42),
|
||||
NewDependency(time.Now()),
|
||||
}
|
||||
|
||||
var depsForFieldsOfStruct = []*Dependency{
|
||||
NewDependency(embedded1{Age: 42}),
|
||||
NewDependency(embedded2{time.Now()}),
|
||||
}
|
||||
|
||||
var depsInterfaces = []*Dependency{
|
||||
NewDependency(func(ctx *context.Context) interface{} {
|
||||
return "name"
|
||||
}),
|
||||
}
|
||||
|
||||
var autoBindings = []*binding{
|
||||
payloadBinding(0, reflect.TypeOf(embedded1{})),
|
||||
payloadBinding(1, reflect.TypeOf(embedded2{})),
|
||||
}
|
||||
|
||||
for _, b := range autoBindings {
|
||||
b.Input.StructFieldIndex = []int{b.Input.Index}
|
||||
}
|
||||
|
||||
var tests = []struct {
|
||||
Value interface{}
|
||||
Registered []*Dependency
|
||||
Expected []*binding
|
||||
}{
|
||||
{ // 0.
|
||||
Value: &controller{},
|
||||
Registered: deps,
|
||||
Expected: []*binding{
|
||||
{
|
||||
Dependency: deps[0],
|
||||
Input: &Input{Index: 0, StructFieldIndex: []int{0}, Type: reflect.TypeOf("")},
|
||||
},
|
||||
{
|
||||
Dependency: deps[1],
|
||||
Input: &Input{Index: 1, StructFieldIndex: []int{1}, Type: serviceTyp},
|
||||
},
|
||||
},
|
||||
},
|
||||
// 1. test controller with pre-defined variables.
|
||||
{
|
||||
Value: &controller{Name: "name_struct", Service: new(serviceImpl)},
|
||||
Expected: []*binding{
|
||||
{
|
||||
Dependency: NewDependency("name_struct"),
|
||||
Input: &Input{Index: 0, StructFieldIndex: []int{0}, Type: reflect.TypeOf("")},
|
||||
},
|
||||
{
|
||||
Dependency: NewDependency(new(serviceImpl)),
|
||||
Input: &Input{Index: 1, StructFieldIndex: []int{1}, Type: serviceTyp},
|
||||
},
|
||||
},
|
||||
},
|
||||
// 2. test controller with pre-defined variables and other deps with the exact order and value
|
||||
// (deps from non zero values should be registerded only, if not the Dependency:name_struct will fail for sure).
|
||||
{
|
||||
Value: &controller{Name: "name_struct", Service: new(serviceImpl)},
|
||||
Registered: deps,
|
||||
Expected: []*binding{
|
||||
{
|
||||
Dependency: NewDependency("name_struct"),
|
||||
Input: &Input{Index: 0, StructFieldIndex: []int{0}, Type: reflect.TypeOf("")},
|
||||
},
|
||||
{
|
||||
Dependency: NewDependency(new(serviceImpl)),
|
||||
Input: &Input{Index: 1, StructFieldIndex: []int{1}, Type: serviceTyp},
|
||||
},
|
||||
},
|
||||
},
|
||||
// 3. test embedded structs with anonymous and exported.
|
||||
{
|
||||
Value: &controllerEmbeddingExported{},
|
||||
Registered: depsForAnonymousEmbedded,
|
||||
Expected: []*binding{
|
||||
{
|
||||
Dependency: depsForAnonymousEmbedded[0],
|
||||
Input: &Input{Index: 0, StructFieldIndex: []int{0, 0}, Type: reflect.TypeOf(0)},
|
||||
},
|
||||
{
|
||||
Dependency: depsForAnonymousEmbedded[1],
|
||||
Input: &Input{Index: 1, StructFieldIndex: []int{1, 0}, Type: reflect.TypeOf(time.Time{})},
|
||||
},
|
||||
},
|
||||
},
|
||||
// 4. test for anonymous but not exported (should still be 2, unexported structs are binded).
|
||||
{
|
||||
Value: &controllerEmbeddingUnexported{},
|
||||
Registered: depsForAnonymousEmbedded,
|
||||
Expected: []*binding{
|
||||
{
|
||||
Dependency: depsForAnonymousEmbedded[0],
|
||||
Input: &Input{Index: 0, StructFieldIndex: []int{0, 0}, Type: reflect.TypeOf(0)},
|
||||
},
|
||||
{
|
||||
Dependency: depsForAnonymousEmbedded[1],
|
||||
Input: &Input{Index: 1, StructFieldIndex: []int{1, 0}, Type: reflect.TypeOf(time.Time{})},
|
||||
},
|
||||
},
|
||||
},
|
||||
// 5. test for auto-bindings with zero registered.
|
||||
{
|
||||
Value: &controller2{},
|
||||
Registered: nil,
|
||||
Expected: autoBindings,
|
||||
},
|
||||
// 6. test for embedded with named fields which should NOT contain any registered deps
|
||||
// except the two auto-bindings for structs,
|
||||
{
|
||||
Value: &controller2{},
|
||||
Registered: depsForAnonymousEmbedded,
|
||||
Expected: autoBindings,
|
||||
}, // 7. and only embedded struct's fields are readen, otherwise we expect the struct to be a dependency.
|
||||
{
|
||||
Value: &controller2{},
|
||||
Registered: depsForFieldsOfStruct,
|
||||
Expected: []*binding{
|
||||
{
|
||||
Dependency: depsForFieldsOfStruct[0],
|
||||
Input: &Input{Index: 0, StructFieldIndex: []int{0}, Type: reflect.TypeOf(embedded1{})},
|
||||
},
|
||||
{
|
||||
Dependency: depsForFieldsOfStruct[1],
|
||||
Input: &Input{Index: 1, StructFieldIndex: []int{1}, Type: reflect.TypeOf(embedded2{})},
|
||||
},
|
||||
},
|
||||
},
|
||||
// 8. test one exported and other not exported.
|
||||
{
|
||||
Value: &controller3{},
|
||||
Registered: []*Dependency{depsForFieldsOfStruct[0]},
|
||||
Expected: []*binding{
|
||||
{
|
||||
Dependency: depsForFieldsOfStruct[0],
|
||||
Input: &Input{Index: 0, StructFieldIndex: []int{0}, Type: reflect.TypeOf(embedded1{})},
|
||||
},
|
||||
},
|
||||
},
|
||||
// 9. test same as the above but by registering all dependencies.
|
||||
{
|
||||
Value: &controller3{},
|
||||
Registered: depsForFieldsOfStruct,
|
||||
Expected: []*binding{
|
||||
{
|
||||
Dependency: depsForFieldsOfStruct[0],
|
||||
Input: &Input{Index: 0, StructFieldIndex: []int{0}, Type: reflect.TypeOf(embedded1{})},
|
||||
},
|
||||
},
|
||||
},
|
||||
// 10. test bind an interface{}.
|
||||
{
|
||||
Value: &controller{},
|
||||
Registered: depsInterfaces,
|
||||
Expected: []*binding{
|
||||
{
|
||||
Dependency: depsInterfaces[0],
|
||||
Input: &Input{Index: 0, StructFieldIndex: []int{0}, Type: reflect.TypeOf("")},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
bindings := getBindingsForStruct(reflect.ValueOf(tt.Value), tt.Registered, 0, nil)
|
||||
|
||||
if expected, got := len(tt.Expected), len(bindings); expected != got {
|
||||
t.Logf("[%d] expected bindings length to be: %d but got: %d:\n", i, expected, got)
|
||||
for _, b := range bindings {
|
||||
t.Logf("\t%s\n", b)
|
||||
}
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
for j, b := range bindings {
|
||||
if tt.Expected[j] == nil {
|
||||
t.Fatalf("[%d:%d] expected dependency was not found!", i, j)
|
||||
}
|
||||
|
||||
if expected := tt.Expected[j]; !expected.Equal(b) {
|
||||
t.Fatalf("[%d:%d] expected binding:\n%s\nbut got:\n%s", i, j, expected, b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/kataras/iris/v12/context"
|
||||
"github.com/kataras/iris/v12/sessions"
|
||||
"github.com/kataras/iris/context"
|
||||
"github.com/kataras/iris/sessions"
|
||||
|
||||
"github.com/kataras/golog"
|
||||
)
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
package hero_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/kataras/iris/v12"
|
||||
. "github.com/kataras/iris/v12/hero"
|
||||
"github.com/kataras/iris/v12/httptest"
|
||||
)
|
||||
|
||||
var errTyp = reflect.TypeOf((*error)(nil)).Elem()
|
||||
|
||||
// isError returns true if "typ" is type of `error`.
|
||||
func isError(typ reflect.Type) bool {
|
||||
return typ.Implements(errTyp)
|
||||
}
|
||||
|
||||
type (
|
||||
testInput struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
testOutput struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
fn = func(id int, in testInput) testOutput {
|
||||
return testOutput{
|
||||
ID: id,
|
||||
Name: in.Name,
|
||||
}
|
||||
}
|
||||
|
||||
expectedOutput = testOutput{
|
||||
ID: 42,
|
||||
Name: "makis",
|
||||
}
|
||||
|
||||
input = testInput{
|
||||
Name: "makis",
|
||||
}
|
||||
)
|
||||
|
||||
func TestContainerHandler(t *testing.T) {
|
||||
app := iris.New()
|
||||
|
||||
c := New()
|
||||
postHandler := c.Handler(fn)
|
||||
app.Post("/{id:int}", postHandler)
|
||||
|
||||
e := httptest.New(t, app)
|
||||
path := fmt.Sprintf("/%d", expectedOutput.ID)
|
||||
e.POST(path).WithJSON(input).Expect().Status(httptest.StatusOK).JSON().Equal(expectedOutput)
|
||||
}
|
||||
|
||||
func TestContainerInject(t *testing.T) {
|
||||
c := New()
|
||||
|
||||
expected := testInput{Name: "test"}
|
||||
c.Register(expected)
|
||||
c.Register(&expected)
|
||||
|
||||
// struct value.
|
||||
var got1 testInput
|
||||
if err := c.Inject(&got1); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(expected, got1) {
|
||||
t.Fatalf("[struct value] expected: %#+v but got: %#+v", expected, got1)
|
||||
}
|
||||
|
||||
// ptr.
|
||||
var got2 *testInput
|
||||
if err := c.Inject(&got2); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(&expected, got2) {
|
||||
t.Fatalf("[ptr] expected: %#+v but got: %#+v", &expected, got2)
|
||||
}
|
||||
|
||||
// register implementation, expect interface.
|
||||
expected3 := &testServiceImpl{prefix: "prefix: "}
|
||||
c.Register(expected3)
|
||||
|
||||
var got3 testService
|
||||
if err := c.Inject(&got3); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expected3, got3) {
|
||||
t.Fatalf("[service] expected: %#+v but got: %#+v", expected3, got3)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContainerUseResultHandler(t *testing.T) {
|
||||
c := New()
|
||||
resultLogger := func(next ResultHandler) ResultHandler {
|
||||
return func(ctx iris.Context, v interface{}) error {
|
||||
t.Logf("%#+v", v)
|
||||
return next(ctx, v)
|
||||
}
|
||||
}
|
||||
|
||||
c.UseResultHandler(resultLogger)
|
||||
expectedResponse := map[string]interface{}{"injected": true}
|
||||
c.UseResultHandler(func(next ResultHandler) ResultHandler {
|
||||
return func(ctx iris.Context, v interface{}) error {
|
||||
return next(ctx, expectedResponse)
|
||||
}
|
||||
})
|
||||
c.UseResultHandler(resultLogger)
|
||||
|
||||
handler := c.Handler(func(id int) testOutput {
|
||||
return testOutput{
|
||||
ID: id,
|
||||
Name: "kataras",
|
||||
}
|
||||
})
|
||||
|
||||
app := iris.New()
|
||||
app.Get("/{id:int}", handler)
|
||||
|
||||
e := httptest.New(t, app)
|
||||
e.GET("/42").Expect().Status(httptest.StatusOK).JSON().Equal(expectedResponse)
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
|
||||
"reflect"
|
||||
|
||||
"github.com/kataras/iris/v12/context"
|
||||
"github.com/kataras/iris/context"
|
||||
)
|
||||
|
||||
type (
|
||||
|
||||
@@ -1,206 +0,0 @@
|
||||
package hero_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/kataras/iris/v12/context"
|
||||
. "github.com/kataras/iris/v12/hero"
|
||||
)
|
||||
|
||||
type testDependencyTest struct {
|
||||
Dependency interface{}
|
||||
Expected interface{}
|
||||
}
|
||||
|
||||
func TestDependency(t *testing.T) {
|
||||
var tests = []testDependencyTest{
|
||||
{
|
||||
Dependency: "myValue",
|
||||
Expected: "myValue",
|
||||
},
|
||||
{
|
||||
Dependency: struct{ Name string }{"name"},
|
||||
Expected: struct{ Name string }{"name"},
|
||||
},
|
||||
{
|
||||
Dependency: func(*context.Context, *Input) (reflect.Value, error) {
|
||||
return reflect.ValueOf(42), nil
|
||||
},
|
||||
Expected: 42,
|
||||
},
|
||||
{
|
||||
Dependency: DependencyHandler(func(*context.Context, *Input) (reflect.Value, error) {
|
||||
return reflect.ValueOf(255), nil
|
||||
}),
|
||||
Expected: 255,
|
||||
},
|
||||
{
|
||||
Dependency: func(*context.Context) (reflect.Value, error) {
|
||||
return reflect.ValueOf("OK without Input"), nil
|
||||
},
|
||||
Expected: "OK without Input",
|
||||
},
|
||||
{
|
||||
|
||||
Dependency: func(*context.Context, ...string) (reflect.Value, error) {
|
||||
return reflect.ValueOf("OK variadic ignored"), nil
|
||||
},
|
||||
Expected: "OK variadic ignored",
|
||||
},
|
||||
{
|
||||
|
||||
Dependency: func(*context.Context) reflect.Value {
|
||||
return reflect.ValueOf("OK without Input and error")
|
||||
},
|
||||
Expected: "OK without Input and error",
|
||||
},
|
||||
{
|
||||
|
||||
Dependency: func(*context.Context, ...int) reflect.Value {
|
||||
return reflect.ValueOf("OK without error and variadic ignored")
|
||||
},
|
||||
Expected: "OK without error and variadic ignored",
|
||||
},
|
||||
{
|
||||
|
||||
Dependency: func(*context.Context) interface{} {
|
||||
return "1"
|
||||
},
|
||||
Expected: "1",
|
||||
},
|
||||
{
|
||||
|
||||
Dependency: func(*context.Context) interface{} {
|
||||
return false
|
||||
},
|
||||
Expected: false,
|
||||
},
|
||||
}
|
||||
|
||||
testDependencies(t, tests)
|
||||
}
|
||||
|
||||
// Test dependencies that depend on previous one(s).
|
||||
func TestDependentDependency(t *testing.T) {
|
||||
msgBody := "prefix: it is a deep dependency"
|
||||
newMsgBody := msgBody + " new"
|
||||
var tests = []testDependencyTest{
|
||||
// test three level depth and error.
|
||||
{ // 0
|
||||
Dependency: &testServiceImpl{prefix: "prefix:"},
|
||||
Expected: &testServiceImpl{prefix: "prefix:"},
|
||||
},
|
||||
{ // 1
|
||||
Dependency: func(service testService) testMessage {
|
||||
return testMessage{Body: service.Say("it is a deep") + " dependency"}
|
||||
},
|
||||
Expected: testMessage{Body: msgBody},
|
||||
},
|
||||
{ // 2
|
||||
Dependency: func(msg testMessage) string {
|
||||
return msg.Body
|
||||
},
|
||||
Expected: msgBody,
|
||||
},
|
||||
{ // 3
|
||||
Dependency: func(msg testMessage) error {
|
||||
return fmt.Errorf(msg.Body)
|
||||
},
|
||||
Expected: fmt.Errorf(msgBody),
|
||||
},
|
||||
// Test depend on more than one previous registered dependencies and require a before-previous one.
|
||||
{ // 4
|
||||
Dependency: func(body string, msg testMessage) string {
|
||||
if body != msg.Body {
|
||||
t.Fatalf("body[%s] != msg.Body[%s]", body, msg.Body)
|
||||
}
|
||||
|
||||
return body + " new"
|
||||
},
|
||||
Expected: newMsgBody,
|
||||
},
|
||||
// Test dependency order by expecting the first <string> returning value and not the later-on registered dependency(#4).
|
||||
// 5
|
||||
{
|
||||
Dependency: func(body string) string {
|
||||
return body
|
||||
},
|
||||
Expected: newMsgBody,
|
||||
},
|
||||
}
|
||||
|
||||
testDependencies(t, tests)
|
||||
}
|
||||
|
||||
func testDependencies(t *testing.T, tests []testDependencyTest) {
|
||||
t.Helper()
|
||||
|
||||
c := New()
|
||||
for i, tt := range tests {
|
||||
d := c.Register(tt.Dependency)
|
||||
|
||||
if d == nil {
|
||||
t.Fatalf("[%d] expected %#+v to be converted to a valid dependency", i, tt)
|
||||
}
|
||||
|
||||
val, err := d.Handle(context.NewContext(nil), &Input{})
|
||||
|
||||
if expectError := isError(reflect.TypeOf(tt.Expected)); expectError {
|
||||
val = reflect.ValueOf(err)
|
||||
err = nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("[%d] expected a nil error but got: %v", i, err)
|
||||
}
|
||||
|
||||
if !val.CanInterface() {
|
||||
t.Fatalf("[%d] expected output value to be accessible: %T", i, val)
|
||||
}
|
||||
|
||||
if expected, got := fmt.Sprintf("%#+v", tt.Expected), fmt.Sprintf("%#+v", val.Interface()); expected != got {
|
||||
t.Fatalf("[%d] expected return value to be:\n%s\nbut got:\n%s", i, expected, got)
|
||||
}
|
||||
|
||||
// t.Logf("[%d] %s", i, d)
|
||||
// t.Logf("[%d] output: %#+v", i, val.Interface())
|
||||
}
|
||||
}
|
||||
|
||||
func TestDependentDependencyInheritanceStatic(t *testing.T) {
|
||||
// Tests the following case #1564:
|
||||
// Logger
|
||||
// func(Logger) S1
|
||||
// ^ Should be static because Logger
|
||||
// is a structure, a static dependency.
|
||||
//
|
||||
// func(Logger) S2
|
||||
// func(S1, S2) S3
|
||||
// ^ Should be marked as static dependency
|
||||
// because everything that depends on are static too.
|
||||
|
||||
type S1 struct {
|
||||
msg string
|
||||
}
|
||||
|
||||
type S2 struct {
|
||||
msg2 string
|
||||
}
|
||||
|
||||
serviceDep := NewDependency(&testServiceImpl{prefix: "1"})
|
||||
d1 := NewDependency(func(t testService) S1 {
|
||||
return S1{t.Say("2")}
|
||||
}, serviceDep)
|
||||
if !d1.Static {
|
||||
t.Fatalf("d1 dependency should be static: %#+v", d1)
|
||||
}
|
||||
|
||||
d2 := NewDependency(func(t testService, s S1) S2 {
|
||||
return S2{"3"}
|
||||
}, serviceDep, d1)
|
||||
if !d2.Static {
|
||||
t.Fatalf("d2 dependency should be static: %#+v", d2)
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/kataras/iris/v12/context"
|
||||
"github.com/kataras/iris/context"
|
||||
|
||||
"github.com/fatih/structs"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
@@ -1,281 +0,0 @@
|
||||
package hero_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/kataras/iris/v12"
|
||||
"github.com/kataras/iris/v12/context"
|
||||
"github.com/kataras/iris/v12/httptest"
|
||||
|
||||
. "github.com/kataras/iris/v12/hero"
|
||||
)
|
||||
|
||||
func GetText() string {
|
||||
return "text"
|
||||
}
|
||||
|
||||
func GetStatus() int {
|
||||
return iris.StatusBadGateway
|
||||
}
|
||||
|
||||
func GetTextWithStatusOk() (string, int) {
|
||||
return "OK", iris.StatusOK
|
||||
}
|
||||
|
||||
// tests should have output arguments mixed
|
||||
func GetStatusWithTextNotOkBy(first string, second string) (int, string) {
|
||||
return iris.StatusForbidden, "NOT_OK_" + first + second
|
||||
}
|
||||
|
||||
func GetTextAndContentType() (string, string) {
|
||||
return "<b>text</b>", "text/html"
|
||||
}
|
||||
|
||||
type testCustomResult struct {
|
||||
HTML string
|
||||
}
|
||||
|
||||
// The only one required function to make that a custom Response dispatcher.
|
||||
func (r testCustomResult) Dispatch(ctx iris.Context) {
|
||||
_, _ = ctx.HTML(r.HTML)
|
||||
}
|
||||
|
||||
func GetCustomResponse() testCustomResult {
|
||||
return testCustomResult{"<b>text</b>"}
|
||||
}
|
||||
|
||||
func GetCustomResponseWithStatusOk() (testCustomResult, int) {
|
||||
return testCustomResult{"<b>OK</b>"}, iris.StatusOK
|
||||
}
|
||||
|
||||
func GetCustomResponseWithStatusNotOk() (testCustomResult, int) {
|
||||
return testCustomResult{"<b>internal server error</b>"}, iris.StatusInternalServerError
|
||||
}
|
||||
|
||||
type testCustomStruct struct {
|
||||
Name string `json:"name" xml:"name"`
|
||||
Age int `json:"age" xml:"age"`
|
||||
}
|
||||
|
||||
func GetCustomStruct() testCustomStruct {
|
||||
return testCustomStruct{"Iris", 2}
|
||||
}
|
||||
|
||||
func GetCustomStructWithStatusNotOk() (testCustomStruct, int) {
|
||||
return testCustomStruct{"Iris", 2}, iris.StatusInternalServerError
|
||||
}
|
||||
|
||||
func GetCustomStructWithContentType() (testCustomStruct, string) {
|
||||
return testCustomStruct{"Iris", 2}, "text/xml"
|
||||
}
|
||||
|
||||
func GetCustomStructWithError(ctx iris.Context) (s testCustomStruct, err error) {
|
||||
s = testCustomStruct{"Iris", 2}
|
||||
if ctx.URLParamExists("err") {
|
||||
err = errors.New("omit return of testCustomStruct and fire error")
|
||||
}
|
||||
|
||||
// it should send the testCustomStruct as JSON if error is nil
|
||||
// otherwise it should fire the default error(BadRequest) with the error's text.
|
||||
return
|
||||
}
|
||||
|
||||
type err struct {
|
||||
Status int `json:"status_code"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
func (e err) Dispatch(ctx iris.Context) {
|
||||
// write the status code based on the err's StatusCode.
|
||||
ctx.StatusCode(e.Status)
|
||||
// send to the client the whole object as json
|
||||
_, _ = ctx.JSON(e)
|
||||
}
|
||||
|
||||
func GetCustomErrorAsDispatcher() err {
|
||||
return err{iris.StatusBadRequest, "this is my error as json"}
|
||||
}
|
||||
|
||||
func GetCustomTypedNilEmptyResponse() iris.Map {
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetCustomTypedPtrNilEmptyResponse() *iris.Map {
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetCustomMapNilEmptyResponse() map[string]interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetCustomPtrStructNilEmptyResponse() *testCustomStruct {
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestFuncResult(t *testing.T) {
|
||||
app := iris.New()
|
||||
h := New()
|
||||
// for any 'By', by is not required but we use this suffix here, like controllers
|
||||
// to make it easier for the future to resolve if any bug.
|
||||
// add the binding for path parameters.
|
||||
|
||||
app.Get("/text", h.Handler(GetText))
|
||||
app.Get("/status", h.Handler(GetStatus))
|
||||
app.Get("/text/with/status/ok", h.Handler(GetTextWithStatusOk))
|
||||
app.Get("/status/with/text/not/ok/{first}/{second}", h.Handler(GetStatusWithTextNotOkBy))
|
||||
app.Get("/text/and/content/type", h.Handler(GetTextAndContentType))
|
||||
//
|
||||
app.Get("/custom/response", h.Handler(GetCustomResponse))
|
||||
app.Get("/custom/response/with/status/ok", h.Handler(GetCustomResponseWithStatusOk))
|
||||
app.Get("/custom/response/with/status/not/ok", h.Handler(GetCustomResponseWithStatusNotOk))
|
||||
//
|
||||
app.Get("/custom/struct", h.Handler(GetCustomStruct))
|
||||
app.Get("/custom/struct/with/status/not/ok", h.Handler(GetCustomStructWithStatusNotOk))
|
||||
app.Get("/custom/struct/with/content/type", h.Handler(GetCustomStructWithContentType))
|
||||
app.Get("/custom/struct/with/error", h.Handler(GetCustomStructWithError))
|
||||
app.Get("/custom/error/as/dispatcher", h.Handler(GetCustomErrorAsDispatcher))
|
||||
|
||||
app.Get("/custom/nil/typed", h.Handler(GetCustomTypedNilEmptyResponse))
|
||||
app.Get("/custom/nil/typed/ptr", h.Handler(GetCustomTypedPtrNilEmptyResponse))
|
||||
app.Get("/custom/nil/map", h.Handler(GetCustomMapNilEmptyResponse))
|
||||
app.Get("/custom/nil/struct", h.Handler(GetCustomPtrStructNilEmptyResponse))
|
||||
|
||||
e := httptest.New(t, app)
|
||||
|
||||
e.GET("/text").Expect().Status(iris.StatusOK).
|
||||
Body().Equal("text")
|
||||
|
||||
e.GET("/status").Expect().Status(iris.StatusBadGateway)
|
||||
|
||||
e.GET("/text/with/status/ok").Expect().Status(iris.StatusOK).
|
||||
Body().Equal("OK")
|
||||
|
||||
e.GET("/status/with/text/not/ok/first/second").Expect().Status(iris.StatusForbidden).
|
||||
Body().Equal("NOT_OK_firstsecond")
|
||||
// Author's note: <-- if that fails means that the last binder called for both input args,
|
||||
// see path_param_binder.go
|
||||
|
||||
e.GET("/text/and/content/type").Expect().Status(iris.StatusOK).
|
||||
ContentType("text/html", "utf-8").
|
||||
Body().Equal("<b>text</b>")
|
||||
|
||||
e.GET("/custom/response").Expect().Status(iris.StatusOK).
|
||||
ContentType("text/html", "utf-8").
|
||||
Body().Equal("<b>text</b>")
|
||||
e.GET("/custom/response/with/status/ok").Expect().Status(iris.StatusOK).
|
||||
ContentType("text/html", "utf-8").
|
||||
Body().Equal("<b>OK</b>")
|
||||
e.GET("/custom/response/with/status/not/ok").Expect().Status(iris.StatusInternalServerError).
|
||||
ContentType("text/html", "utf-8").
|
||||
Body().Equal("<b>internal server error</b>")
|
||||
|
||||
expectedResultFromCustomStruct := map[string]interface{}{
|
||||
"name": "Iris",
|
||||
"age": 2,
|
||||
}
|
||||
e.GET("/custom/struct").Expect().Status(iris.StatusOK).
|
||||
JSON().Equal(expectedResultFromCustomStruct)
|
||||
e.GET("/custom/struct/with/status/not/ok").Expect().Status(iris.StatusInternalServerError).
|
||||
JSON().Equal(expectedResultFromCustomStruct)
|
||||
e.GET("/custom/struct/with/content/type").Expect().Status(iris.StatusOK).
|
||||
ContentType("text/xml", "utf-8")
|
||||
e.GET("/custom/struct/with/error").Expect().Status(iris.StatusOK).
|
||||
JSON().Equal(expectedResultFromCustomStruct)
|
||||
e.GET("/custom/struct/with/error").WithQuery("err", true).Expect().
|
||||
Status(iris.StatusBadRequest). // the default status code if error is not nil
|
||||
// the content should be not JSON it should be the status code's text
|
||||
// it will fire the error's text
|
||||
Body().Equal("omit return of testCustomStruct and fire error")
|
||||
|
||||
e.GET("/custom/error/as/dispatcher").Expect().
|
||||
Status(iris.StatusBadRequest). // the default status code if error is not nil
|
||||
// the content should be not JSON it should be the status code's text
|
||||
// it will fire the error's text
|
||||
JSON().Equal(err{iris.StatusBadRequest, "this is my error as json"})
|
||||
|
||||
// its result is nil should give an empty response but content-type is set correctly.
|
||||
e.GET("/custom/nil/typed").Expect().
|
||||
Status(iris.StatusOK).ContentType(context.ContentJSONHeaderValue).Body().Empty()
|
||||
e.GET("/custom/nil/typed/ptr").Expect().
|
||||
Status(iris.StatusOK).ContentType(context.ContentJSONHeaderValue).Body().Empty()
|
||||
e.GET("/custom/nil/map").Expect().
|
||||
Status(iris.StatusOK).ContentType(context.ContentJSONHeaderValue).Body().Empty()
|
||||
e.GET("/custom/nil/struct").Expect().
|
||||
Status(iris.StatusOK).ContentType(context.ContentJSONHeaderValue).Body().Empty()
|
||||
}
|
||||
|
||||
type (
|
||||
testPreflightRequest struct {
|
||||
FailCode int `json:"fail_code"` // for the sake of the test.
|
||||
}
|
||||
|
||||
testPreflightResponse struct {
|
||||
Code int
|
||||
Message string
|
||||
}
|
||||
)
|
||||
|
||||
func (r testPreflightResponse) Preflight(ctx iris.Context) error {
|
||||
if r.Code == httptest.StatusInternalServerError {
|
||||
return fmt.Errorf("custom error")
|
||||
}
|
||||
|
||||
ctx.StatusCode(r.Code)
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestPreflightResult(t *testing.T) {
|
||||
app := iris.New()
|
||||
c := New()
|
||||
handler := c.Handler(func(in testPreflightRequest) testPreflightResponse {
|
||||
return testPreflightResponse{Code: in.FailCode, Message: http.StatusText(in.FailCode)}
|
||||
})
|
||||
app.Post("/", handler)
|
||||
handler2 := c.Handler(func(in testInput) (int, testOutput) {
|
||||
return httptest.StatusAccepted, testOutput{Name: in.Name}
|
||||
})
|
||||
app.Post("/alternative", handler2)
|
||||
|
||||
e := httptest.New(t, app)
|
||||
|
||||
expected1 := testPreflightResponse{Code: httptest.StatusOK, Message: "OK"}
|
||||
e.POST("/").WithJSON(testPreflightRequest{FailCode: expected1.Code}).
|
||||
Expect().Status(httptest.StatusOK).JSON().Equal(expected1)
|
||||
|
||||
expected2 := testPreflightResponse{Code: httptest.StatusBadRequest, Message: "Bad Request"}
|
||||
e.POST("/").WithJSON(testPreflightRequest{FailCode: expected2.Code}).
|
||||
Expect().Status(httptest.StatusBadRequest).JSON().Equal(expected2)
|
||||
|
||||
// Test error returned from Preflight.
|
||||
e.POST("/").WithJSON(testPreflightRequest{FailCode: httptest.StatusInternalServerError}).
|
||||
Expect().Status(httptest.StatusBadRequest).Body().Equal("custom error")
|
||||
|
||||
// Can be done without Preflight as the second output argument can be a status code.
|
||||
expected4 := testOutput{Name: "my_name"}
|
||||
e.POST("/alternative").WithJSON(testInput{expected4.Name}).
|
||||
Expect().Status(httptest.StatusAccepted).JSON().Equal(expected4)
|
||||
}
|
||||
|
||||
func TestResponseErr(t *testing.T) {
|
||||
app := iris.New()
|
||||
var expectedErr = errors.New("response error")
|
||||
|
||||
app.OnAnyErrorCode(func(ctx iris.Context) {
|
||||
err := ctx.GetErr()
|
||||
if err != expectedErr {
|
||||
t.Fatalf("expected error value does not match")
|
||||
}
|
||||
|
||||
ctx.WriteString(err.Error())
|
||||
})
|
||||
|
||||
app.ConfigureContainer().Get("/", func() Response {
|
||||
return Response{Code: iris.StatusBadGateway, Err: expectedErr}
|
||||
})
|
||||
|
||||
e := httptest.New(t, app)
|
||||
e.GET("/").Expect().Status(iris.StatusBadGateway).Body().Equal("response error")
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/kataras/iris/v12/context"
|
||||
"github.com/kataras/iris/context"
|
||||
)
|
||||
|
||||
type (
|
||||
|
||||
@@ -1,287 +0,0 @@
|
||||
package hero_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/kataras/iris/v12"
|
||||
. "github.com/kataras/iris/v12/hero"
|
||||
"github.com/kataras/iris/v12/httptest"
|
||||
)
|
||||
|
||||
// dynamic func
|
||||
type testUserStruct struct {
|
||||
ID int64 `json:"id" form:"id" url:"id"`
|
||||
Username string `json:"username" form:"username" url:"username"`
|
||||
}
|
||||
|
||||
func testBinderFunc(ctx iris.Context) testUserStruct {
|
||||
id, _ := ctx.Params().GetInt64("id")
|
||||
username := ctx.Params().Get("username")
|
||||
return testUserStruct{
|
||||
ID: id,
|
||||
Username: username,
|
||||
}
|
||||
}
|
||||
|
||||
// service
|
||||
type (
|
||||
// these testService and testServiceImpl could be in lowercase, unexported
|
||||
// but the `Say` method should be exported however we have those exported
|
||||
// because of the controller handler test.
|
||||
testService interface {
|
||||
Say(string) string
|
||||
}
|
||||
testServiceImpl struct {
|
||||
prefix string
|
||||
}
|
||||
)
|
||||
|
||||
func (s *testServiceImpl) Say(message string) string {
|
||||
return s.prefix + " " + message
|
||||
}
|
||||
|
||||
var (
|
||||
// binders, as user-defined
|
||||
testBinderFuncUserStruct = testBinderFunc
|
||||
testBinderService = &testServiceImpl{prefix: "say"}
|
||||
testBinderFuncParam = func(ctx iris.Context) string {
|
||||
return ctx.Params().Get("param")
|
||||
}
|
||||
|
||||
// consumers
|
||||
// a context as first input arg, which is not needed to be binded manually,
|
||||
// and a user struct which is binded to the input arg by the #1 func(ctx) any binder.
|
||||
testConsumeUserHandler = func(ctx iris.Context, user testUserStruct) {
|
||||
ctx.JSON(user)
|
||||
}
|
||||
|
||||
// just one input arg, the service which is binded by the #2 service binder.
|
||||
testConsumeServiceHandler = func(service testService) string {
|
||||
return service.Say("something")
|
||||
}
|
||||
// just one input arg, a standar string which is binded by the #3 func(ctx) any binder.
|
||||
testConsumeParamHandler = func(myParam string) string {
|
||||
return "param is: " + myParam
|
||||
}
|
||||
)
|
||||
|
||||
func TestHandler(t *testing.T) {
|
||||
Register(testBinderFuncUserStruct)
|
||||
Register(testBinderService)
|
||||
Register(testBinderFuncParam)
|
||||
var (
|
||||
h1 = Handler(testConsumeUserHandler)
|
||||
h2 = Handler(testConsumeServiceHandler)
|
||||
h3 = Handler(testConsumeParamHandler)
|
||||
)
|
||||
|
||||
testAppWithHeroHandlers(t, h1, h2, h3)
|
||||
}
|
||||
|
||||
func testAppWithHeroHandlers(t *testing.T, h1, h2, h3 iris.Handler) {
|
||||
app := iris.New()
|
||||
app.Get("/{id:int64}/{username:string}", h1)
|
||||
app.Get("/service", h2)
|
||||
app.Get("/param/{param:string}", h3)
|
||||
|
||||
expectedUser := testUserStruct{
|
||||
ID: 42,
|
||||
Username: "kataras",
|
||||
}
|
||||
|
||||
e := httptest.New(t, app)
|
||||
// 1
|
||||
e.GET(fmt.Sprintf("/%d/%s", expectedUser.ID, expectedUser.Username)).Expect().Status(httptest.StatusOK).
|
||||
JSON().Equal(expectedUser)
|
||||
// 2
|
||||
e.GET("/service").Expect().Status(httptest.StatusOK).
|
||||
Body().Equal("say something")
|
||||
// 3
|
||||
e.GET("/param/the_param_value").Expect().Status(httptest.StatusOK).
|
||||
Body().Equal("param is: the_param_value")
|
||||
}
|
||||
|
||||
// TestBindFunctionAsFunctionInputArgument tests to bind
|
||||
// a whole dynamic function based on the current context
|
||||
// as an input argument in the hero handler's function.
|
||||
func TestBindFunctionAsFunctionInputArgument(t *testing.T) {
|
||||
app := iris.New()
|
||||
postsBinder := func(ctx iris.Context) func(string) string {
|
||||
return ctx.PostValue // or FormValue, the same here.
|
||||
}
|
||||
|
||||
h := New(postsBinder).Handler(func(get func(string) string) string {
|
||||
// send the `ctx.PostValue/FormValue("username")` value
|
||||
// to the client.
|
||||
return get("username")
|
||||
})
|
||||
|
||||
app.Post("/", h)
|
||||
|
||||
e := httptest.New(t, app)
|
||||
|
||||
expectedUsername := "kataras"
|
||||
e.POST("/").WithFormField("username", expectedUsername).
|
||||
Expect().Status(iris.StatusOK).Body().Equal(expectedUsername)
|
||||
}
|
||||
|
||||
func TestPayloadBinding(t *testing.T) {
|
||||
h := New()
|
||||
|
||||
ptrHandler := h.Handler(func(input *testUserStruct /* ptr */) string {
|
||||
return input.Username
|
||||
})
|
||||
|
||||
valHandler := h.Handler(func(input testUserStruct) string {
|
||||
return input.Username
|
||||
})
|
||||
|
||||
app := iris.New()
|
||||
app.Get("/", ptrHandler)
|
||||
app.Post("/", ptrHandler)
|
||||
app.Post("/2", valHandler)
|
||||
|
||||
e := httptest.New(t, app)
|
||||
|
||||
// JSON
|
||||
e.POST("/").WithJSON(iris.Map{"username": "makis"}).Expect().Status(httptest.StatusOK).Body().Equal("makis")
|
||||
e.POST("/2").WithJSON(iris.Map{"username": "kataras"}).Expect().Status(httptest.StatusOK).Body().Equal("kataras")
|
||||
|
||||
// FORM (url-encoded)
|
||||
e.POST("/").WithFormField("username", "makis").Expect().Status(httptest.StatusOK).Body().Equal("makis")
|
||||
// FORM (multipart)
|
||||
e.POST("/").WithMultipart().WithFormField("username", "makis").Expect().Status(httptest.StatusOK).Body().Equal("makis")
|
||||
|
||||
// POST URL query.
|
||||
e.POST("/").WithQuery("username", "makis").Expect().Status(httptest.StatusOK).Body().Equal("makis")
|
||||
// GET URL query.
|
||||
e.GET("/").WithQuery("username", "makis").Expect().Status(httptest.StatusOK).Body().Equal("makis")
|
||||
}
|
||||
|
||||
/* Author's notes:
|
||||
If aksed or required by my company, make the following test to pass but think downsides of code complexity and performance-cost
|
||||
before begin the implementation of it.
|
||||
- Dependencies without depending on other values can be named "root-level dependencies"
|
||||
- Dependencies could be linked (a new .DependsOn?) to a "root-level dependency"(or by theirs same-level deps too?) with much
|
||||
more control if "root-level dependencies" are named, e.g.:
|
||||
b.Register("db", &myDBImpl{})
|
||||
b.Register("user_dep", func(db myDB) User{...}).DependsOn("db")
|
||||
b.Handler(func(user User) error{...})
|
||||
b.Handler(func(ctx iris.Context, reuseDB myDB) {...})
|
||||
Why linked over automatically? Because more than one dependency can implement the same input and
|
||||
end-user does not care about ordering the registered ones.
|
||||
Link with `DependsOn` SHOULD be optional, if exists then limit the available dependencies,
|
||||
`DependsOn` SHOULD accept comma-separated values, e.g. "db, otherdep" and SHOULD also work
|
||||
by calling it multiple times i.e `Depends("db").DependsOn("otherdep")`.
|
||||
Handlers should also be able to explicitly limit the list of
|
||||
their available dependencies per-handler, a `.DependsOn` feature SHOULD exist there too.
|
||||
|
||||
Also, note that with the new implementation a `*hero.Input` value can be accepted on dynamic dependencies,
|
||||
that value contains an `Options.Dependencies` field which lists all the registered dependencies,
|
||||
so, in theory, end-developers could achieve same results by hand-code(inside the dependency's function body).
|
||||
|
||||
26 Feb 2020. Gerasimos Maropoulos
|
||||
______________________________________________
|
||||
|
||||
29 Feb 2020. It's done.
|
||||
*/
|
||||
|
||||
type testMessage struct {
|
||||
Body string
|
||||
}
|
||||
|
||||
func TestDependentDependencies(t *testing.T) {
|
||||
b := New()
|
||||
b.Register(&testServiceImpl{prefix: "prefix:"})
|
||||
b.Register(func(service testService) testMessage {
|
||||
return testMessage{Body: service.Say("it is a deep") + " dependency"}
|
||||
})
|
||||
var (
|
||||
h1 = b.Handler(func(msg testMessage) string {
|
||||
return msg.Body
|
||||
})
|
||||
h2 = b.Handler(func(reuse testService) string {
|
||||
return reuse.Say("message")
|
||||
})
|
||||
)
|
||||
|
||||
app := iris.New()
|
||||
app.Get("/h1", h1)
|
||||
app.Get("/h2", h2)
|
||||
|
||||
e := httptest.New(t, app)
|
||||
e.GET("/h1").Expect().Status(httptest.StatusOK).Body().Equal("prefix: it is a deep dependency")
|
||||
e.GET("/h2").Expect().Status(httptest.StatusOK).Body().Equal("prefix: message")
|
||||
}
|
||||
|
||||
func TestHandlerPathParams(t *testing.T) {
|
||||
// See white box `TestPathParams` test too.
|
||||
// All cases should pass.
|
||||
app := iris.New()
|
||||
handler := func(id uint64) string {
|
||||
return fmt.Sprintf("%d", id)
|
||||
}
|
||||
|
||||
app.Party("/users").ConfigureContainer(func(api *iris.APIContainer) {
|
||||
api.Get("/{id:uint64}", handler)
|
||||
})
|
||||
|
||||
app.Party("/editors/{id:uint64}").ConfigureContainer(func(api *iris.APIContainer) {
|
||||
api.Get("/", handler)
|
||||
})
|
||||
|
||||
// should receive the last one, as we expected only one useful for MVC (there is a similar test there too).
|
||||
app.ConfigureContainer().Get("/{ownerID:uint64}/book/{booKID:uint64}", handler)
|
||||
|
||||
e := httptest.New(t, app)
|
||||
|
||||
for _, testReq := range []*httptest.Request{
|
||||
e.GET("/users/42"),
|
||||
e.GET("/editors/42"),
|
||||
e.GET("/1/book/42"),
|
||||
} {
|
||||
testReq.Expect().Status(httptest.StatusOK).Body().Equal("42")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegisterDependenciesFromContext(t *testing.T) {
|
||||
// Tests serve-time struct dependencies through a common Iris middleware.
|
||||
app := iris.New()
|
||||
app.Use(func(ctx iris.Context) {
|
||||
ctx.RegisterDependency(testUserStruct{Username: "kataras"})
|
||||
ctx.Next()
|
||||
})
|
||||
app.Use(func(ctx iris.Context) {
|
||||
ctx.RegisterDependency(&testServiceImpl{prefix: "say"})
|
||||
ctx.Next()
|
||||
})
|
||||
|
||||
app.ConfigureContainer(func(api *iris.APIContainer) {
|
||||
api.Get("/", func(u testUserStruct) string {
|
||||
return u.Username
|
||||
})
|
||||
|
||||
api.Get("/service", func(s *testServiceImpl) string {
|
||||
return s.Say("hello")
|
||||
})
|
||||
|
||||
// Note: we are not allowed to pass the service as an interface here
|
||||
// because the container will, correctly, panic because it will expect
|
||||
// a dependency to be registered before server ran.
|
||||
api.Get("/both", func(s *testServiceImpl, u testUserStruct) string {
|
||||
return s.Say(u.Username)
|
||||
})
|
||||
|
||||
api.Get("/non", func() string {
|
||||
return "nothing"
|
||||
})
|
||||
})
|
||||
|
||||
e := httptest.New(t, app)
|
||||
|
||||
e.GET("/").Expect().Status(httptest.StatusOK).Body().Equal("kataras")
|
||||
e.GET("/service").Expect().Status(httptest.StatusOK).Body().Equal("say hello")
|
||||
e.GET("/both").Expect().Status(httptest.StatusOK).Body().Equal("say kataras")
|
||||
e.GET("/non").Expect().Status(httptest.StatusOK).Body().Equal("nothing")
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
package hero
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/kataras/iris/v12/context"
|
||||
)
|
||||
|
||||
func TestPathParams(t *testing.T) {
|
||||
got := ""
|
||||
h := New()
|
||||
handler := h.Handler(func(firstname string, lastname string) {
|
||||
got = firstname + lastname
|
||||
})
|
||||
|
||||
h.Register(func(ctx *context.Context) func() string { return func() string { return "" } })
|
||||
handlerWithOther := h.Handler(func(f func() string, firstname string, lastname string) {
|
||||
got = f() + firstname + lastname
|
||||
})
|
||||
|
||||
handlerWithOtherBetweenThem := h.Handler(func(firstname string, f func() string, lastname string) {
|
||||
got = firstname + lastname
|
||||
})
|
||||
|
||||
ctx := context.NewContext(nil)
|
||||
ctx.Params().Set("firstname", "Gerasimos")
|
||||
ctx.Params().Set("lastname", "Maropoulos")
|
||||
handler(ctx)
|
||||
expected := "GerasimosMaropoulos"
|
||||
if got != expected {
|
||||
t.Fatalf("[0] expected the params 'firstname' + 'lastname' to be '%s' but got '%s'", expected, got)
|
||||
}
|
||||
|
||||
got = ""
|
||||
handlerWithOther(ctx)
|
||||
expected = "GerasimosMaropoulos"
|
||||
if got != expected {
|
||||
t.Fatalf("[1] expected the params 'firstname' + 'lastname' to be '%s' but got '%s'", expected, got)
|
||||
}
|
||||
|
||||
got = ""
|
||||
handlerWithOtherBetweenThem(ctx)
|
||||
expected = "GerasimosMaropoulos"
|
||||
if got != expected {
|
||||
t.Fatalf("[2] expected the params 'firstname' + 'lastname' to be '%s' but got '%s'", expected, got)
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"net"
|
||||
"reflect"
|
||||
|
||||
"github.com/kataras/iris/v12/context"
|
||||
"github.com/kataras/iris/context"
|
||||
)
|
||||
|
||||
func valueOf(v interface{}) reflect.Value {
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
package hero
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type testInterface interface {
|
||||
Get() string
|
||||
}
|
||||
|
||||
var testInterfaceTyp = reflect.TypeOf((*testInterface)(nil)).Elem()
|
||||
|
||||
type testImplPtr struct{}
|
||||
|
||||
func (*testImplPtr) Get() string { return "get_ptr" }
|
||||
|
||||
type testImpl struct{}
|
||||
|
||||
func (testImpl) Get() string { return "get" }
|
||||
|
||||
func TestEqualTypes(t *testing.T) {
|
||||
of := reflect.TypeOf
|
||||
|
||||
var tests = map[reflect.Type]reflect.Type{
|
||||
of("string"): of("input"),
|
||||
of(42): of(10),
|
||||
testInterfaceTyp: testInterfaceTyp,
|
||||
of(new(testImplPtr)): testInterfaceTyp,
|
||||
of(new(testImpl)): testInterfaceTyp,
|
||||
of(testImpl{}): testInterfaceTyp,
|
||||
}
|
||||
|
||||
for binding, input := range tests {
|
||||
if !equalTypes(binding, input) {
|
||||
t.Fatalf("expected type of: %s to be equal to the binded one of: %s", input, binding)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/kataras/iris/v12/context"
|
||||
"github.com/kataras/iris/context"
|
||||
)
|
||||
|
||||
// Sorter is the type for sort customization of a struct's fields
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
package hero_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/kataras/iris/v12"
|
||||
. "github.com/kataras/iris/v12/hero"
|
||||
"github.com/kataras/iris/v12/httptest"
|
||||
)
|
||||
|
||||
type testStruct struct {
|
||||
Ctx iris.Context
|
||||
}
|
||||
|
||||
func (c *testStruct) MyHandler(name string) testOutput {
|
||||
return fn(42, testInput{Name: name})
|
||||
}
|
||||
|
||||
func (c *testStruct) MyHandler2(id int, in testInput) testOutput {
|
||||
return fn(id, in)
|
||||
}
|
||||
|
||||
func (c *testStruct) MyHandler3(in testInput) testOutput {
|
||||
return fn(42, in)
|
||||
}
|
||||
|
||||
func (c *testStruct) MyHandler4() {
|
||||
c.Ctx.WriteString("MyHandler4")
|
||||
}
|
||||
|
||||
func TestStruct(t *testing.T) {
|
||||
app := iris.New()
|
||||
|
||||
b := New()
|
||||
s := b.Struct(&testStruct{}, 0)
|
||||
|
||||
postHandler := s.MethodHandler("MyHandler", 0) // fallbacks such as {path} and {string} should registered first when same path.
|
||||
app.Post("/{name:string}", postHandler)
|
||||
postHandler2 := s.MethodHandler("MyHandler2", 0)
|
||||
app.Post("/{id:int}", postHandler2)
|
||||
postHandler3 := s.MethodHandler("MyHandler3", 0)
|
||||
app.Post("/myHandler3", postHandler3)
|
||||
getHandler := s.MethodHandler("MyHandler4", 0)
|
||||
app.Get("/myHandler4", getHandler)
|
||||
|
||||
e := httptest.New(t, app)
|
||||
e.POST("/" + input.Name).Expect().Status(httptest.StatusOK).JSON().Equal(expectedOutput)
|
||||
path := fmt.Sprintf("/%d", expectedOutput.ID)
|
||||
e.POST(path).WithJSON(input).Expect().Status(httptest.StatusOK).JSON().Equal(expectedOutput)
|
||||
e.POST("/myHandler3").WithJSON(input).Expect().Status(httptest.StatusOK).JSON().Equal(expectedOutput)
|
||||
e.GET("/myHandler4").Expect().Status(httptest.StatusOK).Body().Equal("MyHandler4")
|
||||
}
|
||||
|
||||
type testStructErrorHandler struct{}
|
||||
|
||||
func (s *testStructErrorHandler) HandleError(ctx iris.Context, err error) {
|
||||
ctx.StopWithError(httptest.StatusConflict, err)
|
||||
}
|
||||
|
||||
func (s *testStructErrorHandler) Handle(errText string) error {
|
||||
return errors.New(errText)
|
||||
}
|
||||
|
||||
func TestStructErrorHandler(t *testing.T) {
|
||||
b := New()
|
||||
s := b.Struct(&testStructErrorHandler{}, 0)
|
||||
|
||||
app := iris.New()
|
||||
app.Get("/{errText:string}", s.MethodHandler("Handle", 0))
|
||||
|
||||
expectedErrText := "an error"
|
||||
e := httptest.New(t, app)
|
||||
e.GET("/" + expectedErrText).Expect().Status(httptest.StatusConflict).Body().Equal(expectedErrText)
|
||||
}
|
||||
|
||||
type (
|
||||
testServiceInterface1 interface {
|
||||
Parse() string
|
||||
}
|
||||
|
||||
testServiceImpl1 struct {
|
||||
inner string
|
||||
}
|
||||
|
||||
testServiceInterface2 interface {
|
||||
}
|
||||
|
||||
testServiceImpl2 struct {
|
||||
tf int
|
||||
}
|
||||
|
||||
testControllerDependenciesSorter struct {
|
||||
Service2 testServiceInterface2
|
||||
Service1 testServiceInterface1
|
||||
}
|
||||
)
|
||||
|
||||
func (s *testServiceImpl1) Parse() string {
|
||||
return s.inner
|
||||
}
|
||||
|
||||
func (c *testControllerDependenciesSorter) Index() string {
|
||||
return fmt.Sprintf("%#+v | %#+v", c.Service1, c.Service2)
|
||||
}
|
||||
|
||||
func TestStructFieldsSorter(t *testing.T) { // see https://github.com/kataras/iris/issues/1343
|
||||
b := New()
|
||||
b.Register(&testServiceImpl1{"parser"})
|
||||
b.Register(&testServiceImpl2{24})
|
||||
s := b.Struct(&testControllerDependenciesSorter{}, 0)
|
||||
|
||||
app := iris.New()
|
||||
app.Get("/", s.MethodHandler("Index", 0))
|
||||
|
||||
e := httptest.New(t, app)
|
||||
|
||||
expectedBody := `&hero_test.testServiceImpl1{inner:"parser"} | &hero_test.testServiceImpl2{tf:24}`
|
||||
e.GET("/").Expect().Status(httptest.StatusOK).Body().Equal(expectedBody)
|
||||
}
|
||||
Reference in New Issue
Block a user