Example #1
0
func TestFilePath(t *testing.T) {
	t.Parallel()

	var tests = []struct {
		param    string
		expected bool
		osType   int
	}{
		{"c:\\" + strings.Repeat("a", 32767), true, Win}, //See http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx#maxpath
		{"c:\\" + strings.Repeat("a", 32768), false, Win},
		{"c:\\path\\file (x86)\bar", true, Win},
		{"c:\\path\\file", true, Win},
		{"c:\\path\\file:exe", false, Unknown},
		{"C:\\", true, Win},
		{"c:\\path\\file\\", true, Win},
		{"c:/path/file/", false, Unknown},
		{"/path/file/", true, Unix},
		{"/path/file:SAMPLE/", true, Unix},
		{"/path/file:/.txt", true, Unix},
		{"/path", true, Unix},
	}
	for _, test := range tests {
		actual, osType := IsFilePath(test.param)
		if actual != test.expected || osType != test.osType {
			t.Errorf("Expected IsFilePath(%q) to be %v, got %v", test.param, test.expected, actual)
		}
	}
}

func TestIsLatitude(t *testing.T) {
	t.Parallel()

	var tests = []struct {
		param    string
		expected bool
	}{
		{"", false},
		{"-90.000", true},
		{"+90", true},
		{"47.1231231", true},
		{"+99.9", false},
		{"108", false},
	}
	for _, test := range tests {
		actual := IsLatitude(test.param)
		if actual != test.expected {
			t.Errorf("Expected IsLatitude(%q) to be %v, got %v", test.param, test.expected, actual)
		}
	}
}

func TestIsLongitude(t *testing.T) {
	t.Parallel()

	var tests = []struct {
		param    string
		expected bool
	}{
		{"", false},
		{"-180.000", true},
		{"180.1", false},
		{"+73.234", true},
		{"+382.3811", false},
		{"23.11111111", true},
	}
	for _, test := range tests {
		actual := IsLongitude(test.param)
		if actual != test.expected {
			t.Errorf("Expected IsLongitude(%q) to be %v, got %v", test.param, test.expected, actual)
		}
	}
}

func TestIsSSN(t *testing.T) {
	t.Parallel()

	var tests = []struct {
		param    string
		expected bool
	}{
		{"", false},
		{"00-90-8787", false},
		{"66690-76", false},
		{"191 60 2869", true},
		{"191-60-2869", true},
	}
	for _, test := range tests {
		actual := IsSSN(test.param)
		if actual != test.expected {
			t.Errorf("Expected IsSSN(%q) to be %v, got %v", test.param, test.expected, actual)
		}
	}
}

func TestIsMongoID(t *testing.T) {
	t.Parallel()

	var tests = []struct {
		param    string
		expected bool
	}{
		{"507f1f77bcf86cd799439011", true},
		{"507f1f77bcf86cd7994390", false},
		{"507f1f77bcf86cd79943901z", false},
		{"507f1f77bcf86cd799439011 ", false},
		{"", false},
	}
	for _, test := range tests {
		actual := IsMongoID(test.param)
		if actual != test.expected {
			t.Errorf("Expected IsMongoID(%q) to be %v, got %v", test.param, test.expected, actual)
		}
	}
}

type Address struct {
	Street string `valid:"-"`
	Zip    string `json:"zip" valid:"numeric,required"`
}

type User struct {
	Name     string `valid:"required"`
	Email    string `valid:"required,email"`
	Password string `valid:"required"`
	Age      int    `valid:"required,numeric,@#\u0000"`
	Home     *Address
	Work     []Address
}

type UserValid struct {
	Name     string `valid:"required"`
	Email    string `valid:"required,email"`
	Password string `valid:"required"`
	Age      int    `valid:"required"`
	Home     *Address
	Work     []Address
}

type PrivateStruct struct {
	privateField string `valid:"required,alpha,d_k"`
	NonZero      int
	ListInt      []int
	ListString   []string `valid:"alpha"`
	Work         [2]Address
	Home         Address
	Map          map[string]Address
}

type NegationStruct struct {
	NotInt string `valid:"!int"`
	Int    string `valid:"int"`
}

func TestValidateNegationStruct(t *testing.T) {
	var tests = []struct {
		param    NegationStruct
		expected bool
	}{
		{NegationStruct{"a1", "11"}, true},
		{NegationStruct{"*****@*****.**", "11"}, true},
		{NegationStruct{"123456----", "11"}, true},
		{NegationStruct{"::1", "11"}, true},
		{NegationStruct{"123.123", "11"}, true},
		{NegationStruct{"a1", "a1"}, false},
		{NegationStruct{"11", "a1"}, false},
		{NegationStruct{"11", "11"}, false},
	}
	for _, test := range tests {
		actual, err := ValidateStruct(test.param)
		if actual != test.expected {
			t.Errorf("Expected ValidateStruct(%q) to be %v, got %v", test.param, test.expected, actual)
			if err != nil {
				t.Errorf("Got Error on ValidateStruct(%q): %s", test.param, err)
			}
		}
	}
}

