gopkg.in/rethinkdb/rethinkdb-go.v6@v6.2.2/encoding/decoder_test.go (about)

     1  package encoding
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"errors"
     7  	"image"
     8  	"reflect"
     9  	"testing"
    10  )
    11  
    12  type T struct {
    13  	X string
    14  	Y int
    15  	Z int `rethinkdb:"-"`
    16  }
    17  
    18  type U struct {
    19  	Alphabet string `rethinkdb:"alpha"`
    20  }
    21  
    22  type V struct {
    23  	F1 interface{}
    24  	F2 int32
    25  	F3 string
    26  }
    27  
    28  type tx struct {
    29  	x int
    30  }
    31  
    32  var txType = reflect.TypeOf((*tx)(nil)).Elem()
    33  
    34  // Test data structures for anonymous fields.
    35  
    36  type Point struct {
    37  	Z int
    38  }
    39  
    40  type Top struct {
    41  	Level0 int
    42  	Embed0
    43  	*Embed0a
    44  	*Embed0b `rethinkdb:"e,omitempty"` // treated as named
    45  	Embed0c  `rethinkdb:"-"`           // ignored
    46  	Loop
    47  	Embed0p // has Point with X, Y, used
    48  	Embed0q // has Point with Z, used
    49  }
    50  
    51  type Embed0 struct {
    52  	Level1a int // overridden by Embed0a's Level1a with tag
    53  	Level1b int // used because Embed0a's Level1b is renamed
    54  	Level1c int // used because Embed0a's Level1c is ignored
    55  	Level1d int // annihilated by Embed0a's Level1d
    56  	Level1e int `rethinkdb:"x"` // annihilated by Embed0a.Level1e
    57  }
    58  
    59  type Embed0a struct {
    60  	Level1a int `rethinkdb:"Level1a,omitempty"`
    61  	Level1b int `rethinkdb:"LEVEL1B,omitempty"`
    62  	Level1c int `rethinkdb:"-"`
    63  	Level1d int // annihilated by Embed0's Level1d
    64  	Level1f int `rethinkdb:"x"` // annihilated by Embed0's Level1e
    65  }
    66  
    67  type Embed0b Embed0
    68  
    69  type Embed0c Embed0
    70  
    71  type Embed0p struct {
    72  	image.Point
    73  }
    74  
    75  type Embed0q struct {
    76  	Point
    77  }
    78  
    79  type Loop struct {
    80  	Loop1 int `rethinkdb:",omitempty"`
    81  	Loop2 int `rethinkdb:",omitempty"`
    82  	*Loop
    83  }
    84  
    85  // From reflect test:
    86  // The X in S6 and S7 annihilate, but they also block the X in S8.S9.
    87  type S5 struct {
    88  	S6
    89  	S7
    90  	S8
    91  }
    92  
    93  type S6 struct {
    94  	X int
    95  }
    96  
    97  type S7 S6
    98  
    99  type S8 struct {
   100  	S9
   101  }
   102  
   103  type S9 struct {
   104  	X int
   105  	Y int
   106  }
   107  
   108  // From reflect test:
   109  // The X in S11.S6 and S12.S6 annihilate, but they also block the X in S13.S8.S9.
   110  type S10 struct {
   111  	S11
   112  	S12
   113  	S13
   114  }
   115  
   116  type S11 struct {
   117  	S6
   118  }
   119  
   120  type S12 struct {
   121  	S6
   122  }
   123  
   124  type S13 struct {
   125  	S8
   126  }
   127  
   128  type PointerBasic struct {
   129  	X int
   130  	Y *int
   131  }
   132  
   133  type Pointer struct {
   134  	PPoint *Point
   135  	Point  Point
   136  }
   137  
   138  type decodeTest struct {
   139  	in  interface{}
   140  	ptr interface{}
   141  	out interface{}
   142  	err error
   143  }
   144  
   145  type Ambig struct {
   146  	// Given "hello", the first match should win.
   147  	First  int `rethinkdb:"HELLO"`
   148  	Second int `rethinkdb:"Hello"`
   149  }
   150  
   151  type SliceStruct struct {
   152  	X []string
   153  }
   154  
   155  // Decode test helper vars
   156  var (
   157  	sampleInt = 2
   158  )
   159  
   160  var decodeTests = []decodeTest{
   161  	// basic types
   162  	{in: true, ptr: new(bool), out: true},
   163  	{in: 1, ptr: new(int), out: 1},
   164  	{in: 1.2, ptr: new(float64), out: 1.2},
   165  	{in: -5, ptr: new(int16), out: int16(-5)},
   166  	{in: 2, ptr: new(string), out: string("2")},
   167  	{in: float64(2.0), ptr: new(interface{}), out: float64(2.0)},
   168  	{in: string("2"), ptr: new(interface{}), out: string("2")},
   169  	{in: "a\u1234", ptr: new(string), out: "a\u1234"},
   170  	{in: []interface{}{}, ptr: new([]string), out: []string{}},
   171  	{in: map[string]interface{}{"X": []interface{}{1, 2, 3}, "Y": 4}, ptr: new(T), out: T{}, err: &DecodeTypeError{reflect.TypeOf(""), reflect.TypeOf([]interface{}{}), ""}},
   172  	{in: map[string]interface{}{"x": 1}, ptr: new(tx), out: tx{}},
   173  	{in: map[string]interface{}{"F1": float64(1), "F2": 2, "F3": 3}, ptr: new(V), out: V{F1: float64(1), F2: int32(2), F3: string("3")}},
   174  	{in: map[string]interface{}{"F1": string("1"), "F2": 2, "F3": 3}, ptr: new(V), out: V{F1: string("1"), F2: int32(2), F3: string("3")}},
   175  	{
   176  		in:  map[string]interface{}{"k1": int64(1), "k2": "s", "k3": []interface{}{int64(1), 2.0, 3e-3}, "k4": map[string]interface{}{"kk1": "s", "kk2": int64(2)}},
   177  		out: map[string]interface{}{"k1": int64(1), "k2": "s", "k3": []interface{}{int64(1), 2.0, 3e-3}, "k4": map[string]interface{}{"kk1": "s", "kk2": int64(2)}},
   178  		ptr: new(interface{}),
   179  	},
   180  
   181  	// Z has a "-" tag.
   182  	{in: map[string]interface{}{"Y": 1, "Z": 2}, ptr: new(T), out: T{Y: 1}},
   183  
   184  	{in: map[string]interface{}{"alpha": "abc", "alphabet": "xyz"}, ptr: new(U), out: U{Alphabet: "abc"}},
   185  	{in: map[string]interface{}{"alpha": "abc"}, ptr: new(U), out: U{Alphabet: "abc"}},
   186  	{in: map[string]interface{}{"alphabet": "xyz"}, ptr: new(U), out: U{}},
   187  
   188  	// array tests
   189  	{in: []interface{}{1, 2, 3}, ptr: new([3]int), out: [3]int{1, 2, 3}},
   190  	{in: []interface{}{1, 2, 3}, ptr: new([1]int), out: [1]int{1}},
   191  	{in: []interface{}{1, 2, 3}, ptr: new([5]int), out: [5]int{1, 2, 3, 0, 0}},
   192  
   193  	// empty array to interface test
   194  	{in: map[string]interface{}{"T": []interface{}{}}, ptr: new(map[string]interface{}), out: map[string]interface{}{"T": []interface{}{}}},
   195  
   196  	{
   197  		in: map[string]interface{}{
   198  			"Level0":  1,
   199  			"Level1b": 2,
   200  			"Level1c": 3,
   201  			"level1d": 4,
   202  			"Level1a": 5,
   203  			"LEVEL1B": 6,
   204  			"e": map[string]interface{}{
   205  				"Level1a": 8,
   206  				"Level1b": 9,
   207  				"Level1c": 10,
   208  				"Level1d": 11,
   209  				"x":       12,
   210  			},
   211  			"Loop1": 13,
   212  			"Loop2": 14,
   213  			"X":     15,
   214  			"Y":     16,
   215  			"Z":     17,
   216  		},
   217  		ptr: new(Top),
   218  		out: Top{
   219  			Level0: 1,
   220  			Embed0: Embed0{
   221  				Level1b: 2,
   222  				Level1c: 3,
   223  			},
   224  			Embed0a: &Embed0a{
   225  				Level1a: 5,
   226  				Level1b: 6,
   227  			},
   228  			Embed0b: &Embed0b{
   229  				Level1a: 8,
   230  				Level1b: 9,
   231  				Level1c: 10,
   232  				Level1d: 11,
   233  			},
   234  			Loop: Loop{
   235  				Loop1: 13,
   236  				Loop2: 14,
   237  			},
   238  			Embed0p: Embed0p{
   239  				Point: image.Point{X: 15, Y: 16},
   240  			},
   241  			Embed0q: Embed0q{
   242  				Point: Point{Z: 17},
   243  			},
   244  		},
   245  	},
   246  	{
   247  		in:  map[string]interface{}{"hello": 1},
   248  		ptr: new(Ambig),
   249  		out: Ambig{First: 1},
   250  	},
   251  	{
   252  		in:  map[string]interface{}{"X": 1, "Y": 2},
   253  		ptr: new(S5),
   254  		out: S5{S8: S8{S9: S9{Y: 2}}},
   255  	},
   256  	{
   257  		in:  map[string]interface{}{"X": 1, "Y": 2},
   258  		ptr: new(S10),
   259  		out: S10{S13: S13{S8: S8{S9: S9{Y: 2}}}},
   260  	},
   261  	{
   262  		in:  map[string]interface{}{"PPoint": map[string]interface{}{"Z": 1}, "Point": map[string]interface{}{"Z": 2}},
   263  		ptr: new(Pointer),
   264  		out: Pointer{PPoint: &Point{Z: 1}, Point: Point{Z: 2}},
   265  	},
   266  	{
   267  		in:  map[string]interface{}{"Point": map[string]interface{}{"Z": 2}},
   268  		ptr: new(Pointer),
   269  		out: Pointer{PPoint: nil, Point: Point{Z: 2}},
   270  	},
   271  	{
   272  		in:  map[string]interface{}{"x": 2},
   273  		ptr: new(PointerBasic),
   274  		out: PointerBasic{X: 2, Y: nil},
   275  	},
   276  	{
   277  		in:  map[string]interface{}{"x": 2, "y": 2},
   278  		ptr: new(PointerBasic),
   279  		out: PointerBasic{X: 2, Y: &sampleInt},
   280  	},
   281  }
   282  
   283  func TestDecode(t *testing.T) {
   284  	for i, tt := range decodeTests {
   285  		if tt.ptr == nil {
   286  			continue
   287  		}
   288  
   289  		// v = new(right-type)
   290  		v := reflect.New(reflect.TypeOf(tt.ptr).Elem())
   291  
   292  		err := Decode(v.Interface(), tt.in)
   293  		if !jsonEqual(err, tt.err) {
   294  			t.Errorf("#%d: got error %v want %v", i, err, tt.err)
   295  			continue
   296  		}
   297  
   298  		if tt.err == nil && !jsonEqual(v.Elem().Interface(), tt.out) {
   299  			t.Errorf("#%d: mismatch\nhave: %+v\nwant: %+v", i, v.Elem().Interface(), tt.out)
   300  			continue
   301  		}
   302  
   303  		// Check round trip.
   304  		if tt.err == nil {
   305  			enc, err := Encode(v.Interface())
   306  			if err != nil {
   307  				t.Errorf("#%d: error re-marshaling: %v", i, err)
   308  				continue
   309  			}
   310  			vv := reflect.New(reflect.TypeOf(tt.ptr).Elem())
   311  
   312  			if err := Decode(vv.Interface(), enc); err != nil {
   313  				t.Errorf("#%d: error re-decodeing: %v", i, err)
   314  				continue
   315  			}
   316  			if !jsonEqual(v.Elem().Interface(), vv.Elem().Interface()) {
   317  				t.Errorf("#%d: mismatch\nhave: %#+v\nwant: %#+v", i, v.Elem().Interface(), vv.Elem().Interface())
   318  				continue
   319  			}
   320  		}
   321  	}
   322  }
   323  
   324  func TestStringKind(t *testing.T) {
   325  	type aMap map[string]int
   326  
   327  	var m1, m2 map[string]int
   328  	m1 = map[string]int{
   329  		"foo": 42,
   330  	}
   331  
   332  	data, err := Encode(m1)
   333  	if err != nil {
   334  		t.Errorf("Unexpected error encoding: %v", err)
   335  	}
   336  
   337  	err = Decode(&m2, data)
   338  	if err != nil {
   339  		t.Errorf("Unexpected error decoding: %v", err)
   340  	}
   341  
   342  	if !jsonEqual(m1, m2) {
   343  		t.Error("Items should be equal after encoding and then decoding")
   344  	}
   345  
   346  }
   347  
   348  // Test handling of unexported fields that should be ignored.
   349  type unexportedFields struct {
   350  	Name string
   351  	m    map[string]interface{} `rethinkdb:"-"`
   352  	m2   map[string]interface{} `rethinkdb:"abcd"`
   353  }
   354  
   355  func TestDecodeUnexported(t *testing.T) {
   356  	input := map[string]interface{}{
   357  		"Name": "Bob",
   358  		"m": map[string]interface{}{
   359  			"x": 123,
   360  		},
   361  		"m2": map[string]interface{}{
   362  			"y": 123,
   363  		},
   364  		"abcd": map[string]interface{}{
   365  			"z": 789,
   366  		},
   367  	}
   368  	want := &unexportedFields{Name: "Bob"}
   369  
   370  	out := &unexportedFields{}
   371  	err := Decode(out, input)
   372  	if err != nil {
   373  		t.Errorf("got error %v, expected nil", err)
   374  	}
   375  	if !jsonEqual(out, want) {
   376  		t.Errorf("got %q, want %q", out, want)
   377  	}
   378  }
   379  
   380  type Foo struct {
   381  	FooBar interface{} `rethinkdb:"foobar"`
   382  }
   383  type Bar struct {
   384  	Baz int `rethinkdb:"baz"`
   385  }
   386  
   387  type UnmarshalerPointer struct {
   388  	Value *UnmarshalerValue
   389  }
   390  
   391  type UnmarshalerValue struct {
   392  	ValueInt    int64
   393  	ValueString string
   394  }
   395  
   396  func (v *UnmarshalerValue) MarshalRQL() (interface{}, error) {
   397  	if v.ValueInt != int64(0) {
   398  		return Encode(v.ValueInt)
   399  	}
   400  	if v.ValueString != "" {
   401  		return Encode(v.ValueString)
   402  	}
   403  
   404  	return Encode(nil)
   405  }
   406  
   407  func (v *UnmarshalerValue) UnmarshalRQL(b interface{}) (err error) {
   408  	n, s := int64(0), ""
   409  
   410  	if err = Decode(&s, b); err == nil {
   411  		v.ValueString = s
   412  		return
   413  	}
   414  	if err = Decode(&n, b); err == nil {
   415  		v.ValueInt = n
   416  
   417  	}
   418  
   419  	return
   420  }
   421  
   422  func TestDecodeUnmarshalerPointer(t *testing.T) {
   423  	input := map[string]interface{}{
   424  		"Value": "abc",
   425  	}
   426  	want := &UnmarshalerPointer{
   427  		Value: &UnmarshalerValue{ValueString: "abc"},
   428  	}
   429  
   430  	out := &UnmarshalerPointer{}
   431  	err := Decode(out, input)
   432  	if err != nil {
   433  		t.Errorf("got error %v, expected nil", err)
   434  	}
   435  	if !jsonEqual(out, want) {
   436  		t.Errorf("got %+v, want %+v", out, want)
   437  	}
   438  }
   439  
   440  func TestDecodeMapIntKeys(t *testing.T) {
   441  	input := map[string]int{"1": 1, "2": 2, "3": 3}
   442  	want := map[int]int{1: 1, 2: 2, 3: 3}
   443  
   444  	out := map[int]int{}
   445  	err := Decode(&out, input)
   446  	if err != nil {
   447  		t.Errorf("got error %v, expected nil", err)
   448  	}
   449  	if !jsonEqual(out, want) {
   450  		t.Errorf("got %q, want %q", out, want)
   451  	}
   452  }
   453  
   454  func TestDecodeCompoundKey(t *testing.T) {
   455  	input := map[string]interface{}{"id": []string{"1", "2"}, "err_a[]": "3", "err_b[": "4", "err_c]": "5"}
   456  	want := Compound{"1", "2", "3", "4", "5"}
   457  
   458  	out := Compound{}
   459  	err := Decode(&out, input)
   460  	if err != nil {
   461  		t.Errorf("got error %v, expected nil", err)
   462  	}
   463  	if !jsonEqual(out, want) {
   464  		t.Errorf("got %q, want %q", out, want)
   465  	}
   466  }
   467  
   468  func TestDecodeNilSlice(t *testing.T) {
   469  	input := map[string]interface{}{"X": nil}
   470  	want := SliceStruct{}
   471  
   472  	out := SliceStruct{}
   473  	err := Decode(&out, input)
   474  	if err != nil {
   475  		t.Errorf("got error %v, expected nil", err)
   476  	}
   477  	if !jsonEqual(out, want) {
   478  		t.Errorf("got %q, want %q", out, want)
   479  	}
   480  }
   481  
   482  func jsonEqual(a, b interface{}) bool {
   483  	// First check using reflect.DeepEqual
   484  	if reflect.DeepEqual(a, b) {
   485  		return true
   486  	}
   487  
   488  	// Then use jsonEqual
   489  	ba, err := json.Marshal(a)
   490  	if err != nil {
   491  		panic(err)
   492  	}
   493  	bb, err := json.Marshal(b)
   494  	if err != nil {
   495  		panic(err)
   496  	}
   497  
   498  	return bytes.Compare(ba, bb) == 0
   499  }
   500  
   501  func TestMergeStruct(t *testing.T) {
   502  	var dst struct {
   503  		Field        string
   504  		AnotherField string
   505  	}
   506  	dst.Field = "change me"
   507  	dst.AnotherField = "don't blank me"
   508  	err := Merge(&dst, map[string]interface{}{"Field": "Changed!"})
   509  	if err != nil {
   510  		t.Error("Cannot merge:", err)
   511  	}
   512  	if dst.AnotherField == "" {
   513  		t.Error("Field has been wiped")
   514  	}
   515  }
   516  
   517  func TestMergeMap(t *testing.T) {
   518  	var dst = make(map[string]string)
   519  	dst["field"] = "change me"
   520  	dst["another_field"] = "don't blank me"
   521  	err := Merge(&dst, map[string]interface{}{"field": "Changed!"})
   522  	if err != nil {
   523  		t.Error("Cannot merge:", err)
   524  	}
   525  	if dst["another_field"] == "" {
   526  		t.Error("Field has been wiped")
   527  	}
   528  }
   529  
   530  func TestDecodeCustomTypeEncodingValue(t *testing.T) {
   531  	type innerType struct {
   532  		Val int
   533  	}
   534  	type outerType struct {
   535  		Inner innerType `rethinkdb:"inner"`
   536  	}
   537  
   538  	want := outerType{Inner: innerType{Val: 5}}
   539  	in := map[string]interface{}{
   540  		"inner": map[string]interface{}{
   541  			"someval": 5,
   542  		},
   543  	}
   544  
   545  	SetTypeEncoding(reflect.TypeOf(innerType{}),
   546  		nil, func(enc interface{}, val reflect.Value) error {
   547  			m := enc.(map[string]interface{})
   548  			val.Set(reflect.ValueOf(innerType{Val: m["someval"].(int)}))
   549  			return nil
   550  		})
   551  
   552  	var out outerType
   553  	err := Decode(&out, in)
   554  	if err != nil {
   555  		t.Errorf("got error %v, expected nil", err)
   556  	}
   557  	if !jsonEqual(out, want) {
   558  		t.Errorf("got %q, want %q", out, want)
   559  	}
   560  }
   561  
   562  func TestDecodeCustomTypeEncodingPointer(t *testing.T) {
   563  	type innerType struct {
   564  		Val int
   565  	}
   566  	type outerType struct {
   567  		Inner *innerType `rethinkdb:"inner"`
   568  	}
   569  
   570  	want := outerType{Inner: &innerType{Val: 5}}
   571  	in := map[string]interface{}{
   572  		"inner": map[string]interface{}{
   573  			"someval": 5,
   574  		},
   575  	}
   576  
   577  	SetTypeEncoding(reflect.TypeOf((*innerType)(nil)),
   578  		nil, func(enc interface{}, val reflect.Value) error {
   579  			m := enc.(map[string]interface{})
   580  			val.Set(reflect.ValueOf(&innerType{Val: m["someval"].(int)}))
   581  			return nil
   582  		})
   583  
   584  	var out outerType
   585  	err := Decode(&out, in)
   586  	if err != nil {
   587  		t.Errorf("got error %v, expected nil", err)
   588  	}
   589  	if !jsonEqual(out, want) {
   590  		t.Errorf("got %+v, want %+v", out, want)
   591  	}
   592  }
   593  
   594  func TestDecodeCustomRootTypeEncodingValue(t *testing.T) {
   595  	type cType struct {
   596  		Val int
   597  	}
   598  
   599  	want := cType{Val: 5}
   600  	in := map[string]interface{}{
   601  		"someval": 5,
   602  	}
   603  
   604  	SetTypeEncoding(reflect.TypeOf(cType{}),
   605  		nil, func(enc interface{}, val reflect.Value) error {
   606  			m := enc.(map[string]interface{})
   607  			val.Set(reflect.ValueOf(cType{Val: m["someval"].(int)}))
   608  			return nil
   609  		})
   610  
   611  	var out cType
   612  	err := Decode(&out, in)
   613  	if err != nil {
   614  		t.Errorf("got error %v, expected nil", err)
   615  	}
   616  	if !jsonEqual(out, want) {
   617  		t.Errorf("got %q, want %q", out, want)
   618  	}
   619  }
   620  
   621  func TestDecodeCustomRootTypeEncodingPointer(t *testing.T) {
   622  	type cType struct {
   623  		Val int
   624  	}
   625  
   626  	want := cType{Val: 5}
   627  	in := map[string]interface{}{
   628  		"someval": 5,
   629  	}
   630  
   631  	SetTypeEncoding(reflect.TypeOf((*cType)(nil)),
   632  		nil, func(enc interface{}, val reflect.Value) error {
   633  			m := enc.(map[string]interface{})
   634  			val.Set(reflect.ValueOf(&cType{Val: m["someval"].(int)}))
   635  			return nil
   636  		})
   637  
   638  	var out *cType
   639  	err := Decode(&out, in)
   640  	if err != nil {
   641  		t.Errorf("got error %v, expected nil", err)
   642  	}
   643  	if !jsonEqual(out, want) {
   644  		t.Errorf("got %q, want %q", out, want)
   645  	}
   646  }
   647  
   648  func TestDecodeCustomTypeEncodingError(t *testing.T) {
   649  	type cType struct {
   650  		Val int
   651  	}
   652  
   653  	in := map[string]interface{}{
   654  		"val": 5,
   655  	}
   656  
   657  	cerr := errors.New("decode error")
   658  	SetTypeEncoding(reflect.TypeOf(cType{}),
   659  		nil, func(enc interface{}, val reflect.Value) error {
   660  			return cerr
   661  		})
   662  
   663  	var out cType
   664  	err := Decode(&out, in)
   665  	if err == nil {
   666  		t.Errorf("got no error, expected %v", cerr)
   667  	}
   668  	if err != cerr {
   669  		t.Errorf("got %v, want %v", err, cerr)
   670  	}
   671  }