328 lines · 5.9 KB · Go
RawDownload
// Copyright 2013 The Go Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file. package query import (	"fmt"	"net/url"	"reflect"	"testing"	"time") type Nested struct {	A   SubNested  `url:"a"`	B   *SubNested `url:"b"`	Ptr *SubNested `url:"ptr,omitempty"`} type SubNested struct {	Value string `url:"value"`} func TestValues_types(t *testing.T) {	str := "string"	strPtr := &str	timeVal := time.Date(2000, 1, 1, 12, 34, 56, 0, time.UTC) 	tests := []struct {		in   interface{}		want url.Values	}{		{			// basic primitives			struct {				A string				B int				C uint				D float32				E bool			}{},			url.Values{				"A": {""},				"B": {"0"},				"C": {"0"},				"D": {"0"},				"E": {"false"},			},		},		{			// pointers			struct {				A *string				B *int				C **string				D *time.Time			}{				A: strPtr,				C: &strPtr,				D: &timeVal,			},			url.Values{				"A": {str},				"B": {""},				"C": {str},				"D": {"2000-01-01T12:34:56Z"},			},		},		{			// slices and arrays			struct {				A []string				B []string `url:",comma"`				C []string `url:",space"`				D [2]string				E [2]string `url:",comma"`				F [2]string `url:",space"`				G []*string `url:",space"`				H []bool    `url:",int,space"`				I []string  `url:",brackets"`				J []string  `url:",semicolon"`				K []string  `url:",numbered"`			}{				A: []string{"a", "b"},				B: []string{"a", "b"},				C: []string{"a", "b"},				D: [2]string{"a", "b"},				E: [2]string{"a", "b"},				F: [2]string{"a", "b"},				G: []*string{&str, &str},				H: []bool{true, false},				I: []string{"a", "b"},				J: []string{"a", "b"},				K: []string{"a", "b"},			},			url.Values{				"A":   {"a", "b"},				"B":   {"a,b"},				"C":   {"a b"},				"D":   {"a", "b"},				"E":   {"a,b"},				"F":   {"a b"},				"G":   {"string string"},				"H":   {"1 0"},				"I[]": {"a", "b"},				"J":   {"a;b"},				"K0":  {"a"},				"K1":  {"b"},			},		},		{			// other types			struct {				A time.Time				B time.Time `url:",unix"`				C bool      `url:",int"`				D bool      `url:",int"`			}{				A: time.Date(2000, 1, 1, 12, 34, 56, 0, time.UTC),				B: time.Date(2000, 1, 1, 12, 34, 56, 0, time.UTC),				C: true,				D: false,			},			url.Values{				"A": {"2000-01-01T12:34:56Z"},				"B": {"946730096"},				"C": {"1"},				"D": {"0"},			},		},		{			struct {				Nest Nested `url:"nest"`			}{				Nested{					A: SubNested{						Value: "that",					},				},			},			url.Values{				"nest[a][value]": {"that"},				"nest[b]":        {""},			},		},		{			struct {				Nest Nested `url:"nest"`			}{				Nested{					Ptr: &SubNested{						Value: "that",					},				},			},			url.Values{				"nest[a][value]":   {""},				"nest[b]":          {""},				"nest[ptr][value]": {"that"},			},		},		{			nil,			url.Values{},		},	} 	for i, tt := range tests {		v, err := Values(tt.in)		if err != nil {			t.Errorf("%d. Values(%q) returned error: %v", i, tt.in, err)		} 		if !reflect.DeepEqual(tt.want, v) {			t.Errorf("%d. Values(%q) returned %v, want %v", i, tt.in, v, tt.want)		}	}} func TestValues_omitEmpty(t *testing.T) {	str := ""	s := struct {		a string		A string		B string  `url:",omitempty"`		C string  `url:"-"`		D string  `url:"omitempty"` // actually named omitempty, not an option		E *string `url:",omitempty"`	}{E: &str} 	v, err := Values(s)	if err != nil {		t.Errorf("Values(%q) returned error: %v", s, err)	} 	want := url.Values{		"A":         {""},		"omitempty": {""},		"E":         {""}, // E is included because the pointer is not empty, even though the string being pointed to is	}	if !reflect.DeepEqual(want, v) {		t.Errorf("Values(%q) returned %v, want %v", s, v, want)	}} type A struct {	B} type B struct {	C string} type D struct {	B	C string} type e struct {	B	C string} type F struct {	e} func TestValues_embeddedStructs(t *testing.T) {	tests := []struct {		in   interface{}		want url.Values	}{		{			A{B{C: "foo"}},			url.Values{"C": {"foo"}},		},		{			D{B: B{C: "bar"}, C: "foo"},			url.Values{"C": {"foo", "bar"}},		},		{			F{e{B: B{C: "bar"}, C: "foo"}}, // With unexported embed			url.Values{"C": {"foo", "bar"}},		},	} 	for i, tt := range tests {		v, err := Values(tt.in)		if err != nil {			t.Errorf("%d. Values(%q) returned error: %v", i, tt.in, err)		} 		if !reflect.DeepEqual(tt.want, v) {			t.Errorf("%d. Values(%q) returned %v, want %v", i, tt.in, v, tt.want)		}	}} func TestValues_invalidInput(t *testing.T) {	_, err := Values("")	if err == nil {		t.Errorf("expected Values() to return an error on invalid input")	}} type EncodedArgs []string func (m EncodedArgs) EncodeValues(key string, v *url.Values) error {	for i, arg := range m {		v.Set(fmt.Sprintf("%s.%d", key, i), arg)	}	return nil} func TestValues_Marshaler(t *testing.T) {	s := struct {		Args EncodedArgs `url:"arg"`	}{[]string{"a", "b", "c"}}	v, err := Values(s)	if err != nil {		t.Errorf("Values(%q) returned error: %v", s, err)	} 	want := url.Values{		"arg.0": {"a"},		"arg.1": {"b"},		"arg.2": {"c"},	}	if !reflect.DeepEqual(want, v) {		t.Errorf("Values(%q) returned %v, want %v", s, v, want)	}} func TestValues_MarshalerWithNilPointer(t *testing.T) {	s := struct {		Args *EncodedArgs `url:"arg"`	}{}	v, err := Values(s)	if err != nil {		t.Errorf("Values(%q) returned error: %v", s, err)	} 	want := url.Values{}	if !reflect.DeepEqual(want, v) {		t.Errorf("Values(%q) returned %v, want %v", s, v, want)	}} func TestTagParsing(t *testing.T) {	name, opts := parseTag("field,foobar,foo")	if name != "field" {		t.Fatalf("name = %q, want field", name)	}	for _, tt := range []struct {		opt  string		want bool	}{		{"foobar", true},		{"foo", true},		{"bar", false},		{"field", false},	} {		if opts.Contains(tt.opt) != tt.want {			t.Errorf("Contains(%q) = %v", tt.opt, !tt.want)		}	}}