func TestValidateStruct(t *testing.T) {

	var tests = []struct {
		param    interface{}
		expected bool
	}{
		{User{"John", "*****@*****.**", "123G#678", 20, &Address{"Street", "123456"}, []Address{Address{"Street", "123456"}, Address{"Street", "123456"}}}, false},
		{User{"John", "john!yahoo.com", "12345678", 20, &Address{"Street", "ABC456D89"}, []Address{Address{"Street", "ABC456D89"}, Address{"Street", "123456"}}}, false},
		{User{"John", "", "12345", 0, &Address{"Street", "123456789"}, []Address{Address{"Street", "ABC456D89"}, Address{"Street", "123456"}}}, false},
		{UserValid{"John", "*****@*****.**", "123G#678", 20, &Address{"Street", "123456"}, []Address{Address{"Street", "123456"}, Address{"Street", "123456"}}}, true},
		{UserValid{"John", "john!yahoo.com", "12345678", 20, &Address{"Street", "ABC456D89"}, []Address{Address{"Street", "ABC456D89"}, Address{"Street", "123456"}}}, false},
		{UserValid{"John", "", "12345", 0, &Address{"Street", "123456789"}, []Address{Address{"Street", "ABC456D89"}, Address{"Street", "123456"}}}, false},
		{nil, true},
		{User{"John", "*****@*****.**", "123G#678", 0, &Address{"Street", "123456"}, []Address{}}, false},
		{"im not a struct", false},
	}
	for _, test := range tests {
		actual, err := ValidateStruct(test.param)
		if actual != test.expected {
			t.Errorf("Expected ValidateStruct(%q) to be %v, got %v", test.param, test.expected, actual)
			if err != nil {
				t.Errorf("Got Error on ValidateStruct(%q): %s", test.param, err)
			}
		}
	}

	TagMap["d_k"] = Validator(func(str string) bool {
		return str == "d_k"
	})
	result, err := ValidateStruct(PrivateStruct{"d_k", 0, []int{1, 2}, []string{"hi", "super"}, [2]Address{Address{"Street", "123456"},
		Address{"Street", "123456"}}, Address{"Street", "123456"}, map[string]Address{"address": Address{"Street", "123456"}}})
	if result != true {
		t.Log("Case ", 6, ": expected ", true, " when result is ", result)
		t.Error(err)
		t.FailNow()
	}
}

func ExampleValidateStruct() {
	type Post struct {
		Title    string `valid:"alphanum,required"`
		Message  string `valid:"duck,ascii"`
		AuthorIP string `valid:"ipv4"`
	}
	post := &Post{"My Example Post", "duck", "123.234.54.3"}

	//Add your own struct validation tags
	TagMap["duck"] = Validator(func(str string) bool {
		return str == "duck"
	})

	result, err := ValidateStruct(post)
	if err != nil {
		println("error: " + err.Error())
	}
	println(result)
}
Example #2
0
func TestFilePath(t *testing.T) {
	t.Parallel()

	var tests = []struct {
		param    string
		expected bool
		osType   int
	}{
		{"c:\\" + strings.Repeat("a", 32767), true, Win}, //See http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx#maxpath
		{"c:\\" + strings.Repeat("a", 32768), false, Win},
		{"c:\\path\\file (x86)\bar", true, Win},
		{"c:\\path\\file", true, Win},
		{"c:\\path\\file:exe", false, Unknown},
		{"C:\\", true, Win},
		{"c:\\path\\file\\", true, Win},
		{"c:/path/file/", false, Unknown},
		{"/path/file/", true, Unix},
		{"/path/file:SAMPLE/", true, Unix},
		{"/path/file:/.txt", true, Unix},
		{"/path", true, Unix},
	}
	for _, test := range tests {
		actual, osType := IsFilePath(test.param)
		if actual != test.expected || osType != test.osType {
			t.Errorf("Expected IsFilePath(%q) to be %v, got %v", test.param, test.expected, actual)
		}
	}
}

func TestIsLatitude(t *testing.T) {
	t.Parallel()

	var tests = []struct {
		param    string
		expected bool
	}{
		{"", false},
		{"-90.000", true},
		{"+90", true},
		{"47.1231231", true},
		{"+99.9", false},
		{"108", false},
	}
	for _, test := range tests {
		actual := IsLatitude(test.param)
		if actual != test.expected {
			t.Errorf("Expected IsLatitude(%q) to be %v, got %v", test.param, test.expected, actual)
		}
	}
}

func TestIsLongitude(t *testing.T) {
	t.Parallel()

	var tests = []struct {
		param    string
		expected bool
	}{
		{"", false},
		{"-180.000", true},
		{"180.1", false},
		{"+73.234", true},
		{"+382.3811", false},
		{"23.11111111", true},
	}
	for _, test := range tests {
		actual := IsLongitude(test.param)
		if actual != test.expected {
			t.Errorf("Expected IsLongitude(%q) to be %v, got %v", test.param, test.expected, actual)
		}
	}
}

