Initial commit: Open sourcing all of the Maple Open Technologies code.
This commit is contained in:
commit
755d54a99d
2010 changed files with 448675 additions and 0 deletions
472
cloud/maplepress-backend/pkg/validation/validator_test.go
Normal file
472
cloud/maplepress-backend/pkg/validation/validator_test.go
Normal file
|
|
@ -0,0 +1,472 @@
|
|||
package validation
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestValidateRequired(t *testing.T) {
|
||||
v := NewValidator()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
value string
|
||||
wantError bool
|
||||
}{
|
||||
{"Valid non-empty string", "test", false},
|
||||
{"Empty string", "", true},
|
||||
{"Whitespace only", " ", true},
|
||||
{"Tab only", "\t", true},
|
||||
{"Newline only", "\n", true},
|
||||
{"Valid with spaces", "hello world", false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := v.ValidateRequired(tt.value, "test_field")
|
||||
if (err != nil) != tt.wantError {
|
||||
t.Errorf("ValidateRequired() error = %v, wantError %v", err, tt.wantError)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateLength(t *testing.T) {
|
||||
v := NewValidator()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
value string
|
||||
min int
|
||||
max int
|
||||
wantError bool
|
||||
}{
|
||||
{"Valid length", "hello", 3, 10, false},
|
||||
{"Too short", "ab", 3, 10, true},
|
||||
{"Too long", "hello world this is too long", 3, 10, true},
|
||||
{"Exact minimum", "abc", 3, 10, false},
|
||||
{"Exact maximum", "0123456789", 3, 10, false},
|
||||
{"No maximum (0)", "very long string here", 3, 0, false},
|
||||
{"Whitespace counted correctly", " test ", 4, 10, false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := v.ValidateLength(tt.value, "test_field", tt.min, tt.max)
|
||||
if (err != nil) != tt.wantError {
|
||||
t.Errorf("ValidateLength() error = %v, wantError %v", err, tt.wantError)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateEmail(t *testing.T) {
|
||||
v := NewValidator()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
email string
|
||||
wantError bool
|
||||
}{
|
||||
// Valid emails
|
||||
{"Valid email", "user@example.com", false},
|
||||
{"Valid email with plus", "user+tag@example.com", false},
|
||||
{"Valid email with dot", "first.last@example.com", false},
|
||||
{"Valid email with hyphen", "user-name@example-domain.com", false},
|
||||
{"Valid email with numbers", "user123@example456.com", false},
|
||||
{"Valid email with subdomain", "user@sub.example.com", false},
|
||||
|
||||
// Invalid emails
|
||||
{"Empty email", "", true},
|
||||
{"Whitespace only", " ", true},
|
||||
{"Missing @", "userexample.com", true},
|
||||
{"Missing domain", "user@", true},
|
||||
{"Missing local part", "@example.com", true},
|
||||
{"No TLD", "user@localhost", true},
|
||||
{"Consecutive dots in local", "user..name@example.com", true},
|
||||
{"Leading dot in local", ".user@example.com", true},
|
||||
{"Trailing dot in local", "user.@example.com", true},
|
||||
{"Double @", "user@@example.com", true},
|
||||
{"Spaces in email", "user name@example.com", true},
|
||||
{"Invalid characters", "user<>@example.com", true},
|
||||
{"Too long", strings.Repeat("a", 320) + "@example.com", true},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := v.ValidateEmail(tt.email, "email")
|
||||
if (err != nil) != tt.wantError {
|
||||
t.Errorf("ValidateEmail() error = %v, wantError %v", err, tt.wantError)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateURL(t *testing.T) {
|
||||
v := NewValidator()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
url string
|
||||
wantError bool
|
||||
}{
|
||||
// Valid URLs
|
||||
{"Valid HTTP URL", "http://example.com", false},
|
||||
{"Valid HTTPS URL", "https://example.com", false},
|
||||
{"Valid URL with path", "https://example.com/path/to/resource", false},
|
||||
{"Valid URL with query", "https://example.com?param=value", false},
|
||||
{"Valid URL with port", "https://example.com:8080", false},
|
||||
{"Valid URL with subdomain", "https://sub.example.com", false},
|
||||
|
||||
// Invalid URLs
|
||||
{"Empty URL", "", true},
|
||||
{"Whitespace only", " ", true},
|
||||
{"Missing scheme", "example.com", true},
|
||||
{"Invalid scheme", "ftp://example.com", true},
|
||||
{"Missing host", "https://", true},
|
||||
{"Invalid characters", "https://exam ple.com", true},
|
||||
{"Too long", "https://" + strings.Repeat("a", 2048) + ".com", true},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := v.ValidateURL(tt.url, "url")
|
||||
if (err != nil) != tt.wantError {
|
||||
t.Errorf("ValidateURL() error = %v, wantError %v", err, tt.wantError)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateHTTPSURL(t *testing.T) {
|
||||
v := NewValidator()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
url string
|
||||
wantError bool
|
||||
}{
|
||||
{"Valid HTTPS URL", "https://example.com", false},
|
||||
{"HTTP URL (should fail)", "http://example.com", true},
|
||||
{"FTP URL (should fail)", "ftp://example.com", true},
|
||||
{"Invalid URL", "not-a-url", true},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := v.ValidateHTTPSURL(tt.url, "url")
|
||||
if (err != nil) != tt.wantError {
|
||||
t.Errorf("ValidateHTTPSURL() error = %v, wantError %v", err, tt.wantError)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateDomain(t *testing.T) {
|
||||
v := NewValidator()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
domain string
|
||||
wantError bool
|
||||
}{
|
||||
// Valid domains
|
||||
{"Valid domain", "example.com", false},
|
||||
{"Valid subdomain", "sub.example.com", false},
|
||||
{"Valid deep subdomain", "deep.sub.example.com", false},
|
||||
{"Valid with hyphen", "my-site.example.com", false},
|
||||
{"Valid with numbers", "site123.example456.com", false},
|
||||
|
||||
// Invalid domains
|
||||
{"Empty domain", "", true},
|
||||
{"Whitespace only", " ", true},
|
||||
{"Too short", "a.b", true},
|
||||
{"Too long", strings.Repeat("a", 254) + ".com", true},
|
||||
{"Label too long", strings.Repeat("a", 64) + ".example.com", true},
|
||||
{"No TLD", "localhost", true},
|
||||
{"Leading hyphen", "-example.com", true},
|
||||
{"Trailing hyphen", "example-.com", true},
|
||||
{"Double dot", "example..com", true},
|
||||
{"Leading dot", ".example.com", true},
|
||||
{"Trailing dot", "example.com.", true},
|
||||
{"Underscore", "my_site.example.com", true},
|
||||
{"Spaces", "my site.example.com", true},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := v.ValidateDomain(tt.domain, "domain")
|
||||
if (err != nil) != tt.wantError {
|
||||
t.Errorf("ValidateDomain() error = %v, wantError %v", err, tt.wantError)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateSlug(t *testing.T) {
|
||||
v := NewValidator()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
slug string
|
||||
wantError bool
|
||||
}{
|
||||
// Valid slugs
|
||||
{"Valid slug", "my-company", false},
|
||||
{"Valid slug with numbers", "company123", false},
|
||||
{"Valid slug all lowercase", "testcompany", false},
|
||||
{"Valid slug with multiple hyphens", "my-test-company", false},
|
||||
|
||||
// Invalid slugs
|
||||
{"Empty slug", "", true},
|
||||
{"Whitespace only", " ", true},
|
||||
{"Too short", "ab", true},
|
||||
{"Too long", strings.Repeat("a", 64), true},
|
||||
{"Uppercase letters", "MyCompany", true},
|
||||
{"Leading hyphen", "-company", true},
|
||||
{"Trailing hyphen", "company-", true},
|
||||
{"Double hyphen", "my--company", true},
|
||||
{"Underscore", "my_company", true},
|
||||
{"Spaces", "my company", true},
|
||||
{"Special characters", "my@company", true},
|
||||
|
||||
// Reserved slugs
|
||||
{"Reserved: api", "api", true},
|
||||
{"Reserved: admin", "admin", true},
|
||||
{"Reserved: www", "www", true},
|
||||
{"Reserved: login", "login", true},
|
||||
{"Reserved: register", "register", true},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := v.ValidateSlug(tt.slug, "slug")
|
||||
if (err != nil) != tt.wantError {
|
||||
t.Errorf("ValidateSlug() error = %v, wantError %v", err, tt.wantError)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateUUID(t *testing.T) {
|
||||
v := NewValidator()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
uuid string
|
||||
wantError bool
|
||||
}{
|
||||
{"Valid UUID v4", "550e8400-e29b-41d4-a716-446655440000", false},
|
||||
{"Valid UUID v4 lowercase", "123e4567-e89b-42d3-a456-426614174000", false},
|
||||
{"Empty UUID", "", true},
|
||||
{"Invalid format", "not-a-uuid", true},
|
||||
{"Invalid version", "550e8400-e29b-21d4-a716-446655440000", true},
|
||||
{"Missing hyphens", "550e8400e29b41d4a716446655440000", true},
|
||||
{"Too short", "550e8400-e29b-41d4-a716", true},
|
||||
{"With uppercase", "550E8400-E29B-41D4-A716-446655440000", false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := v.ValidateUUID(tt.uuid, "id")
|
||||
if (err != nil) != tt.wantError {
|
||||
t.Errorf("ValidateUUID() error = %v, wantError %v", err, tt.wantError)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateEnum(t *testing.T) {
|
||||
v := NewValidator()
|
||||
|
||||
allowedValues := []string{"free", "basic", "pro", "enterprise"}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
value string
|
||||
wantError bool
|
||||
}{
|
||||
{"Valid: free", "free", false},
|
||||
{"Valid: basic", "basic", false},
|
||||
{"Valid: pro", "pro", false},
|
||||
{"Valid: enterprise", "enterprise", false},
|
||||
{"Invalid: premium", "premium", true},
|
||||
{"Invalid: empty", "", true},
|
||||
{"Invalid: wrong case", "FREE", true},
|
||||
{"Invalid: typo", "basi", true},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := v.ValidateEnum(tt.value, "plan_tier", allowedValues)
|
||||
if (err != nil) != tt.wantError {
|
||||
t.Errorf("ValidateEnum() error = %v, wantError %v", err, tt.wantError)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateRange(t *testing.T) {
|
||||
v := NewValidator()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
value int
|
||||
min int
|
||||
max int
|
||||
wantError bool
|
||||
}{
|
||||
{"Valid within range", 5, 1, 10, false},
|
||||
{"Valid at minimum", 1, 1, 10, false},
|
||||
{"Valid at maximum", 10, 1, 10, false},
|
||||
{"Below minimum", 0, 1, 10, true},
|
||||
{"Above maximum", 11, 1, 10, true},
|
||||
{"No maximum (0)", 1000, 1, 0, false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := v.ValidateRange(tt.value, "count", tt.min, tt.max)
|
||||
if (err != nil) != tt.wantError {
|
||||
t.Errorf("ValidateRange() error = %v, wantError %v", err, tt.wantError)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateNoHTML(t *testing.T) {
|
||||
v := NewValidator()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
value string
|
||||
wantError bool
|
||||
}{
|
||||
{"Plain text", "Hello world", false},
|
||||
{"Text with punctuation", "Hello, world!", false},
|
||||
{"HTML tag <script>", "<script>alert('xss')</script>", true},
|
||||
{"HTML tag <img>", "<img src='x'>", true},
|
||||
{"HTML tag <div>", "<div>content</div>", true},
|
||||
{"HTML tag <a>", "<a href='#'>link</a>", true},
|
||||
{"Less than symbol", "5 < 10", false},
|
||||
{"Greater than symbol", "10 > 5", false},
|
||||
{"Both symbols", "5 < x < 10", false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := v.ValidateNoHTML(tt.value, "content")
|
||||
if (err != nil) != tt.wantError {
|
||||
t.Errorf("ValidateNoHTML() error = %v, wantError %v", err, tt.wantError)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSanitizeString(t *testing.T) {
|
||||
v := NewValidator()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{"Trim whitespace", " hello ", "hello"},
|
||||
{"Remove null bytes", "hello\x00world", "helloworld"},
|
||||
{"Already clean", "hello", "hello"},
|
||||
{"Empty string", "", ""},
|
||||
{"Only whitespace", " ", ""},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := v.SanitizeString(tt.input)
|
||||
if result != tt.expected {
|
||||
t.Errorf("SanitizeString() = %q, want %q", result, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStripHTML(t *testing.T) {
|
||||
v := NewValidator()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{"Remove script tag", "<script>alert('xss')</script>", "alert('xss')"},
|
||||
{"Remove div tag", "<div>content</div>", "content"},
|
||||
{"Remove multiple tags", "<p>Hello <b>world</b></p>", "Hello world"},
|
||||
{"No tags", "plain text", "plain text"},
|
||||
{"Empty string", "", ""},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := v.StripHTML(tt.input)
|
||||
if result != tt.expected {
|
||||
t.Errorf("StripHTML() = %q, want %q", result, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateAndSanitizeString(t *testing.T) {
|
||||
v := NewValidator()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
minLen int
|
||||
maxLen int
|
||||
wantValue string
|
||||
wantError bool
|
||||
}{
|
||||
{"Valid and clean", "hello", 3, 10, "hello", false},
|
||||
{"Trim and validate", " hello ", 3, 10, "hello", false},
|
||||
{"Too short after trim", " a ", 3, 10, "", true},
|
||||
{"Too long", "hello world this is too long", 3, 10, "", true},
|
||||
{"Empty after trim", " ", 3, 10, "", true},
|
||||
{"Valid with null byte removed", "hel\x00lo", 3, 10, "hello", false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result, err := v.ValidateAndSanitizeString(tt.input, "test_field", tt.minLen, tt.maxLen)
|
||||
if (err != nil) != tt.wantError {
|
||||
t.Errorf("ValidateAndSanitizeString() error = %v, wantError %v", err, tt.wantError)
|
||||
}
|
||||
if !tt.wantError && result != tt.wantValue {
|
||||
t.Errorf("ValidateAndSanitizeString() = %q, want %q", result, tt.wantValue)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidatePrintable(t *testing.T) {
|
||||
v := NewValidator()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
value string
|
||||
wantError bool
|
||||
}{
|
||||
{"All printable", "Hello World 123!", false},
|
||||
{"With tabs and newlines", "Hello\tWorld\n", false},
|
||||
{"With control character", "Hello\x01World", true},
|
||||
{"With bell character", "Hello\x07", true},
|
||||
{"Empty string", "", false},
|
||||
{"Unicode printable", "Hello 世界", false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := v.ValidatePrintable(tt.value, "test_field")
|
||||
if (err != nil) != tt.wantError {
|
||||
t.Errorf("ValidatePrintable() error = %v, wantError %v", err, tt.wantError)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue