Skip to content

Commit 2313e3f

Browse files
Allow to forbid digits in Faker::Internet.password method
This change allows to allow or not digits inside the password. If `digits: true`, a digit is automatically added.
1 parent b9ef4e9 commit 2313e3f

File tree

2 files changed

+66
-32
lines changed

2 files changed

+66
-32
lines changed

lib/faker/default/internet.rb

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ def username(specifier: nil, separators: %w[. _])
108108
# @param max_length [Integer] The maximum length of the password
109109
# @param mix_case [Boolean] Toggles if uppercased letters are allowed. If true, at least one will be added.
110110
# @param special_characters [Boolean] Toggles if special characters are allowed. If true, at least one will be added.
111+
# @param digits [Boolean] Toggles if digits are allowed. If true, at least one will be added.
111112
#
112113
# @return [String]
113114
#
@@ -121,9 +122,13 @@ def username(specifier: nil, separators: %w[. _])
121122
# Faker::Internet.password(min_length: 10, max_length: 20, mix_case: true) #=> "3k5qS15aNmG"
122123
# @example
123124
# Faker::Internet.password(min_length: 10, max_length: 20, mix_case: true, special_characters: true) #=> "*%NkOnJsH4"
125+
# @example
126+
# Faker::Internet.password(min_length: 10, max_length: 20, mix_case: true, special_characters: true, digits: true) #=> "$f%Ym*sc91e4O^"
127+
# @example
128+
# Faker::Internet.password(min_length: 10, max_length: 20, mix_case: true, special_characters: true, digits: false) #=> "rI$XYZgmBONFKWIZ$B"
124129
#
125130
# @faker.version 2.1.3
126-
def password(min_length: 8, max_length: 16, mix_case: true, special_characters: false)
131+
def password(min_length: 8, max_length: 16, mix_case: true, special_characters: false, digits: true)
127132
raise ArgumentError, 'min_length and max_length must be greater than or equal to one' if min_length < 1 || max_length < 1
128133
raise ArgumentError, 'min_length must be smaller than or equal to max_length' unless min_length <= max_length
129134

