from django.test import TestCase from rest_framework.exceptions import ValidationError from .. import test from ...categories.models import Category from ...users.test import create_test_user from ..mergeconflict import MergeConflict class MergeConflictTests(TestCase): def setUp(self): self.category = Category.objects.get(slug="first-category") self.user = create_test_user("User", "user@example.com") def create_plain_thread(self): return test.post_thread(self.category) def create_poll_thread(self): thread = test.post_thread(self.category) test.post_poll(thread, self.user) return thread def create_best_answer_thread(self): thread = test.post_thread(self.category) best_answer = test.reply_thread(thread) thread.set_best_answer(self.user, best_answer) thread.synchronize() thread.save() return thread def test_plain_threads_no_conflicts(self): """threads without items of interest don't conflict""" threads = [self.create_plain_thread() for i in range(10)] merge_conflict = MergeConflict(threads=threads) self.assertFalse(merge_conflict.is_merge_conflict()) self.assertEqual(merge_conflict.get_conflicting_fields(), []) merge_conflict.is_valid(raise_exception=True) self.assertEqual(merge_conflict.get_resolution(), {}) def test_one_best_answer_one_plain(self): """thread with best answer and plain thread don't conflict""" threads = [self.create_best_answer_thread(), self.create_plain_thread()] merge_conflict = MergeConflict(threads=threads) self.assertFalse(merge_conflict.is_merge_conflict()) self.assertEqual(merge_conflict.get_conflicting_fields(), []) merge_conflict.is_valid(raise_exception=True) self.assertEqual(merge_conflict.get_resolution(), {"best_answer": threads[0]}) def test_one_poll_one_plain(self): """thread with poll and plain thread don't conflict""" threads = [self.create_poll_thread(), self.create_plain_thread()] merge_conflict = MergeConflict(threads=threads) self.assertFalse(merge_conflict.is_merge_conflict()) self.assertEqual(merge_conflict.get_conflicting_fields(), []) merge_conflict.is_valid(raise_exception=True) self.assertEqual(merge_conflict.get_resolution(), {"poll": threads[0].poll}) def test_one_best_answer_one_poll(self): """thread with best answer and thread with poll don't conflict""" threads = [self.create_poll_thread(), self.create_best_answer_thread()] merge_conflict = MergeConflict(threads=threads) self.assertFalse(merge_conflict.is_merge_conflict()) def test_one_best_answer_one_poll_one_plain(self): """thread with best answer, thread with poll and plain thread don't conflict""" threads = [ self.create_plain_thread(), self.create_poll_thread(), self.create_best_answer_thread(), ] merge_conflict = MergeConflict(threads=threads) self.assertFalse(merge_conflict.is_merge_conflict()) def test_three_best_answers_one_poll_two_plain_conflict(self): """three threads with best answer, thread with poll and two plain threads conflict""" best_answers = [self.create_best_answer_thread() for i in range(3)] polls = [self.create_poll_thread()] threads = ( [self.create_plain_thread(), self.create_plain_thread()] + best_answers + polls ) merge_conflict = MergeConflict(threads=threads) self.assertTrue(merge_conflict.is_merge_conflict()) self.assertEqual(merge_conflict.get_conflicting_fields(), ["best_answer"]) # without choice, conflict lists resolutions try: merge_conflict.is_valid(raise_exception=True) self.fail("merge_conflict.is_valid() should raise ValidationError") except ValidationError as e: self.assertTrue(merge_conflict.is_merge_conflict()) self.assertEqual(merge_conflict.get_conflicting_fields(), ["best_answer"]) self.assertEqual( e.detail, { "best_answers": [["0", "Unmark all best answers"]] + [[str(thread.id), thread.title] for thread in best_answers] }, ) # conflict validates choice try: merge_conflict = MergeConflict({"best_answer": threads[0].id}, threads) merge_conflict.is_valid(raise_exception=True) self.fail("merge_conflict.is_valid() should raise ValidationError") except ValidationError as e: self.assertTrue(merge_conflict.is_merge_conflict()) self.assertEqual(e.detail, {"best_answer": ["Invalid choice."]}) # conflict returns selected resolution merge_conflict = MergeConflict({"best_answer": best_answers[0].id}, threads) self.assertTrue(merge_conflict.is_merge_conflict()) self.assertEqual(merge_conflict.get_conflicting_fields(), ["best_answer"]) merge_conflict.is_valid(raise_exception=True) self.assertEqual( merge_conflict.get_resolution(), {"best_answer": best_answers[0], "poll": polls[0].poll}, ) # conflict returns no-choice resolution merge_conflict = MergeConflict({"best_answer": 0}, threads) self.assertTrue(merge_conflict.is_merge_conflict()) self.assertEqual(merge_conflict.get_conflicting_fields(), ["best_answer"]) merge_conflict.is_valid(raise_exception=True) self.assertEqual( merge_conflict.get_resolution(), {"best_answer": None, "poll": polls[0].poll}, ) def test_one_best_answer_three_polls_two_plain_conflict(self): """one thread with best answer, three threads with poll and two plain threads conflict""" best_answers = [self.create_best_answer_thread()] polls = [self.create_poll_thread() for i in range(3)] threads = ( [self.create_plain_thread(), self.create_plain_thread()] + best_answers + polls ) merge_conflict = MergeConflict(threads=threads) self.assertTrue(merge_conflict.is_merge_conflict()) self.assertEqual(merge_conflict.get_conflicting_fields(), ["poll"]) # without choice, conflict lists resolutions try: merge_conflict.is_valid(raise_exception=True) self.fail("merge_conflict.is_valid() should raise ValidationError") except ValidationError as e: self.assertTrue(merge_conflict.is_merge_conflict()) self.assertEqual(merge_conflict.get_conflicting_fields(), ["poll"]) self.assertEqual( e.detail, { "polls": [["0", "Delete all polls"]] + [ [ str(thread.poll.id), "%s (%s)" % (thread.poll.question, thread.title), ] for thread in polls ] }, ) # conflict validates choice try: merge_conflict = MergeConflict({"poll": threads[0].id}, threads) merge_conflict.is_valid(raise_exception=True) self.fail("merge_conflict.is_valid() should raise ValidationError") except ValidationError as e: self.assertTrue(merge_conflict.is_merge_conflict()) self.assertEqual(e.detail, {"poll": ["Invalid choice."]}) # conflict returns selected resolution merge_conflict = MergeConflict({"poll": polls[0].poll.id}, threads) self.assertTrue(merge_conflict.is_merge_conflict()) self.assertEqual(merge_conflict.get_conflicting_fields(), ["poll"]) merge_conflict.is_valid(raise_exception=True) self.assertEqual( merge_conflict.get_resolution(), {"best_answer": best_answers[0], "poll": polls[0].poll}, ) # conflict returns no-choice resolution merge_conflict = MergeConflict({"poll": 0}, threads) self.assertTrue(merge_conflict.is_merge_conflict()) self.assertEqual(merge_conflict.get_conflicting_fields(), ["poll"]) merge_conflict.is_valid(raise_exception=True) self.assertEqual( merge_conflict.get_resolution(), {"best_answer": best_answers[0], "poll": None}, ) def test_three_best_answers_three_polls_two_plain_conflict(self): """multiple conflict is handled""" best_answers = [self.create_best_answer_thread() for i in range(3)] polls = [self.create_poll_thread() for i in range(3)] threads = ( [self.create_plain_thread(), self.create_plain_thread()] + best_answers + polls ) merge_conflict = MergeConflict(threads=threads) self.assertTrue(merge_conflict.is_merge_conflict()) self.assertEqual( merge_conflict.get_conflicting_fields(), ["best_answer", "poll"] ) # without choice, conflict lists all resolutions try: merge_conflict.is_valid(raise_exception=True) self.fail("merge_conflict.is_valid() should raise ValidationError") except ValidationError as e: self.assertTrue(merge_conflict.is_merge_conflict()) self.assertEqual( merge_conflict.get_conflicting_fields(), ["best_answer", "poll"] ) self.assertEqual( e.detail, { "best_answers": [["0", "Unmark all best answers"]] + [[str(thread.id), thread.title] for thread in best_answers], "polls": [["0", "Delete all polls"]] + [ [ str(thread.poll.id), "%s (%s)" % (thread.poll.question, thread.title), ] for thread in polls ], }, ) # conflict validates all choices if single choice was given try: merge_conflict = MergeConflict({"best_answer": threads[0].id}, threads) merge_conflict.is_valid(raise_exception=True) self.fail("merge_conflict.is_valid() should raise ValidationError") except ValidationError as e: self.assertTrue(merge_conflict.is_merge_conflict()) self.assertEqual( e.detail, {"best_answer": ["Invalid choice."], "poll": ["Invalid choice."]}, ) try: merge_conflict = MergeConflict({"poll": threads[0].id}, threads) merge_conflict.is_valid(raise_exception=True) self.fail("merge_conflict.is_valid() should raise ValidationError") except ValidationError as e: self.assertTrue(merge_conflict.is_merge_conflict()) self.assertEqual( e.detail, {"best_answer": ["Invalid choice."], "poll": ["Invalid choice."]}, ) # conflict validates all choices if all choices were given try: merge_conflict = MergeConflict( {"best_answer": threads[0].id, "poll": threads[0].id}, threads ) merge_conflict.is_valid(raise_exception=True) self.fail("merge_conflict.is_valid() should raise ValidationError") except ValidationError as e: self.assertTrue(merge_conflict.is_merge_conflict()) self.assertEqual( e.detail, {"best_answer": ["Invalid choice."], "poll": ["Invalid choice."]}, ) # conflict returns selected resolutions valid_choices = {"best_answer": best_answers[0].id, "poll": polls[0].poll.id} merge_conflict = MergeConflict(valid_choices, threads) self.assertTrue(merge_conflict.is_merge_conflict()) self.assertEqual( merge_conflict.get_conflicting_fields(), ["best_answer", "poll"] ) merge_conflict.is_valid(raise_exception=True) self.assertEqual( merge_conflict.get_resolution(), {"best_answer": best_answers[0], "poll": polls[0].poll}, ) # conflict returns no-choice resolution merge_conflict = MergeConflict({"best_answer": 0, "poll": 0}, threads) self.assertTrue(merge_conflict.is_merge_conflict()) self.assertEqual( merge_conflict.get_conflicting_fields(), ["best_answer", "poll"] ) merge_conflict.is_valid(raise_exception=True) self.assertEqual( merge_conflict.get_resolution(), {"best_answer": None, "poll": None} ) # conflict allows mixing no-choice with choice merge_conflict = MergeConflict( {"best_answer": best_answers[0].id, "poll": 0}, threads ) self.assertTrue(merge_conflict.is_merge_conflict()) self.assertEqual( merge_conflict.get_conflicting_fields(), ["best_answer", "poll"] ) merge_conflict.is_valid(raise_exception=True) self.assertEqual( merge_conflict.get_resolution(), {"best_answer": best_answers[0], "poll": None}, ) merge_conflict = MergeConflict( {"best_answer": 0, "poll": polls[0].poll.id}, threads ) self.assertTrue(merge_conflict.is_merge_conflict()) self.assertEqual( merge_conflict.get_conflicting_fields(), ["best_answer", "poll"] ) merge_conflict.is_valid(raise_exception=True) self.assertEqual( merge_conflict.get_resolution(), {"best_answer": None, "poll": polls[0].poll}, )