Skip to content
Snippets Groups Projects
tests.py 17.9 KiB
Newer Older
""" This module tests all our quiz models"""
dominip89's avatar
dominip89 committed

import os
import shutil
import tempfile

from django.core.files.uploadedfile import SimpleUploadedFile
from django.core.management import call_command
from django.utils.translation import get_language, activate
from django.urls import reverse
from django.test import TestCase, override_settings
from rest_framework.test import APITestCase
from django.conf import settings
from .models import (
    Sport,
    Criterion,
    CriterionRating,
    CallToMove,
    KnowledgeSnack,
    Question,
)


class SportModelTest(TestCase):
    """Tests the sport model"""

    def setUp(self):
        self.name = "HIIT"
        self.url = (
            "https://www.buchsys.de/fu-berlin/angebote/aktueller_zeitraum/_HIIT_"
            "-_High_Intensity_Interval_Training___HOME.html "
        )
        self.test_sport = Sport(
            name=self.name,
            url=self.url,
        )

        self.test_sport.save()

    def test_sport_can_be_created(self):
        """New sport is written to the database"""
        test_sport = Sport.objects.first()
        self.assertEqual(test_sport.name, self.name)
        self.assertEqual(test_sport.url, self.url)


class CriterionRatingTest(TestCase):
    """Tests the Relation between Sport and Criterion"""
    def setUp(self):
        self.name = "HIIT"
        self.url = (
            "https://www.buchsys.de/fu-berlin/angebote/aktueller_zeitraum/_HIIT_"
            "-_High_Intensity_Interval_Training___HOME.html "
        )
        self.test_sport = Sport(
            name=self.name,
            url=self.url,
        )

        self.test_sport.save()

        self.criterion = Criterion(name="Einzelsport")
        self.criterion.save()

    def test_can_rate_criterion_for_sport(self):
        """A rating for a specific criterion can be added to a sport"""
        self.test_sport.rate(self.criterion, 10)
        self.assertEqual(self.test_sport.criteria_ratings.first(), self.criterion)
        self.assertEqual(self.test_sport.get_rating(self.criterion), 10)
    def test_rating_can_be_changed(self):
        """
        If a sport is rated again then the corresponding relation is changed,
        instead of recreated
        """
        first_rating_object = self.test_sport.rate(self.criterion, 10)
        second_rating_object = self.test_sport.rate(self.criterion, 8)
        self.assertEqual(first_rating_object, second_rating_object)
        self.assertEqual(self.test_sport.get_rating(criterion=self.criterion), 8)

FIXTURE_IMAGES = os.path.join(settings.BASE_DIR, "quiz", "fixtures", "images")
MEDIA_ROOT = tempfile.mkdtemp(
    suffix="testing"
)  # Create a temp directory for files created during tests


@override_settings(MEDIA_ROOT=MEDIA_ROOT)
class CallToMoveTest(TestCase):
    """Tests the Model for Call To Move"""

    def setUp(self):
        tempfile.mkdtemp(suffix="testing")  # recreate tmp folder before each test

        self.text = "Kreise deine Arme vor der nächsten Frage 3x nach hinten"
        self.image_name = "test_image.png"
        self.image_path = os.path.join(FIXTURE_IMAGES, self.image_name)
        self.image = SimpleUploadedFile(
            name=self.image_name,
            content=open(self.image_path, "rb").read(),
            content_type="image/png",
        )
        self.call_to_move = CallToMove(text=self.text, image=self.image)
        self.call_to_move.save()

    def tearDown(self) -> None:
        """Delete the temp dir after each test"""
        shutil.rmtree(MEDIA_ROOT, ignore_errors=True)

    def test_can_create_call_to_move(self):
        """A call to move can be correctly created"""
        self.assertEqual(self.call_to_move.text, self.text)
        self.assertEqual(self.call_to_move.image.name, self.image.name)

    def test_can_save_and_load_call_to_move(self):
        """A saved Call to Move can be loaded"""
        call_to_move = CallToMove.objects.first()
        self.assertEqual(call_to_move.text, self.text)
        self.assertEqual(call_to_move.image.name, self.image.name)


@override_settings(MEDIA_ROOT=MEDIA_ROOT)
class KnowledgeSnackTest(TestCase):
    """Tests the Model for Knowledge Snack"""

    def setUp(self):
        tempfile.mkdtemp(suffix="testing")  # recreate tmp folder before each test

