From fce45660fc5ac77dc9d0440dfd24b916f8c0a292 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Gandra=C3=9F?= Date: Sat, 5 Oct 2024 17:03:23 +0200 Subject: [PATCH] Create unit tests for demo mode --- CHANGELOG.md | 1 + archiveworker/quiz_archive_job.py | 2 +- tests/test_demomode.py | 100 ++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 tests/test_demomode.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e181eb..86067d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ archive worker service right away. Productive instances of the quiz archive worker service will remain fully unaffected by this. - Improve documentation and add reference to official documentation website +- Create unit tests for demo mode - Update Python dependencies diff --git a/archiveworker/quiz_archive_job.py b/archiveworker/quiz_archive_job.py index 46a9a4e..f46f25a 100644 --- a/archiveworker/quiz_archive_job.py +++ b/archiveworker/quiz_archive_job.py @@ -64,9 +64,9 @@ def __init__(self, jobid: UUID, job_request: JobArchiveRequest): # Limit number of attempts in demo mode if self.request.tasks['archive_quiz_attempts']: if Config.DEMO_MODE: + self.logger.info("Demo mode: Only processing the first 10 quiz attempts!") if len(self.request.tasks['archive_quiz_attempts']['attemptids']) > 10: self.request.tasks['archive_quiz_attempts']['attemptids'] = self.request.tasks['archive_quiz_attempts']['attemptids'][:10] - self.logger.info("Demo mode: Only processing the first 10 quiz attempts!") def __eq__(self, other): if isinstance(other, self.__class__): diff --git a/tests/test_demomode.py b/tests/test_demomode.py new file mode 100644 index 0000000..7b54f10 --- /dev/null +++ b/tests/test_demomode.py @@ -0,0 +1,100 @@ +# Moodle Quiz Archive Worker +# Copyright (C) 2024 Niels Gandraß +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import logging +import os +import tarfile +import tempfile +import time + +import pytest + +from archiveworker.custom_types import JobStatus +from archiveworker.moodle_quiz_archive_worker import start_processing_thread +from config import Config +from .conftest import client, TestUtils +import tests.fixtures as fixtures + + +class TestDemoMode: + + @classmethod + def setup_class(cls): + cls.wait_for_readysignal_orig = Config.REPORT_WAIT_FOR_READY_SIGNAL + Config.REPORT_WAIT_FOR_READY_SIGNAL = False + Config.DEMO_MODE = True + + @classmethod + def teardown_class(cls): + Config.REPORT_WAIT_FOR_READY_SIGNAL = cls.wait_for_readysignal_orig + Config.DEMO_MODE = False + + @pytest.mark.timeout(30) + def test_archive_full_quiz_demomode(self, client, caplog) -> None: + """ + Tests the full quiz archiving process with all tasks enabled. Data is + taken from the reference quiz fixture. + + :param client: Flask test client + :param caplog: Pytest log capturing fixture + :return: None + """ + caplog.set_level(logging.INFO) + + with fixtures.reference_quiz_full.MoodleAPIMock() as mock: + # Create job and process it + r = client.post('/archive', json=fixtures.reference_quiz_full.ARCHIVE_API_REQUEST) + assert r.status_code == 200 + jobid = r.json['jobid'] + + start_processing_thread() + + # Wait for job to be processed + while True: + time.sleep(0.5) + r = client.get(f'/status/{jobid}') + assert r.json['status'] != JobStatus.FAILED + + if r.json['status'] == JobStatus.FINISHED: + break + + # Ensure that demo mode was logged + assert "Demo mode: Only processing" in caplog.text + assert "Demo mode: Skipping download of backup" in caplog.text + + # Validate that an artifact was uploaded + job_uploads = mock.get_uploaded_files() + assert len(job_uploads) == 1, 'Expected exactly one uploaded artifact' + job_artifact = job_uploads[1]['file'] + assert job_artifact.is_file(), 'Uploaded artifact is not a valid file' + + # Extract artifact and validate contents + with tarfile.open(job_artifact, 'r:gz') as tar: + with tempfile.TemporaryDirectory() as tempdir: + tar.extractall(tempdir, filter=tarfile.tar_filter) + + # Validate attempt reports exist + for attemptid in fixtures.reference_quiz_full.ARCHIVE_API_REQUEST['task_archive_quiz_attempts']['attemptids']: + fbase = os.path.join(tempdir, f'attempts/attempt-{attemptid}/attempt-{attemptid}') + TestUtils.assert_is_file_with_size(fbase+'.pdf', 200*1024, 2000*1024) + + # Validate Moodle backups are placeholders + for backup in fixtures.reference_quiz_full.ARCHIVE_API_REQUEST['task_moodle_backups']: + backupfile = os.path.join(tempdir, 'backups/', backup['filename']) + TestUtils.assert_is_file_with_size(backupfile, 64, 1024) + + with open(backupfile, 'r') as f: + assert "DEMO MODE" in f.read()