Skip to content

Commit

Permalink
chore: Add missing tests
Browse files Browse the repository at this point in the history
  • Loading branch information
svillegas-cdd committed Oct 12, 2023
1 parent a16515b commit 27ece07
Showing 1 changed file with 257 additions and 1 deletion.
258 changes: 257 additions & 1 deletion tests/test_management.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,78 @@
import builtins
from io import StringIO

from django.core.management import call_command
from django.core.management import call_command, CommandError
from django.test import TestCase

from fd_dj_accounts import management
from fd_dj_accounts.management.commands import createsuperuser
from fd_dj_accounts.models import User


MOCK_INPUT_KEY_TO_PROMPTS = {
'bypass': ['Bypass password validation and create user anyway? [y/N]: '],
'email_address': ['Email address: '],
'password': ['Password: '],
}


def mock_inputs(inputs):
"""
Decorator to temporarily replace input/getpass to allow interactive
createsuperuser.
"""
def inner(test_func):
def wrapped(*args):
class mock_getpass:
@staticmethod
def getpass(prompt=b'Password: ', stream=None):
if callable(inputs['password']):
return inputs['password']()
return inputs['password']

def mock_input(prompt):
assert '__proxy__' not in prompt
response = None
for key, val in inputs.items():
if val == 'KeyboardInterrupt':
raise KeyboardInterrupt
# get() fallback because sometimes 'key' is the actual
# prompt rather than a shortcut name.
prompt_msgs = MOCK_INPUT_KEY_TO_PROMPTS.get(key, key)
if isinstance(prompt_msgs, list):
prompt_msgs = [msg() if callable(msg) else msg for msg in prompt_msgs]
if prompt in prompt_msgs:
if callable(val):
response = val()
else:
response = val
break
if response is None:
raise ValueError('Mock input for %r not found.' % prompt)
return response

old_getpass = createsuperuser.getpass
old_input = builtins.input
createsuperuser.getpass = mock_getpass
builtins.input = mock_input
try:
test_func(*args)
finally:
createsuperuser.getpass = old_getpass
builtins.input = old_input
return wrapped
return inner


class MockTTY:
"""
A fake stdin object that pretends to be a TTY to be used in conjunction
with mock_inputs.
"""
def isatty(self):
return True


class GetDefaultUsernameTestCase(TestCase):
def test_simple(self) -> None:
self.assertEqual(management.get_default_username(), '')
Expand Down Expand Up @@ -36,6 +102,196 @@ def test_basic_usage(self) -> None:
# created password should be unusable
self.assertFalse(u.has_usable_password())

def test_no_email_argument(self):
new_io = StringIO()
with self.assertRaisesMessage(CommandError, 'You must use --email_address with --noinput.'):
call_command('createsuperuser', interactive=False, stdout=new_io)

def test_skip_if_not_in_TTY(self):
"""
If the command is not called from a TTY, it should be skipped and a
message should be displayed
"""

class FakeStdin:
"""A fake stdin object that has isatty() return False."""

def isatty(self):
return False

out = StringIO()
call_command(
"createsuperuser",
stdin=FakeStdin(),
stdout=out,
interactive=True,
)

self.assertEqual(User._default_manager.count(), 0)
self.assertIn("Superuser creation skipped", out.getvalue())

def test_interactive_basic_usage(self):
@mock_inputs({
'email_address': 'new_user@somewhere.org',
'password': 'nopasswd',
})
def createsuperuser():
new_io = StringIO()
call_command(
"createsuperuser",
interactive=True,
stdout=new_io,
stdin=MockTTY(),
)
command_output = new_io.getvalue().strip()
self.assertEqual(command_output, 'Superuser created successfully.')

createsuperuser()

users = User.objects.filter(email_address="new_user@somewhere.org")
self.assertEqual(users.count(), 1)

def test_unique_usermane_validation(self):
new_io = StringIO()
# Create user with email address 'new_user@somewhere'.
call_command(
"createsuperuser",
interactive=False,
email_address="fake_email@somewhere.org",
stdout=new_io,
)

# The first two email_address duplicated emails, but the third is a valid one.
entered_emails = [
"fake_email@somewhere.org",
"fake_email@somewhere.org",
"other_email@somewhere.org",
]

def duplicated_emails_then_valid():
return entered_emails.pop(0)

@mock_inputs({
'email_address': duplicated_emails_then_valid,
'password': 'nopasswd',
})
def createsuperuser():
std_out = StringIO()
call_command(
"createsuperuser",
interactive=True,
stdin=MockTTY(),
stderr=std_out,
stdout=std_out,
)
self.assertEqual(
std_out.getvalue().strip(),
"Error: That email address is already taken.\n"
"Superuser created successfully."
)

createsuperuser()

users = User.objects.filter(email_address="new_user@somewhere.org")
self.assertEqual(users.count(), 1)

def test_blank_username_non_interactive(self):
new_io = StringIO()
with self.assertRaisesMessage(CommandError, 'Email address cannot be blank.'):
call_command(
'createsuperuser',
email_address='',
interactive=False,
stdin=MockTTY(),
stdout=new_io,
stderr=new_io,
)

def test_blank_username(self):
"""Creation fails if --username is blank."""
new_io = StringIO()
with self.assertRaisesMessage(CommandError, 'Email address cannot be blank.'):
call_command(
'createsuperuser',
email_address='',
stdin=MockTTY(),
stdout=new_io,
stderr=new_io,
)

def test_validation_blank_password_entered(self):
"""
Creation should fail if the user enters blank passwords.
"""
new_io = StringIO()

# The first two passwords are empty strings, but the second two are
# valid.
entered_passwords = ["", "", "password2", "password2"]

def blank_passwords_then_valid():
return entered_passwords.pop(0)

@mock_inputs({
'password': blank_passwords_then_valid,
'email_address': 'new_user@somewhere.org',
})
def test(self):
call_command(
"createsuperuser",
interactive=True,
stdin=MockTTY(),
stdout=new_io,
stderr=new_io,
)
self.assertEqual(
new_io.getvalue().strip(),
"Error: Blank passwords aren't allowed.\n"
"Superuser created successfully."
)

test(self)

def test_password_validation_bypass(self):
"""
Password validation can be bypassed by entering 'y' at the prompt.
"""
new_io = StringIO()

@mock_inputs({
'email_address': 'joe@example.com',
'password': '1234567890',
'bypass': 'y',
})
def test(self):
call_command(
'createsuperuser',
interactive=True,
stdin=MockTTY(),
stdout=new_io,
stderr=new_io,
)
self.assertEqual(
new_io.getvalue().strip(),
'This password is entirely numeric.\n'
'Superuser created successfully.'
)

test(self)

@mock_inputs({'email_address': 'KeyboardInterrupt'})
def test_keyboard_interrupt(self):
new_io = StringIO()
with self.assertRaises(SystemExit):
call_command(
'createsuperuser',
interactive=True,
stdin=MockTTY(),
stdout=new_io,
stderr=new_io,
)
self.assertEqual(new_io.getvalue(), '\nOperation cancelled.\n')

# TODO: Add tests.
# See:
# - https://github.com/django/django/blob/3.2/tests/auth_tests/test_management.py#L253-L1036
Expand Down

0 comments on commit 27ece07

Please sign in to comment.