add validation error type, fix tests, add number tests
This commit is contained in:
+5
-2
@@ -1,6 +1,9 @@
|
||||
package validate
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAll(t *testing.T) {
|
||||
type TestCase[T any] struct {
|
||||
@@ -31,7 +34,7 @@ func TestAll(t *testing.T) {
|
||||
|
||||
err := tc.F(tc.Input)
|
||||
|
||||
if err != tc.Err {
|
||||
if !errors.Is(err, tc.Err) {
|
||||
t.Errorf("Expected error %v, got %v", tc.Err, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
package validate
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Validation error.
|
||||
var (
|
||||
ErrDisallowedChars = errors.New("contains disallowed characters")
|
||||
ErrDisallowedChars = NewError("contains disallowed characters")
|
||||
)
|
||||
|
||||
// Chars validates whether a string contains only allowed characters.
|
||||
func Chars(allow string) func(string) error {
|
||||
return func(value string) error {
|
||||
rs := []rune(value)
|
||||
for _, r := range rs {
|
||||
for _, r := range value {
|
||||
if !strings.ContainsRune(allow, r) {
|
||||
return ErrDisallowedChars
|
||||
}
|
||||
|
||||
+5
-2
@@ -1,6 +1,9 @@
|
||||
package validate
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestChars(t *testing.T) {
|
||||
type TestCase struct {
|
||||
@@ -22,7 +25,7 @@ func TestChars(t *testing.T) {
|
||||
f := Chars(tc.C)
|
||||
err := f(tc.Input)
|
||||
|
||||
if err != tc.Err {
|
||||
if !errors.Is(err, tc.Err) {
|
||||
t.Errorf("Expected error %v, got %v", tc.Err, err)
|
||||
}
|
||||
}
|
||||
|
||||
+5
-2
@@ -1,6 +1,9 @@
|
||||
package validate
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEmail(t *testing.T) {
|
||||
type TestCase struct {
|
||||
@@ -18,7 +21,7 @@ func TestEmail(t *testing.T) {
|
||||
|
||||
err := Email(tc.Input)
|
||||
|
||||
if err != tc.Err {
|
||||
if !errors.Is(err, tc.Err) {
|
||||
t.Errorf("Expected error %v, got %v", tc.Err, err)
|
||||
}
|
||||
}
|
||||
|
||||
+6
-3
@@ -1,6 +1,9 @@
|
||||
package validate
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEqualInt(t *testing.T) {
|
||||
type TestCase struct {
|
||||
@@ -21,7 +24,7 @@ func TestEqualInt(t *testing.T) {
|
||||
f := Equal(tc.C)
|
||||
err := f(tc.I)
|
||||
|
||||
if err != tc.Err {
|
||||
if !errors.Is(err, tc.Err) {
|
||||
t.Errorf("Expected error %v, got %v", tc.Err, err)
|
||||
}
|
||||
}
|
||||
@@ -46,7 +49,7 @@ func TestEqualStr(t *testing.T) {
|
||||
f := Equal(tc.C)
|
||||
err := f(tc.I)
|
||||
|
||||
if err != tc.Err {
|
||||
if !errors.Is(err, tc.Err) {
|
||||
t.Errorf("Expected error %v, got %v", tc.Err, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package validate
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Validation error.
|
||||
var (
|
||||
Err = Error{}
|
||||
)
|
||||
|
||||
// Error represents a validation error.
|
||||
type Error struct {
|
||||
Message string
|
||||
Data []any
|
||||
}
|
||||
|
||||
// Error retrieves the message of a validation Error.
|
||||
// If it has Data, the message will be formatted.
|
||||
func (e Error) Error() string {
|
||||
if len(e.Data) > 0 {
|
||||
return fmt.Sprintf(e.Message, e.Data...)
|
||||
}
|
||||
return e.Message
|
||||
}
|
||||
|
||||
// Is determines whether the Error is an instance of the target.
|
||||
// https://pkg.go.dev/errors#Is
|
||||
//
|
||||
// If the target is a validation error and specifies a message, this function returns true if the messages match.
|
||||
// If the target is an empty validation error, this function always returns true.
|
||||
func (e Error) Is(target error) bool {
|
||||
if t, ok := target.(Error); ok {
|
||||
return t.Message == e.Message || t.Message == ""
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (e Error) With(value any) Error {
|
||||
if e.Data == nil {
|
||||
e.Data = []any{}
|
||||
}
|
||||
e.Data = append(e.Data, value)
|
||||
return e
|
||||
}
|
||||
|
||||
// NewError creates a new validation error.
|
||||
func NewError(message string) Error {
|
||||
return Error{
|
||||
Message: message,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package validate
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestErrorIs(t *testing.T) {
|
||||
type TestCase struct {
|
||||
Err error
|
||||
Target error
|
||||
Is bool
|
||||
}
|
||||
|
||||
testCases := []TestCase{
|
||||
// Is any validation error
|
||||
{Err: Err, Target: Err, Is: true},
|
||||
{Err: ErrDisallowedChars, Target: Err, Is: true},
|
||||
{Err: ErrMustBeGreater, Target: Err, Is: true},
|
||||
|
||||
// Is specific validation error
|
||||
{Err: ErrDisallowedChars, Target: ErrDisallowedChars, Is: true},
|
||||
{Err: ErrMustBeGreater, Target: ErrMustBeGreater, Is: true},
|
||||
|
||||
// Is not specific validation error
|
||||
{Err: Err, Target: ErrDisallowedChars},
|
||||
{Err: Err, Target: ErrMustBeGreater},
|
||||
{Err: ErrMustBeGreater, Target: ErrDisallowedChars},
|
||||
{Err: ErrDisallowedChars, Target: ErrMustBeGreater},
|
||||
|
||||
// Is not any other error
|
||||
{Err: ErrDisallowedChars, Target: errors.New("contains disallowed characters")},
|
||||
{Err: ErrMustBeGreater, Target: errors.New("must be greater than %v")},
|
||||
}
|
||||
|
||||
for i, tc := range testCases {
|
||||
t.Logf("(%d) Testing %v against %v", i, tc.Err, tc.Target)
|
||||
|
||||
if errors.Is(tc.Err, tc.Target) {
|
||||
if !tc.Is {
|
||||
t.Errorf("%v should not equal %v", tc.Err, tc.Target)
|
||||
}
|
||||
} else {
|
||||
if tc.Is {
|
||||
t.Errorf("%v should equal %v", tc.Err, tc.Target)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-2
@@ -1,6 +1,7 @@
|
||||
package validate
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -25,7 +26,7 @@ func TestInInt(t *testing.T) {
|
||||
f := In(tc.A...)
|
||||
err := f(tc.Input)
|
||||
|
||||
if err != tc.Err {
|
||||
if !errors.Is(err, tc.Err) {
|
||||
t.Errorf("Expected error %v, got %v", tc.Err, err)
|
||||
}
|
||||
}
|
||||
@@ -52,7 +53,7 @@ func TestInString(t *testing.T) {
|
||||
f := In(tc.A...)
|
||||
err := f(tc.Input)
|
||||
|
||||
if err != tc.Err {
|
||||
if !errors.Is(err, tc.Err) {
|
||||
t.Errorf("Expected error %v, got %v", tc.Err, err)
|
||||
}
|
||||
}
|
||||
|
||||
+7
-4
@@ -1,6 +1,9 @@
|
||||
package validate
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMaxLength(t *testing.T) {
|
||||
type TestCase struct {
|
||||
@@ -22,7 +25,7 @@ func TestMaxLength(t *testing.T) {
|
||||
f := MaxLength(tc.L)
|
||||
err := f(tc.Input)
|
||||
|
||||
if err != tc.Err {
|
||||
if !errors.Is(err, tc.Err) {
|
||||
t.Errorf("Expected error %v, got %v", tc.Err, err)
|
||||
}
|
||||
}
|
||||
@@ -30,8 +33,8 @@ func TestMaxLength(t *testing.T) {
|
||||
|
||||
func TestMinLength(t *testing.T) {
|
||||
type TestCase struct {
|
||||
L int
|
||||
Input string
|
||||
L int
|
||||
Err error
|
||||
}
|
||||
|
||||
@@ -48,7 +51,7 @@ func TestMinLength(t *testing.T) {
|
||||
f := MinLength(tc.L)
|
||||
err := f(tc.Input)
|
||||
|
||||
if err != tc.Err {
|
||||
if !errors.Is(err, tc.Err) {
|
||||
t.Errorf("Expected error %v, got %v", tc.Err, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,91 +1,104 @@
|
||||
package validate
|
||||
|
||||
import "errors"
|
||||
|
||||
// Validation error.
|
||||
var (
|
||||
ErrTooHigh = errors.New("too high")
|
||||
ErrTooLow = errors.New("too low")
|
||||
ErrMustBeGreater = NewError("must be greater than %v")
|
||||
ErrMustBeGreaterOrEqual = NewError("must be greater than or equal to %v")
|
||||
ErrMustBeLess = NewError("must be less than %v")
|
||||
ErrMustBeLessOrEqual = NewError("must be less than or equal to %v")
|
||||
)
|
||||
|
||||
// Max validates whether an integer is less than or equal to a given maximum.
|
||||
// If exclusive is true, an equal value will also produce an error.
|
||||
func Max(n int, exclusive bool) func(int) error {
|
||||
return func(value int) error {
|
||||
if exclusive {
|
||||
if value >= n {
|
||||
return ErrTooHigh
|
||||
return ErrMustBeLess.With(n)
|
||||
}
|
||||
}
|
||||
if value > n {
|
||||
return ErrTooHigh
|
||||
return ErrMustBeLessOrEqual.With(n)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MaxFloat32 validates whether a float32 is less than or equal to a given maximum.
|
||||
// If exclusive is true, an equal value will also produce an error.
|
||||
func MaxFloat32(n float32, exclusive bool) func(float32) error {
|
||||
return func(value float32) error {
|
||||
if exclusive {
|
||||
if value >= n {
|
||||
return ErrTooHigh
|
||||
return ErrMustBeLess.With(n)
|
||||
}
|
||||
}
|
||||
if value > n {
|
||||
return ErrTooHigh
|
||||
return ErrMustBeLessOrEqual.With(n)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MaxFloat64 validates whether a float64 is less than or equal to a given maximum.
|
||||
// If exclusive is true, an equal value will also produce an error.
|
||||
func MaxFloat64(n float64, exclusive bool) func(float64) error {
|
||||
return func(value float64) error {
|
||||
if exclusive {
|
||||
if value >= n {
|
||||
return ErrTooHigh
|
||||
return ErrMustBeLess.With(n)
|
||||
}
|
||||
}
|
||||
if value > n {
|
||||
return ErrTooHigh
|
||||
return ErrMustBeLessOrEqual.With(n)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Min validates whether an integer is less than or equal to a given maximum.
|
||||
// If exclusive is true, an equal value will also produce an error.
|
||||
func Min(n int, exclusive bool) func(int) error {
|
||||
return func(value int) error {
|
||||
if exclusive {
|
||||
if value <= n {
|
||||
return ErrTooHigh
|
||||
return ErrMustBeGreater.With(n)
|
||||
}
|
||||
}
|
||||
if value < n {
|
||||
return ErrTooHigh
|
||||
return ErrMustBeGreaterOrEqual.With(n)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MinFloat32 validates whether a float32 is less than or equal to a given maximum.
|
||||
// If exclusive is true, an equal value will also produce an error.
|
||||
func MinFloat32(n float32, exclusive bool) func(float32) error {
|
||||
return func(value float32) error {
|
||||
if exclusive {
|
||||
if value <= n {
|
||||
return ErrTooHigh
|
||||
return ErrMustBeGreater.With(n)
|
||||
}
|
||||
}
|
||||
if value < n {
|
||||
return ErrTooHigh
|
||||
return ErrMustBeGreaterOrEqual.With(n)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MinFloat64 validates whether a float64 is less than or equal to a given maximum.
|
||||
// If exclusive is true, an equal value will also produce an error.
|
||||
func MinFloat64(n float64, exclusive bool) func(float64) error {
|
||||
return func(value float64) error {
|
||||
if exclusive {
|
||||
if value <= n {
|
||||
return ErrTooHigh
|
||||
return ErrMustBeGreater.With(n)
|
||||
}
|
||||
}
|
||||
if value < n {
|
||||
return ErrTooHigh
|
||||
return ErrMustBeGreaterOrEqual.With(n)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
+168
@@ -0,0 +1,168 @@
|
||||
package validate
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMax(t *testing.T) {
|
||||
type TestCase struct {
|
||||
Input int
|
||||
N int
|
||||
Excl bool
|
||||
Err error
|
||||
}
|
||||
|
||||
testCases := []TestCase{
|
||||
{Input: 10, N: 0, Err: ErrMustBeLessOrEqual},
|
||||
{Input: 10, N: 10},
|
||||
{Input: 10, N: 15},
|
||||
{Input: 10, N: 10, Excl: true, Err: ErrMustBeLess},
|
||||
}
|
||||
|
||||
for n, tc := range testCases {
|
||||
t.Logf("(%d) Testing %d against maximum of %d", n, tc.Input, tc.N)
|
||||
|
||||
f := Max(tc.N, tc.Excl)
|
||||
err := f(tc.Input)
|
||||
|
||||
if !errors.Is(err, tc.Err) {
|
||||
t.Errorf("Expected error %v, got %v", tc.Err, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMaxFloat32(t *testing.T) {
|
||||
type TestCase struct {
|
||||
Input float32
|
||||
N float32
|
||||
Excl bool
|
||||
Err error
|
||||
}
|
||||
|
||||
testCases := []TestCase{
|
||||
{Input: 10, N: 0, Err: ErrMustBeLessOrEqual},
|
||||
{Input: 10, N: 10},
|
||||
{Input: 10, N: 15},
|
||||
{Input: 10, N: 10, Excl: true, Err: ErrMustBeLess},
|
||||
}
|
||||
|
||||
for n, tc := range testCases {
|
||||
t.Logf("(%d) Testing %g against maximum of %g", n, tc.Input, tc.N)
|
||||
|
||||
f := MaxFloat32(tc.N, tc.Excl)
|
||||
err := f(tc.Input)
|
||||
|
||||
if !errors.Is(err, tc.Err) {
|
||||
t.Errorf("Expected error %v, got %v", tc.Err, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMaxFloat64(t *testing.T) {
|
||||
type TestCase struct {
|
||||
Input float64
|
||||
N float64
|
||||
Excl bool
|
||||
Err error
|
||||
}
|
||||
|
||||
testCases := []TestCase{
|
||||
{Input: 10, N: 0, Err: ErrMustBeLessOrEqual},
|
||||
{Input: 10, N: 10},
|
||||
{Input: 10, N: 15},
|
||||
{Input: 10, N: 10, Excl: true, Err: ErrMustBeLess},
|
||||
}
|
||||
|
||||
for n, tc := range testCases {
|
||||
t.Logf("(%d) Testing %g against maximum of %g", n, tc.Input, tc.N)
|
||||
|
||||
f := MaxFloat64(tc.N, tc.Excl)
|
||||
err := f(tc.Input)
|
||||
|
||||
if !errors.Is(err, tc.Err) {
|
||||
t.Errorf("Expected error %v, got %v", tc.Err, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMin(t *testing.T) {
|
||||
type TestCase struct {
|
||||
Input int
|
||||
N int
|
||||
Excl bool
|
||||
Err error
|
||||
}
|
||||
|
||||
testCases := []TestCase{
|
||||
{Input: 10, N: 0},
|
||||
{Input: 10, N: 10},
|
||||
{Input: 10, N: 15, Err: ErrMustBeGreaterOrEqual},
|
||||
{Input: 10, N: 10, Excl: true, Err: ErrMustBeGreater},
|
||||
}
|
||||
|
||||
for n, tc := range testCases {
|
||||
t.Logf("(%d) Testing %d against minimum of %d", n, tc.Input, tc.N)
|
||||
|
||||
f := Min(tc.N, tc.Excl)
|
||||
err := f(tc.Input)
|
||||
|
||||
if !errors.Is(err, tc.Err) {
|
||||
t.Errorf("Expected error %v, got %v", tc.Err, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMinFloat32(t *testing.T) {
|
||||
type TestCase struct {
|
||||
Input float32
|
||||
N float32
|
||||
Excl bool
|
||||
Err error
|
||||
}
|
||||
|
||||
testCases := []TestCase{
|
||||
{Input: 10, N: 0},
|
||||
{Input: 10, N: 10},
|
||||
{Input: 10, N: 15, Err: ErrMustBeGreaterOrEqual},
|
||||
{Input: 10, N: 10, Excl: true, Err: ErrMustBeGreater},
|
||||
}
|
||||
|
||||
for n, tc := range testCases {
|
||||
t.Logf("(%d) Testing %g against minimum of %g", n, tc.Input, tc.N)
|
||||
|
||||
f := MinFloat32(tc.N, tc.Excl)
|
||||
err := f(tc.Input)
|
||||
|
||||
if !errors.Is(err, tc.Err) {
|
||||
t.Errorf("Expected error %v, got %v", tc.Err, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMinFloat64(t *testing.T) {
|
||||
type TestCase struct {
|
||||
Input float64
|
||||
N float64
|
||||
Excl bool
|
||||
Err error
|
||||
}
|
||||
|
||||
testCases := []TestCase{
|
||||
{Input: 10, N: 0},
|
||||
{Input: 10, N: 10},
|
||||
{Input: 10, N: 15, Err: ErrMustBeGreaterOrEqual},
|
||||
{Input: 10, N: 10, Excl: true, Err: ErrMustBeGreater},
|
||||
}
|
||||
|
||||
for n, tc := range testCases {
|
||||
t.Logf("(%d) Testing %g against minimum of %g", n, tc.Input, tc.N)
|
||||
|
||||
f := MinFloat64(tc.N, tc.Excl)
|
||||
err := f(tc.Input)
|
||||
|
||||
if !errors.Is(err, tc.Err) {
|
||||
t.Errorf("Expected error %v, got %v", tc.Err, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
+6
-3
@@ -1,6 +1,9 @@
|
||||
package validate
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMaxSize(t *testing.T) {
|
||||
type TestCase struct {
|
||||
@@ -21,7 +24,7 @@ func TestMaxSize(t *testing.T) {
|
||||
f := MaxSize[int](tc.L)
|
||||
err := f(tc.Input)
|
||||
|
||||
if err != tc.Err {
|
||||
if !errors.Is(err, tc.Err) {
|
||||
t.Errorf("Expected error %v, got %v", tc.Err, err)
|
||||
}
|
||||
}
|
||||
@@ -46,7 +49,7 @@ func TestMinSize(t *testing.T) {
|
||||
f := MinSize[int](tc.L)
|
||||
err := f(tc.Input)
|
||||
|
||||
if err != tc.Err {
|
||||
if !errors.Is(err, tc.Err) {
|
||||
t.Errorf("Expected error %v, got %v", tc.Err, err)
|
||||
}
|
||||
}
|
||||
|
||||
+5
-2
@@ -1,6 +1,9 @@
|
||||
package validate
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestUUID(t *testing.T) {
|
||||
type TestCase struct {
|
||||
@@ -23,7 +26,7 @@ func TestUUID(t *testing.T) {
|
||||
|
||||
err := UUID(tc.Input)
|
||||
|
||||
if err != tc.Err {
|
||||
if !errors.Is(err, tc.Err) {
|
||||
t.Errorf("Expected error %v, got %v", tc.Err, err)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user