blob: 437376bd2747004208d45aa4c27824079e3a620b [file] [log] [blame]
gioe71b12b2026-07-29 10:02:37 +04001package main
2
3import (
4 "reflect"
5 "strings"
6 "testing"
7)
8
9const (
10 testUsernameLengthMessage = "Username must be at least 3 characters long."
11 testPasswordLengthMessage = "Password must be at least 20 characters long."
12 testPasswordCompositionMessage = "Password must contain at least one digit, lower&upper case and special characters"
13)
14
15func TestValidateUsernameBoundaries(t *testing.T) {
16 tests := []struct {
17 name string
18 username string
19 want []ValidationError
20 }{
21 {"empty", "", []ValidationError{{Field: "username", Message: testUsernameLengthMessage}}},
22 {"two ASCII bytes", "ab", []ValidationError{{Field: "username", Message: testUsernameLengthMessage}}},
23 {"three ASCII bytes", "abc", nil},
24 {"existing byte semantics", "é", []ValidationError{{Field: "username", Message: testUsernameLengthMessage}}},
25 {"four UTF-8 bytes", "éé", nil},
26 }
27 for _, tt := range tests {
28 t.Run(tt.name, func(t *testing.T) {
29 if got := validateUsername(tt.username); !reflect.DeepEqual(got, tt.want) {
30 t.Fatalf("validateUsername() = %#v, want %#v", got, tt.want)
31 }
32 })
33 }
34}
35
36func TestValidatePasswordLengthAndDeterministicOrdering(t *testing.T) {
37 if got := validatePassword("short"); !reflect.DeepEqual(got, []ValidationError{
38 {Field: "password", Message: testPasswordLengthMessage},
39 {Field: "password", Message: testPasswordCompositionMessage},
40 }) {
41 t.Fatalf("short password errors = %#v", got)
42 }
43 if got := validatePassword("Aa1!" + strings.Repeat("x", 15)); len(got) != 1 || got[0].Message != testPasswordLengthMessage {
44 t.Fatalf("19-byte password errors = %#v", got)
45 }
46 if got := validatePassword("Aa1!" + strings.Repeat("x", 16)); len(got) != 0 {
47 t.Fatalf("20-byte password errors = %#v", got)
48 }
49}
50
51func TestValidatePasswordUnicodeCategories(t *testing.T) {
52 valid := "Éé١ " + strings.Repeat("x", 16)
53 if got := validatePassword(valid); len(got) != 0 {
54 t.Fatalf("Unicode category password errors = %#v", got)
55 }
56}
57
58func TestValidatePasswordAcceptsEveryConfiguredASCIISpecial(t *testing.T) {
59 for _, special := range " !\"#$%&'()*+,-./:;<=>?@[\\]^_{|}~" {
60 t.Run(string(special), func(t *testing.T) {
61 password := "Aa1" + strings.Repeat("x", 16) + string(special)
62 if got := validatePassword(password); len(got) != 0 {
63 t.Fatalf("configured special %q rejected: %#v", special, got)
64 }
65 })
66 }
67}
68
69func TestValidatePasswordRejectsUnconfiguredPunctuation(t *testing.T) {
70 password := "Aa1" + strings.Repeat("x", 16) + "§"
71 got := validatePassword(password)
72 if !reflect.DeepEqual(got, []ValidationError{{Field: "password", Message: testPasswordCompositionMessage}}) {
73 t.Fatalf("unconfigured punctuation errors = %#v", got)
74 }
75}