blob: 437376bd2747004208d45aa4c27824079e3a620b [file] [log] [blame]
package main
import (
"reflect"
"strings"
"testing"
)
const (
testUsernameLengthMessage = "Username must be at least 3 characters long."
testPasswordLengthMessage = "Password must be at least 20 characters long."
testPasswordCompositionMessage = "Password must contain at least one digit, lower&upper case and special characters"
)
func TestValidateUsernameBoundaries(t *testing.T) {
tests := []struct {
name string
username string
want []ValidationError
}{
{"empty", "", []ValidationError{{Field: "username", Message: testUsernameLengthMessage}}},
{"two ASCII bytes", "ab", []ValidationError{{Field: "username", Message: testUsernameLengthMessage}}},
{"three ASCII bytes", "abc", nil},
{"existing byte semantics", "é", []ValidationError{{Field: "username", Message: testUsernameLengthMessage}}},
{"four UTF-8 bytes", "éé", nil},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := validateUsername(tt.username); !reflect.DeepEqual(got, tt.want) {
t.Fatalf("validateUsername() = %#v, want %#v", got, tt.want)
}
})
}
}
func TestValidatePasswordLengthAndDeterministicOrdering(t *testing.T) {
if got := validatePassword("short"); !reflect.DeepEqual(got, []ValidationError{
{Field: "password", Message: testPasswordLengthMessage},
{Field: "password", Message: testPasswordCompositionMessage},
}) {
t.Fatalf("short password errors = %#v", got)
}
if got := validatePassword("Aa1!" + strings.Repeat("x", 15)); len(got) != 1 || got[0].Message != testPasswordLengthMessage {
t.Fatalf("19-byte password errors = %#v", got)
}
if got := validatePassword("Aa1!" + strings.Repeat("x", 16)); len(got) != 0 {
t.Fatalf("20-byte password errors = %#v", got)
}
}
func TestValidatePasswordUnicodeCategories(t *testing.T) {
valid := "Éé١ " + strings.Repeat("x", 16)
if got := validatePassword(valid); len(got) != 0 {
t.Fatalf("Unicode category password errors = %#v", got)
}
}
func TestValidatePasswordAcceptsEveryConfiguredASCIISpecial(t *testing.T) {
for _, special := range " !\"#$%&'()*+,-./:;<=>?@[\\]^_{|}~" {
t.Run(string(special), func(t *testing.T) {
password := "Aa1" + strings.Repeat("x", 16) + string(special)
if got := validatePassword(password); len(got) != 0 {
t.Fatalf("configured special %q rejected: %#v", special, got)
}
})
}
}
func TestValidatePasswordRejectsUnconfiguredPunctuation(t *testing.T) {
password := "Aa1" + strings.Repeat("x", 16) + "§"
got := validatePassword(password)
if !reflect.DeepEqual(got, []ValidationError{{Field: "password", Message: testPasswordCompositionMessage}}) {
t.Fatalf("unconfigured punctuation errors = %#v", got)
}
}