1
0
mirror of https://github.com/ggicci/httpin.git synced 2024-12-02 09:01:33 +02:00

refactor: introduce goconvey for testing

This commit is contained in:
Ggicci 2021-04-28 19:05:24 +08:00
parent 994da584db
commit 7f785a0750
12 changed files with 552 additions and 93 deletions

9
directives.go Normal file
View File

@ -0,0 +1,9 @@
package httpin
type Directive struct {
Key string // e.g. query.page, header.x-api-token
}
func BuildDirective(key string) (*Directive, error) {
return &Directive{Key: key}, nil
}

2
go.mod
View File

@ -1,3 +1,5 @@
module github.com/ggicci/httpin
go 1.16
require github.com/smartystreets/goconvey v1.6.4 // indirect

14
go.sum Normal file
View File

@ -0,0 +1,14 @@
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384 h1:TFlARGu6Czu1z7q93HTxcP1P+/ZFC/IKythI5RzrnRg=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=

View File

@ -6,6 +6,9 @@ import (
"net/http"
)
type JSON struct{}
type XML struct{}
func New(inputStruct interface{}) Middleware {
engine, err := NewEngine(inputStruct)
if err != nil {

4
kv.go
View File

@ -69,6 +69,10 @@ func isTimeType(typ reflect.Type) bool {
return typ == timeType
}
func isArrayType(typ reflect.Type) bool {
return typ.Kind() == reflect.Array || typ.Kind() == reflect.Slice
}
func setField(fv reflect.Value, formValue []string) error {
if len(formValue) == 0 {
// TODO(ggicci): throw an error if decorator like "required" set?

View File

@ -12,48 +12,61 @@ import (
)
type Pagination struct {
Page int `query:"page"`
PerPage int `query:"per_page"`
Page int `in:"query.page"`
PerPage int `in:"query.per_page"`
}
type Authorization struct {
AccessToken string `in:"query.access_token,header.x-api-token"`
}
// ChaosQuery is designed to make the normal case test coverage higher.
type ChaosQuery struct {
// Basic Types
BoolValue bool `query:"bool"`
IntValue int `query:"int"`
Int8Value int8 `query:"int8"`
Int16Value int16 `query:"int16"`
Int32Value int32 `query:"int32"`
Int64Value int64 `query:"int64"`
UintValue uint `query:"uint"`
Uint8Value uint8 `query:"uint8"`
Uint16Value uint16 `query:"uint16"`
Uint32Value uint32 `query:"uint32"`
Uint64Value uint64 `query:"uint64"`
Float32Value float32 `query:"float32"`
Float64Value float64 `query:"float64"`
Complex64Value complex64 `query:"complex64"`
Complex128Value complex128 `query:"complex128"`
StringValue string `query:"string"`
BoolValue bool `in:"query.bool"`
IntValue int `in:"query.int"`
Int8Value int8 `in:"query.int8"`
Int16Value int16 `in:"query.int16"`
Int32Value int32 `in:"query.int32"`
Int64Value int64 `in:"query.int64"`
UintValue uint `in:"query.uint"`
Uint8Value uint8 `in:"query.uint8"`
Uint16Value uint16 `in:"query.uint16"`
Uint32Value uint32 `in:"query.uint32"`
Uint64Value uint64 `in:"query.uint64"`
Float32Value float32 `in:"query.float32"`
Float64Value float64 `in:"query.float64"`
Complex64Value complex64 `in:"query.complex64"`
Complex128Value complex128 `in:"query.complex128"`
StringValue string `in:"query.string"`
// Time Type
TimeValue time.Time `query:"time"`
TimeValue time.Time `in:"query.time"`
// Array
BoolList []bool `query:"bools"`
IntList []int `query:"ints"`
FloatList []float64 `query:"floats"`
StringList []string `query:"strings"`
TimeList []time.Time `query:"times"`
BoolList []bool `in:"query.bools"`
IntList []int `in:"query.ints"`
FloatList []float64 `in:"query.floats"`
StringList []string `in:"query.strings"`
TimeList []time.Time `in:"query.times"`
}
type ProductLocation struct {
Area string `json:"area" xml:"area"`
Floor int `json:"fl" xml:"fl"`
Number int `json:"no" xml:"no"`
}
type ProductQuery struct {
CreatedAt time.Time `query:"created_at"`
Color string `query:"color"`
IsSoldout bool `query:"is_soldout"`
SortBy []string `query:"sort_by"`
SortDesc []bool `query:"sort_desc"`
CreatedAt time.Time `in:"query.created_at,required"`
Color string `in:"query.color"`
IsSoldout bool `in:"query.is_soldout"`
SortBy []string `in:"query.sort_by"`
SortDesc []bool `in:"query.sort_desc"`
Authorization
Pagination
Locations []ProductLocation `in:"body,json"`
}
type ObjectID struct {
@ -64,13 +77,13 @@ type ObjectID struct {
}
type Cursor struct {
AfterMarker ObjectID `query:"after"`
BeforeMarker ObjectID `query:"before"`
Limit int `query:"limit"`
AfterMarker ObjectID `in:"query.after"`
BeforeMarker ObjectID `in:"query.before"`
Limit int `in:"query.limit"`
}
type MessageQuery struct {
UserId string `query:"uid"`
UserId string `in:"query.uid"`
Cursor
}
@ -80,7 +93,7 @@ type PositionXY struct {
}
type PointsQuery struct {
Positions []PositionXY `query:"positions"`
Positions []PositionXY `in:"query.positions"`
}
func TestKV_NormalCase(t *testing.T) {

1
patch.go Normal file
View File

@ -0,0 +1 @@
package httpin

9
patch_test.go Normal file
View File

@ -0,0 +1,9 @@
package httpin_test
import "github.com/ggicci/httpin"
type ProductPatch struct {
Title httpin.String `json:"title"`
Color httpin.String `json:"color"`
Quantity httpin.Int `json:"quantity"`
}

View File

@ -1,7 +1,7 @@
package httpin
import (
"bytes"
"errors"
"fmt"
"reflect"
"strings"
@ -11,62 +11,51 @@ type Resolver interface {
Resolve([]byte) interface{}
}
type ResolverContext struct {
Source string // e.g. query, header, body
Key string // e.g. page, x-api-token
resolver Resolver
type FieldResolver struct {
Type reflect.Type
Field reflect.StructField
Path []string
Directives []*Directive
Fields []*FieldResolver
}
type TypeResolver struct {
Type reflect.Type
Field reflect.StructField
Resolvers []ResolverContext
Path []string
Fields []TypeResolver
}
func (r *TypeResolver) IsRoot() bool {
func (r *FieldResolver) IsRoot() bool {
return r.Field.Name == ""
}
func BuildTypeResolver(t reflect.Type) (TypeResolver, error) {
return buildTypeResolver(t)
}
func (r *TypeResolver) dump(buffer *bytes.Buffer, indent int) {
// indent
buffer.WriteString(fmt.Sprintf("%s-", strings.Repeat(" ", indent)))
// field name
if !r.IsRoot() {
buffer.WriteString(" [" + r.Field.Name + "]")
// BuildFieldResolver builds a resolver for the specified struct type.
// Which helps resolving fields data from input sources.
func BuildFieldResolver(t reflect.Type) (*FieldResolver, error) {
if t.Kind() != reflect.Struct {
return nil, errors.New("input type must be struct")
}
// type
buffer.WriteString(" " + r.Type.String())
buffer.WriteString(fmt.Sprintf(" (%d)", len(r.Fields)))
buffer.WriteString("\n")
for _, field := range r.Fields {
field.dump(buffer, indent+4)
root := &FieldResolver{Type: t}
for i := 0; i < t.NumField(); i++ {
fieldResolver, err := buildFieldResolver(root, t.Field(i))
if err != nil {
return root, err
}
root.Fields = append(root.Fields, fieldResolver)
}
return root, nil
}
func (t *TypeResolver) DumpTree() string {
var buffer bytes.Buffer
buffer.WriteString("\n")
t.dump(&buffer, 0)
return string(buffer.Bytes())
}
func buildTypeResolver(t reflect.Type) (TypeResolver, error) {
root := TypeResolver{
Type: t,
Field: reflect.StructField{},
Resolvers: nil,
Path: []string{},
Fields: make([]TypeResolver, 0),
func buildFieldResolver(parent *FieldResolver, field reflect.StructField) (*FieldResolver, error) {
t := field.Type
root := &FieldResolver{
Type: t,
Field: field,
Path: make([]string, len(parent.Path)+1),
}
copy(root.Path, parent.Path)
root.Path[len(root.Path)-1] = field.Name
directives, err := parseStructTag(field)
if err != nil {
return nil, fmt.Errorf("parse struct tag error: %w", err)
}
root.Directives = directives
if isBasicType(t) {
return root, nil
@ -76,15 +65,17 @@ func buildTypeResolver(t reflect.Type) (TypeResolver, error) {
return root, nil
}
if isArrayType(t) {
return root, nil
}
if t.Kind() == reflect.Struct {
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
child, err := buildTypeResolver(field.Type)
fieldResolver, err := buildFieldResolver(root, t.Field(i))
if err != nil {
return root, err
}
child.Field = field
root.Fields = append(root.Fields, child)
root.Fields = append(root.Fields, fieldResolver)
}
return root, nil
@ -92,3 +83,22 @@ func buildTypeResolver(t reflect.Type) (TypeResolver, error) {
return root, UnsupportedTypeError{Type: t}
}
func parseStructTag(field reflect.StructField) ([]*Directive, error) {
directives := make([]*Directive, 0)
// Parse and build resolvers from field struct tag. Tag examples:
// "query.name"
// "query.access_token,header.x-api-token"
inTag := field.Tag.Get("in")
if inTag == "" {
return directives, nil // skip
}
for _, key := range strings.Split(inTag, ",") {
directive, err := BuildDirective(key)
if err != nil {
return nil, err
}
directives = append(directives, directive)
}
return directives, nil
}

View File

@ -1,6 +1,7 @@
package httpin_test
import (
"encoding/json"
"reflect"
"testing"
@ -8,15 +9,11 @@ import (
)
func TestResolver_Build(t *testing.T) {
resolver, err := httpin.BuildTypeResolver(reflect.TypeOf(ProductQuery{}))
resolver, err := httpin.BuildFieldResolver(reflect.TypeOf(ProductQuery{}))
if err != nil {
t.Error("build type resolver failed")
t.Error("build resolver failed:", err)
t.Fail()
}
debug := resolver.DumpTree()
if len(debug) == 0 {
t.Error("empty tree")
t.Fail()
}
t.Log(debug)
jsonContent, _ := json.Marshal(resolver)
t.Log(string(jsonContent))
}

277
types.go
View File

@ -1,173 +1,448 @@
package httpin
import "time"
import (
"encoding/json"
"time"
)
type Bool struct {
Value bool
Valid bool // Valid is true if the corresponding key were found in the source
}
func (t Bool) MarshalJSON() ([]byte, error) {
return json.Marshal(t.Value)
}
func (t *Bool) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, &t.Value)
}
type Int struct {
Value int
Valid bool
}
func (t Int) MarshalJSON() ([]byte, error) {
return json.Marshal(t.Value)
}
func (t *Int) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, &t.Value)
}
type Int8 struct {
Value int8
Valid bool
}
func (t Int8) MarshalJSON() ([]byte, error) {
return json.Marshal(t.Value)
}
func (t *Int8) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, &t.Value)
}
type Int16 struct {
Value int16
Valid bool
}
func (t Int16) MarshalJSON() ([]byte, error) {
return json.Marshal(t.Value)
}
func (t *Int16) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, &t.Value)
}
type Int32 struct {
Value int32
Valid bool
}
func (t Int32) MarshalJSON() ([]byte, error) {
return json.Marshal(t.Value)
}
func (t *Int32) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, &t.Value)
}
type Int64 struct {
Value int64
Valid bool
}
func (t Int64) MarshalJSON() ([]byte, error) {
return json.Marshal(t.Value)
}
func (t *Int64) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, &t.Value)
}
type Uint struct {
Value uint
Valid bool
}
func (t Uint) MarshalJSON() ([]byte, error) {
return json.Marshal(t.Value)
}
func (t *Uint) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, &t.Value)
}
type Uint8 struct {
Value uint8
Valid bool
}
func (t Uint8) MarshalJSON() ([]byte, error) {
return json.Marshal(t.Value)
}
func (t *Uint8) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, &t.Value)
}
type Uint16 struct {
Value uint16
Valid bool
}
func (t Uint16) MarshalJSON() ([]byte, error) {
return json.Marshal(t.Value)
}
func (t *Uint16) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, &t.Value)
}
type Uint32 struct {
Value uint32
Valid bool
}
func (t Uint32) MarshalJSON() ([]byte, error) {
return json.Marshal(t.Value)
}
func (t *Uint32) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, &t.Value)
}
type Uint64 struct {
Value uint64
Valid bool
}
func (t Uint64) MarshalJSON() ([]byte, error) {
return json.Marshal(t.Value)
}
func (t *Uint64) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, &t.Value)
}
type Float32 struct {
Value float32
Valid bool
}
func (t Float32) MarshalJSON() ([]byte, error) {
return json.Marshal(t.Value)
}
func (t *Float32) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, &t.Value)
}
type Float64 struct {
Value float64
Valid bool
}
func (t Float64) MarshalJSON() ([]byte, error) {
return json.Marshal(t.Value)
}
func (t *Float64) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, &t.Value)
}
type Complex64 struct {
Value complex64
Valid bool
}
func (t Complex64) MarshalJSON() ([]byte, error) {
return json.Marshal(t.Value)
}
func (t *Complex64) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, &t.Value)
}
type Complex128 struct {
Value complex128
Valid bool
}
func (t Complex128) MarshalJSON() ([]byte, error) {
return json.Marshal(t.Value)
}
func (t *Complex128) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, &t.Value)
}
type String struct {
Value string
Valid bool
}
func (t String) MarshalJSON() ([]byte, error) {
return json.Marshal(t.Value)
}
func (t *String) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, &t.Value)
}
type Time struct {
Value time.Time
Valid bool
}
func (t Time) MarshalJSON() ([]byte, error) {
return json.Marshal(t.Value)
}
func (t *Time) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, &t.Value)
}
type BoolArray struct {
Value []bool
Valid bool
}
func (t BoolArray) MarshalJSON() ([]byte, error) {
return json.Marshal(t.Value)
}
func (t *BoolArray) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, &t.Value)
}
type IntArray struct {
Value []int
Valid bool
}
func (t IntArray) MarshalJSON() ([]byte, error) {
return json.Marshal(t.Value)
}
func (t *IntArray) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, &t.Value)
}
type Int8Array struct {
Value []int8
Valid bool
}
func (t Int8Array) MarshalJSON() ([]byte, error) {
return json.Marshal(t.Value)
}
func (t *Int8Array) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, &t.Value)
}
type Int16Array struct {
Value []int16
Valid bool
}
func (t Int16Array) MarshalJSON() ([]byte, error) {
return json.Marshal(t.Value)
}
func (t *Int16Array) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, &t.Value)
}
type Int32Array struct {
Value []int32
Valid bool
}
func (t Int32Array) MarshalJSON() ([]byte, error) {
return json.Marshal(t.Value)
}
func (t *Int32Array) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, &t.Value)
}
type Int64Array struct {
Value []int64
Valid bool
}
func (t Int64Array) MarshalJSON() ([]byte, error) {
return json.Marshal(t.Value)
}
func (t *Int64Array) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, &t.Value)
}
type UintArray struct {
Value []uint
Valid bool
}
func (t UintArray) MarshalJSON() ([]byte, error) {
return json.Marshal(t.Value)
}
func (t *UintArray) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, &t.Value)
}
type Uint8Array struct {
Value []uint8
Valid bool
}
func (t Uint8Array) MarshalJSON() ([]byte, error) {
return json.Marshal(t.Value)
}
func (t *Uint8Array) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, &t.Value)
}
type Uint16Array struct {
Value []uint16
Valid bool
}
func (t Uint16Array) MarshalJSON() ([]byte, error) {
return json.Marshal(t.Value)
}
func (t *Uint16Array) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, &t.Value)
}
type Uint32Array struct {
Value []uint32
Valid bool
}
func (t Uint32Array) MarshalJSON() ([]byte, error) {
return json.Marshal(t.Value)
}
func (t *Uint32Array) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, &t.Value)
}
type Uint64Array struct {
Value []uint64
Valid bool
}
func (t Uint64Array) MarshalJSON() ([]byte, error) {
return json.Marshal(t.Value)
}
func (t *Uint64Array) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, &t.Value)
}
type Float32Array struct {
Value []float32
Valid bool
}
func (t Float32Array) MarshalJSON() ([]byte, error) {
return json.Marshal(t.Value)
}
func (t *Float32Array) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, &t.Value)
}
type Float64Array struct {
Value []float64
Valid bool
}
func (t Float64Array) MarshalJSON() ([]byte, error) {
return json.Marshal(t.Value)
}
func (t *Float64Array) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, &t.Value)
}
type Complex64Array struct {
Value []complex64
Valid bool
}
func (t Complex64Array) MarshalJSON() ([]byte, error) {
return json.Marshal(t.Value)
}
func (t *Complex64Array) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, &t.Value)
}
type Complex128Array struct {
Value []complex128
Valid bool
}
func (t Complex128Array) MarshalJSON() ([]byte, error) {
return json.Marshal(t.Value)
}
func (t *Complex128Array) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, &t.Value)
}
type StringArray struct {
Value []string
Valid bool
}
func (t StringArray) MarshalJSON() ([]byte, error) {
return json.Marshal(t.Value)
}
func (t *StringArray) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, &t.Value)
}
type TimeArray struct {
Value []time.Time
Valid bool
}
func (t TimeArray) MarshalJSON() ([]byte, error) {
return json.Marshal(t.Value)
}
func (t *TimeArray) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, &t.Value)
}

