Validate package tests

This commit is contained in:
anthdm 2024-06-23 08:40:24 +02:00
parent 597c53d4ed
commit e97af35676
6 changed files with 176 additions and 22 deletions

2
Makefile Normal file
View file

@ -0,0 +1,2 @@
test:
@go test -v ./...

View file

@ -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
View file

@ -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=

View file

@ -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.

View file

@ -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
// 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 := getFieldValueByName(data, fieldName)
}
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
}

View file

@ -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)
fmt.Println(errors)
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)
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) {