def test_uniqueness_of_request_keys(sql_alchemy_fixture, party_account_fixture): fixture = party_account_fixture system_account = fixture.new_system_account(activated=False) @stubclass(VerificationRequest) class VerificationRequestStub(VerificationRequest): __tablename__ = 'verificationrequeststub' __mapper_args__ = {'polymorphic_identity': 'verificationrequeststub'} id = Column(Integer, ForeignKey(VerificationRequest.id), primary_key=True) def generate_salt(self): self.salt = 'not unique' with sql_alchemy_fixture.persistent_test_classes(VerificationRequestStub): request1 = VerificationRequestStub() Session.add(request1) request2 = VerificationRequestStub() Session.add(request2) clashing_request = VerificationRequestStub() Session.add(clashing_request) assert request1.as_secret_key() != clashing_request.as_secret_key() assert request2.as_secret_key() != clashing_request.as_secret_key()
def demo_setup(sql_alchemy_fixture): sql_alchemy_fixture.commit = True Session.add(Address(name='John Doe', email_address='*****@*****.**')) Session.add( Address(name='Jane Johnson', email_address='*****@*****.**')) Session.add(Address(name='Jack Black', email_address='*****@*****.**'))
def do_demo_setup(self): Session.add(Address(name='John Doe', email_address='*****@*****.**')) Session.add(Address(name='Jane Johnson', email_address='*****@*****.**')) Session.add(Address(name='Jack Black', email_address='*****@*****.**')) Session.flush() Session.commit()
def test_session_data_disappears_when_session_does(web_fixture): """When a UserSession is deleted, all associated SessionData disappear as well.""" fixture = web_fixture UserSession.initialise_web_session_on(fixture.context) user_session = fixture.context.session ui_name = 'user_interface' channel_name = 'channel' session_data = SessionData(web_session=user_session, view_path='/', ui_name=ui_name, channel_name=channel_name) Session.add(session_data) Session.flush() Session.delete(user_session) assert Session.query(SessionData).filter_by( id=session_data.id).count() == 0 assert Session.query(UserSession).filter_by( id=user_session.id).count() == 0
def test_session_keeps_living(web_fixture): """When SessionData is deleted, the associated UserSession is not affected.""" fixture = web_fixture UserSession.initialise_web_session_on(fixture.context) user_session = fixture.context.session ui_name = 'user_interface' channel_name = 'channel' session_data = SessionData(web_session=user_session, view_path='/', ui_name=ui_name, channel_name=channel_name) Session.add(session_data) Session.flush() Session.delete(session_data) assert Session.query(SessionData).filter_by( id=session_data.id).count() == 0 assert Session.query(UserSession).filter_by( id=user_session.id).one() is user_session
def test_optimistic_concurrency(web_fixture, sql_alchemy_fixture, concurrency_fixture): """A user is prompted to handle the situation where data would be overwritten when submitting a form that was originally rendered based on older data. """ fixture = concurrency_fixture with sql_alchemy_fixture.persistent_test_classes(fixture.ModelObject): model_object = fixture.model_object Session.add(model_object) wsgi_app = web_fixture.new_wsgi_app( child_factory=fixture.MyForm.factory()) web_fixture.reahl_server.set_app(wsgi_app) browser = web_fixture.driver_browser browser.open('/') # The form submit does not overwrite any data changed by other means assert not fixture.is_concurrency_error_displayed() browser.type(XPath.input_labelled('Some field'), 'something') fixture.make_concurrent_change_in_backend() browser.click(XPath.button_labelled('Submit')) assert fixture.is_concurrency_error_displayed() assert fixture.concurrent_change_is_present() # When presented with such an error, the user can click on a button to reset all inputs to the now-current values browser.click(XPath.button_labelled('Reset input')) assert not fixture.is_concurrency_error_displayed() assert browser.get_value( XPath.input_labelled('Some field')) == 'changed by someone else' browser.type(XPath.input_labelled('Some field'), 'final changed value') browser.click(XPath.button_labelled('Submit')) assert not fixture.is_concurrency_error_displayed() assert model_object.some_field == 'final changed value'
def deferred_action_times_out_with_shared_requirements(self, fixture): """If a DeferredAction times out, it will not nuke Requirements shared with another DeferredAction.""" with fixture.persistent_test_classes(fixture.MyDeferredAction, fixture.SomeObject): requirements1 = [Requirement()] requirements2 = [Requirement(), Requirement()] deferred_action1 = fixture.MyDeferredAction(fixture.one_object, requirements=requirements2, deadline=fixture.future_time) Session.add(deferred_action1) deferred_action2 = fixture.MyDeferredAction(fixture.another_object, requirements=requirements1+requirements2, deadline=fixture.future_time) Session.add(deferred_action2) Session.flush() # If one DeferredAction times out, the remaining one and its Requirements are left intact deferred_action1.deadline=fixture.past_time ReahlEgg.do_daily_maintenance_for_egg('reahl-domain') vassert( fixture.one_object.deadline_flag ) vassert( not fixture.another_object.deadline_flag ) vassert( Session.query(Requirement).count() == 3 ) vassert( Session.query(DeferredAction).count() == 1 ) for requirement in requirements1+requirements2: vassert( set(requirement.deferred_actions) == {deferred_action2} ) # When no more DeferredActions are held onto by Requirements, those Requirements are deleted deferred_action2.deadline=fixture.past_time ReahlEgg.do_daily_maintenance_for_egg('reahl-domain') vassert( fixture.one_object.deadline_flag ) vassert( fixture.another_object.deadline_flag ) vassert( Session.query(Requirement).count() == 0 ) vassert( Session.query(DeferredAction).count() == 0 )
def register_help_pending(self, fixture): verification_requests = Session.query(VerifyEmailRequest) unactivated_account = fixture.new_system_account(email='*****@*****.**', activated=False) activation_request = VerifyEmailRequest(email=unactivated_account.email, subject_config='accounts.activation_subject', email_config='accounts.activation_email') Session.add(activation_request) deferred_activation = ActivateAccount(system_account=unactivated_account, requirements=[activation_request]) Session.add(deferred_activation) fixture.browser.open('/a_ui/registerHelp') fixture.browser.type('//input[@name="email"]', unactivated_account.email) fixture.browser.click('//input[@value="Investigate"]') vassert( fixture.browser.location_path == '/a_ui/registerHelp/pending' ) vassert( verification_requests.count() == 1 ) fixture.mailer.reset() fixture.browser.click('//input[@value="Send"]') vassert( verification_requests.count() == 1 ) vassert( fixture.mailer.mail_sent ) vassert( fixture.browser.location_path == '/a_ui/registerHelp/pending/sent' )
def test_activate_via_key(party_account_fixture): fixture = party_account_fixture system_account = fixture.new_system_account(email='*****@*****.**', activated=False) activation_request = VerifyEmailRequest(email=system_account.email, subject_config='accounts.activation_subject', email_config='accounts.activation_email') Session.add(activation_request) deferred_activation = ActivateAccount(system_account=system_account, requirements=[activation_request]) Session.add(deferred_activation) account_management_interface = fixture.account_management_interface # Case where there is an email mismatch account_management_interface.email = '*****@*****.**' account_management_interface.secret = activation_request.as_secret_key() with expected(InvalidEmailException, test=assert_is_set_to_commit): account_management_interface.verify_email() assert not system_account.registration_activated # Case where there is a key mismatch account_management_interface.email = system_account.email account_management_interface.secret = 'a key that is invalid' with expected(KeyException): account_management_interface.verify_email() assert not system_account.registration_activated # Case where it works assert not system_account.registration_activated assert not system_account.account_enabled assert not system_account.registration_date account_management_interface.email = system_account.email account_management_interface.secret = activation_request.as_secret_key() account_management_interface.verify_email() assert system_account.registration_activated assert system_account.account_enabled assert_recent( system_account.registration_date ) assert Session.query(VerifyEmailRequest).filter_by(id=activation_request.id).count() == 0
def deferred_action_times_out(self, fixture): """If all its Requirements are not fulfilled before its deadline has been reached, a DeferredAction executes its deadline action; then, it and its Requirements are deleted""" with fixture.persistent_test_classes(fixture.MyDeferredAction, fixture.SomeObject): requirements = [Requirement(), Requirement(), Requirement()] deferred_action = fixture.MyDeferredAction(fixture.one_object, requirements=requirements, deadline=fixture.future_time) Session.add(deferred_action) Session.flush() vassert( deferred_action.deadline == fixture.future_time ) ReahlEgg.do_daily_maintenance_for_egg('reahl-domain') vassert( not fixture.one_object.deadline_flag ) vassert( not fixture.another_object.deadline_flag ) vassert( Session.query(Requirement).count() == 3 ) vassert( Session.query(DeferredAction).count() == 1 ) deferred_action.deadline = fixture.past_time ReahlEgg.do_daily_maintenance_for_egg('reahl-domain') vassert( fixture.one_object.deadline_flag ) vassert( not fixture.another_object.deadline_flag ) vassert( Session.query(Requirement).count() == 0 ) vassert( Session.query(DeferredAction).count() == 0 )
def test_optimistic_concurrency_with_ajax(web_fixture, sql_alchemy_fixture, concurrency_fixture): """When a concurrent change happens after an Ajax refresh, it is ignored in subsequent Ajax refreshes, but upon an eventual submit the user is prompted. """ fixture = concurrency_fixture with sql_alchemy_fixture.persistent_test_classes(fixture.ModelObject): model_object = fixture.model_object Session.add(model_object) model_object.some_trigger_field = 'some value' model_object.some_field = 'some value' wsgi_app = web_fixture.new_wsgi_app( child_factory=fixture.MyForm.factory(), enable_js=True) web_fixture.reahl_server.set_app(wsgi_app) browser = web_fixture.driver_browser browser.open('/') # Trigger refreshing once so that we create construction_client_side_state which may influence subsequent refreshings browser.type(XPath.input_labelled('Some field'), 'something for the fun of it') with browser.refresh_expected_for('#myform', fixture.refresh_expected_for_form),\ browser.refresh_expected_for('#inner_div', True): browser.type(XPath.input_labelled('Some trigger field'), 'something else') fixture.make_concurrent_change_in_backend() assert not fixture.is_concurrency_error_displayed() browser.type(XPath.input_labelled('Some trigger field'), 'something else again') assert not fixture.is_concurrency_error_displayed() browser.click(XPath.button_labelled('Submit')) assert fixture.is_concurrency_error_displayed()
def test_reading_cookies_on_initialising_a_session(web_fixture): fixture = web_fixture # Case: session cookie not set in Request UserSession.initialise_web_session_on(fixture.context) assert not fixture.context.session.is_active() assert not fixture.context.session.is_secured() # Case: session cookie set in Request fixture.context.session = None user_session = UserSession() user_session.set_last_activity_time() Session.add(user_session) fixture.request.headers['Cookie'] = ascii_as_bytes_or_str( 'reahl=%s' % user_session.as_key()) UserSession.initialise_web_session_on(fixture.context) assert fixture.context.session is user_session assert fixture.context.session.is_active() assert not fixture.context.session.is_secured() # Case: session cookie set, secure cookie also set in Request, https fixture.request.scheme = 'https' fixture.context.session = None user_session = UserSession() user_session.set_last_activity_time() Session.add(user_session) fixture.request.headers['Cookie'] = ascii_as_bytes_or_str('reahl=%s , reahl_secure=%s' % \ (user_session.as_key(), user_session.secure_salt)) UserSession.initialise_web_session_on(fixture.context) assert fixture.context.session is user_session assert fixture.context.session.is_active() assert fixture.context.session.is_secured() # Case: session cookie set, secure cookie also set in Request, http fixture.request.scheme = 'http' fixture.context.session = None user_session = UserSession() user_session.set_last_activity_time() Session.add(user_session) fixture.request.headers['Cookie'] = ascii_as_bytes_or_str('reahl=%s , reahl_secure=%s' % \ (user_session.as_key(), user_session.secure_salt)) UserSession.initialise_web_session_on(fixture.context) assert fixture.context.session is user_session assert fixture.context.session.is_active() assert not fixture.context.session.is_secured()
def reserve(cls, email, password, party): cls.assert_email_unique(email) system_account = cls(owner=party, email=email) Session.add(system_account) system_account.set_new_password(email, password) verification_request = VerifyEmailRequest(email=email, subject_config='accounts.activation_subject', email_config='accounts.activation_email') Session.add(verification_request) Session.flush() deferred_activation = ActivateAccount(system_account=system_account, requirements=[verification_request]) Session.add(deferred_activation) system_account.send_activation_notification() return system_account
def callable_object(): Session.add(TestObject(name='new object')) assert Session.query(TestObject).count() == 1 return 'value returned from method'
def save(self): self.added_date = datetime.now() Session.add(self)
def test_exception_handling(reahl_system_fixture, web_fixture, sql_alchemy_fixture): """When a DomainException happens during the handling of an Event: The database is rolled back. The browser is redirected to GET the original view again (not the target). The screen still displays the values the user initially typed, not those on the ModelObject. """ fixture = web_fixture class ModelObject(Base): __tablename__ = 'test_event_handling_exception_handling' id = Column(Integer, primary_key=True) field_name = Column(Integer) def handle_event(self): self.field_name = 1 raise DomainException() @exposed def events(self, events): events.an_event = Event(label='click me', action=Action(self.handle_event)) @exposed def fields(self, fields): fields.field_name = IntegerField(default=3) with sql_alchemy_fixture.persistent_test_classes(ModelObject): model_object = ModelObject() Session.add(model_object) class MyForm(Form): def __init__(self, view, name, other_view): super(MyForm, self).__init__(view, name) self.define_event_handler(model_object.events.an_event, target=other_view) self.add_child(ButtonInput(self, model_object.events.an_event)) self.add_child(TextInput(self, model_object.fields.field_name)) class MainUI(UserInterface): def assemble(self): self.define_page(HTML5Page).use_layout(BasicPageLayout()) home = self.define_view('/', title='Home page') other_view = self.define_view('/page2', title='Page 2') home.set_slot('main', MyForm.factory('myform', other_view)) wsgi_app = fixture.new_wsgi_app(site_root=MainUI) browser = Browser( wsgi_app ) # Dont use a real browser, because it will also hit many other URLs for js and css which confuse the issue browser.open('/') assert not model_object.field_name browser.type("//input[@type='text']", '5') # any database stuff that happened when the form was submitted was rolled back # with CallMonitor(reahl_system_fixture.system_control.orm_control.rollback) as monitor: # browser.click(XPath.button_labelled('click me')) # assert monitor.times_called == 1 browser.click(XPath.button_labelled('click me')) # the value input by the user is still displayed on the form, NOT the actual value on the model object assert not model_object.field_name retained_value = browser.get_value("//input[@type='text']") assert retained_value == '5' # the browser is still on the page with the form which triggered the exception assert browser.current_url.path == '/'
def request_email_change(self, new_email): self.assert_account_live() self.assert_email_unique(new_email) Session.add(ChangeAccountEmail(self, new_email)) self.send_email_change_mail()
def new_another_object(self): another = self.SomeObject(name='another') Session.add(another) return another
def test_invalid_non_trigger_input_corner_case(web_fixture, query_string_fixture, sql_alchemy_fixture): """If an invalid value was submitted via ajax for a non-trigger input, and a valid value is submitted for is with a form, and a DomainException happens... then the non-trigger input must retain its new, valid value.""" fixture = scenario class ModelObject(Base): __tablename__ = 'test_responsive_disclosure_recalculate_invalids' id = Column(Integer, primary_key=True) choice = Column(Integer, default=1) choice3 = Column(Integer, default=9) calculated_state = Column(Integer, default=0) def recalculate(self): self.calculated_state = self.choice * 10 def submit(self): raise DomainException(message='An exception happened on submit') @exposed def events(self, events): events.choice_changed = Event(action=Action(self.recalculate)) events.submit = Event(action=Action(self.submit)) @exposed def fields(self, fields): fields.choice = ChoiceField([Choice(1, IntegerField(label='One')), Choice(2, IntegerField(label='Two')), Choice(3, IntegerField(label='Three'))], label='Choice') fields.choice3 = ChoiceField([Choice(7, IntegerField(label='Seven')), Choice(8, IntegerField(label='Eight')), Choice(9, IntegerField(label='Nine'))], label='Choice3') fields.calculated_state = IntegerField(label='Calculated', writable=Allowed(False)) class MyForm(Form): def __init__(self, view, an_object): super(MyForm, self).__init__(view, 'myform') self.an_object = an_object self.enable_refresh(on_refresh=an_object.events.choice_changed) if self.exception: self.add_child(P(self.view, text=str(self.exception))) self.change_trigger_input = TextInput(self, an_object.fields.choice, refresh_widget=self) self.add_child(Label(view, for_input=self.change_trigger_input)) self.add_child(self.change_trigger_input) self.add_child(P(self.view, text='My choice state is now %s' % an_object.choice)) self.change3_non_trigger_input = TextInput(self, an_object.fields.choice3) self.add_child(Label(view, for_input=self.change3_non_trigger_input)) self.add_child(self.change3_non_trigger_input) self.add_child(P(self.view, text='My calculated state is now %s' % an_object.calculated_state)) self.define_event_handler(an_object.events.submit) self.add_child(ButtonInput(self, an_object.events.submit)) class MainWidgetWithPersistentModelObject(Widget): def __init__(self, view): super(MainWidgetWithPersistentModelObject, self).__init__(view) an_object = fixture.model_object self.add_child(MyForm(view, an_object)) with sql_alchemy_fixture.persistent_test_classes(ModelObject): fixture.model_object = ModelObject() Session.add(fixture.model_object) wsgi_app = web_fixture.new_wsgi_app(enable_js=True, child_factory=MainWidgetWithPersistentModelObject.factory()) web_fixture.reahl_server.set_app(wsgi_app) browser = web_fixture.driver_browser browser.open('/') assert browser.wait_for(query_string_fixture.is_state_labelled_now, 'My choice state', 1) assert browser.wait_for(query_string_fixture.is_state_labelled_now, 'My calculated state', '10') browser.type(XPath.input_labelled('Choice3'), 'other invalid input') browser.type(XPath.input_labelled('Choice'), '2') browser.press_tab() assert browser.wait_for(query_string_fixture.is_state_labelled_now, 'My calculated state', '20') assert browser.is_element_value(XPath.input_labelled('Choice3'), 'other invalid input') browser.type(XPath.input_labelled('Choice3'), '8') browser.click(XPath.button_labelled('submit')) assert browser.is_element_present(XPath.paragraph().including_text('An exception happened on submit')) assert browser.is_element_value(XPath.input_labelled('Choice3'), '8')
def save(self): self.added_date = datetime.date.today() Session.add(self)
def new_user(self): user = User(name='John Doe', email_address='*****@*****.**') user.set_password(self.password) Session.add(user) return user
def new_party(self): party = Party() Session.add(party) Session.flush() return party
def new_queue(self, name=None): name = name or 'A queue' queue = Queue(name=name) Session.add(queue) return queue
def submit(self): Session.add(self)
def save(self): login_session = LoginSession.for_current_session() self.account = login_session.account Session.add(self)
def new_account(self): account = EmailAndPasswordSystemAccount(email='*****@*****.**') Session.add(account) account.set_new_password(account.email, self.password) account.activate() return account
def save(self): Session.add(self)
def new_address_book(self, owner=None): owner = owner or self.account address_book = AddressBook(owner=owner) Session.add(address_book) return address_book
def submit(self): self.attach_uploaded_files() Session.add(self)
def new_one_object(self): one = self.SomeObject(name='one') Session.add(one) return one