@@ -140,6 +145,11 @@ def password(min_length: 8, max_length: 16, mix_case: true, special_characters:
140145
required_min_length += 1
141146
end
142147

148+
if digits
149+
character_types << :digits
150+
required_min_length += 1
151+
end
152+
143153
raise ArgumentError, "min_length should be at least #{required_min_length} to enable #{character_types.join(', ')} configuration" if min_length < required_min_length
144154

145155
target_length = rand(min_length..max_length)
@@ -152,10 +162,6 @@ def password(min_length: 8, max_length: 16, mix_case: true, special_characters:
152162
password << sample(lower_chars)
153163
character_bag += lower_chars
154164

155-
digits = ('0'..'9').to_a
156-
password << sample(digits)
157-
character_bag += digits
158-
159165
if mix_case
160166
upper_chars = self::ULetters
161167
password << sample(upper_chars)
@@ -168,6 +174,12 @@ def password(min_length: 8, max_length: 16, mix_case: true, special_characters:
168174
character_bag += special_chars
169175
end
170176

177+
if digits
178+
digits_chars = ('0'..'9').to_a
179+
password << sample(digits_chars)
180+
character_bag += digits_chars
181+
end
182+
171183
password << sample(character_bag) while password.length < target_length
172184

173185
shuffle(password).join

test/faker/default/test_faker_internet.rb

Lines changed: 49 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -232,8 +232,8 @@ def test_password_with_min_length_eq_1_without_mix_case
232232
end
233233

234234
def test_password_with_min_length_and_max_length
235-
min_length = 2
236-
max_length = 5
235+
min_length = 3
236+
max_length = 6
237237
password = @tester.password(min_length: min_length, max_length: max_length)
238238

239239
assert_match(/\w+/, password)
@@ -265,34 +265,39 @@ def test_password_without_special_chars
265265
assert_match(/[^!@#$%\^&*]+/, @tester.password(min_length: 8, max_length: 12, mix_case: true))
266266
end
267267

268-
def test_password_with_special_chars_and_mixed_case
268+
def test_password_with_digits
269+
assert_match(/[0-9]+/, @tester.password(min_length: 8, max_length: 12, digits: true))
270+
end
271+
272+
def test_password_without_digits
273+
assert_match(/^[^0-9]+$/, @tester.password(min_length: 8, max_length: 12, digits: false))
274+
end
275+
276+
def test_password_with_special_chars_and_mixed_case_and_digits
269277
32.times do
270-
password = @tester.password(min_length: 4, max_length: 6, mix_case: true, special_characters: true)
278+
password = @tester.password(min_length: 4, max_length: 6, mix_case: true, special_characters: true, digits: true)
271279

272-
assert_match(/[!@#$%\^&*]+/, password)
273-
assert_match(/[A-z]+/, password)
280+
assert_match(/^[A-Za-z0-9!@#$%\^&*]+$/, password)
274281
end
275282
end
276283

277-
def test_deterministic_password_with_special_chars_and_mixed_case
278-
deterministically_verify -> { @tester.password(min_length: 4, max_length: 6, mix_case: true, special_characters: true) }, depth: 4 do |password|
279-
assert_match(/[!@#$%\^&*]+/, password)
280-
assert_match(/[A-z]+/, password)
284+
def test_deterministic_password_with_special_chars_and_mixed_case_and_digits
285+
deterministically_verify -> { @tester.password(min_length: 4, max_length: 6, mix_case: true, special_characters: true, digits: true) }, depth: 4 do |password|
286+
assert_match(/^[A-Za-z0-9!@#$%\^&*]+$/, password)
281287
end
282288
end
283289

284-
def test_password_with_special_chars_and_mixed_case_on_3chars_password
290+
def test_password_with_special_chars_and_mixed_case_and_digits_on_4chars_password
285291
16.times do
286-
password = @tester.password(min_length: 3, max_length: 6, mix_case: true, special_characters: true)
292+
password = @tester.password(min_length: 4, max_length: 6, mix_case: true, special_characters: true, digits: true)
287293

288-
assert_match(/[!@#$%\^&*]+/, password)
289-
assert_match(/[A-z]+/, password)
294+
assert_match(/^[A-Za-z0-9!@#$%\^&*]+$/, password)
290295
end
291296
end
292297

293-
def test_password_with_invalid_min_length_for_mix_case_and_special_characters
294-
assert_raise_message 'min_length should be at least 3 to enable mix_case, special_characters configuration' do
295-
@tester.password(min_length: 1, mix_case: true, special_characters: true)
298+
def test_password_with_invalid_min_length_for_mix_case_and_special_characters_and_digits
299+
assert_raise_message 'min_length should be at least 4 to enable mix_case, special_characters, digits configuration' do
300+
@tester.password(min_length: 1, mix_case: true, special_characters: true, digits: true)
296301
end
297302
end
298303

@@ -306,35 +311,52 @@ def test_password_with_invalid_min_max_length
306311

307312
def test_password_with_invalid_min_length_for_special_characters_only
308313
error = assert_raises(ArgumentError) do
309-
@tester.password(min_length: 0, mix_case: false, special_characters: true)
314+
@tester.password(min_length: 0, mix_case: false, special_characters: true, digits: false)
310315
end
311316

312317
assert_equal 'min_length and max_length must be greater than or equal to one', error.message
313318
end
314319

315320
def test_password_with_invalid_min_length_for_mix_case_only
316321
error = assert_raises(ArgumentError) do
317-
@tester.password(min_length: 1, mix_case: true)
322+
@tester.password(min_length: 1, mix_case: true, special_characters: false, digits: false)
318323
end
319324

320325
assert_equal 'min_length should be at least 2 to enable mix_case configuration', error.message
321326
end
322327

328+
def test_password_with_invalid_min_length_for_digits_only
329+
error = assert_raises(ArgumentError) do
330+
@tester.password(min_length: 0, mix_case: false, special_characters: false, digits: true)
331+
end
332+
333+
assert_equal 'min_length and max_length must be greater than or equal to one', error.message
334+
end
335+
323336
def test_password_with_compatible_min_length_and_requirements
324337
assert_nothing_raised do
325-
[false, true].each do |value|
326-
min_length = value ? 2 : 1
327-
@tester.password(min_length: min_length, mix_case: value, special_characters: !value)
338+
[false, true].each do |mix_case|
339+
[false, true].each do |special_characters|
340+
[false, true].each do |digits|
341+
min_length = [[mix_case ? 2 : 0, special_characters ? 1 : 0, digits ? 1 : 0].sum, 1].max
342+
343+
@tester.password(min_length: min_length, mix_case: mix_case, special_characters: special_characters, digits: digits)
344+
end
345+
end
328346
end
329347
end
330348
end
331349

332350
def test_deterministic_password_with_compatible_min_length_and_requirements
333-
[false, true].each do |value|
334-
min_length = value ? 2 : 1
335-
336-
deterministically_verify -> { @tester.password(min_length: min_length, mix_case: value, special_characters: !value) }, depth: 4 do |password|
337-
assert_nothing_raised { password }
351+
[false, true].each do |mix_case|
352+
[false, true].each do |special_characters|
353+
[false, true].each do |digits|
354+
min_length = [[mix_case ? 2 : 0, special_characters ? 1 : 0, digits ? 1 : 0].sum, 1].max
355+
356+
deterministically_verify -> { @tester.password(min_length: min_length, mix_case: mix_case, special_characters: special_characters, digits: digits) }, depth: 4 do |password|
357+
assert_nothing_raised { password }
358+
end
359+
end
338360
end
339361
end
340362
end

0 commit comments

Comments
 (0)