2 // This file is part of Moodle - http://moodle.org/
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.
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.
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/>.
18 * Unit tests for the core_rating implementation of the Privacy API.
20 * @package core_rating
22 * @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26 defined('MOODLE_INTERNAL') ||
die();
29 require_once($CFG->dirroot
. '/rating/lib.php');
31 use \core_rating\privacy\provider
;
32 use \core_privacy\local\request\writer
;
35 * Unit tests for the core_rating implementation of the Privacy API.
37 * @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
38 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
40 class core_rating_privacy_testcase
extends \core_privacy\tests\provider_testcase
{
43 * Rate something as a user.
46 * @param string $component
47 * @param string $ratingarea
49 * @param \context $context
50 * @param string $score
52 protected function rate_as_user($userid, $component, $ratingarea, $itemid, $context, $score) {
54 $rm = new rating_manager();
55 $ratingoptions = (object) [
56 'component' => $component,
57 'ratingarea' => $ratingarea,
61 // Rate all courses as u1, and the course category too..
62 $ratingoptions->itemid
= $itemid;
63 $ratingoptions->userid
= $userid;
64 $ratingoptions->context
= $context;
65 $rating = new \rating
($ratingoptions);
66 $rating->update_rating($score);
70 * Ensure that the get_sql_join function returns valid SQL which returns the correct list of rated itemids.
72 public function test_get_sql_join() {
74 $this->resetAfterTest();
76 $course1 = $this->getDataGenerator()->create_course();
77 $course2 = $this->getDataGenerator()->create_course();
78 $course3 = $this->getDataGenerator()->create_course();
80 $u1 = $this->getDataGenerator()->create_user();
81 $u2 = $this->getDataGenerator()->create_user();
82 $u3 = $this->getDataGenerator()->create_user();
85 $rm = new rating_manager();
86 $ratingoptions = (object) [
87 'component' => 'core_course',
88 'ratingarea' => 'course',
92 // Rate all courses as u1, and something else in the same context.
93 $this->rate_as_user($u1->id
, 'core_course', 'course', $course1->id
, \context_course
::instance($course1->id
), 25);
94 $this->rate_as_user($u1->id
, 'core_course', 'course', $course2->id
, \context_course
::instance($course2->id
), 50);
95 $this->rate_as_user($u1->id
, 'core_course', 'course', $course3->id
, \context_course
::instance($course3->id
), 75);
96 $this->rate_as_user($u1->id
, 'core_course', 'files', $course3->id
, \context_course
::instance($course3->id
), 99);
98 // Rate course2 as u2, and something else in a different context/component..
99 $this->rate_as_user($u2->id
, 'core_course', 'course', $course2->id
, \context_course
::instance($course2->id
), 90);
100 $this->rate_as_user($u2->id
, 'user', 'user', $u3->id
, \context_user
::instance($u3->id
), 10);
102 // Return any course which the u1 has rated.
103 // u1 rated all three courses.
104 $ratingquery = provider
::get_sql_join('r', 'core_course', 'course', 'c.id', $u1->id
);
105 $sql = "SELECT c.id FROM {course} c {$ratingquery->join} WHERE {$ratingquery->userwhere}";
106 $courses = $DB->get_records_sql($sql, $ratingquery->params
);
108 $this->assertCount(3, $courses);
109 $this->assertTrue(isset($courses[$course1->id
]));
110 $this->assertTrue(isset($courses[$course2->id
]));
111 $this->assertTrue(isset($courses[$course3->id
]));
113 // User u1 rated files in course 3 only.
114 $ratingquery = provider
::get_sql_join('r', 'core_course', 'files', 'c.id', $u1->id
);
115 $sql = "SELECT c.id FROM {course} c {$ratingquery->join} WHERE {$ratingquery->userwhere}";
116 $courses = $DB->get_records_sql($sql, $ratingquery->params
);
118 $this->assertCount(1, $courses);
119 $this->assertFalse(isset($courses[$course1->id
]));
120 $this->assertFalse(isset($courses[$course2->id
]));
121 $this->assertTrue(isset($courses[$course3->id
]));
123 // Return any course which the u2 has rated.
124 // User u2 rated only course 2.
125 $ratingquery = provider
::get_sql_join('r', 'core_course', 'course', 'c.id', $u2->id
);
126 $sql = "SELECT c.id FROM {course} c {$ratingquery->join} WHERE {$ratingquery->userwhere}";
127 $courses = $DB->get_records_sql($sql, $ratingquery->params
);
129 $this->assertCount(1, $courses);
130 $this->assertFalse(isset($courses[$course1->id
]));
131 $this->assertTrue(isset($courses[$course2->id
]));
132 $this->assertFalse(isset($courses[$course3->id
]));
135 $ratingquery = provider
::get_sql_join('r', 'user', 'user', 'u.id', $u2->id
);
136 $sql = "SELECT u.id FROM {user} u {$ratingquery->join} WHERE {$ratingquery->userwhere}";
137 $users = $DB->get_records_sql($sql, $ratingquery->params
);
139 $this->assertCount(1, $users);
140 $this->assertFalse(isset($users[$u1->id
]));
141 $this->assertFalse(isset($users[$u2->id
]));
142 $this->assertTrue(isset($users[$u3->id
]));
144 // Return any course which the u3 has rated.
145 // User u3 did not rate anything.
146 $ratingquery = provider
::get_sql_join('r', 'core_course', 'course', 'c.id', $u3->id
);
147 $sql = "SELECT c.id FROM {course} c {$ratingquery->join} WHERE {$ratingquery->userwhere}";
148 $courses = $DB->get_records_sql($sql, $ratingquery->params
);
150 $this->assertCount(0, $courses);
151 $this->assertFalse(isset($courses[$course1->id
]));
152 $this->assertFalse(isset($courses[$course2->id
]));
153 $this->assertFalse(isset($courses[$course3->id
]));
157 * Ensure that export_area_ratings exports all ratings that a user has made, and all ratings for a users own content.
159 public function test_export_area_ratings() {
161 $this->resetAfterTest();
163 $course1 = $this->getDataGenerator()->create_course();
164 $course2 = $this->getDataGenerator()->create_course();
165 $course3 = $this->getDataGenerator()->create_course();
167 $u1 = $this->getDataGenerator()->create_user();
168 $u2 = $this->getDataGenerator()->create_user();
169 $u3 = $this->getDataGenerator()->create_user();
172 $rm = new rating_manager();
173 $ratingoptions = (object) [
174 'component' => 'core_course',
175 'ratingarea' => 'course',
179 // Rate all courses as u1, and something else in the same context.
180 $this->rate_as_user($u1->id
, 'core_course', 'course', $course1->id
, \context_course
::instance($course1->id
), 25);
181 $this->rate_as_user($u1->id
, 'core_course', 'course', $course2->id
, \context_course
::instance($course2->id
), 50);
182 $this->rate_as_user($u1->id
, 'core_course', 'course', $course3->id
, \context_course
::instance($course3->id
), 75);
183 $this->rate_as_user($u1->id
, 'core_course', 'files', $course3->id
, \context_course
::instance($course3->id
), 99);
184 $this->rate_as_user($u1->id
, 'user', 'user', $u3->id
, \context_user
::instance($u3->id
), 10);
186 // Rate course2 as u2, and something else in a different context/component..
187 $this->rate_as_user($u2->id
, 'core_course', 'course', $course2->id
, \context_course
::instance($course2->id
), 90);
188 $this->rate_as_user($u2->id
, 'user', 'user', $u3->id
, \context_user
::instance($u3->id
), 20);
191 // User 1 rated all three courses, and the core_course, and user 3.
192 // User 1::course1 is stored in [] subcontext.
193 $context = \context_course
::instance($course1->id
);
195 provider
::export_area_ratings($u1->id
, $context, $subcontext, 'core_course', 'course', $course1->id
, true);
197 $writer = writer
::with_context($context);
198 $this->assertTrue($writer->has_any_data());
199 $rating = $writer->get_related_data($subcontext, 'rating');
200 $this->assert_has_rating($u1, 25, $rating);
202 // User 1::course2 is stored in ['foo'] subcontext.
203 $context = \context_course
::instance($course2->id
);
204 $subcontext = ['foo'];
205 provider
::export_area_ratings($u1->id
, $context, $subcontext, 'core_course', 'course', $course2->id
, true);
207 $writer = writer
::with_context($context);
208 $this->assertTrue($writer->has_any_data());
209 $result = $writer->get_related_data($subcontext, 'rating');
210 $this->assertCount(1, $result);
211 $this->assert_has_rating($u1, 50, $result);
213 // User 1::course3 is stored in ['foo'] subcontext.
214 $context = \context_course
::instance($course3->id
);
215 $subcontext = ['foo'];
216 provider
::export_area_ratings($u1->id
, $context, $subcontext, 'core_course', 'course', $course3->id
, true);
218 $writer = writer
::with_context($context);
219 $this->assertTrue($writer->has_any_data());
220 $result = $writer->get_related_data($subcontext, 'rating');
221 $this->assertCount(1, $result);
222 $this->assert_has_rating($u1, 75, $result);
224 // User 1::course3::files is stored in ['foo', 'files'] subcontext.
225 $context = \context_course
::instance($course3->id
);
226 $subcontext = ['foo', 'files'];
227 provider
::export_area_ratings($u1->id
, $context, $subcontext, 'core_course', 'files', $course3->id
, true);
229 $writer = writer
::with_context($context);
230 $this->assertTrue($writer->has_any_data());
231 $result = $writer->get_related_data($subcontext, 'rating');
232 $this->assertCount(1, $result);
233 $this->assert_has_rating($u1, 99, $result);
235 // Both users 1 and 2 rated user 3.
236 // Exporting the data for user 3 should include both of those ratings.
237 $context = \context_user
::instance($u3->id
);
238 $subcontext = ['user'];
239 provider
::export_area_ratings($u3->id
, $context, $subcontext, 'user', 'user', $u3->id
, false);
241 $writer = writer
::with_context($context);
242 $this->assertTrue($writer->has_any_data());
243 $result = $writer->get_related_data($subcontext, 'rating');
244 $this->assertCount(2, $result);
245 $this->assert_has_rating($u1, 10, $result);
246 $this->assert_has_rating($u2, 20, $result);
250 * Test delete_ratings() method.
252 public function test_delete_ratings() {
254 $this->resetAfterTest();
256 $course1 = $this->getDataGenerator()->create_course();
257 $course2 = $this->getDataGenerator()->create_course();
258 $course3 = $this->getDataGenerator()->create_course();
260 $u1 = $this->getDataGenerator()->create_user();
261 $u2 = $this->getDataGenerator()->create_user();
262 $u3 = $this->getDataGenerator()->create_user();
264 // Rate all courses as u1, and something else in the same context.
265 $this->rate_as_user($u1->id
, 'core_course', 'course', $course1->id
, \context_course
::instance($course1->id
), 25);
266 $this->rate_as_user($u1->id
, 'core_course', 'course', $course2->id
, \context_course
::instance($course2->id
), 50);
267 $this->rate_as_user($u1->id
, 'core_course', 'course', $course3->id
, \context_course
::instance($course3->id
), 75);
268 $this->rate_as_user($u1->id
, 'core_course', 'files', $course3->id
, \context_course
::instance($course3->id
), 99);
269 $this->rate_as_user($u1->id
, 'core_user', 'user', $u3->id
, \context_user
::instance($u3->id
), 10);
271 // Rate course2 as u2, and something else in a different context/component..
272 $this->rate_as_user($u2->id
, 'core_course', 'course', $course2->id
, \context_course
::instance($course2->id
), 90);
273 $this->rate_as_user($u2->id
, 'core_user', 'user', $u3->id
, \context_user
::instance($u3->id
), 20);
275 // Delete all ratings in course1.
276 $expectedratingscount = $DB->count_records('rating');
277 core_rating\privacy\provider
::delete_ratings(\context_course
::instance($course1->id
));
278 $expectedratingscount -= 1;
279 $this->assertEquals($expectedratingscount, $DB->count_records('rating'));
281 // Delete ratings in course2 specifying wrong component.
282 core_rating\privacy\provider
::delete_ratings(\context_course
::instance($course2->id
), 'other_component');
283 $this->assertEquals($expectedratingscount, $DB->count_records('rating'));
285 // Delete ratings in course2 specifying correct component.
286 core_rating\privacy\provider
::delete_ratings(\context_course
::instance($course2->id
), 'core_course');
287 $expectedratingscount -= 2;
288 $this->assertEquals($expectedratingscount, $DB->count_records('rating'));
290 // Delete user ratings specifyng all attributes.
291 core_rating\privacy\provider
::delete_ratings(\context_user
::instance($u3->id
), 'core_user', 'user', $u3->id
);
292 $expectedratingscount -= 2;
293 $this->assertEquals($expectedratingscount, $DB->count_records('rating'));
297 * Test delete_ratings_select() method.
299 public function test_delete_ratings_select() {
301 $this->resetAfterTest();
303 $course1 = $this->getDataGenerator()->create_course();
304 $course2 = $this->getDataGenerator()->create_course();
305 $course3 = $this->getDataGenerator()->create_course();
307 $u1 = $this->getDataGenerator()->create_user();
308 $u2 = $this->getDataGenerator()->create_user();
309 $u3 = $this->getDataGenerator()->create_user();
311 // Rate all courses as u1, and something else in the same context.
312 $this->rate_as_user($u1->id
, 'core_course', 'course', $course1->id
, \context_course
::instance($course1->id
), 25);
313 $this->rate_as_user($u1->id
, 'core_course', 'course', $course2->id
, \context_course
::instance($course2->id
), 50);
314 $this->rate_as_user($u1->id
, 'core_course', 'course', $course3->id
, \context_course
::instance($course3->id
), 75);
315 $this->rate_as_user($u1->id
, 'core_course', 'files', $course3->id
, \context_course
::instance($course3->id
), 99);
316 $this->rate_as_user($u1->id
, 'core_user', 'user', $u3->id
, \context_user
::instance($u3->id
), 10);
318 // Rate course2 as u2, and something else in a different context/component..
319 $this->rate_as_user($u2->id
, 'core_course', 'course', $course2->id
, \context_course
::instance($course2->id
), 90);
320 $this->rate_as_user($u2->id
, 'core_user', 'user', $u3->id
, \context_user
::instance($u3->id
), 20);
322 // Delete ratings in course1.
323 list($sql, $params) = $DB->get_in_or_equal([$course1->id
, $course2->id
], SQL_PARAMS_NAMED
);
324 $expectedratingscount = $DB->count_records('rating');
325 core_rating\privacy\provider
::delete_ratings_select(\context_course
::instance($course1->id
),
326 'core_course', 'course', $sql, $params);
327 $expectedratingscount -= 1;
328 $this->assertEquals($expectedratingscount, $DB->count_records('rating'));
332 * Assert that a user has the correct rating.
334 * @param \stdClass $author The user with the rating
335 * @param int $score The rating that was given
336 * @param \stdClass[] The ratings which were found
338 protected function assert_has_rating($author, $score, $actual) {
340 foreach ($actual as $rating) {
341 if ($author->id
== $rating->author
) {
343 $this->assertEquals($score, $rating->rating
);
346 $this->assertTrue($found);