MDL-63658 core_favourites: adding paging support to the service layer
[moodle.git] / favourites / tests / repository_test.php
CommitLineData
d4e98ee5
JD
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/**
18 * Testing the repository objects within core_favourites.
19 *
20 * @package core_favourites
21 * @category test
22 * @copyright 2018 Jake Dallimore <jrhdallimore@gmail.com>
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 */
25
26defined('MOODLE_INTERNAL') || die();
27
4a02aae5 28use \core_favourites\local\repository\favourites_repository;
d4e98ee5
JD
29
30/**
31 * Test class covering the favourites_repository.
32 *
33 * @copyright 2018 Jake Dallimore <jrhdallimore@gmail.com>
34 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
35 */
36class favourites_repository_testcase extends advanced_testcase {
37
38 public function setUp() {
39 $this->resetAfterTest();
40 }
41
42 // Basic setup stuff to be reused in most tests.
43 protected function setup_users_and_courses() {
44 $user1 = self::getDataGenerator()->create_user();
45 $user1context = \context_user::instance($user1->id);
46 $user2 = self::getDataGenerator()->create_user();
47 $user2context = \context_user::instance($user2->id);
48 $course1 = self::getDataGenerator()->create_course();
49 $course2 = self::getDataGenerator()->create_course();
50 $course1context = context_course::instance($course1->id);
51 $course2context = context_course::instance($course2->id);
52 return [$user1context, $user2context, $course1context, $course2context];
53 }
54
55 /**
56 * Verify the basic create operation can create records, and is validated.
57 */
58 public function test_add() {
59 list($user1context, $user2context, $course1context, $course2context) = $this->setup_users_and_courses();
60
61 // Create a favourites repository and favourite a course.
62 $favouritesrepo = new favourites_repository($user1context);
63
64 $favcourse = (object)[
65 'userid' => $user1context->instanceid,
66 'component' => 'core_course',
67 'itemtype' => 'course',
68 'itemid' => $course1context->instanceid,
69 'contextid' => $course1context->id,
70 ];
71 $timenow = time(); // Reference only, to check that the created item has a time equal to or greater than this.
72 $favourite = $favouritesrepo->add($favcourse);
73
74 // Verify we get the record back.
75 $this->assertInstanceOf(\stdClass::class, $favourite);
76 $this->assertEquals('core_course', $favourite->component);
77 $this->assertEquals('course', $favourite->itemtype);
78
79 // Verify the returned object has additional properties, created as part of the add.
80 $this->assertObjectHasAttribute('ordering', $favourite);
81 $this->assertObjectHasAttribute('timecreated', $favourite);
82 $this->assertGreaterThanOrEqual($timenow, $favourite->timecreated);
83
84 // Try to save the same record again and confirm the store throws an exception.
85 $this->expectException('dml_write_exception');
86 $favouritesrepo->add($favcourse);
87 }
88
89 /**
90 * Tests that malformed favourites cannot be saved.
91 */
92 public function test_add_malformed_favourite() {
93 list($user1context, $user2context, $course1context, $course2context) = $this->setup_users_and_courses();
94
95 // Create a favourites repository and favourite a course.
96 $favouritesrepo = new favourites_repository($user1context);
97
98 $favcourse = (object)[
99 'userid' => $user1context->instanceid,
100 'component' => 'core_course',
101 'itemtype' => 'course',
102 'itemid' => $course1context->instanceid,
103 'contextid' => $course1context->id,
104 'anotherfield' => 'cat'
105 ];
106
107 $this->expectException('moodle_exception');
108 $favouritesrepo->add($favcourse);
109 }
110
111 /**
112 * Tests that incomplete favourites cannot be saved.
113 */
114 public function test_add_incomplete_favourite() {
115 list($user1context, $user2context, $course1context, $course2context) = $this->setup_users_and_courses();
116
117 // Create a favourites repository and favourite a course.
118 $favouritesrepo = new favourites_repository($user1context);
119
120 $favcourse = (object)[
121 'component' => 'core_course',
122 'itemtype' => 'course',
123 'itemid' => $course1context->instanceid
124 ];
125
126 $this->expectException('moodle_exception');
127 $favouritesrepo->add($favcourse);
128 }
129
130 public function test_add_all_basic() {
131 list($user1context, $user2context, $course1context, $course2context) = $this->setup_users_and_courses();
132
133 // Create a favourites repository and favourite several courses.
134 $favouritesrepo = new favourites_repository($user1context);
135 $favcourses = [];
136
137 $favcourses[] = (object)[
138 'userid' => $user1context->instanceid,
139 'component' => 'core_course',
140 'itemtype' => 'course',
141 'itemid' => $course1context->instanceid,
142 'contextid' => $course1context->id,
143 ];
144 $favcourses[] = (object)[
145 'userid' => $user1context->instanceid,
146 'component' => 'core_course',
147 'itemtype' => 'course',
148 'itemid' => $course2context->instanceid,
149 'contextid' => $course2context->id,
150 ];
151 $timenow = time(); // Reference only, to check that the created item has a time equal to or greater than this.
152 $favourites = $favouritesrepo->add_all($favcourses);
153
154 $this->assertInternalType('array', $favourites);
155 $this->assertCount(2, $favourites);
156 foreach ($favourites as $favourite) {
157 // Verify we get the record back.
158 $this->assertEquals('core_course', $favourite->component);
159 $this->assertEquals('course', $favourite->itemtype);
160
161 // Verify the returned object has additional properties, created as part of the add.
162 $this->assertObjectHasAttribute('ordering', $favourite);
163 $this->assertObjectHasAttribute('timecreated', $favourite);
164 $this->assertGreaterThanOrEqual($timenow, $favourite->timecreated);
165 }
166
167 // Try to save the same record again and confirm the store throws an exception.
168 $this->expectException('dml_write_exception');
169 $favouritesrepo->add_all($favcourses);
170 }
171
172 /**
173 * Tests reading from the repository by instance id.
174 */
175 public function test_find() {
176 list($user1context, $user2context, $course1context, $course2context) = $this->setup_users_and_courses();
177
178 // Create a favourites repository and favourite a course.
179 $favouritesrepo = new favourites_repository($user1context);
180 $favourite = (object) [
181 'userid' => $user1context->instanceid,
182 'component' => 'core_course',
183 'itemtype' => 'course',
184 'itemid' => $course1context->instanceid,
185 'contextid' => $course1context->id
186 ];
187 $favourite = $favouritesrepo->add($favourite);
188
189 // Now, from the repo, get the single favourite we just created, by id.
190 $userfavourite = $favouritesrepo->find($favourite->id);
191 $this->assertInstanceOf(\stdClass::class, $userfavourite);
192 $this->assertObjectHasAttribute('timecreated', $userfavourite);
193
194 // Try to get a favourite we know doesn't exist.
195 // We expect an exception in this case.
196 $this->expectException(dml_exception::class);
197 $favouritesrepo->find(1);
198 }
199
200 /**
201 * Test verifying that find_all() returns all favourites, or an empty array.
202 */
203 public function test_find_all() {
204 list($user1context, $user2context, $course1context, $course2context) = $this->setup_users_and_courses();
205
206 $favouritesrepo = new favourites_repository($user1context);
207
208 // Verify that for an empty repository, find_all returns an empty array.
209 $this->assertEquals([], $favouritesrepo->find_all());
210
211 // Save a favourite for 2 courses, in different areas.
212 $favourite = (object) [
213 'userid' => $user1context->instanceid,
214 'component' => 'core_course',
215 'itemtype' => 'course',
216 'itemid' => $course1context->instanceid,
217 'contextid' => $course1context->id
218 ];
219 $favourite2 = (object) [
220 'userid' => $user1context->instanceid,
221 'component' => 'core_course',
222 'itemtype' => 'anothertype',
223 'itemid' => $course2context->instanceid,
224 'contextid' => $course2context->id
225 ];
226 $favouritesrepo->add($favourite);
227 $favouritesrepo->add($favourite2);
228
229 // Verify that find_all returns both of our favourites.
230 $favourites = $favouritesrepo->find_all();
231 $this->assertCount(2, $favourites);
232 foreach ($favourites as $fav) {
233 $this->assertObjectHasAttribute('id', $fav);
234 $this->assertObjectHasAttribute('timecreated', $fav);
235 }
236 }
237
8ffbe9c1
JD
238 /**
239 * Testing the pagination of the find_all method.
240 */
241 public function test_find_all_pagination() {
242 list($user1context, $user2context, $course1context, $course2context) = $this->setup_users_and_courses();
243
244 $favouritesrepo = new favourites_repository($user1context);
245
246 // Verify that for an empty repository, find_all with any combination of page options returns an empty array.
247 $this->assertEquals([], $favouritesrepo->find_all(0, 0));
248 $this->assertEquals([], $favouritesrepo->find_all(0, 10));
249 $this->assertEquals([], $favouritesrepo->find_all(1, 0));
250 $this->assertEquals([], $favouritesrepo->find_all(1, 10));
251
252 // Save 10 arbitrary favourites to the repo.
253 foreach (range(1, 10) as $i) {
254 $favourite = (object) [
255 'userid' => $user1context->instanceid,
256 'component' => 'core_course',
257 'itemtype' => 'course',
258 'itemid' => $i,
259 'contextid' => $course1context->id
260 ];
261 $favouritesrepo->add($favourite);
262 }
263
264 // Verify we have 10 favourites.
265 $this->assertEquals(10, $favouritesrepo->count());
266
267 // Verify we can fetch the first page of 5 records.
268 $favourites = $favouritesrepo->find_all(0, 5);
269 $this->assertCount(5, $favourites);
270
271 // Verify we can fetch the second page.
272 $favourites = $favouritesrepo->find_all(5, 5);
273 $this->assertCount(5, $favourites);
274
275 // Verify the third page request ends with an empty array.
276 $favourites = $favouritesrepo->find_all(10, 5);
277 $this->assertCount(0, $favourites);
278 }
279
d4e98ee5
JD
280 /**
281 * Test retrieval of a user's favourites for a given criteria, in this case, area.
282 */
283 public function test_find_by() {
284 list($user1context, $user2context, $course1context, $course2context) = $this->setup_users_and_courses();
285
286 // Create a favourites repository and favourite a course.
287 $favouritesrepo = new favourites_repository($user1context);
288 $favourite = (object) [
289 'userid' => $user1context->instanceid,
290 'component' => 'core_course',
291 'itemtype' => 'course',
292 'itemid' => $course1context->instanceid,
293 'contextid' => $course1context->id
294 ];
295 $favouritesrepo->add($favourite);
296
297 // From the repo, get the list of favourites for the 'core_course/course' area.
298 $userfavourites = $favouritesrepo->find_by(['component' => 'core_course', 'itemtype' => 'course']);
299 $this->assertInternalType('array', $userfavourites);
300 $this->assertCount(1, $userfavourites);
301
302 // Try to get a list of favourites for a non-existent area.
303 $userfavourites = $favouritesrepo->find_by(['component' => 'core_cannibalism', 'itemtype' => 'course']);
304 $this->assertInternalType('array', $userfavourites);
305 $this->assertCount(0, $userfavourites);
306 }
307
8ffbe9c1
JD
308 /**
309 * Testing the pagination of the find_by method.
310 */
311 public function test_find_by_pagination() {
312 list($user1context, $user2context, $course1context, $course2context) = $this->setup_users_and_courses();
313
314 $favouritesrepo = new favourites_repository($user1context);
315
316 // Verify that for an empty repository, find_all with any combination of page options returns an empty array.
317 $this->assertEquals([], $favouritesrepo->find_by([], 0, 0));
318 $this->assertEquals([], $favouritesrepo->find_by([], 0, 10));
319 $this->assertEquals([], $favouritesrepo->find_by([], 1, 0));
320 $this->assertEquals([], $favouritesrepo->find_by([], 1, 10));
321
322 // Save 10 arbitrary favourites to the repo.
323 foreach (range(1, 10) as $i) {
324 $favourite = (object) [
325 'userid' => $user1context->instanceid,
326 'component' => 'core_course',
327 'itemtype' => 'course',
328 'itemid' => $i,
329 'contextid' => $course1context->id
330 ];
331 $favouritesrepo->add($favourite);
332 }
333
334 // Verify we have 10 favourites.
335 $this->assertEquals(10, $favouritesrepo->count());
336
337 // Verify a request for a page, when no criteria match, results in an empty array.
338 $favourites = $favouritesrepo->find_by(['component' => 'core_message'], 0, 5);
339 $this->assertCount(0, $favourites);
340
341 // Verify we can fetch a the first page of 5 records.
342 $favourites = $favouritesrepo->find_by(['component' => 'core_course'], 0, 5);
343 $this->assertCount(5, $favourites);
344
345 // Verify we can fetch the second page.
346 $favourites = $favouritesrepo->find_by(['component' => 'core_course'], 5, 5);
347 $this->assertCount(5, $favourites);
348
349 // Verify the third page request ends with an empty array.
350 $favourites = $favouritesrepo->find_by(['component' => 'core_course'], 10, 5);
351 $this->assertCount(0, $favourites);
352 }
353
d4e98ee5
JD
354 /**
355 * Test the count_by() method.
356 */
357 public function test_count_by() {
358 list($user1context, $user2context, $course1context, $course2context) = $this->setup_users_and_courses();
359
360 // Create a favourites repository and add 2 favourites in different areas.
361 $favouritesrepo = new favourites_repository($user1context);
362 $favourite = (object) [
363 'userid' => $user1context->instanceid,
364 'component' => 'core_course',
365 'itemtype' => 'course',
366 'itemid' => $course1context->instanceid,
367 'contextid' => $course1context->id
368 ];
369 $favourite2 = (object) [
370 'userid' => $user1context->instanceid,
371 'component' => 'core_course',
372 'itemtype' => 'anothertype',
373 'itemid' => $course2context->instanceid,
374 'contextid' => $course2context->id
375 ];
376 $favouritesrepo->add($favourite);
377 $favouritesrepo->add($favourite2);
378
379 // Verify counts can be restricted by criteria.
380 $this->assertEquals(1, $favouritesrepo->count_by(['userid' => $user1context->instanceid, 'component' => 'core_course',
381 'itemtype' => 'course']));
382 $this->assertEquals(1, $favouritesrepo->count_by(['userid' => $user1context->instanceid, 'component' => 'core_course',
383 'itemtype' => 'anothertype']));
384 $this->assertEquals(0, $favouritesrepo->count_by(['userid' => $user1context->instanceid, 'component' => 'core_course',
385 'itemtype' => 'nonexistenttype']));
386 }
387
388 public function test_exists() {
389 list($user1context, $user2context, $course1context, $course2context) = $this->setup_users_and_courses();
390
391 // Create a favourites repository and favourite a course.
392 $favouritesrepo = new favourites_repository($user1context);
393 $favourite = (object) [
394 'userid' => $user1context->instanceid,
395 'component' => 'core_course',
396 'itemtype' => 'course',
397 'itemid' => $course1context->instanceid,
398 'contextid' => $course1context->id
399 ];
400 $createdfavourite = $favouritesrepo->add($favourite);
401
402 // Verify the existence of the favourite in the repo.
403 $this->assertTrue($favouritesrepo->exists($createdfavourite->id));
404
405 // Verify exists returns false for non-existent favourite.
406 $this->assertFalse($favouritesrepo->exists(1));
407 }
408
409 public function test_exists_by_area() {
410 list($user1context, $user2context, $course1context, $course2context) = $this->setup_users_and_courses();
411
412 // Create a favourites repository and favourite two courses, in different areas.
413 $favouritesrepo = new favourites_repository($user1context);
414 $favourite = (object) [
415 'userid' => $user1context->instanceid,
416 'component' => 'core_course',
417 'itemtype' => 'course',
418 'itemid' => $course1context->instanceid,
419 'contextid' => $course1context->id
420 ];
421 $favourite2 = (object) [
422 'userid' => $user1context->instanceid,
423 'component' => 'core_course',
424 'itemtype' => 'anothertype',
425 'itemid' => $course2context->instanceid,
426 'contextid' => $course2context->id
427 ];
428 $favourite1 = $favouritesrepo->add($favourite);
429 $favourite2 = $favouritesrepo->add($favourite2);
430
431 // Verify the existence of the favourites.
432 $this->assertTrue($favouritesrepo->exists_by_area($user1context->instanceid, 'core_course', 'course', $favourite1->itemid,
433 $favourite1->contextid));
434 $this->assertTrue($favouritesrepo->exists_by_area($user1context->instanceid, 'core_course', 'anothertype',
435 $favourite2->itemid, $favourite2->contextid));
436
437 // Verify that we can't find a favourite from one area, in another.
438 $this->assertFalse($favouritesrepo->exists_by_area($user1context->instanceid, 'core_course', 'anothertype',
439 $favourite1->itemid, $favourite1->contextid));
440 }
441
442 /**
443 * Test the update() method, by simulating a user changing the ordering of a favourite.
444 */
445 public function test_update() {
446 list($user1context, $user2context, $course1context, $course2context) = $this->setup_users_and_courses();
447
448 // Create a favourites repository and favourite a course.
449 $favouritesrepo = new favourites_repository($user1context);
450 $favourite = (object) [
451 'userid' => $user1context->instanceid,
452 'component' => 'core_course',
453 'itemtype' => 'course',
454 'itemid' => $course1context->instanceid,
455 'contextid' => $course1context->id
456 ];
457 $favourite1 = $favouritesrepo->add($favourite);
458
459 // Verify we can update the ordering for 2 favourites.
460 $favourite1->ordering = 1;
461 $favourite1 = $favouritesrepo->update($favourite1);
462 $this->assertInstanceOf(stdClass::class, $favourite1);
463 $this->assertAttributeEquals('1', 'ordering', $favourite1);
464 }
465
466 public function test_delete() {
467 list($user1context, $user2context, $course1context, $course2context) = $this->setup_users_and_courses();
468
469 // Create a favourites repository and favourite a course.
470 $favouritesrepo = new favourites_repository($user1context);
471 $favourite = (object) [
472 'userid' => $user1context->instanceid,
473 'component' => 'core_course',
474 'itemtype' => 'course',
475 'itemid' => $course1context->instanceid,
476 'contextid' => $course1context->id
477 ];
478 $favourite = $favouritesrepo->add($favourite);
479
480 // Verify the existence of the favourite in the repo.
481 $this->assertTrue($favouritesrepo->exists($favourite->id));
482
483 // Now, delete the favourite and confirm it's not retrievable.
484 $favouritesrepo->delete($favourite->id);
485 $this->assertFalse($favouritesrepo->exists($favourite->id));
486 }
487
488 public function test_delete_by_area() {
489 list($user1context, $user2context, $course1context, $course2context) = $this->setup_users_and_courses();
490
491 // Create a favourites repository and favourite two courses, in different areas.
492 $favouritesrepo = new favourites_repository($user1context);
493 $favourite = (object) [
494 'userid' => $user1context->instanceid,
495 'component' => 'core_course',
496 'itemtype' => 'course',
497 'itemid' => $course1context->instanceid,
498 'contextid' => $course1context->id
499 ];
500 $favourite2 = (object) [
501 'userid' => $user1context->instanceid,
502 'component' => 'core_course',
503 'itemtype' => 'anothertype',
504 'itemid' => $course2context->instanceid,
505 'contextid' => $course2context->id
506 ];
507 $favourite1 = $favouritesrepo->add($favourite);
508 $favourite2 = $favouritesrepo->add($favourite2);
509
510 // Verify we have 2 items in the repo.
511 $this->assertEquals(2, $favouritesrepo->count());
512
513 // Try to delete by a non-existent area, and confirm it doesn't remove anything.
514 $favouritesrepo->delete_by_area($user1context->instanceid, 'core_course', 'donaldduck');
515 $this->assertEquals(2, $favouritesrepo->count());
516
517 // Try to delete by a non-existent area, and confirm it doesn't remove anything.
518 $favouritesrepo->delete_by_area($user1context->instanceid, 'core_course', 'cat');
519 $this->assertEquals(2, $favouritesrepo->count());
520
521 // Delete by area, and confirm we have one record left, from the 'core_course/anothertype' area.
522 $favouritesrepo->delete_by_area($user1context->instanceid, 'core_course', 'course');
523 $this->assertEquals(1, $favouritesrepo->count());
524 $this->assertFalse($favouritesrepo->exists($favourite1->id));
525 $this->assertTrue($favouritesrepo->exists($favourite2->id));
526 }
527}