def test_empty_stub(): """an EmptyStub can be created with instance attributes""" #normal case value = EmptyStub() stub = EmptyStub(a_value = value) assert stub.a_value is value
def test_invalid_schedule_name_raises(): """A useful error is raised when an attempt is made to schedule a call in a phase that is not defined.""" migration_schedule = MigrationSchedule(EmptyStub(), EmptyStub(), []) with expected(ProgrammerError, test=r'A phase with name<wrong_name> does not exist\.'): migration_schedule.schedule('wrong_name', EmptyStub(), EmptyStub(), EmptyStub())
def test_file_marshalling(fixture): """A FileField receives as input a list of UploadedFile objects. Its marshalling job consists of merely changing such a list into a single value, or returning it as is depending on the setting of its allow_multiple flag. """ field = FileField() obj = fixture.model_object field.bind('file_value', obj) files = [EmptyStub(), EmptyStub()] # Single file only field.from_input(files[:1]) assert obj.file_value == files[0] obj.file_value = files[0] assert field.as_input() == '' # Multiple files field = FileField(allow_multiple=True) field.bind('file_value', obj) field.from_input(files) assert obj.file_value == files obj.file_value = files assert field.as_input() == ''
def view_rights_propagate_to_a(fixture): """The access rights specified for a View are propagated to an A, made from a Bookmark to that View.""" fixture.view.write_check = EmptyStub() fixture.view.read_check = EmptyStub() a = A.from_bookmark(fixture.view, fixture.view.as_bookmark()) vassert( a.read_check is fixture.view.read_check ) vassert( a.write_check is fixture.view.write_check )
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_global_state(): """A Field can store its data in a global dict so that it can be recreated later with the same underlying data.""" ExecutionContext().install() state_dict = {} a = Field() a.bind('x', a) a.input_status = EmptyStub() a.validation_error = EmptyStub() a.user_input = EmptyStub() a.parsed_input = EmptyStub() a.activate_global_field_data_store(state_dict) a.initial_value = EmptyStub() b = Field() b.bind('x', b) assert a.initial_value is not b.initial_value assert a.input_status is not b.input_status assert a.validation_error is not b.validation_error assert a.user_input is not b.user_input assert a.parsed_input is not b.parsed_input b.activate_global_field_data_store(state_dict) assert a.initial_value is b.initial_value assert a.input_status is b.input_status assert a.validation_error is b.validation_error assert a.user_input is b.user_input assert a.parsed_input is b.parsed_input
def test_namespaces(): ExecutionContext().install() state_dict = {} a = Field() a.bind('x', a) # Case: namespaces change the name of the Field b = a.in_namespace('deeper') assert a.name == 'x' assert b.name == 'deeper-x' # Case: namespaces can be nested c = b.in_namespace('even') assert c.name == 'even-deeper-x' # Case: a Field *in* different namespace, but made from another share the same data a.initial_value = EmptyStub() a.input_status = EmptyStub() a.validation_error = EmptyStub() a.user_input = EmptyStub() a.parsed_input = EmptyStub() assert a.initial_value is b.initial_value is c.initial_value assert a.input_status is b.input_status is c.input_status assert a.validation_error is b.validation_error is c.validation_error assert a.user_input is b.user_input is c.user_input assert a.parsed_input is b.parsed_input is c.parsed_input
class ColumnScenarios(Fixture): sort_key = EmptyStub() heading = 'A heading' row_item = EmptyStub(some_attribute=True) @scenario def static_column(self): """StaticColumn represents an attribute of the item in a row, using a Field to translate the value of that attribute into a string and to specify a column header.""" self.column = StaticColumn(BooleanField(label=self.heading), 'some_attribute', sort_key=self.sort_key) self.expected_cell_html = 'on' # as translated by BooleanField self.expected_heading_html = '<span>A heading</span>' @scenario def dynamic_column(self): """DynamicColumn uses a function to create a Widget on the fly for each cell and uses the specified heading.""" def make_span(view, data_item): return Span(view, text='Answer: %s' % (data_item.some_attribute)) self.column = DynamicColumn(self.heading, make_span, sort_key=self.sort_key) self.expected_cell_html = '<span>Answer: True</span>' # raw attribute used self.expected_heading_html = '<span>A heading</span>' @scenario def dynamic_column_static_heading(self): """A DynamicColumn can also use a function to create its heading on the fly.""" def make_span(view, data_item): return Span(view, text='Answer: %s' % (data_item.some_attribute)) def make_heading(view): return P(view, text=self.heading) self.column = DynamicColumn(make_heading, make_span, sort_key=self.sort_key) self.expected_cell_html = '<span>Answer: True</span>' # raw attribute used self.expected_heading_html = '<p>A heading</p>'
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 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_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 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 re_binding_behaviour_of_field_index(self, fixture): """FieldIndexes wont bind a field if it already is bound.""" model_object1 = EmptyStub() model_object2 = EmptyStub() bound_field = Field() bound_field.bind('bound_field', model_object2) vassert( bound_field.is_bound ) vassert( bound_field.bound_to is model_object2 ) index = FieldIndex(model_object1) index.new_name_for_bound_field = bound_field vassert( index.new_name_for_bound_field.name is 'bound_field' ) vassert( bound_field.bound_to is model_object2 )
def test_re_binding_behaviour_of_field_index(fixture): """FieldIndexes wont bind a field if it already is bound.""" model_object1 = EmptyStub() model_object2 = EmptyStub() bound_field = Field() bound_field.bind('bound_field', model_object2) assert bound_field.is_bound assert bound_field.bound_to is model_object2 index = FieldIndex(model_object1) index.new_name_for_bound_field = bound_field assert index.new_name_for_bound_field.name is 'bound_field' assert bound_field.bound_to is model_object2
def test_contents(): """A Session, Config or SystemControl may be set on the ExecutionContext.""" some_context = ExecutionContext() session = EmptyStub() config = EmptyStub() system_control = EmptyStub() some_context.session = session some_context.config = config some_context.system_control = system_control assert some_context.session is session assert some_context.config is config assert some_context.system_control is system_control
def contents(self, fixture): """A Session, Config or SystemControl may be set on the ExecutionContext.""" some_context = ExecutionContext() session = EmptyStub() config = EmptyStub() system_control = EmptyStub() some_context.set_session(session) some_context.set_config(config) some_context.set_system_control(system_control) vassert(some_context.session is session) vassert(some_context.config is config) vassert(some_context.system_control is system_control)
def interface_with_meta_info(self, fixture): """A Reahl component can publish a ReahlEgg instance to supply extra meta information about itself. Such interfaces with extra information are also often used from a flattened list in dependency order.""" easter_egg.clear() easter_egg.add_dependency('reahl-component') # The interface for a component is published via the reahl.eggs entry point line = 'Egg = reahl.component.eggs:ReahlEgg' easter_egg.add_entry_point_from_line('reahl.eggs', line) # Interfaces can be queried in dependency order too interfaces_in_order = ReahlEgg.compute_all_relevant_interfaces( easter_egg.as_requirement_string()) vassert(len(interfaces_in_order) == 2) # That of reahl-component itself, and of the easteregg [interface ] = [i for i in interfaces_in_order if i.distribution is easter_egg] # The meta-info that can be obtained via such an interface vassert(interface.configuration_spec is None) orm_control = EmptyStub() vassert(interface.get_persisted_classes_in_order(orm_control) == []) vassert(interface.migrations_in_order == []) vassert(interface.get_roles_to_add() == []) # Hooks for allowing a component to do its own housekeeping with expected(NoException): interface.do_daily_maintenance()
def integer_marshalling(self, fixture): field = IntegerField() obj = EmptyStub() field.bind('integer_value', obj) # From input field.from_input('5') vassert( obj.integer_value is 5 ) # As input obj.integer_value = 6 vassert( field.as_input() == '6' ) # As input - corner case obj.integer_value = 0 vassert( field.as_input() == '0' )
def test_password_access(fixture): """A PasswordField is world writable, but not readable by anyone.""" field = PasswordField() field.bind('password_field', EmptyStub()) assert not field.can_read() assert field.can_write()
def basic_marshalling(self, fixture): field = Field() obj = EmptyStub() field.bind('value', obj) # From input field.from_input('abc') vassert( obj.value == 'abc' ) # As input obj.value = 'def' vassert( field.as_input() == 'def' ) # As input - corner case obj.value = '' vassert( field.as_input() == '' )
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_error_reporting_on_breaking_migrations(): """When there is an error during execution of a Migration, the code where it was scheduled is reported.""" class SomeObject: def please_call_me(self): raise Exception('breaking intentionally') some_object = SomeObject() migration_schedule = MigrationSchedule(EmptyStub(), EmptyStub(), []) migration = Migration(migration_schedule) migration.schedule('alter', some_object.please_call_me) def is_please_call_me_on_stack(ex): return re.match(".*The above Exception happened for the migration that was scheduled here:.* migration.schedule\('alter', some_object.please_call_me\)", str(ex), re.MULTILINE|re.S) with expected(ExceptionDuringMigration, test=is_please_call_me_on_stack): migration_schedule.execute_all()
def test_basic_marshalling(fixture): field = Field() obj = EmptyStub() field.bind('value', obj) # From input field.from_input('abc') assert obj.value == 'abc' # As input obj.value = 'def' assert field.as_input() == 'def' # As input - corner case obj.value = '' assert field.as_input() == ''
def test_integer_marshalling(fixture): field = IntegerField() obj = EmptyStub() field.bind('integer_value', obj) # From input field.from_input('5') assert obj.integer_value is 5 # As input obj.integer_value = 6 assert field.as_input() == '6' # As input - corner case obj.integer_value = 0 assert field.as_input() == '0'
def test_getting_modified_copy(fixture): """It is possible to get a modified copy of an existing field if you want to link it with different constraints on a different input""" other_constraint = ValidationConstraint('Other error') other_constraint.name = 'other' field = Field() field.add_validation_constraint(other_constraint) model_object = EmptyStub() field.bind('field_name', model_object) # Getting a copy new_field = field.copy() assert new_field is not field assert new_field.name == field.name assert new_field.storage_object == field.storage_object assert new_field.default == field.default assert new_field.label == field.label copied_other_constraint = new_field.get_validation_constraint_named(other_constraint.name) assert copied_other_constraint.field is new_field new_validation_constraints = [i.__class__ for i in new_field.validation_constraints] old_validation_constraints = [i.__class__ for i in field.validation_constraints] assert new_validation_constraints == old_validation_constraints assert new_field.validation_constraints != field.validation_constraints assert new_field.validation_constraints is not field.validation_constraints assert new_field.access_rights is not field.access_rights assert new_field.access_rights.readable is field.access_rights.readable assert new_field.access_rights.writable is field.access_rights.writable # Getting a required copy assert not field.required required_field = field.as_required(required_message='new required message') assert required_field.required required = required_field.get_validation_constraint_named(RequiredConstraint.name) assert required.error_message.template == 'new required message' # Getting copy that's not required field.make_required('') assert field.required optional_field = field.as_optional() assert not optional_field.required # Getting copy with a ValidationConstraint of certain type removed assert field.required more_lax_field = field.without_validation_constraint(RequiredConstraint) assert not more_lax_field.required # Getting copy with a new ValidationConstraint added field.make_optional() assert not field.required more_strict_field = field.with_validation_constraint(RequiredConstraint()) assert more_strict_field.required # Getting copy with a new label assert field.label != 'new label' differently_labelled_field = field.with_label('new label') assert differently_labelled_field.label == 'new label'
def test_translator_singleton_thread_safety(): """The SystemWideCatalogue.get_instance() is this thread-safe.""" SystemWideCatalogue.instance = None # To "reset" the singleton, else its __init__ will NEVER be called in this test SystemWideCatalogue.get_instance().map_lock.acquire() saved_state = EmptyStub() saved_state.lock_released = False def release_lock(): SystemWideCatalogue.get_instance().map_lock.release() saved_state.lock_released = True timer = Timer(.1, release_lock) timer.start() context = LocaleContextStub().install() _ = Catalogue('reahl-component') _.gettext('test string') timer.cancel() assert saved_state.lock_released
def read_basic_object(self, fixture): fixture.reader.register(fixture.tag_name, fixture.test_class) parent = EmptyStub() read_object = fixture.reader.read_file(fixture.file, parent) vassert(isinstance(read_object, fixture.test_class)) vassert(read_object.attr1 == fixture.string) vassert(read_object.attr2 == fixture.integer) vassert(read_object.parent is parent)
def tailored_access_make_inputs_security_sensitive(self, fixture): """An Input is sensitive if explicitly set as sensitive, or if its Fields has non-defaulted mechanisms for determiing access rights.""" form = Form(fixture.view, 'some_form') field = Field(default=3, readable=Allowed(True)) field.bind('field_name', EmptyStub()) input_widget = TextInput(form, field) vassert(input_widget.is_security_sensitive)
def test_read_basic_object(basic_object_setup): fixture = basic_object_setup fixture.reader.register(fixture.tag_name, fixture.test_class) parent = EmptyStub() read_object = fixture.reader.read_file(fixture.file, parent) assert isinstance(read_object, fixture.test_class) assert read_object.attr1 == fixture.string assert read_object.attr2 == fixture.integer assert read_object.parent is parent
def test_schedule_executes_phases_with_parameters(): """When a MigrationSchedule executes the calls that were scheduled from a Migration, the methods are actually called, and passed the correct arguments.""" class SomeObject: def please_call_me(self, arg, kwarg=None): pass some_object = SomeObject() migration_schedule = MigrationSchedule(EmptyStub(), EmptyStub(), []) migration = Migration(migration_schedule) with CallMonitor(some_object.please_call_me) as monitor: migration.schedule('alter', some_object.please_call_me, 'myarg', kwarg='mykwarg') migration_schedule.execute_all() assert monitor.calls[0].args == ('myarg',) assert monitor.calls[0].kwargs == dict(kwarg='mykwarg')