"""test_models.py: runs tests on the models for digit."""
import pytest
from core.models import (Grade,
                         Subject,
                         Question,
                         Comment,
                         Option,
                         Topic,
                         Block,
                         Syllabus,
                         StateException,
                         )
from django.test import TestCase
from django.contrib.auth.models import User


class TestQuestion(TestCase):
    """Test the Question Model."""

    def setUp(self):
        """Create questions for testing."""
        grade_test = Grade(name="Grade Example")
        grade_test.save()
        subject_test = Subject(name="addition",
                               grade=grade_test)
        subject_test.save()
        question1 = Question(question_content='what is 1 + 1?',
                             answer_content='This is an addition question',
                             subject=subject_test)
        question1.save()

    def test_question_default_state(self):
        """Confirm that default state is Incomplete."""
        question1 = Question.objects.all()[0]
        assert(question1.state == question1.INCOMPLETE)

    def test_question_state_from_incomplete(self):
        """Check that question state.

        Confirm that state can only go from 'incomplete' to
        'ready for review'.
        """
        question1 = Question.objects.all()[0]

        with pytest.raises(StateException) as exception_info:
            question1.change_to_needs_reworking()
        assert(exception_info.value.__str__() ==
               "Incorrect state change. Current state is 0")
        assert(question1.state == question1.INCOMPLETE)

        with pytest.raises(StateException) as exception_info:
            question1.change_to_complete()
        assert(exception_info.value.__str__() ==
               "Incorrect state change. Current state is 0")
        assert(question1.state == question1.INCOMPLETE)

        with pytest.raises(StateException) as exception_info:
            question1.change_to_flagged()
        assert(exception_info.value.__str__() ==
               "Incorrect state change. Current state is 0")
        assert(question1.state == question1.INCOMPLETE)

        question1.change_to_review_ready()
        assert(question1.state == question1.REVIEW_READY)

    def test_question_state_from_ready_for_review(self):
        """Check that question state.

        Confirm that state can only go from 'ready to review' to
        'complete' or 'needs reworking'.
        """
        question1 = Question.objects.all()[0]
        question1.state = question1.REVIEW_READY

        with pytest.raises(StateException) as exception_info:
            question1.change_to_review_ready()
        assert(exception_info.value.__str__() ==
               "Incorrect state change. Current state is 1")

        with pytest.raises(StateException) as exception_info:
            question1.change_to_flagged()
        assert(exception_info.value.__str__() ==
               "Incorrect state change. Current state is 1")
        assert(question1.state == question1.REVIEW_READY)

        question1.change_to_complete()
        assert(question1.state == question1.COMPLETE)

        question1.state = question1.REVIEW_READY

        question1.change_to_needs_reworking()
        assert(question1.state == question1.NEEDS_REWORKING)

    def test_question_state_from_needs_reworking(self):
        """Check that question state.

        Confirm that state can only go from 'needs reworking' to
        'ready for review'.
        """
        question1 = Question.objects.all()[0]
        question1.state = question1.NEEDS_REWORKING

        with pytest.raises(StateException) as exception_info:
            question1.change_to_needs_reworking()
        assert(exception_info.value.__str__() ==
               "Incorrect state change. Current state is 2")
        assert(question1.state == question1.NEEDS_REWORKING)

        with pytest.raises(StateException) as exception_info:
            question1.change_to_complete()
        assert(exception_info.value.__str__() ==
               "Incorrect state change. Current state is 2")
        assert(question1.state == question1.NEEDS_REWORKING)

        with pytest.raises(StateException) as exception_info:
            question1.change_to_flagged()
        assert(exception_info.value.__str__() ==
               "Incorrect state change. Current state is 2")
        assert(question1.state == question1.NEEDS_REWORKING)

        question1.change_to_review_ready()
        assert(question1.state == question1.REVIEW_READY)

    def test_question_state_from_complete(self):
        """Check that question state.

        Confirm that state can only go from 'complete' to
        'flagged for review'.
        """
        question1 = Question.objects.all()[0]
        question1.state = question1.COMPLETE

        with pytest.raises(StateException) as exception_info:
            question1.change_to_review_ready()
        assert(exception_info.value.__str__() ==
               "Incorrect state change. Current state is 3")
        assert(question1.state == question1.COMPLETE)

        with pytest.raises(StateException) as exception_info:
            question1.change_to_complete()
        assert(exception_info.value.__str__() ==
               "Incorrect state change. Current state is 3")
        assert(question1.state == question1.COMPLETE)

        with pytest.raises(StateException) as exception_info:
            question1.change_to_needs_reworking()
        assert(exception_info.value.__str__() ==
               "Incorrect state change. Current state is 3")
        assert(question1.state == question1.COMPLETE)

        question1.change_to_flagged()
        assert(question1.state == question1.FLAGGED)

    def test_question_state_from_flagged_for_review(self):
        """Check that question state.

        Confirm that state can only go from 'flagged for review' to
        'complete'.
        """
        question1 = Question.objects.all()[0]
        question1.state = question1.FLAGGED

        with pytest.raises(StateException) as exception_info:
            question1.change_to_review_ready()
        assert(exception_info.value.__str__() ==
               "Incorrect state change. Current state is 4")
        assert(question1.state == question1.FLAGGED)

        with pytest.raises(StateException) as exception_info:
            question1.change_to_needs_reworking()
        assert(exception_info.value.__str__() ==
               "Incorrect state change. Current state is 4")
        assert(question1.state == question1.FLAGGED)

        with pytest.raises(StateException) as exception_info:
            question1.change_to_flagged()
        assert(exception_info.value.__str__() ==
               "Incorrect state change. Current state is 4")
        assert(question1.state == question1.FLAGGED)

        question1.change_to_complete()
        assert(question1.state == question1.COMPLETE)

    def test_question_option_save(self):
        """Test that question cannot have option with correct answer."""
        question1 = Question.objects.all()[0]

        option = Option.objects.first()
        option.correct = True
        option.save()

        assert(len(question1.option_set.all()) == 3)
        assert(len(Option.objects.all()) == 3)

    def test_get_comments(self):
        """
        Test that the get_comments() function returns all comments
        relating to a question.
        """
        user = User.objects.create(username="testuser")
        question1 = Question.objects.all()[0]
        Comment.objects.create(text="Test comment!", question=question1, user=user)
        Comment.objects.create(text="Another comment!", question=question1, user=user)

        assert(len(question1.get_comments()) == 2)
        assert(question1.get_comments()[0].text == "Test comment!")
        assert(question1.get_comments()[0].created_at < question1.get_comments()[1].created_at)

    def test_get_options(self):
        """
        Test that the get_options() function returns all options
        relating to a question.
        """
        question1 = Question.objects.all()[0]

        assert(question1.get_number_of_options() == 3)

    def test_get_state(self):
        question1 = Question.objects.all()[0]

        assert(question1.state == question1.INCOMPLETE)
        assert(question1.get_state() == "Incomplete")


