diff --git a/src/tests/base/test_registry.py b/src/tests/base/test_registry.py new file mode 100644 index 000000000..e8b1faf92 --- /dev/null +++ b/src/tests/base/test_registry.py @@ -0,0 +1,153 @@ +from unittest import mock + +import pytest + +from pretix.base.logentrytypes import LogEntryTypeRegistry, LogEntryType, ItemLogEntryType +from pretix.base.signals import Registry + + +def test_registry_classes(): + animal_type_registry = Registry({"type": lambda s: s.__name__, "classis": lambda s: s.classis}) + + @animal_type_registry.register + class Cat: + classis = 'mammalia' + def make_sound(self): + return "meow" + + @animal_type_registry.register + class Dog: + classis = 'mammalia' + def make_sound(self): + return "woof" + + @animal_type_registry.register + class Cricket: + classis = 'insecta' + def make_sound(self): + return "chirp" + + # test retrieving and instantiating a class based on metadata value + clz, meta = animal_type_registry.get(type="Cat") + assert clz().make_sound() == "meow" + assert meta.get('type') == "Cat" + + clz, meta = animal_type_registry.get(type="Dog") + assert clz().make_sound() == "woof" + assert meta.get('type') == "Dog" + + # check that None is returned when no class exists with the specified metadata value + clz, meta = animal_type_registry.get(type="Unicorn") + assert clz is None + assert meta is None + + # check that an error is raised when trying to retrieve by an undefined metadata key + with pytest.raises(Exception): + _, _ = animal_type_registry.get(whatever="hello") + + # test finding all entries with a given metadata value + mammals = animal_type_registry.filter(classis='mammalia') + assert set(cls for cls, meta in mammals) == {Cat, Dog} + assert all(meta['classis'] == 'mammalia' for cls, meta in mammals) + + insects = animal_type_registry.filter(classis='insecta') + assert set(cls for cls, meta in insects) == {Cricket} + + fantasy = animal_type_registry.filter(classis='fantasia') + assert set(cls for cls, meta in fantasy) == set() + + # check normal object instantiation still works with our decorator + assert Cat().make_sound() == "meow" + + +def test_registry_instances(): + animal_sound_registry = Registry({"animal": lambda s: s.animal}) + + @animal_sound_registry.new("dog", "woof") + @animal_sound_registry.new("cricket", "chirp") + class AnimalSound: + def __init__(self, animal, sound): + self.animal = animal + self.sound = sound + + def make_sound(self): + return self.sound + + @animal_sound_registry.new() + class CatSound(AnimalSound): + def __init__(self): + super().__init__(animal="cat", sound=["meow", "meww", "miaou"]) + self.i = 0 + + def make_sound(self): + self.i += 1 + return self.sound[self.i % len(self.sound)] + + # test registry + assert animal_sound_registry.get(animal='dog')[0].make_sound() == "woof" + assert animal_sound_registry.get(animal='dog')[0].make_sound() == "woof" + assert animal_sound_registry.get(animal='cricket')[0].make_sound() == "chirp" + assert animal_sound_registry.get(animal='cat')[0].make_sound() == "meww" + assert animal_sound_registry.get(animal='cat')[0].make_sound() == "miaou" + assert animal_sound_registry.get(animal='cat')[0].make_sound() == "meow" + + # check normal object instantiation still works with our decorator + assert AnimalSound("test", "test").make_sound() == "test" + + +def test_registry_prevent_duplicates(): + my_registry = Registry({"animal": lambda s: s.animal}) + + class AnimalSound: + def __init__(self, animal, sound): + self.animal = animal + self.sound = sound + + cat = AnimalSound("cat", "meow") + my_registry.register(cat) + + with pytest.raises(RuntimeError): + my_registry.register(cat) + + +def test_logentrytype_registry(): + reg = LogEntryTypeRegistry() + + with mock.patch('pretix.base.signals.get_defining_app') as mock_get_defining_app: + mock_get_defining_app.return_value = 'my_plugin' + + @reg.new("foo.mytype") + class MyType(LogEntryType): + pass + + @reg.new("foo.myothertype") + class MyOtherType(LogEntryType): + pass + + typ, meta = reg.get(action_type="foo.mytype") + assert isinstance(typ, MyType) + assert meta['action_type'] == "foo.mytype" + assert meta['plugin'] == 'my_plugin' + + typ, meta = reg.get(action_type="foo.myothertype") + assert isinstance(typ, MyOtherType) + assert meta['action_type'] == "foo.myothertype" + assert meta['plugin'] is None + + by_my_plugin = reg.filter(plugin='my_plugin') + assert set(type(typ) for typ, meta in by_my_plugin) == {MyType} + + +def test_logentrytype_registry_validation(): + reg = LogEntryTypeRegistry() + + with pytest.raises(TypeError, match='Must not register base classes, only derived ones'): + reg.register(LogEntryType("foo.mytype")) + + with pytest.raises(TypeError, match='Must not register base classes, only derived ones'): + reg.new_from_dict({"foo.mytype": "My Log Entry"})(ItemLogEntryType) + + with pytest.raises(TypeError, match='Entries must be derived from LogEntryType'): + @reg.new() + class MyType: + pass