1 package password
2
3 import (
4 "io"
5 "strings"
6 "sync/atomic"
7 "testing"
8 )
9
10 type (
11 MockReader struct {
12 Counter int64
13 }
14 )
15
16 const (
17 N = 10000
18 )
19
20 func (mr *MockReader) Read(data []byte) (int, error) {
21 for i := 0; i < len(data); i++ {
22 data[i] = byte(atomic.AddInt64(&mr.Counter, 1))
23 }
24 return len(data), nil
25 }
26
27 func testHasDuplicates(tb testing.TB, s string) bool {
28 found := make(map[rune]struct{}, len(s))
29 for _, ch := range s {
30 if _, ok := found[ch]; ok {
31 return true
32 }
33 found[ch] = struct{}{}
34 }
35 return false
36 }
37
38 func testGeneratorGenerate(t *testing.T, reader io.Reader) {
39 t.Parallel()
40
41 gen, err := NewGenerator(nil)
42 if reader != nil {
43 gen.reader = reader
44 }
45 if err != nil {
46 t.Fatal(err)
47 }
48
49 t.Run("exceeds_length", func(t *testing.T) {
50 t.Parallel()
51
52 if _, err := gen.Generate(0, 1, 0, false, false); err != ErrExceedsTotalLength {
53 t.Errorf("expected %q to be %q", err, ErrExceedsTotalLength)
54 }
55
56 if _, err := gen.Generate(0, 0, 1, false, false); err != ErrExceedsTotalLength {
57 t.Errorf("expected %q to be %q", err, ErrExceedsTotalLength)
58 }
59 })
60
61 t.Run("exceeds_letters_available", func(t *testing.T) {
62 t.Parallel()
63
64 if _, err := gen.Generate(1000, 0, 0, false, false); err != ErrLettersExceedsAvailable {
65 t.Errorf("expected %q to be %q", err, ErrLettersExceedsAvailable)
66 }
67 })
68
69 t.Run("exceeds_digits_available", func(t *testing.T) {
70 t.Parallel()
71
72 if _, err := gen.Generate(52, 11, 0, false, false); err != ErrDigitsExceedsAvailable {
73 t.Errorf("expected %q to be %q", err, ErrDigitsExceedsAvailable)
74 }
75 })
76
77 t.Run("exceeds_symbols_available", func(t *testing.T) {
78 t.Parallel()
79
80 if _, err := gen.Generate(52, 0, 31, false, false); err != ErrSymbolsExceedsAvailable {
81 t.Errorf("expected %q to be %q", err, ErrSymbolsExceedsAvailable)
82 }
83 })
84
85 t.Run("gen_lowercase", func(t *testing.T) {
86 t.Parallel()
87
88 for i := 0; i < N; i++ {
89 res, err := gen.Generate(i%len(LowerLetters), 0, 0, true, true)
90 if err != nil {
91 t.Error(err)
92 }
93
94 if res != strings.ToLower(res) {
95 t.Errorf("%q is not lowercase", res)
96 }
97 }
98 })
99
100 t.Run("gen_uppercase", func(t *testing.T) {
101 t.Parallel()
102
103 res, err := gen.Generate(1000, 0, 0, false, true)
104 if err != nil {
105 t.Error(err)
106 }
107
108 if res == strings.ToLower(res) {
109 t.Errorf("%q does not include uppercase", res)
110 }
111 })
112
113 t.Run("gen_no_repeats", func(t *testing.T) {
114 t.Parallel()
115
116 for i := 0; i < N; i++ {
117 res, err := gen.Generate(52, 10, 30, false, false)
118 if err != nil {
119 t.Error(err)
120 }
121
122 if testHasDuplicates(t, res) {
123 t.Errorf("%q should not have duplicates", res)
124 }
125 }
126 })
127 }
128
129 func TestGeneratorGenerate(t *testing.T) {
130 testGeneratorGenerate(t, nil)
131 }
132
133 func TestGenerator_Reader_Generate(t *testing.T) {
134 testGeneratorGenerate(t, &MockReader{})
135 }
136
137 func testGeneratorGenerateCustom(t *testing.T, reader io.Reader) {
138 t.Parallel()
139
140 gen, err := NewGenerator(&GeneratorInput{
141 LowerLetters: "abcde",
142 UpperLetters: "ABCDE",
143 Symbols: "!@#$%",
144 Digits: "01234",
145 Reader: reader,
146 })
147 if err != nil {
148 t.Fatal(err)
149 }
150
151 for i := 0; i < N; i++ {
152 res, err := gen.Generate(52, 10, 10, false, true)
153 if err != nil {
154 t.Error(err)
155 }
156
157 if strings.Contains(res, "f") {
158 t.Errorf("%q should only contain lower letters abcde", res)
159 }
160
161 if strings.Contains(res, "F") {
162 t.Errorf("%q should only contain upper letters ABCDE", res)
163 }
164
165 if strings.Contains(res, "&") {
166 t.Errorf("%q should only include symbols !@#$%%", res)
167 }
168
169 if strings.Contains(res, "5") {
170 t.Errorf("%q should only contain digits 01234", res)
171 }
172 }
173 }
174
175 func TestGeneratorGenerateCustom(t *testing.T) {
176 testGeneratorGenerateCustom(t, nil)
177 }
178
179 func TestGenerator_Reader_Generate_Custom(t *testing.T) {
180 testGeneratorGenerateCustom(t, &MockReader{})
181 }
182
View as plain text