Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
// This file is part of Moodle - http://moodle.org/
3
//
4
// Moodle is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// Moodle is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
 
17
namespace mod_quiz\external;
18
 
19
defined('MOODLE_INTERNAL') || die();
20
 
21
require_once(__DIR__ . '/../../../../webservice/tests/helpers.php');
22
 
23
use coding_exception;
24
use core_question_generator;
25
use externallib_advanced_testcase;
26
use mod_quiz\quiz_attempt;
27
use mod_quiz\quiz_settings;
28
use required_capability_exception;
29
use stdClass;
30
 
31
/**
32
 * Test for the reopen_attempt and get_reopen_attempt_confirmation services.
33
 *
34
 * @package   mod_quiz
35
 * @category  external
36
 * @copyright 2023 The Open University
37
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
38
 * @covers \mod_quiz\external\reopen_attempt
39
 * @covers \mod_quiz\external\get_reopen_attempt_confirmation
40
 */
41
class reopen_attempt_test extends externallib_advanced_testcase {
42
    /** @var stdClass|null if we make a quiz attempt, we store the student object here. */
43
    protected $student;
44
 
11 efrain 45
    public function test_reopen_attempt_service_works(): void {
1 efrain 46
        [$attemptid] = $this->create_attempt_at_quiz_with_one_shortanswer_question();
47
 
48
        reopen_attempt::execute($attemptid);
49
 
50
        $attemptobj = quiz_attempt::create($attemptid);
51
        $this->assertEquals(quiz_attempt::IN_PROGRESS, $attemptobj->get_state());
52
    }
53
 
11 efrain 54
    public function test_reopen_attempt_service_checks_permissions(): void {
1 efrain 55
        [$attemptid] = $this->create_attempt_at_quiz_with_one_shortanswer_question();
56
 
57
        $unprivilegeduser = $this->getDataGenerator()->create_user();
58
        $this->setUser($unprivilegeduser);
59
 
60
        $this->expectException(required_capability_exception::class);
61
        reopen_attempt::execute($attemptid);
62
    }
63
 
11 efrain 64
    public function test_reopen_attempt_service_checks_attempt_state(): void {
1 efrain 65
        [$attemptid] = $this->create_attempt_at_quiz_with_one_shortanswer_question(quiz_attempt::IN_PROGRESS);
66
 
67
        $this->expectExceptionMessage("Attempt $attemptid is in the wrong state (In progress) to be reopened.");
68
        reopen_attempt::execute($attemptid);
69
    }
70
 
11 efrain 71
    public function test_get_reopen_attempt_confirmation_staying_open(): void {
1 efrain 72
        global $DB;
73
        [$attemptid, $quizid] = $this->create_attempt_at_quiz_with_one_shortanswer_question();
74
        $DB->set_field('quiz', 'timeclose', 0, ['id' => $quizid]);
75
 
76
        $message = get_reopen_attempt_confirmation::execute($attemptid);
77
 
78
        $this->assertEquals('<p>This will reopen attempt 1 by ' . fullname($this->student) .
79
                '.</p><p>The attempt will remain open and can be continued.</p>',
80
                $message);
81
    }
82
 
11 efrain 83
    public function test_get_reopen_attempt_confirmation_staying_open_until(): void {
1 efrain 84
        global $DB;
85
        [$attemptid, $quizid] = $this->create_attempt_at_quiz_with_one_shortanswer_question();
86
        $timeclose = time() + HOURSECS;
87
        $DB->set_field('quiz', 'timeclose', $timeclose, ['id' => $quizid]);
88
 
89
        $message = get_reopen_attempt_confirmation::execute($attemptid);
90
 
91
        $this->assertEquals('<p>This will reopen attempt 1 by ' . fullname($this->student) .
92
                '.</p><p>The attempt will remain open and can be continued until the quiz closes on ' .
93
                userdate($timeclose) . '.</p>',
94
                $message);
95
    }
96
 
11 efrain 97
    public function test_get_reopen_attempt_confirmation_submitting(): void {
1 efrain 98
        global $DB;
99
        [$attemptid, $quizid] = $this->create_attempt_at_quiz_with_one_shortanswer_question();
100
        $timeclose = time() - HOURSECS;
101
        $DB->set_field('quiz', 'timeclose', $timeclose, ['id' => $quizid]);
102
 
103
        $message = get_reopen_attempt_confirmation::execute($attemptid);
104
 
105
        $this->assertEquals('<p>This will reopen attempt 1 by ' . fullname($this->student) .
106
                '.</p><p>The attempt will be immediately submitted for grading.</p>',
107
                $message);
108
    }
109
 
11 efrain 110
    public function test_get_reopen_attempt_confirmation_service_checks_permissions(): void {
1 efrain 111
        [$attemptid] = $this->create_attempt_at_quiz_with_one_shortanswer_question();
112
 
113
        $unprivilegeduser = $this->getDataGenerator()->create_user();
114
        $this->setUser($unprivilegeduser);
115
 
116
        $this->expectException(required_capability_exception::class);
117
        get_reopen_attempt_confirmation::execute($attemptid);
118
    }
119
 
11 efrain 120
    public function test_get_reopen_attempt_confirmation_service_checks_attempt_state(): void {
1 efrain 121
        [$attemptid] = $this->create_attempt_at_quiz_with_one_shortanswer_question(quiz_attempt::IN_PROGRESS);
122
 
123
        $this->expectExceptionMessage("Attempt $attemptid is in the wrong state (In progress) to be reopened.");
124
        get_reopen_attempt_confirmation::execute($attemptid);
125
    }
126
 
127
    /**
128
     * Create a quiz of one shortanswer question and an attempt in a given state.
129
     *
130
     * @param string $attemptstate the desired attempt state. quiz_attempt::ABANDONED or ::IN_PROGRESS.
131
     * @return array with two elements, the attempt id and the quiz id.
132
     */
133
    protected function create_attempt_at_quiz_with_one_shortanswer_question(
134
        string $attemptstate = quiz_attempt::ABANDONED
135
    ): array {
136
        global $SITE;
137
        $this->resetAfterTest();
138
 
139
        // Make a quiz.
140
        $timeclose = time() + HOURSECS;
141
        $quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz');
142
 
143
        $quiz = $quizgenerator->create_instance([
144
            'course' => $SITE->id,
145
            'timeclose' => $timeclose,
146
            'overduehandling' => 'autoabandon'
147
        ]);
148
 
149
        // Create a question.
150
        /** @var core_question_generator $questiongenerator */
151
        $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
152
        $cat = $questiongenerator->create_question_category();
153
        $saq = $questiongenerator->create_question('shortanswer', null, ['category' => $cat->id]);
154
 
155
        // Add them to the quiz.
156
        $quizobj = quiz_settings::create($quiz->id);
157
        quiz_add_quiz_question($saq->id, $quiz, 0, 1);
158
        $quizobj->get_grade_calculator()->recompute_quiz_sumgrades();
159
 
160
        // Make a user to do the quiz.
161
        $this->student = $this->getDataGenerator()->create_user();
162
        $this->setUser($this->student);
163
        $quizobj = quiz_settings::create($quiz->id, $this->student->id);
164
 
165
        // Start the attempt.
166
        $attempt = quiz_prepare_and_start_new_attempt($quizobj, 1, null);
167
        $attemptobj = quiz_attempt::create($attempt->id);
168
 
169
        if ($attemptstate === quiz_attempt::ABANDONED) {
170
            // Attempt goes overdue (e.g. if cron ran).
171
            $attemptobj->process_abandon($timeclose + 2 * get_config('quiz', 'graceperiodmin'), false);
172
        } else if ($attemptstate !== quiz_attempt::IN_PROGRESS) {
173
            throw new coding_exception('Status ' . $attemptstate . ' not currently supported.');
174
        }
175
 
176
        // Set current user to admin before we return.
177
        $this->setAdminUser();
178
 
179
        return [$attemptobj->get_attemptid(), $attemptobj->get_quizid()];
180
    }
181
}