Skip to content

Instantly share code, notes, and snippets.

@jrheard
Created April 27, 2018 04:17
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jrheard/552d5280186d60c0870c0c2132f3c6a0 to your computer and use it in GitHub Desktop.
Save jrheard/552d5280186d60c0870c0c2132f3c6a0 to your computer and use it in GitHub Desktop.
how to use hypothesis to generate "valid" passwords
import random
import string
from hypothesis import given
from hypothesis import settings
from hypothesis import strategies as st
import password_checker
def is_password_good(password):
return password_checker.is_password_good('jrheard', '12345', password)
def test_too_short_rejected():
assert is_password_good('z$6') == False
def test_containing_student_info_is_bad():
assert is_password_good('carlsJrHeard!') == False
assert is_password_good('Hi there! 12345') == False
def test_exactly_eight_characters():
assert is_password_good('abc123!@') == True
def test_two_categories_not_enough():
assert is_password_good('1234abcd') == False
assert is_password_good('ABCDabcd') == False
assert is_password_good('!!!!aaaa') == False
assert is_password_good('!!!AAA!A') == False
def test_three_categories_is_good():
assert is_password_good('abc123!@') == True
assert is_password_good('abcABC123') == True
assert is_password_good('!@#ABCabc') == True
VALID_PASSWORD_CHARACTERS = (
string.ascii_lowercase
+ string.ascii_uppercase
+ string.digits
+ string.punctuation
)
short_password_strategy = st.text(alphabet=VALID_PASSWORD_CHARACTERS, max_size=7)
@given(short_password=short_password_strategy)
def test_too_short_rejected_via_hypothesis(short_password):
assert is_password_good(short_password) == False
@st.composite
def valid_password_composite(draw):
"""Generates valid passwords to be given to the student's password checker.
See https://hypothesis.readthedocs.io/en/latest/data.html#composite-strategies.
"""
# Valid passwords can contain 3 or 4 categories of character.
num_categories_present = random.randint(3, 4)
booleans = [True] * num_categories_present
if num_categories_present == 3:
booleans.append(False)
random.shuffle(booleans)
contains_lowercase, contains_uppercase, contains_digits, contains_symbols = booleans
strategies = []
if contains_lowercase:
strategies.append(draw(st.text(alphabet=string.ascii_lowercase, min_size=1)))
if contains_uppercase:
strategies.append(draw(st.text(alphabet=string.ascii_uppercase, min_size=1)))
if contains_digits:
strategies.append(draw(st.text(alphabet=string.digits, min_size=1)))
if contains_symbols:
strategies.append(draw(st.text(alphabet=string.punctuation, min_size=1)))
password = list(''.join(strategies))
random.shuffle(password)
return ''.join(password)
valid_passwords = valid_password_composite().filter(lambda s: len(s) >= 8)
def doesnt_contain_student_info(password):
return '12345' not in password and 'jrheard' not in password
passwords_without_student_info = valid_passwords.filter(doesnt_contain_student_info)
@settings(max_examples=2000)
@given(password=passwords_without_student_info)
def test_long_enough_passwords_are_good(password):
assert is_password_good(password) == True
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment