Validate package tests
This commit is contained in:
parent
597c53d4ed
commit
e97af35676
6 changed files with 176 additions and 22 deletions
2
Makefile
Normal file
2
Makefile
Normal file
|
@ -0,0 +1,2 @@
|
|||
test:
|
||||
@go test -v ./...
|
|
@ -3,17 +3,14 @@ package event
|
|||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEventSubscribeEmit(t *testing.T) {
|
||||
var (
|
||||
expect = 1
|
||||
wg = sync.WaitGroup{}
|
||||
)
|
||||
wg.Add(1)
|
||||
Subscribe("foo.bar", func(_ context.Context, event any) {
|
||||
expect := 1
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
Subscribe("foo.a", func(_ context.Context, event any) {
|
||||
defer cancel()
|
||||
value, ok := event.(int)
|
||||
if !ok {
|
||||
t.Errorf("expected int got %v", reflect.TypeOf(event))
|
||||
|
@ -21,16 +18,15 @@ func TestEventSubscribeEmit(t *testing.T) {
|
|||
if value != 1 {
|
||||
t.Errorf("expected %d got %d", expect, value)
|
||||
}
|
||||
wg.Done()
|
||||
})
|
||||
Emit("foo.bar", expect)
|
||||
wg.Wait()
|
||||
Emit("foo.a", expect)
|
||||
<-ctx.Done()
|
||||
}
|
||||
|
||||
func TestUnsubscribe(t *testing.T) {
|
||||
sub := Subscribe("foo.bar", func(_ context.Context, _ any) {})
|
||||
sub := Subscribe("foo.b", func(_ context.Context, _ any) {})
|
||||
Unsubscribe(sub)
|
||||
if _, ok := stream.subs["foo.bar"]; ok {
|
||||
if _, ok := stream.subs["foo.b"]; ok {
|
||||
t.Errorf("expected topic foo.bar to be deleted")
|
||||
}
|
||||
}
|
||||
|
|
12
go.sum
12
go.sum
|
@ -1,8 +1,20 @@
|
|||
github.com/a-h/templ v0.2.707 h1:T1Gkd2ugbRglZ9rYw/VBchWOSZVKmetDbBkm4YubM7U=
|
||||
github.com/a-h/templ v0.2.707/go.mod h1:5cqsugkq9IerRNucNsI4DEamdHPsoGMQy99DzydLhM8=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
|
||||
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
|
||||
github.com/gorilla/sessions v1.3.0 h1:XYlkq7KcpOB2ZhHBPv5WpjMIxrQosiZanfoy1HLZFzg=
|
||||
github.com/gorilla/sessions v1.3.0/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
|
||||
var (
|
||||
emailRegex = regexp.MustCompile(`^[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,4}$`)
|
||||
urlRegex = regexp.MustCompile(`^(http(s)?://)?([\da-z\.-]+)\.([a-z\.]{2,6})([/\w \.-]*)*/?$`)
|
||||
urlRegex = regexp.MustCompile(`^(https?:\/\/)?(www\.)?([a-zA-Z0-9\-]+\.)+[a-zA-Z]{2,}(\/[a-zA-Z0-9\-._~:\/?#\[\]@!$&'()*+,;=]*)?$`)
|
||||
)
|
||||
|
||||
// RuleSet holds the state of a single rule.
|
||||
|
|
|
@ -39,7 +39,7 @@ func (e Errors) Has(field string) bool {
|
|||
// Schema represents a validation schema.
|
||||
type Schema map[string][]RuleSet
|
||||
|
||||
// Merge merges the two given schemas returning a new Schema.
|
||||
// Merge merges the two given schemas, returning a new Schema.
|
||||
func Merge(schema, other Schema) Schema {
|
||||
newSchema := Schema{}
|
||||
maps.Copy(newSchema, schema)
|
||||
|
@ -75,9 +75,14 @@ func Request(r *http.Request, data any, schema Schema) (Errors, bool) {
|
|||
func validate(data any, schema Schema, errors Errors) (Errors, bool) {
|
||||
ok := true
|
||||
for fieldName, ruleSets := range schema {
|
||||
// Uppercase the field name so we never check un-exported fields
|
||||
fieldName = string(unicode.ToUpper(rune(fieldName[0]))) + fieldName[1:]
|
||||
fieldValue := getFieldValueByName(data, fieldName)
|
||||
// Uppercase the field name so we never check un-exported fields.
|
||||
// But we need to watch out for member fields that are uppercased by
|
||||
// the user. For example (URL, ID, ...)
|
||||
if !isUppercase(fieldName) {
|
||||
fieldName = string(unicode.ToUpper(rune(fieldName[0]))) + fieldName[1:]
|
||||
}
|
||||
|
||||
fieldValue := getFieldAndTagByName(data, fieldName)
|
||||
for _, set := range ruleSets {
|
||||
set.FieldValue = fieldValue
|
||||
set.FieldName = fieldName
|
||||
|
@ -98,7 +103,7 @@ func validate(data any, schema Schema, errors Errors) (Errors, bool) {
|
|||
return errors, ok
|
||||
}
|
||||
|
||||
func getFieldValueByName(v any, name string) any {
|
||||
func getFieldAndTagByName(v any, name string) any {
|
||||
val := reflect.ValueOf(v)
|
||||
if val.Kind() == reflect.Ptr {
|
||||
val = val.Elem()
|
||||
|
@ -168,3 +173,12 @@ func parseRequest(r *http.Request, v any) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func isUppercase(s string) bool {
|
||||
for _, ch := range s {
|
||||
if !unicode.IsUpper(rune(ch)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -2,6 +2,9 @@ package validate
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -28,6 +31,65 @@ var testSchema = Schema{
|
|||
"username": Rules(Required),
|
||||
}
|
||||
|
||||
func TestValidateRequest(t *testing.T) {
|
||||
var (
|
||||
email = "foo@bar.com"
|
||||
password = "superHunter123@"
|
||||
firstName = "Anthony"
|
||||
website = "http://foo.com"
|
||||
randomNumber = 123
|
||||
randomFloat = 9.999
|
||||
)
|
||||
formValues := url.Values{}
|
||||
formValues.Set("email", email)
|
||||
formValues.Set("password", password)
|
||||
formValues.Set("firstName", firstName)
|
||||
formValues.Set("url", website)
|
||||
formValues.Set("brandom", fmt.Sprint(randomNumber))
|
||||
formValues.Set("arandom", fmt.Sprint(randomFloat))
|
||||
encodedValues := formValues.Encode()
|
||||
|
||||
req, err := http.NewRequest("POST", "http://foo.com", strings.NewReader(encodedValues))
|
||||
assert.Nil(t, err)
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
|
||||
type SignupData struct {
|
||||
Email string `form:"email"`
|
||||
Password string `form:"password"`
|
||||
FirstName string `form:"firstName"`
|
||||
URL string `form:"url"`
|
||||
ARandomRenamedNumber int `form:"brandom"`
|
||||
ARandomRenamedFloat float64 `form:"arandom"`
|
||||
}
|
||||
|
||||
schema := Schema{
|
||||
"Email": Rules(Email),
|
||||
"Password": Rules(
|
||||
Required,
|
||||
ContainsDigit,
|
||||
ContainsUpper,
|
||||
ContainsSpecial,
|
||||
Min(7),
|
||||
),
|
||||
"FirstName": Rules(Min(3), Max(50)),
|
||||
"URL": Rules(URL),
|
||||
"ARandomRenamedNumber": Rules(GT(100), LT(124)),
|
||||
"ARandomRenamedFloat": Rules(GT(9.0), LT(10.1)),
|
||||
}
|
||||
|
||||
var data SignupData
|
||||
errors, ok := Request(req, &data, schema)
|
||||
assert.True(t, ok)
|
||||
assert.Empty(t, errors)
|
||||
|
||||
assert.Equal(t, data.Email, email)
|
||||
assert.Equal(t, data.Password, password)
|
||||
assert.Equal(t, data.FirstName, firstName)
|
||||
assert.Equal(t, data.URL, website)
|
||||
assert.Equal(t, data.ARandomRenamedNumber, randomNumber)
|
||||
assert.Equal(t, data.ARandomRenamedFloat, randomFloat)
|
||||
}
|
||||
|
||||
func TestTime(t *testing.T) {
|
||||
type Foo struct {
|
||||
CreatedAt time.Time
|
||||
|
@ -48,21 +110,89 @@ func TestTime(t *testing.T) {
|
|||
|
||||
func TestURL(t *testing.T) {
|
||||
type Foo struct {
|
||||
URL string
|
||||
URL string `v:"URL"`
|
||||
}
|
||||
foo := Foo{
|
||||
URL: "not an url",
|
||||
}
|
||||
schema := Schema{
|
||||
"url": Rules(URL),
|
||||
"URL": Rules(URL),
|
||||
}
|
||||
errors, ok := Validate(foo, schema)
|
||||
assert.False(t, ok)
|
||||
assert.NotEmpty(t, errors)
|
||||
|
||||
foo.URL = "www.user.com"
|
||||
validURLS := []string{
|
||||
"http://google.com",
|
||||
"http://www.google.com",
|
||||
"https://www.google.com",
|
||||
"https://www.google.com",
|
||||
"www.google.com",
|
||||
"https://book.com/sales",
|
||||
"app.book.com",
|
||||
"app.book.com/signup",
|
||||
}
|
||||
|
||||
for _, url := range validURLS {
|
||||
foo.URL = url
|
||||
errors, ok = Validate(foo, schema)
|
||||
assert.True(t, ok)
|
||||
assert.Empty(t, errors)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContainsUpper(t *testing.T) {
|
||||
type Foo struct {
|
||||
Password string
|
||||
}
|
||||
foo := Foo{"hunter"}
|
||||
schema := Schema{
|
||||
"Password": Rules(ContainsUpper),
|
||||
}
|
||||
errors, ok := Validate(foo, schema)
|
||||
assert.False(t, ok)
|
||||
assert.NotEmpty(t, errors)
|
||||
|
||||
foo.Password = "Hunter"
|
||||
errors, ok = Validate(foo, schema)
|
||||
assert.True(t, ok)
|
||||
fmt.Println(errors)
|
||||
assert.Empty(t, errors)
|
||||
}
|
||||
|
||||
func TestContainsDigit(t *testing.T) {
|
||||
type Foo struct {
|
||||
Password string
|
||||
}
|
||||
foo := Foo{"hunter"}
|
||||
schema := Schema{
|
||||
"Password": Rules(ContainsDigit),
|
||||
}
|
||||
errors, ok := Validate(foo, schema)
|
||||
assert.False(t, ok)
|
||||
assert.NotEmpty(t, errors)
|
||||
|
||||
foo.Password = "Hunter1"
|
||||
errors, ok = Validate(foo, schema)
|
||||
assert.True(t, ok)
|
||||
assert.Empty(t, errors)
|
||||
}
|
||||
|
||||
func TestContainsSpecial(t *testing.T) {
|
||||
type Foo struct {
|
||||
Password string
|
||||
}
|
||||
foo := Foo{"hunter"}
|
||||
schema := Schema{
|
||||
"Password": Rules(ContainsSpecial),
|
||||
}
|
||||
errors, ok := Validate(foo, schema)
|
||||
assert.False(t, ok)
|
||||
assert.NotEmpty(t, errors)
|
||||
|
||||
foo.Password = "Hunter@"
|
||||
errors, ok = Validate(foo, schema)
|
||||
assert.True(t, ok)
|
||||
assert.Empty(t, errors)
|
||||
}
|
||||
|
||||
func TestRuleIn(t *testing.T) {
|
||||
|
|
Loading…
Reference in a new issue