def test_set_new_password(party_account_fixture): fixture = party_account_fixture system_account = fixture.system_account new_password_request = NewPasswordRequest(system_account=system_account) Session.add(new_password_request) new_password = system_account.password * 2 account_management_interface = AccountManagementInterface() Session.add(account_management_interface) # Case: the key is invalid invalid_key = 'in va lid key which is also too long and contains spaces' account_management_interface.email = system_account.email account_management_interface.secret = invalid_key account_management_interface.password = new_password with expected(KeyException): account_management_interface.choose_new_password() # Case: the email is invalid invalid_email = 'i am not a valid email' account_management_interface.email = invalid_email account_management_interface.secret = new_password_request.as_secret_key() account_management_interface.password = new_password with expected(InvalidEmailException): account_management_interface.choose_new_password() # Case: the key is valid account_management_interface.email = system_account.email account_management_interface.secret = new_password_request.as_secret_key() account_management_interface.password = new_password account_management_interface.choose_new_password() system_account.authenticate(new_password) # Should not raise exception
def test_boolean_validation(fixture): obj = EmptyStub() field = BooleanField() field.bind('boolean_attribute', obj) # Case: invalid invalid_boolean_name = ['negative', 'affirmative', '+', '-', None] for boolean_candidate in invalid_boolean_name: with expected(AllowedValuesConstraint): field.set_user_input(boolean_candidate) assert field.validation_error is field.get_validation_constraint_named( 'pattern') # Case: valid field.from_input('on') assert obj.boolean_attribute is True assert field.as_input() == 'on' field.from_input('off') assert obj.boolean_attribute is False assert field.as_input() == 'off' # Case: required means True for BooleanField field = BooleanField(required=True) field.bind('boolean_attribute', obj) with expected(AllowedValuesConstraint): field.set_user_input('off') assert field.validation_error is field.get_validation_constraint_named( 'pattern') with expected(NoException): field.from_input('on')
def test_arguments_to_event(fixture): """Only Action objects can be sent as action= when creating an Event. The arguments passed to readable and writable should be callable objects with correct signature.""" # action= with expected(IsInstance): Event(action=EmptyStub()) def check_exc(expected_message, ex): message = str(ex).split(':')[1][1:] assert message.startswith(expected_message) # readable/writable are callable with expected(IsCallable, test=functools.partial( check_exc, 'readable should be a callable object')): Event(readable=EmptyStub()) with expected(IsCallable, test=functools.partial( check_exc, 'writable should be a callable object')): Event(writable=EmptyStub()) # readable/writable have correct signature def do_nothing(a, b, c, d): pass with expected(IncorrectArgumentError): Event(readable=do_nothing) with expected(IncorrectArgumentError): Event(writable=do_nothing)
def test_request_email_change(reahl_system_fixture, party_account_fixture): fixture = party_account_fixture system_account = fixture.new_system_account(activated=False) mailer_stub = fixture.mailer new_email = '*****@*****.**' account_management_interface = fixture.new_account_management_interface( system_account=system_account) # Case where the user account has not been activated assert isinstance(system_account.status, AccountNotActivated) account_management_interface.new_email = new_email with expected(AccountNotActiveException): account_management_interface.request_email_change() system_account.activate() system_account.disable() # Case where the user account is disabled for another reason assert isinstance(system_account.status, AccountDisabled) account_management_interface.new_email = new_email with expected(AccountNotActiveException): account_management_interface.request_email_change() system_account.enable() # Case where the user account is active and enabled, but a clashing email name is requested other_party = Party() clashing_new_email = '*****@*****.**' clashing_system_account = fixture.new_system_account( party=other_party, email=clashing_new_email, activated=True) account_management_interface.new_email = clashing_new_email with expected(NotUniqueException): account_management_interface.request_email_change() # Case where the user account is active and enabled, and a new unique email name is requested assert Session.query(ChangeAccountEmail).count() == 0 account_management_interface.new_email = new_email account_management_interface.request_email_change() new_email_request = Session.query(ChangeAccountEmail).filter_by( system_account=system_account).one().verify_email_request assert mailer_stub.mail_recipients == [new_email] assert mailer_stub.mail_sender == reahl_system_fixture.config.accounts.admin_email substitutions = { 'email': new_email, 'secret_key': new_email_request.as_secret_key() } expected_subject = Template( reahl_system_fixture.config.accounts.email_change_subject).substitute( substitutions) assert mailer_stub.mail_subject == expected_subject expected_message = Template( reahl_system_fixture.config.accounts.email_change_email).substitute( substitutions) assert mailer_stub.mail_message == expected_message # Case where a email name is requested which matches an already pending one account_management_interface.new_email = new_email with expected(NotUniqueException): account_management_interface.request_email_change()
def test_verify_email_change(party_account_fixture): fixture = party_account_fixture system_account = fixture.system_account new_email = '*****@*****.**' change_email_action = ChangeAccountEmail(system_account, new_email) Session.add(change_email_action) request = change_email_action.verify_email_request account_management_interface = fixture.account_management_interface # Case where there is a password mismatch account_management_interface.email = new_email account_management_interface.password = '******' account_management_interface.secret = request.as_secret_key() with expected(InvalidPasswordException, test=assert_is_set_to_commit): account_management_interface.verify_email() assert system_account.email != new_email # Case where there is a key mismatch account_management_interface.email = new_email account_management_interface.password = system_account.password account_management_interface.secret = 'invalid key' with expected(KeyException): account_management_interface.verify_email() assert system_account.email != new_email # Case where it works assert system_account.email != new_email account_management_interface.email = new_email account_management_interface.password = system_account.password account_management_interface.secret = request.as_secret_key() account_management_interface.verify_email() assert Session.query(VerifyEmailRequest).filter_by( id=request.id).count() == 0 assert system_account.email == new_email
def test_reahl_additions(): ExecutionContext().install() try: metadata.bind = 'sqlite:///:memory:' metadata.create_all() address = Address() Session.add(address) email_field = address.fields.email_address # While a programmer would not usually write code like this, # it is useful to show how the framework can use Fields and Events # to obtain more information about a certain Field/Event: assert email_field.label == 'Email' # Fields are used (amongst other things) to validate user input: with expected(Exception): email_field.from_input('invalid email address') with expected(NoException): assert address.email_address == None email_field.from_input('*****@*****.**') assert address.email_address == '*****@*****.**' # After input was given, the field is set on the object it belongs to: # (The value set is a marshalled version of the user input. In this case it is just # a string again, but it could have been, for example an EmailAddress object, # and Integer, or a Date.) assert address.email_address == '*****@*****.**' finally: metadata.bind = None
def test_file_validation(fixture): """A FileField needs to check that the right number of files were submitted, depending on the setting of allow_multiple and/or required. """ field = FileField() obj = fixture.model_object field.bind('file_value', obj) files = [EmptyStub(), EmptyStub()] # Single file only with expected(SingleFileConstraint): field.set_user_input(files) with expected(NoException): field.set_user_input(files[:1]) # Single file that is required field = FileField(required=True) field.bind('file_value', obj) with expected(NoException): field.set_user_input(files[:1]) with expected(RequiredConstraint): field.set_user_input([]) # Multiple files field = FileField(allow_multiple=True) field.bind('file_value', obj) with expected(NoException): field.set_user_input(files)
def test_date_validation(fixture): """A DateField can validate its input based on a min or max value and expects fuzzy but sensible input.""" field = DateField() obj = fixture.model_object field.bind('date_value', obj) # Case invalid with expected(DateConstraint): field.set_user_input('sdfdf') # Case valid with expected(NoException): field.set_user_input('13 Dec') limit_date = datetime.date(2012, 11, 13) before_limit = '12 Nov 2012' after_limit = '14 Nov 2012' # Case Max field = DateField(max_value=limit_date) with expected(MaxValueConstraint): field.set_user_input(after_limit) # Case Min field = DateField(min_value=limit_date) with expected(MinValueConstraint): field.set_user_input(before_limit)
def test_argument_checks_with_deprecated_methods(): """When used with @deprecated, argument checks still work.""" @deprecated('this test class is deprecated', '1.2') class ADeprecatedClass: @arg_checks(y=IsInstance(int), title=IsInstance(str)) def __init__(self, x, y, title='a title', style=None): pass with expected(IsInstance): ADeprecatedClass(1, 'y') with expected(IsInstance): ArgumentCheckedCallable(ADeprecatedClass).checkargs('x', 'y') with expected(IncorrectArgumentError): ArgumentCheckedCallable(ADeprecatedClass, explanation='an explanation').checkargs('x', NotYetAvailable('x'), title='a valid title') class ADeprecatedClass: @deprecated('this instance method is deprecated', '1.3') @arg_checks(y=IsInstance(int), title=IsInstance(str)) def instance_method(self, x, y, title='a title', style=None): pass @deprecated('this class method is deprecated', '2.3') @arg_checks(y=IsInstance(int), title=IsInstance(str)) @classmethod def class_method(cls, x, y, title='a title', style=None): pass with expected(IsInstance): ADeprecatedClass().instance_method('x', 'y') with expected(IsInstance): ADeprecatedClass.class_method('x', 'y')
def wrong_args_to_input(fixture): """Passing the wrong arguments upon constructing an Input results in an error.""" with expected(IsInstance): PrimitiveInput(fixture.form, EmptyStub()) with expected(IsInstance): PrimitiveInput(EmptyStub(), Field())
def test_event_security_action_and_rw(): """Supply either an action or a readable/writable to an Event, but not both.""" def do_nothing(): pass with expected(ProgrammerError): Event(action=Action(do_nothing), readable=Action(do_nothing)) with expected(ProgrammerError): Event(action=Action(do_nothing), writable=Action(do_nothing))
def test_validation(mailer_fixture): fixture = mailer_fixture with expected(InvalidEmailAddressException): MailMessage(fixture.invalid_email_addresses[0], fixture.valid_email_addresses, 'Hi', 'Some Message') with expected(InvalidEmailAddressException): MailMessage(fixture.valid_email_addresses[0], fixture.invalid_email_addresses, 'Hi', 'Some Message')
def test_wrong_args_to_input(simple_input_fixture): """Passing the wrong arguments upon constructing an Input results in an error.""" fixture = simple_input_fixture with expected(IsInstance): PrimitiveInput(fixture.form, EmptyStub(in_namespace=lambda self: self)) with expected(IsInstance): PrimitiveInput(EmptyStub(channel_name='test'), Field())
def allowed_string_options(fixture): """The value of an HTMLAttributeValueOption is constrained to one of its stated valid options if it is set.""" with expected(NoException): HTMLAttributeValueOption('validoption', True, constrain_value_to=['anoption', 'anotheroption', 'validoption']) with expected(NoException): HTMLAttributeValueOption('invalidoption', False, constrain_value_to=['anoption', 'anotheroption', 'validoption']) with expected(ProgrammerError): HTMLAttributeValueOption('invalidoption', True, constrain_value_to=['anoption', 'anotheroption', 'validoption'])
def widget_adding_error(self, fixture): """Passing anything other than other Widgets to .add_child or add_children results in an error.""" widget = Widget(fixture.view) with expected(IsInstance): widget.add_child(EmptyStub()) with expected(IsInstance): widget.add_children([Widget(fixture.view), EmptyStub()])
def equal_to_constraint(self, fixture): other_field = Field(label='other') equal_to_constraint = EqualToConstraint(other_field, '$label, $other_label') other_field.set_user_input('should be equal to this string', ignore_validation=True) #case: valid input with expected(NoException): equal_to_constraint.validate_parsed_value('should be equal to this string' ) #case: invalid input with expected(EqualToConstraint): equal_to_constraint.validate_parsed_value('this is not equal')
def widget_layout_errors(fixture): """A Layout can only be used with a single Widget, and a Widget can only have a single Layout.""" widget_with_layout = Div(fixture.view).use_layout(Layout()) with expected(ProgrammerError): widget_with_layout.use_layout(Layout()) re_used_layout = Layout() widget_with_reused_layout = Div(fixture.view).use_layout(re_used_layout) with expected(ProgrammerError): Div(fixture.view).use_layout(re_used_layout)
def choice_validation(self, fixture): """Input for a ChoiceField is valid only if it matches one of its Choices.""" field = fixture.field # Case invalid with expected(fixture.expected_validation_constraint): field.set_user_input('sdfdf') # Case valid for i in fixture.valid_inputs: with expected(NoException): field.set_user_input(i)
def greater_than_constraint(self, fixture): other_field = IntegerField(label='other') greater_than_constraint = GreaterThanConstraint(other_field, '$label, $other_label') other_field.set_user_input('5', ignore_validation=True) #case: valid input with expected(NoException): greater_than_constraint.validate_parsed_value( 6 ) #case: invalid input with expected(GreaterThanConstraint): greater_than_constraint.validate_parsed_value( 5 )
def test_smaller_than_constraint(fixture): other_field = IntegerField(label='other') smaller_than_constraint = SmallerThanConstraint(other_field, '$label, $other_label') other_field.set_user_input('5', ignore_validation=True) #case: valid input with expected(NoException): smaller_than_constraint.validate_parsed_value( 4 ) #case: invalid input with expected(SmallerThanConstraint): smaller_than_constraint.validate_parsed_value( 5 )
def test_stubbable_is_instance(): """Classes can be marked with a flag to let them pass the IsInstance or IsSubclass checks even though they do not inherit from the specified class.""" class A(object): pass class B(object): is_A = True with expected(NoException): assert IsInstance(A).is_valid(B()) with expected(NoException): assert IsSubclass(A).is_valid(B)
def test_logging_in(reahl_system_fixture, party_account_fixture): fixture = party_account_fixture system_account = fixture.system_account login_session = LoginSession.for_session( reahl_system_fixture.context.session) account_management_interface = fixture.account_management_interface account_management_interface.stay_logged_in = False # Case: successful email attempt assert login_session.account is not system_account account_management_interface.log_in() assert login_session.account is system_account # Case: failed email attempts disable the account login_session.account = None assert system_account.account_enabled account_management_interface.email = system_account.email account_management_interface.password = '******' for i in list(range(3)): with expected(InvalidPasswordException, test=assert_is_set_to_commit): account_management_interface.log_in() assert system_account.failed_logins == i + 1 assert login_session.account is None assert not system_account.account_enabled # Case: Account is locked system_account.disable() assert isinstance(system_account.status, AccountDisabled) with expected(AccountNotActiveException): account_management_interface.log_in() assert login_session.account is None # Case: Account is not activated yet login_session.account = None system_account = fixture.new_system_account(email='*****@*****.**', activated=False) assert isinstance(system_account.status, AccountNotActivated) with expected(AccountNotActiveException): account_management_interface.log_in() assert login_session.account is None # Case: Login for nonexistant email name account_management_interface.email = 'i@do not exist' account_management_interface.password = '******' with expected(InvalidPasswordException, test=assert_not_set_to_commit): account_management_interface.log_in() assert login_session.account is None
def logging_in(self, fixture): system_account = fixture.system_account login_session = LoginSession.for_session(fixture.context.session) account_management_interface = fixture.account_management_interface account_management_interface.stay_logged_in = False # Case: successful email attempt vassert(login_session.account is not system_account) account_management_interface.log_in() vassert(login_session.account is system_account) # Case: failed email attempts disable the account login_session.account = None vassert(system_account.account_enabled) account_management_interface.email = system_account.email account_management_interface.password = '******' for i in list(range(3)): with expected(InvalidPasswordException, test=lambda e: vassert(e.commit)): account_management_interface.log_in() vassert(system_account.failed_logins == i + 1) vassert(login_session.account is None) vassert(not system_account.account_enabled) # Case: Account is locked system_account.disable() vassert(isinstance(system_account.status, AccountDisabled)) with expected(AccountNotActiveException): account_management_interface.log_in() vassert(login_session.account is None) # Case: Account is not activated yet login_session.account = None system_account = fixture.new_system_account(email='*****@*****.**', activated=False) vassert(isinstance(system_account.status, AccountNotActivated)) with expected(AccountNotActiveException): account_management_interface.log_in() vassert(login_session.account is None) # Case: Login for nonexistant email name account_management_interface.email = 'i@do not exist' account_management_interface.password = '******' with expected(InvalidPasswordException, test=lambda e: vassert(not e.commit)): account_management_interface.log_in() vassert(login_session.account is None)
def allowed_values_constraint(self, fixture): allowed_values=['a','b'] allowed_values_constraint = AllowedValuesConstraint(allowed_values=allowed_values) #case: valid input valid_input = allowed_values[1] with expected(NoException): allowed_values_constraint.validate_input(valid_input) #case: invalid input invalid_input = 'ba' assert invalid_input not in allowed_values with expected(AllowedValuesConstraint): allowed_values_constraint.validate_input(invalid_input)
def create_account(self, fixture): login_email = '*****@*****.**' mailer_stub = fixture.mailer # EmailAndPasswordSystemAccount.mailer = mailer_stub account_management_interface = fixture.account_management_interface account_management_interface.email = login_email # Case where the email does not exist as system_account, but as pending new email mailer_stub.reset() other_system_account = fixture.system_account new_email = '*****@*****.**' Session.add(ChangeAccountEmail(other_system_account, new_email)) with expected(NotUniqueException): account_management_interface.email = new_email account_management_interface.register() vassert(not mailer_stub.mail_sent) fixture.system_control.rollback() # Case where it all works vassert(Session.query(ActivateAccount).count() == 0) account_management_interface.email = login_email system_account = account_management_interface.register() [activation_action] = Session.query(ActivateAccount).filter_by( system_account=system_account).all() activation_request = activation_action.requirements[0] vassert(mailer_stub.mail_sent) vassert(system_account.email == account_management_interface.email) # FIXME: These are those dubious tests where the assert just repeats the implementation verbatim vassert(system_account.password_md5 == hashlib.md5( account_management_interface.password.encode('utf-8')).hexdigest()) vassert( system_account.apache_digest == hashlib.md5(('%s:%s:%s' %\ (account_management_interface.email,'',account_management_interface.password)).encode('utf-8')).hexdigest() ) assert_recent(activation_action.deadline - timedelta(days=10)) vassert(not system_account.registration_activated) vassert(not system_account.account_enabled) vassert(not system_account.registration_date) vassert(isinstance(system_account, EmailAndPasswordSystemAccount)) vassert(system_account.owner is None) vassert(system_account.id) # Case where the email name exists mailer_stub.reset() with expected(NotUniqueException): account_management_interface.register() vassert(not mailer_stub.mail_sent)
def file_validation_mime_type(self, fixture): """A FileField can also limit the mimetype of files allowed to be uploaded. """ field = FileField(allow_multiple=True, accept=['text/*']) obj = fixture.model_object field.bind('file_value', obj) files = [UploadedFile('file1', b'stuff 1', 'text/html'), UploadedFile('file2', b'stuff 2', 'text/xml')] with expected(NoException): field.set_user_input(files) files = [UploadedFile('file1', b'stuff 3', 'text/html'), UploadedFile('file2', b'stuff 4', 'application/java')] with expected(MimeTypeConstraint): field.set_user_input(files)
def password_validation(self, fixture): field = PasswordField() # Case: invalid with expected(MinLengthConstraint): field.set_user_input('123') vassert( field.validation_error is field.get_validation_constraint_named('minlength') ) with expected(MaxLengthConstraint): field.set_user_input('1'*21) vassert( field.validation_error is field.get_validation_constraint_named('maxlength') ) # Case: valid with expected(NoException): field.set_user_input('my passwôrd') vassert( not field.validation_error )
def test_checking_arguments(argument_check_fixture): """Methods can be augmented with argument checks. These checks are done when calling such a method, or before an actual call is made using ArgumentCheckedCallable.checkargs.""" fixture = argument_check_fixture with expected(fixture.expected_exception): fixture.callable(*fixture.call_args, **fixture.kwargs) with expected(fixture.expected_exception): ArgumentCheckedCallable(fixture.callable).checkargs(*fixture.args, **fixture.kwargs) wrapped_exception = NoException if fixture.expected_exception is not NoException: wrapped_exception = IncorrectArgumentError with expected(wrapped_exception): ArgumentCheckedCallable(fixture.callable, explanation='some message').checkargs(*fixture.args, **fixture.kwargs)
def file_validation_size(self, fixture): """A FileField can also limit the size if files uploaded. """ field = FileField(allow_multiple=True, max_size_bytes=100) obj = fixture.model_object field.bind('file_value', obj) files = [UploadedFile('file1', b'.'*100, ''), UploadedFile('file2', b'.'*50, '')] with expected(NoException): field.set_user_input(files) files = [UploadedFile('file1', b'.'*100, ''), UploadedFile('file2', b'.'*200, '')] with expected(FileSizeConstraint): field.set_user_input(files)
def file_validation_max_files(self, fixture): """A maximum can be placed upon the number of files that may be uploaded. """ field = FileField(allow_multiple=True, max_files=1) obj = fixture.model_object field.bind('file_value', obj) files = [UploadedFile('file1', b'stuff 1', '')] with expected(NoException): field.set_user_input(files) files = [UploadedFile('file1', b'stuff 2', ''), UploadedFile('file2', b'stuff 3', '')] with expected(MaxFilesConstraint): field.set_user_input(files)