package param import ( "net/url" "reflect" "strings" "testing" "time" ) type Everything struct { Bool bool Int int Uint uint Float float64 Map map[string]int Slice []int String string Struct Sub Time time.Time PBool *bool PInt *int PUint *uint PFloat *float64 PMap *map[string]int PSlice *[]int PString *string PStruct *Sub PTime *time.Time PPInt **int ABool MyBool AInt MyInt AUint MyUint AFloat MyFloat AMap MyMap APtr MyPtr ASlice MySlice AString MyString } type Sub struct { A int B int } type MyBool bool type MyInt int type MyUint uint type MyFloat float64 type MyMap map[MyString]MyInt type MyPtr *MyInt type MySlice []MyInt type MyString string var boolAnswers = map[string]bool{ "true": true, "false": false, "0": false, "1": true, "on": true, "": false, } var testTimeString = "1996-12-19T16:39:57-08:00" var testTime time.Time func init() { testTime, _ = time.Parse(time.RFC3339, testTimeString) } func singletonErrors(t *testing.T, field, valid, invalid string) { e := Everything{} err := Parse(url.Values{field: {invalid}}, &e) if err == nil { t.Errorf("Expected error parsing %q as %s", invalid, field) } err = Parse(url.Values{field + "[]": {valid}}, &e) if err == nil { t.Errorf("Expected error parsing nested %s", field) } err = Parse(url.Values{field + "[nested]": {valid}}, &e) if err == nil { t.Errorf("Expected error parsing nested %s", field) } err = Parse(url.Values{field: {valid, valid}}, &e) if err == nil { t.Errorf("Expected error passing %s twice", field) } } func TestBool(t *testing.T) { t.Parallel() for val, correct := range boolAnswers { e := Everything{} e.Bool = !correct err := Parse(url.Values{"Bool": {val}}, &e) if err != nil { t.Error("Parse error on key: ", val) } assertEqual(t, "e.Bool", correct, e.Bool) } } func TestBoolTyped(t *testing.T) { t.Parallel() e := Everything{} err := Parse(url.Values{"ABool": {"true"}}, &e) if err != nil { t.Error("Parse error for typed bool") } assertEqual(t, "e.ABool", MyBool(true), e.ABool) } func TestBoolErrors(t *testing.T) { t.Parallel() singletonErrors(t, "Bool", "true", "llama") } var intAnswers = map[string]int{ "0": 0, "9001": 9001, "-42": -42, } func TestInt(t *testing.T) { t.Parallel() for val, correct := range intAnswers { e := Everything{} e.Int = 1 err := Parse(url.Values{"Int": {val}}, &e) if err != nil { t.Error("Parse error on key: ", val) } assertEqual(t, "e.Int", correct, e.Int) } } func TestIntTyped(t *testing.T) { t.Parallel() e := Everything{} err := Parse(url.Values{"AInt": {"1"}}, &e) if err != nil { t.Error("Parse error for typed int") } assertEqual(t, "e.AInt", MyInt(1), e.AInt) } func TestIntErrors(t *testing.T) { t.Parallel() singletonErrors(t, "Int", "1", "llama") e := Everything{} err := Parse(url.Values{"Int": {"4.2"}}, &e) if err == nil { t.Error("Expected error parsing float as int") } } var uintAnswers = map[string]uint{ "0": 0, "9001": 9001, } func TestUint(t *testing.T) { t.Parallel() for val, correct := range uintAnswers { e := Everything{} e.Uint = 1 err := Parse(url.Values{"Uint": {val}}, &e) if err != nil { t.Error("Parse error on key: ", val) } assertEqual(t, "e.Uint", correct, e.Uint) } } func TestUintTyped(t *testing.T) { t.Parallel() e := Everything{} err := Parse(url.Values{"AUint": {"1"}}, &e) if err != nil { t.Error("Parse error for typed uint") } assertEqual(t, "e.AUint", MyUint(1), e.AUint) } func TestUintErrors(t *testing.T) { t.Parallel() singletonErrors(t, "Uint", "1", "llama") e := Everything{} err := Parse(url.Values{"Uint": {"4.2"}}, &e) if err == nil { t.Error("Expected error parsing float as uint") } err = Parse(url.Values{"Uint": {"-42"}}, &e) if err == nil { t.Error("Expected error parsing negative number as uint") } } var floatAnswers = map[string]float64{ "0": 0, "9001": 9001, "-42": -42, "9001.0": 9001.0, "4.2": 4.2, "-9.000001": -9.000001, } func TestFloat(t *testing.T) { t.Parallel() for val, correct := range floatAnswers { e := Everything{} e.Float = 1 err := Parse(url.Values{"Float": {val}}, &e) if err != nil { t.Error("Parse error on key: ", val) } assertEqual(t, "e.Float", correct, e.Float) } } func TestFloatTyped(t *testing.T) { t.Parallel() e := Everything{} err := Parse(url.Values{"AFloat": {"1.0"}}, &e) if err != nil { t.Error("Parse error for typed float") } assertEqual(t, "e.AFloat", MyFloat(1.0), e.AFloat) } func TestFloatErrors(t *testing.T) { t.Parallel() singletonErrors(t, "Float", "1.0", "llama") } func TestMap(t *testing.T) { t.Parallel() e := Everything{} err := Parse(url.Values{ "Map[one]": {"1"}, "Map[two]": {"2"}, "Map[three]": {"3"}, }, &e) if err != nil { t.Error("Parse error in map: ", err) } for k, v := range map[string]int{"one": 1, "two": 2, "three": 3} { if mv, ok := e.Map[k]; !ok { t.Errorf("Key %q not in map", k) } else { assertEqual(t, "Map["+k+"]", v, mv) } } } func TestMapTyped(t *testing.T) { t.Parallel() e := Everything{} err := Parse(url.Values{"AMap[one]": {"1"}}, &e) if err != nil { t.Error("Parse error for typed map") } assertEqual(t, "e.AMap[one]", MyInt(1), e.AMap[MyString("one")]) } func TestMapErrors(t *testing.T) { t.Parallel() e := Everything{} err := Parse(url.Values{"Map[]": {"llama"}}, &e) if err == nil { t.Error("expected error parsing empty map key") } err = Parse(url.Values{"Map": {"llama"}}, &e) if err == nil { t.Error("expected error parsing map without key") } err = Parse(url.Values{"Map[": {"llama"}}, &e) if err == nil { t.Error("expected error parsing map with malformed key") } } func testPtr(t *testing.T, key, in string, out interface{}) { e := Everything{} err := Parse(url.Values{key: {in}}, &e) if err != nil { t.Errorf("Parse error while parsing pointer e.%s: %v", key, err) } fieldKey := key if i := strings.IndexRune(fieldKey, '['); i >= 0 { fieldKey = fieldKey[:i] } v := reflect.ValueOf(e).FieldByName(fieldKey) if v.IsNil() { t.Errorf("Expected param to allocate pointer for e.%s", key) } else { assertEqual(t, "*e."+key, out, v.Elem().Interface()) } } func TestPtr(t *testing.T) { t.Parallel() testPtr(t, "PBool", "true", true) testPtr(t, "PInt", "2", 2) testPtr(t, "PUint", "2", uint(2)) testPtr(t, "PFloat", "2.0", 2.0) testPtr(t, "PMap[llama]", "4", map[string]int{"llama": 4}) testPtr(t, "PSlice[]", "4", []int{4}) testPtr(t, "PString", "llama", "llama") testPtr(t, "PStruct[B]", "2", Sub{0, 2}) testPtr(t, "PTime", testTimeString, testTime) foo := 2 testPtr(t, "PPInt", "2", &foo) } func TestPtrTyped(t *testing.T) { t.Parallel() e := Everything{} err := Parse(url.Values{"APtr": {"1"}}, &e) if err != nil { t.Error("Parse error for typed pointer") } assertEqual(t, "e.APtr", MyInt(1), *e.APtr) } func TestSlice(t *testing.T) { t.Parallel() e := Everything{} err := Parse(url.Values{"Slice[]": {"3", "1", "4"}}, &e) if err != nil { t.Error("Parse error for slice") } if e.Slice == nil { t.Fatal("Expected param to allocate a slice") } if len(e.Slice) != 3 { t.Fatal("Expected a slice of length 3") } assertEqual(t, "e.Slice[0]", 3, e.Slice[0]) assertEqual(t, "e.Slice[1]", 1, e.Slice[1]) assertEqual(t, "e.Slice[2]", 4, e.Slice[2]) } func TestSliceTyped(t *testing.T) { t.Parallel() e := Everything{} err := Parse(url.Values{"ASlice[]": {"3", "1", "4"}}, &e) if err != nil { t.Error("Parse error for typed slice") } if e.ASlice == nil { t.Fatal("Expected param to allocate a slice") } if len(e.ASlice) != 3 { t.Fatal("Expected a slice of length 3") } assertEqual(t, "e.ASlice[0]", MyInt(3), e.ASlice[0]) assertEqual(t, "e.ASlice[1]", MyInt(1), e.ASlice[1]) assertEqual(t, "e.ASlice[2]", MyInt(4), e.ASlice[2]) } func TestSliceErrors(t *testing.T) { t.Parallel() e := Everything{} err := Parse(url.Values{"Slice": {"1"}}, &e) if err == nil { t.Error("expected error parsing slice without key") } err = Parse(url.Values{"Slice[llama]": {"1"}}, &e) if err == nil { t.Error("expected error parsing slice with string key") } err = Parse(url.Values{"Slice[": {"1"}}, &e) if err == nil { t.Error("expected error parsing malformed slice key") } } var stringAnswer = "This is the world's best string" func TestString(t *testing.T) { t.Parallel() e := Everything{} err := Parse(url.Values{"String": {stringAnswer}}, &e) if err != nil { t.Error("Parse error in string: ", err) } assertEqual(t, "e.String", stringAnswer, e.String) } func TestStringTyped(t *testing.T) { t.Parallel() e := Everything{} err := Parse(url.Values{"AString": {"llama"}}, &e) if err != nil { t.Error("Parse error for typed string") } assertEqual(t, "e.AString", MyString("llama"), e.AString) } func TestStruct(t *testing.T) { t.Parallel() e := Everything{} err := Parse(url.Values{ "Struct[A]": {"1"}, }, &e) if err != nil { t.Error("Parse error in struct: ", err) } assertEqual(t, "e.Struct.A", 1, e.Struct.A) assertEqual(t, "e.Struct.B", 0, e.Struct.B) err = Parse(url.Values{ "Struct[A]": {"4"}, "Struct[B]": {"2"}, }, &e) if err != nil { t.Error("Parse error in struct: ", err) } assertEqual(t, "e.Struct.A", 4, e.Struct.A) assertEqual(t, "e.Struct.B", 2, e.Struct.B) } func TestStructErrors(t *testing.T) { t.Parallel() e := Everything{} err := Parse(url.Values{"Struct[]": {"llama"}}, &e) if err == nil { t.Error("expected error parsing empty struct key") } err = Parse(url.Values{"Struct": {"llama"}}, &e) if err == nil { t.Error("expected error parsing struct without key") } err = Parse(url.Values{"Struct[": {"llama"}}, &e) if err == nil { t.Error("expected error parsing malformed struct key") } err = Parse(url.Values{"Struct[C]": {"llama"}}, &e) if err == nil { t.Error("expected error parsing unknown") } } func TestTextUnmarshaler(t *testing.T) { t.Parallel() e := Everything{} err := Parse(url.Values{"Time": {testTimeString}}, &e) if err != nil { t.Error("parse error for TextUnmarshaler (Time): ", err) } assertEqual(t, "e.Time", testTime, e.Time) } func TestTextUnmarshalerError(t *testing.T) { t.Parallel() e := Everything{} err := Parse(url.Values{"Time": {"llama"}}, &e) if err == nil { t.Error("expected error parsing llama as time") } }