func TestIsSSN(t *testing.T) {
	t.Parallel()

	var tests = []struct {
		param    string
		expected bool
	}{
		{"", false},
		{"00-90-8787", false},
		{"66690-76", false},
		{"191 60 2869", true},
		{"191-60-2869", true},
	}
	for _, test := range tests {
		actual := IsSSN(test.param)
		if actual != test.expected {
			t.Errorf("Expected IsSSN(%q) to be %v, got %v", test.param, test.expected, actual)
		}
	}
}

func TestIsMongoID(t *testing.T) {
	t.Parallel()

	var tests = []struct {
		param    string
		expected bool
	}{
		{"507f1f77bcf86cd799439011", true},
		{"507f1f77bcf86cd7994390", false},
		{"507f1f77bcf86cd79943901z", false},
		{"507f1f77bcf86cd799439011 ", false},
		{"", false},
	}
	for _, test := range tests {
		actual := IsMongoID(test.param)
		if actual != test.expected {
			t.Errorf("Expected IsMongoID(%q) to be %v, got %v", test.param, test.expected, actual)
		}
	}
}

func TestByteLegnth(t *testing.T) {
	t.Parallel()

	var tests = []struct {
		value    string
		min      string
		max      string
		expected bool
	}{
		{"123456", "0", "100", true},
		{"1239999", "0", "0", false},
		{"1239asdfasf99", "100", "200", false},
		{"1239999asdff29", "10", "30", true},
	}
	for _, test := range tests {
		actual := ByteLength(test.value, test.min, test.max)
		if actual != test.expected {
			t.Errorf("Expected ByteLength(%s, %s, %s) to be %v, got %v", test.value, test.min, test.max, test.expected, actual)
		}
	}
}

type Address struct {
	Street string `valid:"-"`
	Zip    string `json:"zip" valid:"numeric,required"`
}

type User struct {
	Name     string `valid:"required"`
	Email    string `valid:"required,email"`
	Password string `valid:"required"`
	Age      int    `valid:"required,numeric,@#\u0000"`
	Home     *Address
	Work     []Address
}

type UserValid struct {
	Name     string `valid:"required"`
	Email    string `valid:"required,email"`
	Password string `valid:"required"`
	Age      int    `valid:"required"`
	Home     *Address
	Work     []Address
}

type PrivateStruct struct {
	privateField string `valid:"required,alpha,d_k"`
	NonZero      int
	ListInt      []int
	ListString   []string `valid:"alpha"`
	Work         [2]Address
	Home         Address
	Map          map[string]Address
}

type NegationStruct struct {
	NotInt string `valid:"!int"`
	Int    string `valid:"int"`
}

type LengthStruct struct {
	Length string `valid:"length(10|20)"`
}

type Post struct {
	Title    string `valid:"alpha,required"`
	Message  string `valid:"ascii"`
	AuthorIP string `valid:"ipv4"`
}

func TestValidateNegationStruct(t *testing.T) {
	var tests = []struct {
		param    NegationStruct
		expected bool
	}{
		{NegationStruct{"a1", "11"}, true},
		{NegationStruct{"*****@*****.**", "11"}, true},
		{NegationStruct{"123456----", "11"}, true},
		{NegationStruct{"::1", "11"}, true},
		{NegationStruct{"123.123", "11"}, true},
		{NegationStruct{"a1", "a1"}, false},
		{NegationStruct{"11", "a1"}, false},
		{NegationStruct{"11", "11"}, false},
	}
	for _, test := range tests {
		actual, err := ValidateStruct(test.param)
		if actual != test.expected {
			t.Errorf("Expected ValidateStruct(%q) to be %v, got %v", test.param, test.expected, actual)
			if err != nil {
				t.Errorf("Got Error on ValidateStruct(%q): %s", test.param, err)
			}
		}
	}
}

func TestLengthStruct(t *testing.T) {
	var tests = []struct {
		param    interface{}
		expected bool
	}{
		{LengthStruct{"11111"}, false},
		{LengthStruct{"11111111111111111110000000000000000"}, false},
		{LengthStruct{"11dfffdf0099"}, true},
	}

	for _, test := range tests {
		actual, err := ValidateStruct(test.param)
		if actual != test.expected {
			t.Errorf("Expected ValidateStruct(%q) to be %v, got %v", test.param, test.expected, actual)
			if err != nil {
				t.Errorf("Got Error on ValidateStruct(%q): %s", test.param, err)
			}
		}
	}
}

