authentik.lib.tests.test_utils_inheritance
Tests for inheritance helpers.
1"""Tests for inheritance helpers.""" 2 3from contextlib import contextmanager 4 5from django.db import connection, models 6from django.test import TransactionTestCase 7from django.test.utils import isolate_apps 8 9from authentik.lib.utils.inheritance import get_deepest_child 10 11 12@contextmanager 13def temporary_inheritance_models(): 14 """Create a temporary multi-table inheritance graph for testing.""" 15 with isolate_apps("authentik.lib.tests"): 16 17 class GrandParent(models.Model): 18 class Meta: 19 app_label = "tests" 20 21 def __str__(self) -> str: 22 return f"GrandParent({self.pk})" 23 24 class Parent(GrandParent): 25 class Meta: 26 app_label = "tests" 27 28 def __str__(self) -> str: 29 return f"Parent({self.pk})" 30 31 class Child(Parent): 32 class Meta: 33 app_label = "tests" 34 35 def __str__(self) -> str: 36 return f"Child({self.pk})" 37 38 class GrandChild(Child): 39 class Meta: 40 app_label = "tests" 41 42 def __str__(self) -> str: 43 return f"GrandChild({self.pk})" 44 45 with connection.schema_editor() as schema_editor: 46 schema_editor.create_model(GrandParent) 47 schema_editor.create_model(Parent) 48 schema_editor.create_model(Child) 49 schema_editor.create_model(GrandChild) 50 51 try: 52 yield GrandParent, Parent, Child, GrandChild 53 finally: 54 with connection.schema_editor() as schema_editor: 55 schema_editor.delete_model(GrandChild) 56 schema_editor.delete_model(Child) 57 schema_editor.delete_model(Parent) 58 schema_editor.delete_model(GrandParent) 59 60 61class TestInheritanceUtils(TransactionTestCase): 62 """Tests for helper functions in authentik.lib.utils.inheritance.""" 63 64 def test_get_deepest_child_grandparent_to_parent(self): 65 """GrandParent -> Parent.""" 66 with temporary_inheritance_models() as (GrandParent, Parent, _Child, _GrandChild): 67 parent = Parent.objects.create() 68 grandparent = GrandParent.objects.get(pk=parent.pk) 69 70 resolved = get_deepest_child(grandparent) 71 72 self.assertIsInstance(resolved, Parent) 73 self.assertEqual(resolved.pk, parent.pk) 74 75 def test_get_deepest_child_grandparent_to_child(self): 76 """GrandParent -> Child.""" 77 with temporary_inheritance_models() as (GrandParent, _Parent, Child, _GrandChild): 78 child = Child.objects.create() 79 grandparent = GrandParent.objects.get(pk=child.pk) 80 81 resolved = get_deepest_child(grandparent) 82 83 self.assertIsInstance(resolved, Child) 84 self.assertEqual(resolved.pk, child.pk) 85 86 def test_get_deepest_child_grandparent_to_grandchild(self): 87 """GrandParent -> GrandChild.""" 88 with temporary_inheritance_models() as (GrandParent, _Parent, _Child, GrandChild): 89 grandchild = GrandChild.objects.create() 90 grandparent = GrandParent.objects.get(pk=grandchild.pk) 91 92 resolved = get_deepest_child(grandparent) 93 94 self.assertIsInstance(resolved, GrandChild) 95 self.assertEqual(resolved.pk, grandchild.pk) 96 97 def test_get_deepest_child_parent_to_child(self): 98 """Parent -> Child (start from non-root).""" 99 with temporary_inheritance_models() as (_GrandParent, Parent, Child, _GrandChild): 100 child = Child.objects.create() 101 parent = Parent.objects.get(pk=child.pk) 102 103 resolved = get_deepest_child(parent) 104 105 self.assertIsInstance(resolved, Child) 106 self.assertEqual(resolved.pk, child.pk) 107 108 def test_get_deepest_child_no_queries_with_preloaded_relations(self): 109 """No extra queries when the inheritance chain is fully select_related.""" 110 with temporary_inheritance_models() as (GrandParent, _Parent, _Child, GrandChild): 111 grandchild = GrandChild.objects.create() 112 grandparent = GrandParent.objects.select_related("parent__child__grandchild").get( 113 pk=grandchild.pk 114 ) 115 116 with self.assertNumQueries(0): 117 resolved = get_deepest_child(grandparent) 118 119 self.assertIsInstance(resolved, GrandChild)
@contextmanager
def
temporary_inheritance_models():
13@contextmanager 14def temporary_inheritance_models(): 15 """Create a temporary multi-table inheritance graph for testing.""" 16 with isolate_apps("authentik.lib.tests"): 17 18 class GrandParent(models.Model): 19 class Meta: 20 app_label = "tests" 21 22 def __str__(self) -> str: 23 return f"GrandParent({self.pk})" 24 25 class Parent(GrandParent): 26 class Meta: 27 app_label = "tests" 28 29 def __str__(self) -> str: 30 return f"Parent({self.pk})" 31 32 class Child(Parent): 33 class Meta: 34 app_label = "tests" 35 36 def __str__(self) -> str: 37 return f"Child({self.pk})" 38 39 class GrandChild(Child): 40 class Meta: 41 app_label = "tests" 42 43 def __str__(self) -> str: 44 return f"GrandChild({self.pk})" 45 46 with connection.schema_editor() as schema_editor: 47 schema_editor.create_model(GrandParent) 48 schema_editor.create_model(Parent) 49 schema_editor.create_model(Child) 50 schema_editor.create_model(GrandChild) 51 52 try: 53 yield GrandParent, Parent, Child, GrandChild 54 finally: 55 with connection.schema_editor() as schema_editor: 56 schema_editor.delete_model(GrandChild) 57 schema_editor.delete_model(Child) 58 schema_editor.delete_model(Parent) 59 schema_editor.delete_model(GrandParent)
Create a temporary multi-table inheritance graph for testing.
class
TestInheritanceUtils(django.test.testcases.TransactionTestCase):
62class TestInheritanceUtils(TransactionTestCase): 63 """Tests for helper functions in authentik.lib.utils.inheritance.""" 64 65 def test_get_deepest_child_grandparent_to_parent(self): 66 """GrandParent -> Parent.""" 67 with temporary_inheritance_models() as (GrandParent, Parent, _Child, _GrandChild): 68 parent = Parent.objects.create() 69 grandparent = GrandParent.objects.get(pk=parent.pk) 70 71 resolved = get_deepest_child(grandparent) 72 73 self.assertIsInstance(resolved, Parent) 74 self.assertEqual(resolved.pk, parent.pk) 75 76 def test_get_deepest_child_grandparent_to_child(self): 77 """GrandParent -> Child.""" 78 with temporary_inheritance_models() as (GrandParent, _Parent, Child, _GrandChild): 79 child = Child.objects.create() 80 grandparent = GrandParent.objects.get(pk=child.pk) 81 82 resolved = get_deepest_child(grandparent) 83 84 self.assertIsInstance(resolved, Child) 85 self.assertEqual(resolved.pk, child.pk) 86 87 def test_get_deepest_child_grandparent_to_grandchild(self): 88 """GrandParent -> GrandChild.""" 89 with temporary_inheritance_models() as (GrandParent, _Parent, _Child, GrandChild): 90 grandchild = GrandChild.objects.create() 91 grandparent = GrandParent.objects.get(pk=grandchild.pk) 92 93 resolved = get_deepest_child(grandparent) 94 95 self.assertIsInstance(resolved, GrandChild) 96 self.assertEqual(resolved.pk, grandchild.pk) 97 98 def test_get_deepest_child_parent_to_child(self): 99 """Parent -> Child (start from non-root).""" 100 with temporary_inheritance_models() as (_GrandParent, Parent, Child, _GrandChild): 101 child = Child.objects.create() 102 parent = Parent.objects.get(pk=child.pk) 103 104 resolved = get_deepest_child(parent) 105 106 self.assertIsInstance(resolved, Child) 107 self.assertEqual(resolved.pk, child.pk) 108 109 def test_get_deepest_child_no_queries_with_preloaded_relations(self): 110 """No extra queries when the inheritance chain is fully select_related.""" 111 with temporary_inheritance_models() as (GrandParent, _Parent, _Child, GrandChild): 112 grandchild = GrandChild.objects.create() 113 grandparent = GrandParent.objects.select_related("parent__child__grandchild").get( 114 pk=grandchild.pk 115 ) 116 117 with self.assertNumQueries(0): 118 resolved = get_deepest_child(grandparent) 119 120 self.assertIsInstance(resolved, GrandChild)
Tests for helper functions in authentik.lib.utils.inheritance.
def
test_get_deepest_child_grandparent_to_parent(self):
65 def test_get_deepest_child_grandparent_to_parent(self): 66 """GrandParent -> Parent.""" 67 with temporary_inheritance_models() as (GrandParent, Parent, _Child, _GrandChild): 68 parent = Parent.objects.create() 69 grandparent = GrandParent.objects.get(pk=parent.pk) 70 71 resolved = get_deepest_child(grandparent) 72 73 self.assertIsInstance(resolved, Parent) 74 self.assertEqual(resolved.pk, parent.pk)
GrandParent -> Parent.
def
test_get_deepest_child_grandparent_to_child(self):
76 def test_get_deepest_child_grandparent_to_child(self): 77 """GrandParent -> Child.""" 78 with temporary_inheritance_models() as (GrandParent, _Parent, Child, _GrandChild): 79 child = Child.objects.create() 80 grandparent = GrandParent.objects.get(pk=child.pk) 81 82 resolved = get_deepest_child(grandparent) 83 84 self.assertIsInstance(resolved, Child) 85 self.assertEqual(resolved.pk, child.pk)
GrandParent -> Child.
def
test_get_deepest_child_grandparent_to_grandchild(self):
87 def test_get_deepest_child_grandparent_to_grandchild(self): 88 """GrandParent -> GrandChild.""" 89 with temporary_inheritance_models() as (GrandParent, _Parent, _Child, GrandChild): 90 grandchild = GrandChild.objects.create() 91 grandparent = GrandParent.objects.get(pk=grandchild.pk) 92 93 resolved = get_deepest_child(grandparent) 94 95 self.assertIsInstance(resolved, GrandChild) 96 self.assertEqual(resolved.pk, grandchild.pk)
GrandParent -> GrandChild.
def
test_get_deepest_child_parent_to_child(self):
98 def test_get_deepest_child_parent_to_child(self): 99 """Parent -> Child (start from non-root).""" 100 with temporary_inheritance_models() as (_GrandParent, Parent, Child, _GrandChild): 101 child = Child.objects.create() 102 parent = Parent.objects.get(pk=child.pk) 103 104 resolved = get_deepest_child(parent) 105 106 self.assertIsInstance(resolved, Child) 107 self.assertEqual(resolved.pk, child.pk)
Parent -> Child (start from non-root).
def
test_get_deepest_child_no_queries_with_preloaded_relations(self):
109 def test_get_deepest_child_no_queries_with_preloaded_relations(self): 110 """No extra queries when the inheritance chain is fully select_related.""" 111 with temporary_inheritance_models() as (GrandParent, _Parent, _Child, GrandChild): 112 grandchild = GrandChild.objects.create() 113 grandparent = GrandParent.objects.select_related("parent__child__grandchild").get( 114 pk=grandchild.pk 115 ) 116 117 with self.assertNumQueries(0): 118 resolved = get_deepest_child(grandparent) 119 120 self.assertIsInstance(resolved, GrandChild)
No extra queries when the inheritance chain is fully select_related.