from io import StringIO from django.core.management import call_command from django.test import TestCase from misago.acl import ACL_CACHE from misago.cache.test import assert_invalidates_cache from misago.categories.management.commands import fixcategoriestree from misago.categories.models import Category def run_command(): """Run the management command""" command = fixcategoriestree.Command() out = StringIO() call_command(command, stdout=out) class FixCategoriesTreeTests(TestCase): """ The purpose is the verify that the management command fixes the lft/rght values of the thread category tree. """ def setUp(self): Category.objects.create(name='Test', slug='test', parent=Category.objects.root_category()) self.fetch_categories() def assertValidTree(self, expected_tree): root = Category.objects.root_category() queryset = Category.objects.filter(tree_id=root.tree_id).order_by('lft') current_tree = [] for category in queryset: current_tree.append((category, category.get_level(), category.lft, category.rght)) for i, category in enumerate(expected_tree): _category = current_tree[i] if category[0] != _category[0]: self.fail(('expected category at index #%s to be %s, ' 'found %s instead') % (i, category[0], _category[0])) if category[1] != _category[1]: self.fail(('expected level at index #%s to be %s, ' 'found %s instead') % (i, category[1], _category[1])) if category[2] != _category[2]: self.fail(('expected lft at index #%s to be %s, ' 'found %s instead') % (i, category[2], _category[2])) if category[3] != _category[3]: self.fail(('expected lft at index #%s to be %s, ' 'found %s instead') % (i, category[3], _category[3])) def fetch_categories(self): """gets a fresh version from the database""" self.root = Category.objects.root_category() self.first_category = Category.objects.get(slug='first-category') self.test_category = Category.objects.get(slug='test') def test_fix_categories_tree_unaffected(self): """Command should not affect a healthy three""" tree_id = self.root.tree_id run_command() self.fetch_categories() self.assertValidTree([ (self.root, 0, 1, 6), (self.first_category, 1, 2, 3), (self.test_category, 1, 4, 5), ]) self.assertEqual(self.root.tree_id, tree_id, msg="tree_id changed by command") def test_fix_categories_tree_affected(self): """Command should fix a broken tree""" # Root node with too narrow lft/rght range Category.objects.filter(id=self.root.id).update(lft=1, rght=4) # Make conflicting/identical lft/rght range Category.objects.filter(id=self.test_category.id).update(lft=2, rght=3) run_command() self.fetch_categories() self.assertValidTree([ (self.root, 0, 1, 6), (self.test_category, 1, 2, 3), (self.first_category, 1, 4, 5), ]) def test_fixing_categories_tree_invalidates_acl_cache(self): with assert_invalidates_cache(ACL_CACHE): run_command()