func TestValidateStruct(t *testing.T) {

	var tests = []struct {
		param    interface{}
		expected bool
	}{
		{User{"John", "*****@*****.**", "123G#678", 20, &Address{"Street", "123456"}, []Address{Address{"Street", "123456"}, Address{"Street", "123456"}}}, false},
		{User{"John", "john!yahoo.com", "12345678", 20, &Address{"Street", "ABC456D89"}, []Address{Address{"Street", "ABC456D89"}, Address{"Street", "123456"}}}, false},
		{User{"John", "", "12345", 0, &Address{"Street", "123456789"}, []Address{Address{"Street", "ABC456D89"}, Address{"Street", "123456"}}}, false},
		{UserValid{"John", "*****@*****.**", "123G#678", 20, &Address{"Street", "123456"}, []Address{Address{"Street", "123456"}, Address{"Street", "123456"}}}, true},
		{UserValid{"John", "john!yahoo.com", "12345678", 20, &Address{"Street", "ABC456D89"}, []Address{Address{"Street", "ABC456D89"}, Address{"Street", "123456"}}}, false},
		{UserValid{"John", "", "12345", 0, &Address{"Street", "123456789"}, []Address{Address{"Street", "ABC456D89"}, Address{"Street", "123456"}}}, false},
		{nil, true},
		{User{"John", "*****@*****.**", "123G#678", 0, &Address{"Street", "123456"}, []Address{}}, false},
		{"im not a struct", false},
	}
	for _, test := range tests {
		actual, err := ValidateStruct(test.param)
		if actual != test.expected {
			t.Errorf("Expected ValidateStruct(%q) to be %v, got %v", test.param, test.expected, actual)
			if err != nil {
				t.Errorf("Got Error on ValidateStruct(%q): %s", test.param, err)
			}
		}
	}

	TagMap["d_k"] = Validator(func(str string) bool {
		return str == "d_k"
	})
	result, err := ValidateStruct(PrivateStruct{"d_k", 0, []int{1, 2}, []string{"hi", "super"}, [2]Address{Address{"Street", "123456"},
		Address{"Street", "123456"}}, Address{"Street", "123456"}, map[string]Address{"address": Address{"Street", "123456"}}})
	if result != true {
		t.Log("Case ", 6, ": expected ", true, " when result is ", result)
		t.Error(err)
		t.FailNow()
	}
}

func TestRequired(t *testing.T) {

	testString := "foobar"
	var tests = []struct {
		param    interface{}
		expected bool
	}{
		{
			struct {
				Pointer *string `valid:"required"`
			}{},
			false,
		},
		{
			struct {
				Pointer *string `valid:"required"`
			}{
				Pointer: &testString,
			},
			true,
		},
		{
			struct {
				Addr Address `valid:"required"`
			}{},
			false,
		},
		{
			struct {
				Addr Address `valid:"required"`
			}{
				Addr: Address{"", "123"},
			},
			true,
		},
		{
			struct {
				Pointer *Address `valid:"required"`
			}{},
			false,
		},
		{
			struct {
				Pointer *Address `valid:"required"`
			}{
				Pointer: &Address{"", "123"},
			},
			true,
		},
	}
	for _, test := range tests {
		actual, err := ValidateStruct(test.param)
		if actual != test.expected {
			t.Errorf("Expected ValidateStruct(%q) to be %v, got %v", test.param, test.expected, actual)
			if err != nil {
				t.Errorf("Got Error on ValidateStruct(%q): %s", test.param, err)
			}
		}
	}
}

func TestErrorByField(t *testing.T) {
	t.Parallel()

	var tests = []struct {
		param    string
		expected string
	}{
		{"message", ""},
		{"Message", ""},
		{"title", ""},
		{"Title", "My123 does not validate as alpha"},
		{"AuthorIP", "123 does not validate as ipv4"},
	}
	post := &Post{"My123", "duck13126", "123"}
	_, err := ValidateStruct(post)

	for _, test := range tests {
		actual := ErrorByField(err, test.param)
		if actual != test.expected {
			t.Errorf("Expected ErrorByField(%q) to be %v, got %v", test.param, test.expected, actual)
		}
	}
}

func TestValidateStructPointers(t *testing.T) {
	// Struct which uses pointers for values
	type UserWithPointers struct {
		Name         *string `valid:"-"`
		Email        *string `valid:"email"`
		FavoriteFood *string `valid:"length(0|32)"`
		Nerd         *bool   `valid:"-"`
	}

	var tests = []struct {
		param    string
		expected string
	}{
		{"Name", ""},
		{"Email", "invalid does not validate as email"},
		{"FavoriteFood", ""},
		{"Nerd", ""},
	}

	name := "Herman"
	email := "invalid"
	food := "Pizza"
	nerd := true
	user := &UserWithPointers{&name, &email, &food, &nerd}
	_, err := ValidateStruct(user)

	for _, test := range tests {
		actual := ErrorByField(err, test.param)
		if actual != test.expected {
			t.Errorf("Expected ErrorByField(%q) to be %v, got %v", test.param, test.expected, actual)
		}
	}
}