fu2662cw's avatar
fu2662cw committed
        self.text = (
            "Dass Treppensteigen fast 5x so viele Kalorien verbrennt,"
            "als bei der Nutzung des Aufzuges?"
        )
        self.image_name = "test_image.png"
        self.image_path = os.path.join(FIXTURE_IMAGES, self.image_name)
        self.image = SimpleUploadedFile(
            name=self.image_name,
            content=open(self.image_path, "rb").read(),
            content_type="image/png",
        )
        self.knowledge_snack = KnowledgeSnack(text=self.text, image=self.image)
        self.knowledge_snack.save()

    def tearDown(self) -> None:
        """Delete the temp dir after each test"""
        shutil.rmtree(MEDIA_ROOT, ignore_errors=True)

    def test_can_create_knowledge_snack(self):
        """A knowledge snack can be correctly created"""
        self.assertEqual(self.knowledge_snack.text, self.text)
        self.assertEqual(self.knowledge_snack.image.name, self.image.name)

    def test_can_save_and_load_call_to_move(self):
        """A saved Knowledge Snack can be loaded"""
        knowledge_snack = KnowledgeSnack.objects.first()
        self.assertEqual(knowledge_snack.text, self.text)
        self.assertEqual(knowledge_snack.image.name, self.image.name)
fu2662cw's avatar
fu2662cw committed
class CriterionAndQuestionModelTest(TestCase):
    """Tests the Criterion and the Question model which have a One to One Relation"""
fu2662cw's avatar
fu2662cw committed
    def setUp(self):
        self.name = "Einzelsport"
        self.criterion = Criterion(name=self.name)
        self.criterion.save()

    def test_criterion_can_be_saved_and_loaded(self):
        """New criterion can be loaded from the db"""
        test_criterion = Criterion.objects.first()
fu2662cw's avatar
fu2662cw committed
        self.assertEqual(test_criterion.name, self.name)
fu2662cw's avatar
fu2662cw committed
    def test_question_can_be_added(self):
        """
        If a question is added to a criterion, then it is accessible through the criterion.
        """
        text = "Ich trainiere gerne mit anderen im Team"
        question = Question(text=text, criterion=self.criterion)
        question.save()
        self.criterion.question = question
        self.assertEqual(question, self.criterion.question)

    def test_criterion_stays_if_question_deleted(self):
        """If assigned question is deleted the foreign key is set None"""
        text = "Ich trainiere gerne mit anderen im Team"
        question = Question(text=text, criterion=self.criterion)
        question.save()
        question.delete()
        self.criterion = Criterion.objects.first()
fu2662cw's avatar
fu2662cw committed
        with self.assertRaises(Criterion.question.RelatedObjectDoesNotExist):
            self.criterion.question
fu2662cw's avatar
fu2662cw committed

    def test_question_can_be_saved_and_loaded(self):
        """New Question is saved to the db and can be loaded"""
        text = "Ich trainiere gerne mit anderen im Team"
fu2662cw's avatar
fu2662cw committed
        Question(text=text, criterion=self.criterion).save()
fu2662cw's avatar
fu2662cw committed
        test_question = Question.objects.first()
        self.assertEqual(test_question.text, text)


class FixturesTest(TestCase):
    """
    These are the tests for the fixtures in quiz/fixtures.
fu2662cw's avatar
fu2662cw committed
    Fixtures can be used to populate the database with test data.
    They can be used in automated tests, but also in development.
    """
