From bc7329ef0fefd685efe07013c40c37f730e52126 Mon Sep 17 00:00:00 2001 From: Fanan Dala Date: Sat, 30 Jul 2022 09:37:56 +0100 Subject: [PATCH] feat: add image validation --- .gitignore | 1 + README.md | 1 + baked_in.go | 71 +++++++++++++++++++++ doc.go | 8 +++ go.mod | 2 +- go.sum | 10 ++- testdata/music.mp3 | Bin 0 -> 5191 bytes translations/ar/ar.go | 5 ++ translations/ar/ar_test.go | 5 ++ translations/en/en.go | 5 ++ translations/en/en_test.go | 5 ++ translations/es/es.go | 5 ++ translations/es/es_test.go | 5 ++ translations/fa/fa.go | 5 ++ translations/fa/fa_test.go | 7 +- translations/fr/fr.go | 5 ++ translations/fr/fr_test.go | 5 ++ translations/id/id.go | 5 ++ translations/id/id_test.go | 5 ++ translations/it/it.go | 5 ++ translations/it/it_test.go | 5 ++ translations/ja/ja.go | 5 ++ translations/ja/ja_test.go | 5 ++ translations/nl/nl.go | 5 ++ translations/nl/nl_test.go | 5 ++ translations/pt/pt.go | 5 ++ translations/pt/pt_test.go | 5 ++ translations/pt_BR/pt_BR.go | 5 ++ translations/pt_BR/pt_BR_test.go | 5 ++ translations/ru/ru.go | 5 ++ translations/ru/ru_test.go | 5 ++ translations/tr/tr.go | 5 ++ translations/tr/tr_test.go | 5 ++ translations/vi/vi.go | 5 ++ translations/vi/vi_test.go | 5 ++ translations/zh/zh.go | 5 ++ translations/zh/zh_test.go | 5 ++ translations/zh_tw/zh_tw.go | 5 ++ translations/zh_tw/zh_tw_test.go | 5 ++ validator_test.go | 106 +++++++++++++++++++++++++++++++ 40 files changed, 356 insertions(+), 5 deletions(-) create mode 100644 testdata/music.mp3 diff --git a/.gitignore b/.gitignore index 6e43fac..d7edbc3 100644 --- a/.gitignore +++ b/.gitignore @@ -26,5 +26,6 @@ _testmain.go *.test *.out *.txt +/**/*.DS_Store cover.html README.html diff --git a/README.md b/README.md index 8b730b6..6bd2251 100644 --- a/README.md +++ b/README.md @@ -218,6 +218,7 @@ Baked-in Validations | - | - | | dir | Directory | | file | File path | +| image | Image | | isdefault | Is Default | | len | Length | | max | Maximum | diff --git a/baked_in.go b/baked_in.go index f2f0939..c21190b 100644 --- a/baked_in.go +++ b/baked_in.go @@ -7,6 +7,7 @@ import ( "encoding/hex" "encoding/json" "fmt" + "io" "net" "net/url" "os" @@ -20,6 +21,7 @@ import ( "golang.org/x/crypto/sha3" "golang.org/x/text/language" + "github.com/gabriel-vasile/mimetype" urn "github.com/leodido/go-urn" ) @@ -136,6 +138,7 @@ var ( "endswith": endsWith, "startsnotwith": startsNotWith, "endsnotwith": endsNotWith, + "image": isImage, "isbn": isISBN, "isbn10": isISBN10, "isbn13": isISBN13, @@ -1404,6 +1407,74 @@ func isFile(fl FieldLevel) bool { panic(fmt.Sprintf("Bad field type %T", field.Interface())) } +func isImage(fl FieldLevel) bool { + mimetypes := map[string]bool{ + "image/bmp": true, + "image/cis-cod": true, + "image/gif": true, + "image/ief": true, + "image/jpeg": true, + "image/jp2": true, + "image/jpx": true, + "image/jpm": true, + "image/pipeg": true, + "image/png": true, + "image/svg+xml": true, + "image/tiff": true, + "image/webp": true, + "image/x-cmu-raster": true, + "image/x-cmx": true, + "image/x-icon": true, + "image/x-portable-anymap": true, + "image/x-portable-bitmap": true, + "image/x-portable-graymap": true, + "image/x-portable-pixmap": true, + "image/x-rgb": true, + "image/x-xbitmap": true, + "image/x-xpixmap": true, + "image/x-xwindowdump": true, + } + field := fl.Field() + + switch field.Kind() { + case reflect.String: + filePath := field.String() + fileInfo, err := os.Stat(filePath) + + if err != nil { + return false + } + + if fileInfo.IsDir() { + return false + } + + file, err := os.OpenFile(filePath, os.O_RDWR, 0644) + if err != nil { + return false + } + defer file.Close() + + mime, err := mimetype.DetectReader(file) + if err != nil { + return false + } + + _, err = file.Seek(0, io.SeekStart) + if err != nil { + panic(fmt.Sprintf("Could not reset seek start for file. Error: %s", err)) + } + + if _, ok := mimetypes[mime.String()]; ok { + return true + } + + return false + } + + panic(fmt.Sprintf("Bad field type %T", field.Interface())) +} + // isE164 is the validation function for validating if the current field's value is a valid e.164 formatted phone number. func isE164(fl FieldLevel) bool { return e164Regex.MatchString(fl.Field().String()) diff --git a/doc.go b/doc.go index 7341c67..2118190 100644 --- a/doc.go +++ b/doc.go @@ -873,6 +873,14 @@ This is done using os.Stat, which is a platform independent function. Usage: file +Image path + +This validates that a string value contains a valid file path and that +the file exists on the machine and is an image. +This is done using os.Stat and github.com/gabriel-vasile/mimetype + + Usage: image + URL String This validates that a string value contains a valid url diff --git a/go.mod b/go.mod index e281ec0..7d27280 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.13 require ( github.com/davecgh/go-spew v1.1.1 // indirect + github.com/gabriel-vasile/mimetype v1.4.1 github.com/go-playground/assert/v2 v2.0.1 github.com/go-playground/locales v0.14.0 github.com/go-playground/universal-translator v0.18.0 @@ -12,7 +13,6 @@ require ( github.com/rogpeppe/go-internal v1.8.0 // indirect github.com/stretchr/testify v1.7.0 // indirect golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 - golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 // indirect golang.org/x/text v0.3.7 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect diff --git a/go.sum b/go.sum index a1c43c3..a8d0557 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/gabriel-vasile/mimetype v1.4.1 h1:TRWk7se+TOjCYgRth7+1/OYLNiRNIotknkFtf/dnN7Q= +github.com/gabriel-vasile/mimetype v1.4.1/go.mod h1:05Vi0w3Y9c/lNvJOdmIwvrrAhX3rYhfQQCaf9VJcv7M= github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= @@ -31,13 +33,15 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e h1:TsQ7F31D3bUCLeqPT0u+yjp1guoArKaNKmCr22PYgTQ= +golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 h1:siQdpVirKtzPhKl3lZWozZraCFObP8S1v6PRp0bLrtU= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= diff --git a/testdata/music.mp3 b/testdata/music.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..c810c805ce38a0c0a8cac0ed6883db4caeaaf35e GIT binary patch literal 5191 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 literal 0 HcmV?d00001 diff --git a/translations/ar/ar.go b/translations/ar/ar.go index 3886120..038635a 100644 --- a/translations/ar/ar.go +++ b/translations/ar/ar.go @@ -1346,6 +1346,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return t }, }, + { + tag: "image", + translation: "يجب أن تكون {0} صورة صالحة", + override: false, + }, } for _, t := range translations { diff --git a/translations/ar/ar_test.go b/translations/ar/ar_test.go index 93adfe9..6e2a418 100644 --- a/translations/ar/ar_test.go +++ b/translations/ar/ar_test.go @@ -149,6 +149,7 @@ func TestTranslations(t *testing.T) { PostCode string `validate:"postcode_iso3166_alpha2=SG"` PostCodeCountry string PostCodeByField string `validate:"postcode_iso3166_alpha2_field=PostCodeCountry"` + Image string `validate:"image"` } var test Test @@ -676,6 +677,10 @@ func TestTranslations(t *testing.T) { ns: "Test.PostCodeByField", expected: "لا يتطابق PostCodeByField مع تنسيق الرمز البريدي للبلد في حقل PostCodeCountry", }, + { + ns: "Test.Image", + expected: "يجب أن تكون Image صورة صالحة", + }, } for _, tt := range tests { diff --git a/translations/en/en.go b/translations/en/en.go index ee05f91..2817702 100644 --- a/translations/en/en.go +++ b/translations/en/en.go @@ -1356,6 +1356,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er translation: "{0} must be a valid boolean value", override: false, }, + { + tag: "image", + translation: "{0} must be a valid image", + override: false, + }, } for _, t := range translations { diff --git a/translations/en/en_test.go b/translations/en/en_test.go index 9cb6deb..5cc29c8 100644 --- a/translations/en/en_test.go +++ b/translations/en/en_test.go @@ -152,6 +152,7 @@ func TestTranslations(t *testing.T) { PostCodeCountry string PostCodeByField string `validate:"postcode_iso3166_alpha2_field=PostCodeCountry"` BooleanString string `validate:"boolean"` + Image string `validate:"image"` } var test Test @@ -690,6 +691,10 @@ func TestTranslations(t *testing.T) { ns: "Test.BooleanString", expected: "BooleanString must be a valid boolean value", }, + { + ns: "Test.Image", + expected: "Image must be a valid image", + }, } for _, tt := range tests { diff --git a/translations/es/es.go b/translations/es/es.go index 2635408..8907868 100644 --- a/translations/es/es.go +++ b/translations/es/es.go @@ -1326,6 +1326,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return s }, }, + { + tag: "image", + translation: "{0} debe ser una imagen válida", + override: false, + }, } for _, t := range translations { diff --git a/translations/es/es_test.go b/translations/es/es_test.go index 814f2ab..72b2966 100644 --- a/translations/es/es_test.go +++ b/translations/es/es_test.go @@ -142,6 +142,7 @@ func TestTranslations(t *testing.T) { UniqueSlice []string `validate:"unique"` UniqueArray [3]string `validate:"unique"` UniqueMap map[string]string `validate:"unique"` + Image string `validate:"image"` } var test Test @@ -637,6 +638,10 @@ func TestTranslations(t *testing.T) { ns: "Test.UniqueMap", expected: "UniqueMap debe contener valores únicos", }, + { + ns: "Test.Image", + expected: "Image debe ser una imagen válida", + }, } for _, tt := range tests { diff --git a/translations/fa/fa.go b/translations/fa/fa.go index b7c100b..32fc69e 100644 --- a/translations/fa/fa.go +++ b/translations/fa/fa.go @@ -1341,6 +1341,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return t }, }, + { + tag: "image", + translation: "{0} باید یک تصویر معتبر باشد", + override: false, + }, } for _, t := range translations { diff --git a/translations/fa/fa_test.go b/translations/fa/fa_test.go index 1acb8cf..456ebe4 100644 --- a/translations/fa/fa_test.go +++ b/translations/fa/fa_test.go @@ -148,6 +148,7 @@ func TestTranslations(t *testing.T) { PostCode string `validate:"postcode_iso3166_alpha2=SG"` PostCodeCountry string PostCodeByField string `validate:"postcode_iso3166_alpha2_field=PostCodeCountry"` + Image string `validate:"image"` } var test Test @@ -671,8 +672,12 @@ func TestTranslations(t *testing.T) { ns: "Test.PostCodeByField", expected: "PostCodeByField یک کدپستی معتبر کشور فیلد PostCodeCountry نیست", }, + { + ns: "Test.Image", + expected: "Image باید یک تصویر معتبر باشد", + }, } - + for _, tt := range tests { var fe validator.FieldError diff --git a/translations/fr/fr.go b/translations/fr/fr.go index c455195..eb40e81 100644 --- a/translations/fr/fr.go +++ b/translations/fr/fr.go @@ -1316,6 +1316,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return s }, }, + { + tag: "image", + translation: "{0} doit être une image valide", + override: false, + }, } for _, t := range translations { diff --git a/translations/fr/fr_test.go b/translations/fr/fr_test.go index f1ba280..73a1849 100644 --- a/translations/fr/fr_test.go +++ b/translations/fr/fr_test.go @@ -139,6 +139,7 @@ func TestTranslations(t *testing.T) { StrPtrGte *string `validate:"gte=10"` OneOfString string `validate:"oneof=red green"` OneOfInt int `validate:"oneof=5 63"` + Image string `validate:"image"` } var test Test @@ -619,6 +620,10 @@ func TestTranslations(t *testing.T) { ns: "Test.OneOfInt", expected: "OneOfInt doit être l'un des choix suivants [5 63]", }, + { + ns: "Test.Image", + expected: "Image doit être une image valide", + }, } for _, tt := range tests { diff --git a/translations/id/id.go b/translations/id/id.go index 08b6ad5..f881a53 100644 --- a/translations/id/id.go +++ b/translations/id/id.go @@ -1316,6 +1316,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return s }, }, + { + tag: "image", + translation: "{0} harus berupa gambar yang valid", + override: false, + }, } for _, t := range translations { diff --git a/translations/id/id_test.go b/translations/id/id_test.go index 5d80940..6806e10 100644 --- a/translations/id/id_test.go +++ b/translations/id/id_test.go @@ -139,6 +139,7 @@ func TestTranslations(t *testing.T) { StrPtrGte *string `validate:"gte=10"` OneOfString string `validate:"oneof=merah hijau"` OneOfInt int `validate:"oneof=5 63"` + Image string `validate:"image"` } var test Test @@ -619,6 +620,10 @@ func TestTranslations(t *testing.T) { ns: "Test.OneOfInt", expected: "OneOfInt harus berupa salah satu dari [5 63]", }, + { + ns: "Test.Image", + expected: "Image harus berupa gambar yang valid", + }, } for _, tt := range tests { diff --git a/translations/it/it.go b/translations/it/it.go index 0b46fc4..f1cfcef 100644 --- a/translations/it/it.go +++ b/translations/it/it.go @@ -1205,6 +1205,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er override: false, customTransFunc: customTransFuncV1, }, + { + tag: "image", + translation: "{0} deve essere un'immagine valida", + override: false, + }, } for _, t := range translations { diff --git a/translations/it/it_test.go b/translations/it/it_test.go index 98d2ab5..12fe78c 100644 --- a/translations/it/it_test.go +++ b/translations/it/it_test.go @@ -154,6 +154,7 @@ func TestTranslations(t *testing.T) { PostCode string `validate:"postcode_iso3166_alpha2=SG"` PostCodeCountry string PostCodeByField string `validate:"postcode_iso3166_alpha2_field=PostCodeCountry"` + Image string `validate:"image"` } var test Test @@ -706,6 +707,10 @@ func TestTranslations(t *testing.T) { ns: "Test.PostCodeByField", expected: "PostCodeByField non corrisponde al formato del codice postale dello stato nel campo PostCodeCountry", }, + { + ns: "Test.Image", + expected: "Image deve essere un'immagine valida", + }, } for _, tt := range tests { diff --git a/translations/ja/ja.go b/translations/ja/ja.go index 1cc67a4..7ed5e0c 100644 --- a/translations/ja/ja.go +++ b/translations/ja/ja.go @@ -1372,6 +1372,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return s }, }, + { + tag: "image", + translation: "{0} は有効な画像でなければなりません", + override: false, + }, } for _, t := range translations { diff --git a/translations/ja/ja_test.go b/translations/ja/ja_test.go index 7950251..79b0971 100644 --- a/translations/ja/ja_test.go +++ b/translations/ja/ja_test.go @@ -139,6 +139,7 @@ func TestTranslations(t *testing.T) { StrPtrGte *string `validate:"gte=10"` OneOfString string `validate:"oneof=red green"` OneOfInt int `validate:"oneof=5 63"` + Image string `validate:"image"` } var test Test @@ -619,6 +620,10 @@ func TestTranslations(t *testing.T) { ns: "Test.OneOfInt", expected: "OneOfIntは[5 63]のうちのいずれかでなければなりません", }, + { + ns: "Test.Image", + expected: "Image は有効な画像でなければなりません", + }, } for _, tt := range tests { diff --git a/translations/nl/nl.go b/translations/nl/nl.go index ca7c554..a49f185 100644 --- a/translations/nl/nl.go +++ b/translations/nl/nl.go @@ -1316,6 +1316,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return s }, }, + { + tag: "image", + translation: "{0} moet een geldige afbeelding zijn", + override: false, + }, } for _, t := range translations { diff --git a/translations/nl/nl_test.go b/translations/nl/nl_test.go index 137e0c4..9cfeb5b 100644 --- a/translations/nl/nl_test.go +++ b/translations/nl/nl_test.go @@ -139,6 +139,7 @@ func TestTranslations(t *testing.T) { StrPtrGte *string `validate:"gte=10"` OneOfString string `validate:"oneof=red green"` OneOfInt int `validate:"oneof=5 63"` + Image string `validate:"image"` } var test Test @@ -619,6 +620,10 @@ func TestTranslations(t *testing.T) { ns: "Test.OneOfInt", expected: "OneOfInt moet een van de volgende zijn [5 63]", }, + { + ns: "Test.Image", + expected: "Image moet een geldige afbeelding zijn", + }, } for _, tt := range tests { diff --git a/translations/pt/pt.go b/translations/pt/pt.go index 4748331..31fb1a2 100644 --- a/translations/pt/pt.go +++ b/translations/pt/pt.go @@ -1356,6 +1356,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return t }, }, + { + tag: "image", + translation: "{0} deve ser uma imagem válida", + override: false, + }, } for _, t := range translations { diff --git a/translations/pt/pt_test.go b/translations/pt/pt_test.go index 0422ab0..96c8318 100644 --- a/translations/pt/pt_test.go +++ b/translations/pt/pt_test.go @@ -147,6 +147,7 @@ func TestTranslations(t *testing.T) { LowercaseString string `validate:"lowercase"` UppercaseString string `validate:"uppercase"` Datetime string `validate:"datetime=2006-01-02"` + Image string `validate:"image"` } var test Test @@ -662,6 +663,10 @@ func TestTranslations(t *testing.T) { ns: "Test.Datetime", expected: "Datetime não está no formato 2006-01-02", }, + { + ns: "Test.Image", + expected: "Image deve ser uma imagem válida", + }, } for _, tt := range tests { diff --git a/translations/pt_BR/pt_BR.go b/translations/pt_BR/pt_BR.go index d6883aa..ad501da 100644 --- a/translations/pt_BR/pt_BR.go +++ b/translations/pt_BR/pt_BR.go @@ -1321,6 +1321,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er translation: "{0} deve ser um valor booleano válido", override: false, }, + { + tag: "image", + translation: "{0} deve ser uma imagen válido", + override: false, + }, } for _, t := range translations { diff --git a/translations/pt_BR/pt_BR_test.go b/translations/pt_BR/pt_BR_test.go index 426f246..68a8752 100644 --- a/translations/pt_BR/pt_BR_test.go +++ b/translations/pt_BR/pt_BR_test.go @@ -140,6 +140,7 @@ func TestTranslations(t *testing.T) { OneOfString string `validate:"oneof=red green"` OneOfInt int `validate:"oneof=5 63"` BooleanString string `validate:"boolean"` + Image string `validate:"image"` } var test Test @@ -625,6 +626,10 @@ func TestTranslations(t *testing.T) { ns: "Test.BooleanString", expected: "BooleanString deve ser um valor booleano válido", }, + { + ns: "Test.Image", + expected: "Image deve ser uma imagen válido", + }, } for _, tt := range tests { diff --git a/translations/ru/ru.go b/translations/ru/ru.go index 25400c5..5cafb08 100644 --- a/translations/ru/ru.go +++ b/translations/ru/ru.go @@ -1439,6 +1439,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return s }, }, + { + tag: "image", + translation: "{0} должно быть допустимым изображением", + override: false, + }, } for _, t := range translations { diff --git a/translations/ru/ru_test.go b/translations/ru/ru_test.go index c74f0d5..223966f 100644 --- a/translations/ru/ru_test.go +++ b/translations/ru/ru_test.go @@ -164,6 +164,7 @@ func TestTranslations(t *testing.T) { UniqueSlice []string `validate:"unique"` UniqueArray [3]string `validate:"unique"` UniqueMap map[string]string `validate:"unique"` + Image string `validate:"image"` } var test Test @@ -745,6 +746,10 @@ func TestTranslations(t *testing.T) { ns: "Test.UniqueMap", expected: "UniqueMap должен содержать уникальные значения", }, + { + ns: "Test.Image", + expected: "Image должно быть допустимым изображением", + }, } for _, tt := range tests { diff --git a/translations/tr/tr.go b/translations/tr/tr.go index 2e88a20..7c86258 100644 --- a/translations/tr/tr.go +++ b/translations/tr/tr.go @@ -1321,6 +1321,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return s }, }, + { + tag: "image", + translation: "{0} geçerli bir resim olmalıdır", + override: false, + }, } for _, t := range translations { diff --git a/translations/tr/tr_test.go b/translations/tr/tr_test.go index b72329e..e18ac6a 100644 --- a/translations/tr/tr_test.go +++ b/translations/tr/tr_test.go @@ -142,6 +142,7 @@ func TestTranslations(t *testing.T) { UniqueSlice []string `validate:"unique"` UniqueArray [3]string `validate:"unique"` UniqueMap map[string]string `validate:"unique"` + Image string `validate:"image"` } var test Test @@ -637,6 +638,10 @@ func TestTranslations(t *testing.T) { ns: "Test.UniqueMap", expected: "UniqueMap benzersiz değerler içermelidir", }, + { + ns: "Test.Image", + expected: "Image geçerli bir resim olmalıdır", + }, } for _, tt := range tests { diff --git a/translations/vi/vi.go b/translations/vi/vi.go index 009ba4c..170938d 100644 --- a/translations/vi/vi.go +++ b/translations/vi/vi.go @@ -1341,6 +1341,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return t }, }, + { + tag: "image", + translation: "{0} phải là một hình ảnh hợp lệ", + override: false, + }, } for _, t := range translations { diff --git a/translations/vi/vi_test.go b/translations/vi/vi_test.go index 6e12866..d26c42d 100644 --- a/translations/vi/vi_test.go +++ b/translations/vi/vi_test.go @@ -148,6 +148,7 @@ func TestTranslations(t *testing.T) { PostCode string `validate:"postcode_iso3166_alpha2=SG"` PostCodeCountry string PostCodeByField string `validate:"postcode_iso3166_alpha2_field=PostCodeCountry"` + Image string `validate:"image"` } var test Test @@ -671,6 +672,10 @@ func TestTranslations(t *testing.T) { ns: "Test.PostCodeByField", expected: "PostCodeByField sai định dạng postcode của quốc gia tương ứng thuộc trường PostCodeCountry", }, + { + ns: "Test.Image", + expected: "Image phải là một hình ảnh hợp lệ", + }, } for _, tt := range tests { diff --git a/translations/zh/zh.go b/translations/zh/zh.go index 80165d0..7f45681 100644 --- a/translations/zh/zh.go +++ b/translations/zh/zh.go @@ -1428,6 +1428,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return t }, }, + { + tag: "image", + translation: "{0} 必须是有效图像", + override: false, + }, } for _, t := range translations { diff --git a/translations/zh/zh_test.go b/translations/zh/zh_test.go index cf76590..aaf512d 100644 --- a/translations/zh/zh_test.go +++ b/translations/zh/zh_test.go @@ -148,6 +148,7 @@ func TestTranslations(t *testing.T) { LowercaseString string `validate:"lowercase"` UppercaseString string `validate:"uppercase"` Datetime string `validate:"datetime=2006-01-02"` + Image string `validate:"image"` } var test Test @@ -676,6 +677,10 @@ func TestTranslations(t *testing.T) { ns: "Test.Datetime", expected: "Datetime的格式必须是2006-01-02", }, + { + ns: "Test.Image", + expected: "Image 必须是有效图像", + }, } for _, tt := range tests { diff --git a/translations/zh_tw/zh_tw.go b/translations/zh_tw/zh_tw.go index c39a8d7..40cbec2 100644 --- a/translations/zh_tw/zh_tw.go +++ b/translations/zh_tw/zh_tw.go @@ -1324,6 +1324,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er return t }, }, + { + tag: "image", + translation: "{0} 必須是有效圖像", + override: false, + }, } for _, t := range translations { diff --git a/translations/zh_tw/zh_tw_test.go b/translations/zh_tw/zh_tw_test.go index 129c51a..f15f9f2 100644 --- a/translations/zh_tw/zh_tw_test.go +++ b/translations/zh_tw/zh_tw_test.go @@ -140,6 +140,7 @@ func TestTranslations(t *testing.T) { OneOfString string `validate:"oneof=red green"` OneOfInt int `validate:"oneof=5 63"` Datetime string `validate:"datetime=2006-01-02"` + Image string `validate:"image"` } var test Test @@ -626,6 +627,10 @@ func TestTranslations(t *testing.T) { ns: "Test.Datetime", expected: "Datetime與2006-01-02格式不匹配", }, + { + ns: "Test.Image", + expected: "Image 必須是有效圖像", + }, } for _, tt := range tests { diff --git a/validator_test.go b/validator_test.go index 1ebdea5..ca68e99 100644 --- a/validator_test.go +++ b/validator_test.go @@ -8,6 +8,10 @@ import ( "encoding/base64" "encoding/json" "fmt" + "image" + "image/png" + "image/jpeg" + "os" "path/filepath" "reflect" "strings" @@ -5650,6 +5654,108 @@ func TestFileValidation(t *testing.T) { }, "Bad field type int") } +func TestImageValidation(t *testing.T) { + validate := New() + + paths := map[string]string{ + "empty": "", + "directory": "testdata", + "missing": filepath.Join("testdata", "none.png"), + "png": filepath.Join("testdata", "image.png"), + "jpeg": filepath.Join("testdata", "image.jpg"), + "mp3": filepath.Join("testdata", "music.mp3"), + } + + tests := []struct { + title string + param string + expected bool + createImage func() + destroyImage func() + }{ + { + "empty path", + paths["empty"], false, + func () {}, + func () {}, + }, + { + "directory, not a file", + paths["directory"], + false, + func () {}, + func () {}, + }, + { + "missing file", + paths["missing"], + false, + func () {}, + func () {}, + }, + { + "valid png", + paths["png"], + true, + func () { + img := image.NewRGBA(image.Rectangle{image.Point{0, 0}, image.Point{10, 10}}) + f, _ := os.Create(paths["png"]) + err := png.Encode(f, img) + if err != nil { + panic(fmt.Sprintf("Could not encode file in PNG. Error: %s", err)) + } + }, + func () { + os.Remove(paths["png"]) + }, + }, + { + "valid jpeg", + paths["jpeg"], + true, + func () { + var opt jpeg.Options + img := image.NewGray(image.Rect(0, 0, 10, 10)) + f, _ := os.Create(paths["jpeg"]) + err := jpeg.Encode(f, img, &opt) + if err != nil { + panic(fmt.Sprintf("Could not encode file in JPEG. Error: %s", err)) + } + }, + func () { + os.Remove(paths["jpeg"]) + }, + }, + { + "valid mp3", + paths["mp3"], + false, + func () {}, + func () {}, + }, + } + + for _, test := range tests { + test.createImage() + errs := validate.Var(test.param, "image") + + if test.expected { + if !IsEqual(errs, nil) { + t.Fatalf("Test: '%s' failed Error: %s", test.title, errs) + } + } else { + if IsEqual(errs, nil) { + t.Fatalf("Test: '%s' failed Error: %s", test.title, errs) + } + } + test.destroyImage() + } + + PanicMatches(t, func() { + _ = validate.Var(6, "file") + }, "Bad field type int") +} + func TestEthereumAddressValidation(t *testing.T) { validate := New()