func ExampleValidateStruct() {
	type Post struct {
		Title    string `valid:"alphanum,required"`
		Message  string `valid:"duck,ascii"`
		AuthorIP string `valid:"ipv4"`
	}
	post := &Post{"My Example Post", "duck", "123.234.54.3"}

	//Add your own struct validation tags
	TagMap["duck"] = Validator(func(str string) bool {
		return str == "duck"
	})

	result, err := ValidateStruct(post)
	if err != nil {
		println("error: " + err.Error())
	}
	println(result)
}
Example #3
0
func TestFilePath(t *testing.T) {
	t.Parallel()

	var tests = []struct {
		param    string
		expected bool
		osType   int
	}{
		{"c:\\" + strings.Repeat("a", 32767), true, Win}, //See http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx#maxpath
		{"c:\\" + strings.Repeat("a", 32768), false, Win},
		{"c:\\path\\file (x86)\bar", true, Win},
		{"c:\\path\\file", true, Win},
		{"c:\\path\\file:exe", false, Unknown},
		{"C:\\", true, Win},
		{"c:\\path\\file\\", true, Win},
		{"c:/path/file/", false, Unknown},
		{"/path/file/", true, Unix},
		{"/path/file:SAMPLE/", true, Unix},
		{"/path/file:/.txt", true, Unix},
		{"/path", true, Unix},
	}
	for _, test := range tests {
		actual, osType := IsFilePath(test.param)
		if actual != test.expected || osType != test.osType {
			t.Errorf("Expected IsFilePath(%q) to be %v, got %v", test.param, test.expected, actual)
		}
	}
}

func TestIsLatitude(t *testing.T) {
	t.Parallel()

	var tests = []struct {
		param    string
		expected bool
	}{
		{"", false},
		{"-90.000", true},
		{"+90", true},
		{"47.1231231", true},
		{"+99.9", false},
		{"108", false},
	}
	for _, test := range tests {
		actual := IsLatitude(test.param)
		if actual != test.expected {
			t.Errorf("Expected IsLatitude(%q) to be %v, got %v", test.param, test.expected, actual)
		}
	}
}

func TestIsLongitude(t *testing.T) {
	t.Parallel()

	var tests = []struct {
		param    string
		expected bool
	}{
		{"", false},
		{"-180.000", true},
		{"180.1", false},
		{"+73.234", true},
		{"+382.3811", false},
		{"23.11111111", true},
	}
	for _, test := range tests {
		actual := IsLongitude(test.param)
		if actual != test.expected {
			t.Errorf("Expected IsLongitude(%q) to be %v, got %v", test.param, test.expected, actual)
		}
	}
}

func TestIsSSN(t *testing.T) {
	t.Parallel()

	var tests = []struct {
		param    string
		expected bool
	}{
		{"", false},
		{"00-90-8787", false},
		{"66690-76", false},
		{"191 60 2869", true},
		{"191-60-2869", true},
	}
	for _, test := range tests {
		actual := IsSSN(test.param)
		if actual != test.expected {
			t.Errorf("Expected IsSSN(%q) to be %v, got %v", test.param, test.expected, actual)
		}
	}
}

func TestIsMongoID(t *testing.T) {
	t.Parallel()

	var tests = []struct {
		param    string
		expected bool
	}{
		{"507f1f77bcf86cd799439011", true},
		{"507f1f77bcf86cd7994390", false},
		{"507f1f77bcf86cd79943901z", false},
		{"507f1f77bcf86cd799439011 ", false},
		{"", false},
	}
	for _, test := range tests {
		actual := IsMongoID(test.param)
		if actual != test.expected {
			t.Errorf("Expected IsMongoID(%q) to be %v, got %v", test.param, test.expected, actual)
		}
	}
}

func TestByteLength(t *testing.T) {
	t.Parallel()

	var tests = []struct {
		value    string
		min      string
		max      string
		expected bool
	}{
		{"123456", "0", "100", true},
		{"1239999", "0", "0", false},
		{"1239asdfasf99", "100", "200", false},
		{"1239999asdff29", "10", "30", true},
	}
	for _, test := range tests {
		actual := ByteLength(test.value, test.min, test.max)
		if actual != test.expected {
			t.Errorf("Expected ByteLength(%s, %s, %s) to be %v, got %v", test.value, test.min, test.max, test.expected, actual)
		}
	}
}

func TestStringLength(t *testing.T) {
	t.Parallel()

	var tests = []struct {
		value    string
		min      string
		max      string
		expected bool
	}{
		{"123456", "0", "100", true},
		{"1239999", "0", "0", false},
		{"1239asdfasf99", "100", "200", false},
		{"1239999asdff29", "10", "30", true},
		{"あいうえお", "0", "5", true},
		{"あいうえおか", "0", "5", false},
		{"あいうえお", "0", "0", false},
		{"あいうえ", "5", "10", false},
	}
	for _, test := range tests {
		actual := StringLength(test.value, test.min, test.max)
		if actual != test.expected {
			t.Errorf("Expected StringLength(%s, %s, %s) to be %v, got %v", test.value, test.min, test.max, test.expected, actual)
		}
	}
}

type Address struct {
	Street string `valid:"-"`
	Zip    string `json:"zip" valid:"numeric,required"`
}

type User struct {
	Name     string `valid:"required"`
	Email    string `valid:"required,email"`
	Password string `valid:"required"`
	Age      int    `valid:"required,numeric,@#\u0000"`
	Home     *Address
	Work     []Address
}

type UserValid struct {
	Name     string `valid:"required"`
	Email    string `valid:"required,email"`
	Password string `valid:"required"`
	Age      int    `valid:"required"`
	Home     *Address
	Work     []Address
}