fu2662cw's avatar
fu2662cw committed

    fixtures = [
        "sports.json",
        "criteria.json",
        "criterion_ratings.json",
        "questions.json",
        "calls_to_move.json",
        "knowledge_snacks.json",
    ]

    def test_sports_created_by_fixture(self):
        """If the sports fixture is loaded there exists a sport with the given data."""
        sport = Sport.objects.get(pk=1)
        self.assertEqual(sport.name, "Jiu Jitsu")
        self.assertEqual(sport.url, "http://www.test.de")

    def test_criterion_created_by_fixture(self):
        """If the criteria fixture is loaded there exists a criterion with the given data"""
        criterion = Criterion.objects.get(pk=1)
        self.assertEqual(criterion.name, "Outdoorsport")

    def test_criterion_rating_created_by_fixture(self):
        """If the criterion_ratings fixture is loaded the given sport has a corresponding rating"""
        criterion = Criterion.objects.get(name="Outdoorsport")
        sport = Sport.objects.get(name="Jiu Jitsu")
        self.assertEqual(sport.get_rating(criterion), 1)

    def test_question_created_by_fixture(self):
        """If the questions fixture is loaded there exists a question with the given data"""
        question = Question.objects.get(pk=1)
        criterion = Criterion.objects.get(name="Outdoorsport")
        self.assertEqual(question.text, "Ich mache am liebsten draußen Sport")
        self.assertEqual(question.criterion, criterion)

    def test_call_to_move_created_by_fixture(self):
        """If the call to move fixture is loaded there exists a call to move with the given data"""
        call_to_move = CallToMove.objects.get(pk=1)
        self.assertEqual(
            call_to_move.text, "Kreise deine Arme vor der nächsten Frage 3x nach hinten"
        )
        self.assertEqual(call_to_move.image.name, "test_image.png")

    def test_knowledge_snack_created_by_fixture(self):
        """
        If the knowledge snack fixture is loaded there exists a knowledge snack with the given data
        """
        knowledge_snack = KnowledgeSnack.objects.get(pk=1)
        self.assertEqual(
            knowledge_snack.text,
            "Dass Treppensteigen fast 5x so viele Kalorien verbrennt, "
            "als die Nutzung des Aufzuges?",
        )
        self.assertEqual(knowledge_snack.image.name, "logo.png")


class SeedingTest(TestCase):
    """Tests the seed_db command in quiz/management/commands"""

    def test_seed_with_complete_data(self):
        """If seed_db is called then there exists a certain number of elements per model"""

        # call the seed command without asking for confirmation
        call_command("seed_db", ["--yes", "--no-superuser"])

        n_sports = 5
        n_criteria = 5
        self.assertEqual(Sport.objects.count(), n_sports)
        self.assertEqual(Criterion.objects.count(), n_criteria)
        self.assertEqual(CriterionRating.objects.count(), n_sports * n_criteria)
        self.assertEqual(Question.objects.count(), n_criteria)
        self.assertEqual(CallToMove.objects.count(), 3)
        self.assertEqual(KnowledgeSnack.objects.count(), 3)


class ModeltranslationFallbackTest(TestCase):
    """
    Tests Behaviour of Modeltranslation when no translation is given
    Also tests Default Language
    """

    def setUp(self):
        """
        Creates a Question and fills the german Translation
        """
        self.criterion = Criterion(name="test")
        self.question = Question(text="", criterion=self.criterion)
        self.criterion.save()
        self.question.save()

    def test_default_language(self):
        """
        Checks whether the Default Language is German
        """
        cur_language = get_language()
        self.assertEqual(cur_language, "de")

    def test_vallback_value(self):
        """
        Checks whether if no Translation is set, the Fallbackvalue is used
        """
        self.assertEqual(self.question.text, ("No Translation for this Field",))


class Modeltranslation_One_Language_Test(TestCase):
    """
    Tests Behaviour when only one language is defined
    """

    def setUp(self):
        """
        Creates a Question and fills the german Translation
        """
        self.criterion = Criterion(name="test")
        self.question = Question(text="", criterion=self.criterion)

        self.question.text = "de_text"

        self.criterion.save()
        self.question.save()

    def test_german_translation(self):
        """
        Check whether obj.text returns the German Translation
        """
        self.assertEqual(self.question.text, "de_text")

    def test_fallback_value_translations(self):
        """
        English Translation is not filled out, check whether german text is returned
        """

        activate("en")
        self.assertNotEqual(self.question.text, self.question.text_en)
        self.assertEqual(self.question.text, self.question.text_de)


class Modeltranslation_Two_Languages_Test(TestCase):
    """
    Tests Behaviour when two languages are defined
    """

    def setUp(self):
        """
        Creates a Question and fills both the german and english Translations
        """
        self.criterion = Criterion(name="test")
        self.question = Question(text="", criterion=self.criterion)

        activate("de")
        self.question.text = "de_text"
        activate("en")
        self.question.text = "en_text"
        activate("de")

        self.criterion.save()
        self.question.save()

    def test_german_translation(self):
        """
        Tests whether German Translation is returned
        """
        activate("de")

        self.assertEqual(self.question.text, self.question.text_de)
        self.assertNotEqual(self.question.text, self.question.text_en)

    def test_english_translation(self):
        """
        Tests whether English Translation is returned
        """
        activate("en")

        self.assertEqual(self.question.text, self.question.text_en)
        self.assertNotEqual(self.question.text, self.question.text_de)