122
types_test.go Normal file
View File

@ -0,0 +1,122 @@
package httpin_test
import (
"encoding/json"
"testing"
"time"
"github.com/ggicci/httpin"
. "github.com/smartystreets/goconvey/convey"
)
func TestTypes_Bool(t *testing.T) {
var v httpin.Bool
Convey("Marshal Bool", t, func() {
So(json.Unmarshal([]byte("true"), &v), ShouldBeNil)
So(v.Value, ShouldBeTrue)
So(v.Valid, ShouldBeFalse)
})
Convey("Unmarshal Bool", t, func() {
bs, err := json.Marshal(v)
So(err, ShouldBeNil)
So(bs, ShouldResemble, []byte("true"))
})
}
func TestTypes_Int(t *testing.T) {
var v httpin.Int
Convey("Marshal Int", t, func() {
So(json.Unmarshal([]byte("2015"), &v), ShouldBeNil)
So(v.Value, ShouldEqual, 2015)
So(v.Valid, ShouldBeFalse)
})
Convey("Unmarshal Int", t, func() {
bs, err := json.Marshal(v)
So(err, ShouldBeNil)
So(bs, ShouldResemble, []byte("2015"))
})
}
func TestTypes_Uint(t *testing.T) {
var v httpin.Uint
Convey("Marshal Int", t, func() {
So(json.Unmarshal([]byte("2045"), &v), ShouldBeNil)
So(v.Value, ShouldEqual, 2045)
So(v.Valid, ShouldBeFalse)
})
Convey("Unmarshal Int", t, func() {
bs, err := json.Marshal(v)
So(err, ShouldBeNil)
So(bs, ShouldResemble, []byte("2045"))
})
}
func TestTypes_Float32(t *testing.T) {
var v httpin.Float32
Convey("Marshal Float32", t, func() {
So(json.Unmarshal([]byte("3.1415"), &v), ShouldBeNil)
So(v.Value, ShouldEqual, 3.1415)
So(v.Valid, ShouldBeFalse)
})
Convey("Unmarshal Float32", t, func() {
bs, err := json.Marshal(v)
So(err, ShouldBeNil)
So(bs, ShouldResemble, []byte("3.1415"))
})
}
func TestTypes_Time(t *testing.T) {
var v httpin.Time
Convey("Marshal Time", t, func() {
So(json.Unmarshal([]byte("1991-11-10T08:00:00+08:00"), &v), ShouldBeNil)
So(v.Value, ShouldEqual, time.Date(1991, 11, 10, 8, 0, 0, 0, time.FixedZone("E8", 8*3600)))
So(v.Valid, ShouldBeFalse)
})
Convey("Unmarshal Time", t, func() {
bs, err := json.Marshal(v)
So(err, ShouldBeNil)
So(bs, ShouldResemble, []byte("1991-11-10T08:00:00+08:00"))
})
}
func TestTypes_BoolArray(t *testing.T) {
var v httpin.BoolArray
Convey("Marshal BoolArray", t, func() {
So(json.Unmarshal([]byte("[true, false, true]"), &v), ShouldBeNil)
So(v.Value, ShouldResemble, []bool{true, false, true})
So(v.Valid, ShouldBeFalse)
})
Convey("Unmarshal BoolArray", t, func() {
bs, err := json.Marshal(v)
So(err, ShouldBeNil)
So(bs, ShouldResemble, []byte("[true,false,true]"))
})
}
func TestTypes_IntArray(t *testing.T) {
var v httpin.IntArray
Convey("Marshal IntArray", t, func() {
So(json.Unmarshal([]byte("[9, 12, 1024]"), &v), ShouldBeNil)
So(v.Value, ShouldResemble, []int{9, 12, 1024})
So(v.Valid, ShouldBeFalse)
})
Convey("Unmarshal IntArray", t, func() {
bs, err := json.Marshal(v)
So(err, ShouldBeNil)
So(bs, ShouldResemble, []byte("[9,12,1024]"))
})
}