class TestTopic(TestCase):
    """Test the Topic Model."""

    def setUp(self):
        """Create Topic for testing."""

        grade_test = Grade.objects.create(name="Grade Example")
        syllabus_test = Syllabus.objects.create(grade=grade_test)
        Topic.objects.create(name="Financial Mathematics",
                             description="Topic that involves sinking funds "
                                         "and loan calculations",
                             syllabus=syllabus_test, week_start=1,
                             duration=3)

    def test_topic_creates_blocks(self):
        """
        Confirm that blocks are created automatically and associated with the
        topic.
        """
        blocks = Block.objects.all()
        assert(len(blocks) == 3)
        assert(blocks[0].topic.name == "Financial Mathematics")

    def test_topic_creates_questions(self):
        """
        Confirm that questions are created automatically and associated with the
        correct block and topic.
        """
        questions = Question.objects.all()
        assert(len(questions) == 3 * 15)
        assert(questions[0].block.topic.name == "Financial Mathematics")

    def test_topic_number_of_questions(self):
        """
        Confirm that the correct number of questions is returned by the helper
        function.
        """
        questions = Question.objects.all()
        topics = Topic.objects.all()
        assert(len(questions) == topics[0].get_number_of_questions())

    def test_topic_number_of_blocks(self):
        """
        Confirm that the correct number of blocks is returned by the helper
        function.
        """
        blocks = Block.objects.all()
        topics = Topic.objects.all()
        assert(len(blocks) == topics[0].get_number_of_blocks())

    def test_topic_save_does_not_duplicate_questions(self):
        already_created_topic = Topic.objects.get(name="Financial Mathematics")
        count = 0
        for block in Block.objects.filter(topic=already_created_topic):
            for question in Question.objects.filter(block=block):
                count += 1
        assert(count == 45)
        new_description = "This is a new description"
        already_created_topic.description = new_description
        already_created_topic.save()

        edited_topic = Topic.objects.get(name="Financial Mathematics")
        count = 0
        for block in Block.objects.filter(topic=edited_topic):
            for question in Question.objects.filter(block=block):
                count += 1
        assert(count == 45)