class APITest(APITestCase):
    """Tests the Django API"""

    fixtures = [
        "sports.json",
        "criteria.json",
        "criterion_ratings.json",
        "questions.json",
    ]

    def test_get_sport_returns_correct_data(self):
        """Test the API endpoint /sport/{id}"""
        response = self.client.get(reverse("small-sport-list-detail", kwargs={"pk": 1}))
        sport_data = {
            "id": 1,
            "name": "Jiu Jitsu",
            "url": "http://www.test.de",
            "criteria": [{"id": 1, "name": "Outdoorsport", "value": 1}],
        }
        self.assertDictEqual(response.data, sport_data)

    def test_put_sport_makes_correct_changes(self):
        """
        Test the API endpoint /sport/{id} for put requests
        """
        sport_data = {
            "name": "Karate",
            "url": "http://www.test2.de",
            "criteria": [{"id": 1, "name": "Outdoorsport", "value": 1}],
        }

        response = self.client.put(
            reverse("small-sport-list-detail", kwargs={"pk": 1}),
            data=sport_data,
            format="json",
        )
        self.assertEqual(response.data["name"], sport_data["name"])
        self.assertEqual(response.data["url"], sport_data["url"])
        self.assertEqual(len(response.data["criteria"]), Criterion.objects.count())
        self.assertDictEqual(
            response.data["criteria"][0], {"id": 1, "name": "Outdoorsport", "value": 1}
        )

    def test_patch_sport_makes_correct_changes(self):
        """
        Test the API endpoint /sport/{id} for patch requests
        """
        sport_data = {
            "criteria": [{"id": 1, "value": 3}],
        }

        response = self.client.patch(
            reverse("small-sport-list-detail", kwargs={"pk": 1}),
            data=sport_data,
            format="json",
        )
        self.assertEqual(response.data["name"], "Jiu Jitsu")
        self.assertEqual(response.data["url"], "http://www.test.de")
        self.assertEqual(len(response.data["criteria"]), Criterion.objects.count())
        self.assertDictEqual(
            response.data["criteria"][0], {"id": 1, "name": "Outdoorsport", "value": 3}
        )

    def test_get_sports_returns_correct_data(self):
        """Test if API endpoint /sport returns correct sports list"""

        response = self.client.get(reverse("small-sport-list-list"))

        sport_data = [
            {
                "id": 1,
                "is_filled": True,
                "name": "Jiu Jitsu",
                "url": "http://www.test.de",
            }
        ]
        self.assertListEqual(response.data["results"], sport_data)
        self.assertEqual(response.data["count"], 1)

    def test_post_sports_creates_correct_entry(self):
        """Test if post to /sport creates a correct object"""
        sport_data_new = {
            "name": "Karate",
            "url": "http://www.test2.de",
        }

        response = self.client.post(
            reverse("small-sport-list-list"), data=sport_data_new
        )
        self.assertEqual(response.data["name"], sport_data_new["name"])
        self.assertEqual(response.data["url"], sport_data_new["url"])
        self.assertEqual(len(response.data["criteria"]), Criterion.objects.count())
        self.assertEqual(Sport.objects.count(), 2)

    def test_delete_sports_creates_correct_entry(self):
        """Test if delete to /sports deletes an object"""

        response = self.client.delete(
            reverse("small-sport-list-detail", kwargs={"pk": 1})
        )

        self.assertEqual(Sport.objects.count(), 0)

    def test_get_incomplete_sport_list(self):
        """
        Tests if get to /sport/incomplete/ returns a list of incomplete sports
        """

        # Set Up Incomplete Sport
        response = self.client.patch(
            reverse("small-sport-list-detail", kwargs={"pk": 1}),
            data={"criteria": [{"id": 1, "value": -1}]},
            format="json",
        )

        response = self.client.get(reverse("incomplete"))

        self.assertEqual(len(response.data["results"]), 1)
        self.assertEqual(response.data["results"][0]["name"], "Jiu Jitsu")

    def test_get_criteria(self):
        """
        Tests if get /critera/ returns a list of all criteria
        """

        # Set Up Values
        response = self.client.patch(
            reverse("small-sport-list-detail", kwargs={"pk": 1}),
            data={"criteria": [{"id": 1, "value": 7}]},
            format="json",
        )

        response = self.client.get(reverse("criteria"))

        self.assertEqual(len(response.data), Criterion.objects.count())
        self.assertEqual(response.data[0]["number_of_sports_active"], 1)
        self.assertEqual(response.data[0]["sum_of_weights"], 7)