type PrivateStruct struct {
	privateField string `valid:"required,alpha,d_k"`
	NonZero      int
	ListInt      []int
	ListString   []string `valid:"alpha"`
	Work         [2]Address
	Home         Address
	Map          map[string]Address
}

type NegationStruct struct {
	NotInt string `valid:"!int"`
	Int    string `valid:"int"`
}

type LengthStruct struct {
	Length string `valid:"length(10|20)"`
}

type StringLengthStruct struct {
	Length string `valid:"stringlength(10|20)"`
}

type Post struct {
	Title    string `valid:"alpha,required"`
	Message  string `valid:"ascii"`
	AuthorIP string `valid:"ipv4"`
}

type MissingValidationDeclationStruct struct {
	Name  string ``
	Email string `valid:"required,email"`
}

type FieldsRequiredByDefaultButExemptStruct struct {
	Name  string `valid:"-"`
	Email string `valid:"email"`
}

type FieldsRequiredByDefaultButExemptOrOptionalStruct struct {
	Name  string `valid:"-"`
	Email string `valid:"optional,email"`
}

func TestValidateMissingValidationDeclationStruct(t *testing.T) {
	var tests = []struct {
		param    MissingValidationDeclationStruct
		expected bool
	}{
		{MissingValidationDeclationStruct{}, false},
		{MissingValidationDeclationStruct{Name: "TEST", Email: "*****@*****.**"}, false},
	}
	SetFieldsRequiredByDefault(true)
	for _, test := range tests {
		actual, err := ValidateStruct(test.param)
		if actual != test.expected {
			t.Errorf("Expected ValidateStruct(%q) to be %v, got %v", test.param, test.expected, actual)
			if err != nil {
				t.Errorf("Got Error on ValidateStruct(%q): %s", test.param, err)
			}
		}
	}
	SetFieldsRequiredByDefault(false)
}

func TestFieldsRequiredByDefaultButExemptStruct(t *testing.T) {
	var tests = []struct {
		param    FieldsRequiredByDefaultButExemptStruct
		expected bool
	}{
		{FieldsRequiredByDefaultButExemptStruct{}, false},
		{FieldsRequiredByDefaultButExemptStruct{Name: "TEST"}, false},
		{FieldsRequiredByDefaultButExemptStruct{Email: ""}, false},
		{FieldsRequiredByDefaultButExemptStruct{Email: "*****@*****.**"}, true},
	}
	SetFieldsRequiredByDefault(true)
	for _, test := range tests {
		actual, err := ValidateStruct(test.param)
		if actual != test.expected {
			t.Errorf("Expected ValidateStruct(%q) to be %v, got %v", test.param, test.expected, actual)
			if err != nil {
				t.Errorf("Got Error on ValidateStruct(%q): %s", test.param, err)
			}
		}
	}
	SetFieldsRequiredByDefault(false)
}

func TestFieldsRequiredByDefaultButExemptOrOptionalStruct(t *testing.T) {
	var tests = []struct {
		param    FieldsRequiredByDefaultButExemptOrOptionalStruct
		expected bool
	}{
		{FieldsRequiredByDefaultButExemptOrOptionalStruct{}, true},
		{FieldsRequiredByDefaultButExemptOrOptionalStruct{Name: "TEST"}, true},
		{FieldsRequiredByDefaultButExemptOrOptionalStruct{Email: ""}, true},
		{FieldsRequiredByDefaultButExemptOrOptionalStruct{Email: "*****@*****.**"}, true},
		{FieldsRequiredByDefaultButExemptOrOptionalStruct{Email: "test@example"}, false},
	}
	SetFieldsRequiredByDefault(true)
	for _, test := range tests {
		actual, err := ValidateStruct(test.param)
		if actual != test.expected {
			t.Errorf("Expected ValidateStruct(%q) to be %v, got %v", test.param, test.expected, actual)
			if err != nil {
				t.Errorf("Got Error on ValidateStruct(%q): %s", test.param, err)
			}
		}
	}
	SetFieldsRequiredByDefault(false)
}

type CustomByteArray [6]byte

type StructWithCustomByteArray struct {
	ID    CustomByteArray `valid:"customByteArrayValidator"`
	Email string          `valid:"email"`
}

func TestStructWithCustomByteArray(t *testing.T) {
	// add our custom byte array validator that fails when the byte array is pristine (all zeroes)
	CustomTypeTagMap["customByteArrayValidator"] = CustomTypeValidator(func(i interface{}) bool {
		switch v := i.(type) {
		case CustomByteArray:
			for _, e := range v { // check if v is empty, i.e. all zeroes
				if e != 0 {
					return true
				}
			}
		}
		return false
	})
	testCustomByteArray := CustomByteArray{'1', '2', '3', '4', '5', '6'}
	var tests = []struct {
		param    StructWithCustomByteArray
		expected bool
	}{
		{StructWithCustomByteArray{}, false},
		{StructWithCustomByteArray{Email: "*****@*****.**"}, false},
		{StructWithCustomByteArray{ID: testCustomByteArray, Email: "*****@*****.**"}, true},
	}
	for _, test := range tests {
		actual, err := ValidateStruct(test.param)
		if actual != test.expected {
			t.Errorf("Expected ValidateStruct(%q) to be %v, got %v", test.param, test.expected, actual)
			if err != nil {
				t.Errorf("Got Error on ValidateStruct(%q): %s", test.param, err)
			}
		}
	}
}

