package echo import ( "bytes" "io" "mime/multipart" "net/http" "net/http/httptest" "reflect" "strings" "testing" "time" "github.com/stretchr/testify/assert" ) type ( bindTestStruct struct { I int I8 int8 I16 int16 I32 int32 I64 int64 UI uint UI8 uint8 UI16 uint16 UI32 uint32 UI64 uint64 B bool F32 float32 F64 float64 S string cantSet string DoesntExist string T Timestamp Tptr *Timestamp } Timestamp time.Time ) func (t *Timestamp) UnmarshalParam(src string) error { ts, err := time.Parse(time.RFC3339, src) *t = Timestamp(ts) return err } func (t bindTestStruct) GetCantSet() string { return t.cantSet } var values = map[string][]string{ "I": {"0"}, "I8": {"8"}, "I16": {"16"}, "I32": {"32"}, "I64": {"64"}, "UI": {"0"}, "UI8": {"8"}, "UI16": {"16"}, "UI32": {"32"}, "UI64": {"64"}, "B": {"true"}, "F32": {"32.5"}, "F64": {"64.5"}, "S": {"test"}, "cantSet": {"test"}, "T": {"2016-12-06T19:09:05+01:00"}, "Tptr": {"2016-12-06T19:09:05+01:00"}, } func TestBindJSON(t *testing.T) { testBindOkay(t, strings.NewReader(userJSON), MIMEApplicationJSON) testBindError(t, strings.NewReader(invalidContent), MIMEApplicationJSON) } func TestBindXML(t *testing.T) { testBindOkay(t, strings.NewReader(userXML), MIMEApplicationXML) testBindError(t, strings.NewReader(invalidContent), MIMEApplicationXML) } func TestBindForm(t *testing.T) { testBindOkay(t, strings.NewReader(userForm), MIMEApplicationForm) testBindError(t, nil, MIMEApplicationForm) e := New() req, _ := http.NewRequest(POST, "/", strings.NewReader(userForm)) rec := httptest.NewRecorder() c := e.NewContext(req, rec) req.Header.Set(HeaderContentType, MIMEApplicationForm) obj := []struct{ Field string }{} err := c.Bind(&obj) assert.Error(t, err) } func TestBindQueryParams(t *testing.T) { e := New() req, _ := http.NewRequest(GET, "/?id=1&name=Jon Snow", nil) rec := httptest.NewRecorder() c := e.NewContext(req, rec) u := new(user) err := c.Bind(u) if assert.NoError(t, err) { assert.Equal(t, 1, u.ID) assert.Equal(t, "Jon Snow", u.Name) } } func TestBindUnmarshalParam(t *testing.T) { e := New() req, _ := http.NewRequest(GET, "/?ts=2016-12-06T19:09:05Z", nil) rec := httptest.NewRecorder() c := e.NewContext(req, rec) result := struct { T Timestamp `query:"ts"` }{} err := c.Bind(&result) if assert.NoError(t, err) { // assert.Equal(t, Timestamp(reflect.TypeOf(&Timestamp{}), time.Date(2016, 12, 6, 19, 9, 5, 0, time.UTC)), result.T) assert.Equal(t, Timestamp(time.Date(2016, 12, 6, 19, 9, 5, 0, time.UTC)), result.T) } } func TestBindUnmarshalParamPtr(t *testing.T) { e := New() req, _ := http.NewRequest(GET, "/?ts=2016-12-06T19:09:05Z", nil) rec := httptest.NewRecorder() c := e.NewContext(req, rec) result := struct { Tptr *Timestamp `query:"ts"` }{} err := c.Bind(&result) if assert.NoError(t, err) { assert.Equal(t, Timestamp(time.Date(2016, 12, 6, 19, 9, 5, 0, time.UTC)), *result.Tptr) } } func TestBindMultipartForm(t *testing.T) { body := new(bytes.Buffer) mw := multipart.NewWriter(body) mw.WriteField("id", "1") mw.WriteField("name", "Jon Snow") mw.Close() testBindOkay(t, body, mw.FormDataContentType()) } func TestBindUnsupportedMediaType(t *testing.T) { testBindError(t, strings.NewReader(invalidContent), MIMEApplicationJSON) } func TestBindbindData(t *testing.T) { ts := new(bindTestStruct) b := new(DefaultBinder) b.bindData(ts, values, "form") assertBindTestStruct(t, ts) } func TestBindSetWithProperType(t *testing.T) { ts := new(bindTestStruct) typ := reflect.TypeOf(ts).Elem() val := reflect.ValueOf(ts).Elem() for i := 0; i < typ.NumField(); i++ { typeField := typ.Field(i) structField := val.Field(i) if !structField.CanSet() { continue } if len(values[typeField.Name]) == 0 { continue } val := values[typeField.Name][0] err := setWithProperType(typeField.Type.Kind(), val, structField) assert.NoError(t, err) } assertBindTestStruct(t, ts) type foo struct { Bar bytes.Buffer } v := &foo{} typ = reflect.TypeOf(v).Elem() val = reflect.ValueOf(v).Elem() assert.Error(t, setWithProperType(typ.Field(0).Type.Kind(), "5", val.Field(0))) } func TestBindSetFields(t *testing.T) { ts := new(bindTestStruct) val := reflect.ValueOf(ts).Elem() // Int if assert.NoError(t, setIntField("5", 0, val.FieldByName("I"))) { assert.Equal(t, 5, ts.I) } if assert.NoError(t, setIntField("", 0, val.FieldByName("I"))) { assert.Equal(t, 0, ts.I) } // Uint if assert.NoError(t, setUintField("10", 0, val.FieldByName("UI"))) { assert.Equal(t, uint(10), ts.UI) } if assert.NoError(t, setUintField("", 0, val.FieldByName("UI"))) { assert.Equal(t, uint(0), ts.UI) } // Float if assert.NoError(t, setFloatField("15.5", 0, val.FieldByName("F32"))) { assert.Equal(t, float32(15.5), ts.F32) } if assert.NoError(t, setFloatField("", 0, val.FieldByName("F32"))) { assert.Equal(t, float32(0.0), ts.F32) } // Bool if assert.NoError(t, setBoolField("true", val.FieldByName("B"))) { assert.Equal(t, true, ts.B) } if assert.NoError(t, setBoolField("", val.FieldByName("B"))) { assert.Equal(t, false, ts.B) } if assert.NoError(t, unmarshalField("2016-12-06T19:09:05Z", val.FieldByName("T"))) { assert.Equal(t, Timestamp(time.Date(2016, 12, 6, 19, 9, 5, 0, time.UTC)), ts.T) } } func assertBindTestStruct(t *testing.T, ts *bindTestStruct) { assert.Equal(t, 0, ts.I) assert.Equal(t, int8(8), ts.I8) assert.Equal(t, int16(16), ts.I16) assert.Equal(t, int32(32), ts.I32) assert.Equal(t, int64(64), ts.I64) assert.Equal(t, uint(0), ts.UI) assert.Equal(t, uint8(8), ts.UI8) assert.Equal(t, uint16(16), ts.UI16) assert.Equal(t, uint32(32), ts.UI32) assert.Equal(t, uint64(64), ts.UI64) assert.Equal(t, true, ts.B) assert.Equal(t, float32(32.5), ts.F32) assert.Equal(t, float64(64.5), ts.F64) assert.Equal(t, "test", ts.S) assert.Equal(t, "", ts.GetCantSet()) } func testBindOkay(t *testing.T, r io.Reader, ctype string) { e := New() req, _ := http.NewRequest(POST, "/", r) rec := httptest.NewRecorder() c := e.NewContext(req, rec) req.Header.Set(HeaderContentType, ctype) u := new(user) err := c.Bind(u) if assert.NoError(t, err) { assert.Equal(t, 1, u.ID) assert.Equal(t, "Jon Snow", u.Name) } } func testBindError(t *testing.T, r io.Reader, ctype string) { e := New() req, _ := http.NewRequest(POST, "/", r) rec := httptest.NewRecorder() c := e.NewContext(req, rec) req.Header.Set(HeaderContentType, ctype) u := new(user) err := c.Bind(u) switch { case strings.HasPrefix(ctype, MIMEApplicationJSON), strings.HasPrefix(ctype, MIMEApplicationXML), strings.HasPrefix(ctype, MIMEApplicationForm), strings.HasPrefix(ctype, MIMEMultipartForm): if assert.IsType(t, new(HTTPError), err) { assert.Equal(t, http.StatusBadRequest, err.(*HTTPError).Code) } default: if assert.IsType(t, new(HTTPError), err) { assert.Equal(t, ErrUnsupportedMediaType, err) } } }