[(22, 'core.models.Grade', 'Grade', ([], {'name': '"""Grade Example"""'}), False, 'from core.models import Grade, Subject, Question, Comment, Option, Topic, Block, Syllabus, StateException\n'), (24, 'core.models.Subject', 'Subject', ([], {'name': '"""addition"""', 'grade': 'grade_test'}), False, 'from core.models import Grade, Subject, Question, Comment, Option, Topic, Block, Syllabus, StateException\n'), (27, 'core.models.Question', 'Question', ([], {'question_content': '"""what is 1 + 1?"""', 'answer_content': '"""This is an addition question"""', 'subject': 'subject_test'}), False, 'from core.models import Grade, Subject, Question, Comment, Option, Topic, Block, Syllabus, StateException\n'), (188, 'core.models.Option.objects.first', 'Option.objects.first', ([], {}), False, 'from core.models import Grade, Subject, Question, Comment, Option, Topic, Block, Syllabus, StateException\n'), (200, 'django.contrib.auth.models.User.objects.create', 'User.objects.create', ([], {'username': '"""testuser"""'}), False, 'from django.contrib.auth.models import User\n'), (202, 'core.models.Comment.objects.create', 'Comment.objects.create', ([], {'text': '"""Test comment!"""', 'question': 'question1', 'user': 'user'}), False, 'from core.models import Grade, Subject, Question, Comment, Option, Topic, Block, Syllabus, StateException\n'), (203, 'core.models.Comment.objects.create', 'Comment.objects.create', ([], {'text': '"""Another comment!"""', 'question': 'question1', 'user': 'user'}), False, 'from core.models import Grade, Subject, Question, Comment, Option, Topic, Block, Syllabus, StateException\n'), (231, 'core.models.Grade.objects.create', 'Grade.objects.create', ([], {'name': '"""Grade Example"""'}), False, 'from core.models import Grade, Subject, Question, Comment, Option, Topic, Block, Syllabus, StateException\n'), (232, 'core.models.Syllabus.objects.create', 'Syllabus.objects.create', ([], {'grade': 'grade_test'}), False, 'from core.models import Grade, Subject, Question, Comment, Option, Topic, Block, Syllabus, StateException\n'), (233, 'core.models.Topic.objects.create', 'Topic.objects.create', ([], {'name': '"""Financial Mathematics"""', 'description': '"""Topic that involves sinking funds and loan calculations"""', 'syllabus': 'syllabus_test', 'week_start': '(1)', 'duration': '(3)'}), False, 'from core.models import Grade, Subject, Question, Comment, Option, Topic, Block, Syllabus, StateException\n'), (244, 'core.models.Block.objects.all', 'Block.objects.all', ([], {}), False, 'from core.models import Grade, Subject, Question, Comment, Option, Topic, Block, Syllabus, StateException\n'), (253, 'core.models.Question.objects.all', 'Question.objects.all', ([], {}), False, 'from core.models import Grade, Subject, Question, Comment, Option, Topic, Block, Syllabus, StateException\n'), (262, 'core.models.Question.objects.all', 'Question.objects.all', ([], {}), False, 'from core.models import Grade, Subject, Question, Comment, Option, Topic, Block, Syllabus, StateException\n'), (263, 'core.models.Topic.objects.all', 'Topic.objects.all', ([], {}), False, 'from core.models import Grade, Subject, Question, Comment, Option, Topic, Block, Syllabus, StateException\n'), (271, 'core.models.Block.objects.all', 'Block.objects.all', ([], {}), False, 'from core.models import Grade, Subject, Question, Comment, Option, Topic, Block, Syllabus, StateException\n'), (272, 'core.models.Topic.objects.all', 'Topic.objects.all', ([], {}), False, 'from core.models import Grade, Subject, Question, Comment, Option, Topic, Block, Syllabus, StateException\n'), (276, 'core.models.Topic.objects.get', 'Topic.objects.get', ([], {'name': '"""Financial Mathematics"""'}), False, 'from core.models import Grade, Subject, Question, Comment, Option, Topic, Block, Syllabus, StateException\n'), (278, 'core.models.Block.objects.filter', 'Block.objects.filter', ([], {'topic': 'already_created_topic'}), False, 'from core.models import Grade, Subject, Question, Comment, Option, Topic, Block, Syllabus, StateException\n'), (286, 'core.models.Topic.objects.get', 'Topic.objects.get', ([], {'name': '"""Financial Mathematics"""'}), False, 'from core.models import Grade, Subject, Question, Comment, Option, Topic, Block, Syllabus, StateException\n'), (288, 'core.models.Block.objects.filter', 'Block.objects.filter', ([], {'topic': 'edited_topic'}), False, 'from core.models import Grade, Subject, Question, Comment, Option, Topic, Block, Syllabus, StateException\n'), (34, 'core.models.Question.objects.all', 'Question.objects.all', ([], {}), False, 'from core.models import Grade, Subject, Question, Comment, Option, Topic, Block, Syllabus, StateException\n'), (43, 'core.models.Question.objects.all', 'Question.objects.all', ([], {}), False, 'from core.models import Grade, Subject, Question, Comment, Option, Topic, Block, Syllabus, StateException\n'), (45, 'pytest.raises', 'pytest.raises', (['StateException'], {}), False, 'import pytest\n'), (51, 'pytest.raises', 'pytest.raises', (['StateException'], {}), False, 'import pytest\n'), (57, 'pytest.raises', 'pytest.raises', (['StateException'], {}), False, 'import pytest\n'), (72, 'core.models.Question.objects.all', 'Question.objects.all', ([], {}), False, 'from core.models import Grade, Subject, Question, Comment, Option, Topic, Block, Syllabus, StateException\n'), (75, 'pytest.raises', 'pytest.raises', (['StateException'], {}), False, 'import pytest\n'), (80, 'pytest.raises', 'pytest.raises', (['StateException'], {}), False, 'import pytest\n'), (100, 'core.models.Question.objects.all', 'Question.objects.all', ([], {}), False, 'from core.models import Grade, Subject, Question, Comment, Option, Topic, Block, Syllabus, StateException\n'), (103, 'pytest.raises', 'pytest.raises', (['StateException'], {}), False, 'import pytest\n'), (109, 'pytest.raises', 'pytest.raises', (['StateException'], {}), False, 'import pytest\n'), (115, 'pytest.raises', 'pytest.raises', (['StateException'], {}), False, 'import pytest\n'), (130, 'core.models.Question.objects.all', 'Question.objects.all', ([], {}), False, 'from core.models import Grade, Subject, Question, Comment, Option, Topic, Block, Syllabus, StateException\n'), (133, 'pytest.raises', 'pytest.raises', (['StateException'], {}), False, 'import pytest\n'), (139, 'pytest.raises', 'pytest.raises', (['StateException'], {}), False, 'import pytest\n'), (145, 'pytest.raises', 'pytest.raises', (['StateException'], {}), False, 'import pytest\n'), (160, 'core.models.Question.objects.all', 'Question.objects.all', ([], {}), False, 'from core.models import Grade, Subject, Question, Comment, Option, Topic, Block, Syllabus, StateException\n'), (163, 'pytest.raises', 'pytest.raises', (['StateException'], {}), False, 'import pytest\n'), (169, 'pytest.raises', 'pytest.raises', (['StateException'], {}), False, 'import pytest\n'), (175, 'pytest.raises', 'pytest.raises', (['StateException'], {}), False, 'import pytest\n'), (186, 'core.models.Question.objects.all', 'Question.objects.all', ([], {}), False, 'from core.models import Grade, Subject, Question, Comment, Option, Topic, Block, Syllabus, StateException\n'), (201, 'core.models.Question.objects.all', 'Question.objects.all', ([], {}), False, 'from core.models import Grade, Subject, Question, Comment, Option, Topic, Block, Syllabus, StateException\n'), (214, 'core.models.Question.objects.all', 'Question.objects.all', ([], {}), False, 'from core.models import Grade, Subject, Question, Comment, Option, Topic, Block, Syllabus, StateException\n'), (219, 'core.models.Question.objects.all', 'Question.objects.all', ([], {}), False, 'from core.models import Grade, Subject, Question, Comment, Option, Topic, Block, Syllabus, StateException\n'), (279, 'core.models.Question.objects.filter', 'Question.objects.filter', ([], {'block': 'block'}), False, 'from core.models import Grade, Subject, Question, Comment, Option, Topic, Block, Syllabus, StateException\n'), (289, 'core.models.Question.objects.filter', 'Question.objects.filter', ([], {'block': 'block'}), False, 'from core.models import Grade, Subject, Question, Comment, Option, Topic, Block, Syllabus, StateException\n'), (193, 'core.models.Option.objects.all', 'Option.objects.all', ([], {}), False, 'from core.models import Grade, Subject, Question, Comment, Option, Topic, Block, Syllabus, StateException\n')]