func TestValidateNegationStruct(t *testing.T) {
	var tests = []struct {
		param    NegationStruct
		expected bool
	}{
		{NegationStruct{"a1", "11"}, true},
		{NegationStruct{"*****@*****.**", "11"}, true},
		{NegationStruct{"123456----", "11"}, true},
		{NegationStruct{"::1", "11"}, true},
		{NegationStruct{"123.123", "11"}, true},
		{NegationStruct{"a1", "a1"}, false},
		{NegationStruct{"11", "a1"}, false},
		{NegationStruct{"11", "11"}, false},
	}
	for _, test := range tests {
		actual, err := ValidateStruct(test.param)
		if actual != test.expected {
			t.Errorf("Expected ValidateStruct(%q) to be %v, got %v", test.param, test.expected, actual)
			if err != nil {
				t.Errorf("Got Error on ValidateStruct(%q): %s", test.param, err)
			}
		}
	}
}

func TestLengthStruct(t *testing.T) {
	var tests = []struct {
		param    interface{}
		expected bool
	}{
		{LengthStruct{"11111"}, false},
		{LengthStruct{"11111111111111111110000000000000000"}, false},
		{LengthStruct{"11dfffdf0099"}, true},
	}

	for _, test := range tests {
		actual, err := ValidateStruct(test.param)
		if actual != test.expected {
			t.Errorf("Expected ValidateStruct(%q) to be %v, got %v", test.param, test.expected, actual)
			if err != nil {
				t.Errorf("Got Error on ValidateStruct(%q): %s", test.param, err)
			}
		}
	}
}

func TestStringLengthStruct(t *testing.T) {
	var tests = []struct {
		param    interface{}
		expected bool
	}{
		{StringLengthStruct{"11111"}, false},
		{StringLengthStruct{"11111111111111111110000000000000000"}, false},
		{StringLengthStruct{"11dfffdf0099"}, true},
		{StringLengthStruct{"あいうえお"}, false},
		{StringLengthStruct{"あいうえおかきくけこ"}, true},
		{StringLengthStruct{"あいうえおかきくけこさしすせそたちつてと"}, true},
		{StringLengthStruct{"あいうえおかきくけこさしすせそたちつてとな"}, false},
	}

	for _, test := range tests {
		actual, err := ValidateStruct(test.param)
		if actual != test.expected {
			t.Errorf("Expected ValidateStruct(%q) to be %v, got %v", test.param, test.expected, actual)
			if err != nil {
				t.Errorf("Got Error on ValidateStruct(%q): %s", test.param, err)
			}
		}
	}
}

func TestValidateStruct(t *testing.T) {

	var tests = []struct {
		param    interface{}
		expected bool
	}{
		{User{"John", "*****@*****.**", "123G#678", 20, &Address{"Street", "123456"}, []Address{Address{"Street", "123456"}, Address{"Street", "123456"}}}, false},
		{User{"John", "john!yahoo.com", "12345678", 20, &Address{"Street", "ABC456D89"}, []Address{Address{"Street", "ABC456D89"}, Address{"Street", "123456"}}}, false},
		{User{"John", "", "12345", 0, &Address{"Street", "123456789"}, []Address{Address{"Street", "ABC456D89"}, Address{"Street", "123456"}}}, false},
		{UserValid{"John", "*****@*****.**", "123G#678", 20, &Address{"Street", "123456"}, []Address{Address{"Street", "123456"}, Address{"Street", "123456"}}}, true},
		{UserValid{"John", "john!yahoo.com", "12345678", 20, &Address{"Street", "ABC456D89"}, []Address{Address{"Street", "ABC456D89"}, Address{"Street", "123456"}}}, false},
		{UserValid{"John", "", "12345", 0, &Address{"Street", "123456789"}, []Address{Address{"Street", "ABC456D89"}, Address{"Street", "123456"}}}, false},
		{nil, true},
		{User{"John", "*****@*****.**", "123G#678", 0, &Address{"Street", "123456"}, []Address{}}, false},
		{"im not a struct", false},
	}
	for _, test := range tests {
		actual, err := ValidateStruct(test.param)
		if actual != test.expected {
			t.Errorf("Expected ValidateStruct(%q) to be %v, got %v", test.param, test.expected, actual)
			if err != nil {
				t.Errorf("Got Error on ValidateStruct(%q): %s", test.param, err)
			}
		}
	}

	TagMap["d_k"] = Validator(func(str string) bool {
		return str == "d_k"
	})
	result, err := ValidateStruct(PrivateStruct{"d_k", 0, []int{1, 2}, []string{"hi", "super"}, [2]Address{Address{"Street", "123456"},
		Address{"Street", "123456"}}, Address{"Street", "123456"}, map[string]Address{"address": Address{"Street", "123456"}}})
	if result != true {
		t.Log("Case ", 6, ": expected ", true, " when result is ", result)
		t.Error(err)
		t.FailNow()
	}
}

type testByteArray [8]byte
type testByteMap map[byte]byte
type testByteSlice []byte

func TestRequired(t *testing.T) {

	testString := "foobar"
	var tests = []struct {
		param    interface{}
		expected bool
	}{
		{
			struct {
				Pointer *string `valid:"required"`
			}{},
			false,
		},
		{
			struct {
				Pointer *string `valid:"required"`
			}{
				Pointer: &testString,
			},
			true,
		},
		{
			struct {
				Addr Address `valid:"required"`
			}{},
			false,
		},
		{
			struct {
				Addr Address `valid:"required"`
			}{
				Addr: Address{"", "123"},
			},
			true,
		},
		{
			struct {
				Pointer *Address `valid:"required"`
			}{},
			false,
		},
		{
			struct {
				Pointer *Address `valid:"required"`
			}{
				Pointer: &Address{"", "123"},
			},
			true,
		},
		{
			struct {
				TestByteArray testByteArray `valid:"required"`
			}{},
			false,
		},
		{
			struct {
				TestByteArray testByteArray `valid:"required"`
			}{
				testByteArray{},
			},
			false,
		},
		{
			struct {
				TestByteArray testByteArray `valid:"required"`
			}{
				testByteArray{'1', '2', '3', '4', '5', '6', '7', 'A'},
			},
			true,
		},
		{
			struct {
				TestByteMap testByteMap `valid:"required"`
			}{},
			false,
		},
		{
			struct {
				TestByteSlice testByteSlice `valid:"required"`
			}{},
			false,
		},
	}
	for _, test := range tests {
		actual, err := ValidateStruct(test.param)
		if actual != test.expected {
			t.Errorf("Expected ValidateStruct(%q) to be %v, got %v", test.param, test.expected, actual)
			if err != nil {
				t.Errorf("Got Error on ValidateStruct(%q): %s", test.param, err)
			}
		}
	}
}

func TestErrorByField(t *testing.T) {
	t.Parallel()

	var tests = []struct {
		param    string
		expected string
	}{
		{"message", ""},
		{"Message", ""},
		{"title", ""},
		{"Title", "My123 does not validate as alpha"},
		{"AuthorIP", "123 does not validate as ipv4"},
	}
	post := &Post{"My123", "duck13126", "123"}
	_, err := ValidateStruct(post)

	for _, test := range tests {
		actual := ErrorByField(err, test.param)
		if actual != test.expected {
			t.Errorf("Expected ErrorByField(%q) to be %v, got %v", test.param, test.expected, actual)
		}
	}
}

func TestErrorsByField(t *testing.T) {
	t.Parallel()

	var tests = []struct {
		param    string
		expected string
	}{
		{"Title", "My123 does not validate as alpha"},
		{"AuthorIP", "123 does not validate as ipv4"},
	}
	post := &Post{Title: "My123", Message: "duck13126", AuthorIP: "123"}
	_, err := ValidateStruct(post)
	errs := ErrorsByField(err)
	if len(errs) != 2 {
		t.Errorf("There should only be 2 errors but got %v", len(errs))
	}

	for _, test := range tests {
		if actual, ok := errs[test.param]; !ok || actual != test.expected {
			t.Errorf("Expected ErrorsByField(%q) to be %v, got %v", test.param, test.expected, actual)
		}
	}
}

func TestValidateStructPointers(t *testing.T) {
	// Struct which uses pointers for values
	type UserWithPointers struct {
		Name         *string `valid:"-"`
		Email        *string `valid:"email"`
		FavoriteFood *string `valid:"length(0|32)"`
		Nerd         *bool   `valid:"-"`
	}

	var tests = []struct {
		param    string
		expected string
	}{
		{"Name", ""},
		{"Email", "invalid does not validate as email"},
		{"FavoriteFood", ""},
		{"Nerd", ""},
	}

	name := "Herman"
	email := "invalid"
	food := "Pizza"
	nerd := true
	user := &UserWithPointers{&name, &email, &food, &nerd}
	_, err := ValidateStruct(user)

	for _, test := range tests {
		actual := ErrorByField(err, test.param)
		if actual != test.expected {
			t.Errorf("Expected ErrorByField(%q) to be %v, got %v", test.param, test.expected, actual)
		}
	}
}

func ExampleValidateStruct() {
	type Post struct {
		Title    string `valid:"alphanum,required"`
		Message  string `valid:"duck,ascii"`
		AuthorIP string `valid:"ipv4"`
	}
	post := &Post{"My Example Post", "duck", "123.234.54.3"}

	//Add your own struct validation tags
	TagMap["duck"] = Validator(func(str string) bool {
		return str == "duck"
	})

	result, err := ValidateStruct(post)
	if err != nil {
		println("error: " + err.Error())
	}
	println(result)
}