df6a65b95743bf6879fcbe07b3f0ba449843094b
[moodle.git] / mod / data / tests / externallib_test.php
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/>.
17 /**
18  * Database module external functions tests
19  *
20  * @package    mod_data
21  * @category   external
22  * @copyright  2015 Juan Leyva <juan@moodle.com>
23  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  * @since      Moodle 2.9
25  */
27 defined('MOODLE_INTERNAL') || die();
29 global $CFG;
31 require_once($CFG->dirroot . '/webservice/tests/helpers.php');
33 /**
34  * Database module external functions tests
35  *
36  * @package    mod_data
37  * @category   external
38  * @copyright  2015 Juan Leyva <juan@moodle.com>
39  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
40  * @since      Moodle 2.9
41  */
42 class mod_data_external_testcase extends externallib_advanced_testcase {
44     /**
45      * Set up for every test
46      */
47     public function setUp() {
48         global $DB;
49         $this->resetAfterTest();
50         $this->setAdminUser();
52         // Setup test data.
53         $course = new stdClass();
54         $course->groupmode = SEPARATEGROUPS;
55         $course->groupmodeforce = true;
56         $this->course = $this->getDataGenerator()->create_course($course);
57         $this->data = $this->getDataGenerator()->create_module('data', array('course' => $this->course->id));
58         $this->context = context_module::instance($this->data->cmid);
59         $this->cm = get_coursemodule_from_instance('data', $this->data->id);
61         // Create users.
62         $this->student1 = self::getDataGenerator()->create_user();
63         $this->student2 = self::getDataGenerator()->create_user();
64         $this->student3 = self::getDataGenerator()->create_user();
65         $this->teacher = self::getDataGenerator()->create_user();
67         // Users enrolments.
68         $this->studentrole = $DB->get_record('role', array('shortname' => 'student'));
69         $this->teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
70         $this->getDataGenerator()->enrol_user($this->student1->id, $this->course->id, $this->studentrole->id, 'manual');
71         $this->getDataGenerator()->enrol_user($this->student2->id, $this->course->id, $this->studentrole->id, 'manual');
72         $this->getDataGenerator()->enrol_user($this->student3->id, $this->course->id, $this->studentrole->id, 'manual');
73         $this->getDataGenerator()->enrol_user($this->teacher->id, $this->course->id, $this->teacherrole->id, 'manual');
75         $this->group1 = $this->getDataGenerator()->create_group(array('courseid' => $this->course->id));
76         $this->group2 = $this->getDataGenerator()->create_group(array('courseid' => $this->course->id));
77         groups_add_member($this->group1, $this->student1);
78         groups_add_member($this->group1, $this->student2);
79         groups_add_member($this->group2, $this->student3);
80     }
82     /**
83      * Test get databases by courses
84      */
85     public function test_mod_data_get_databases_by_courses() {
86         global $DB;
88         $this->resetAfterTest(true);
90         // Create users.
91         $student = self::getDataGenerator()->create_user();
92         $teacher = self::getDataGenerator()->create_user();
94         // Set to the student user.
95         self::setUser($student);
97         // Create courses to add the modules.
98         $course1 = self::getDataGenerator()->create_course();
99         $course2 = self::getDataGenerator()->create_course();
101         // First database.
102         $record = new stdClass();
103         $record->introformat = FORMAT_HTML;
104         $record->course = $course1->id;
105         $database1 = self::getDataGenerator()->create_module('data', $record);
107         // Second database.
108         $record = new stdClass();
109         $record->introformat = FORMAT_HTML;
110         $record->course = $course2->id;
111         $database2 = self::getDataGenerator()->create_module('data', $record);
113         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
114         $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
116         // Users enrolments.
117         $this->getDataGenerator()->enrol_user($student->id, $course1->id, $studentrole->id, 'manual');
118         $this->getDataGenerator()->enrol_user($teacher->id, $course1->id, $teacherrole->id, 'manual');
120         // Execute real Moodle enrolment as we'll call unenrol() method on the instance later.
121         $enrol = enrol_get_plugin('manual');
122         $enrolinstances = enrol_get_instances($course2->id, true);
123         foreach ($enrolinstances as $courseenrolinstance) {
124             if ($courseenrolinstance->enrol == "manual") {
125                 $instance2 = $courseenrolinstance;
126                 break;
127             }
128         }
129         $enrol->enrol_user($instance2, $student->id, $studentrole->id);
131         // Create what we expect to be returned when querying the two courses.
132         // First for the student user.
133         $expectedfields = array('id', 'coursemodule', 'course', 'name', 'comments', 'timeavailablefrom',
134                             'timeavailableto', 'timeviewfrom', 'timeviewto', 'requiredentries', 'requiredentriestoview',
135                             'intro', 'introformat', 'introfiles', 'maxentries', 'rssarticles', 'singletemplate', 'listtemplate',
136                             'listtemplateheader', 'listtemplatefooter', 'addtemplate', 'rsstemplate', 'rsstitletemplate',
137                             'csstemplate', 'jstemplate', 'asearchtemplate', 'approval', 'defaultsort', 'defaultsortdir', 'manageapproved');
139         // Add expected coursemodule.
140         $database1->coursemodule = $database1->cmid;
141         $database1->introfiles = [];
142         $database2->coursemodule = $database2->cmid;
143         $database2->introfiles = [];
145         $expected1 = array();
146         $expected2 = array();
147         foreach ($expectedfields as $field) {
148             if ($field == 'approval' or $field == 'manageapproved') {
149                 $database1->{$field} = (bool) $database1->{$field};
150                 $database2->{$field} = (bool) $database2->{$field};
151             }
152             $expected1[$field] = $database1->{$field};
153             $expected2[$field] = $database2->{$field};
154         }
155         $expected1['comments'] = (bool) $expected1['comments'];
156         $expected2['comments'] = (bool) $expected2['comments'];
158         $expecteddatabases = array();
159         $expecteddatabases[] = $expected2;
160         $expecteddatabases[] = $expected1;
162         // Call the external function passing course ids.
163         $result = mod_data_external::get_databases_by_courses(array($course2->id, $course1->id));
164         $result = external_api::clean_returnvalue(mod_data_external::get_databases_by_courses_returns(), $result);
165         $this->assertEquals($expecteddatabases, $result['databases']);
167         // Call the external function without passing course id.
168         $result = mod_data_external::get_databases_by_courses();
169         $result = external_api::clean_returnvalue(mod_data_external::get_databases_by_courses_returns(), $result);
170         $this->assertEquals($expecteddatabases, $result['databases']);
172         // Unenrol user from second course and alter expected databases.
173         $enrol->unenrol_user($instance2, $student->id);
174         array_shift($expecteddatabases);
176         // Call the external function without passing course id.
177         $result = mod_data_external::get_databases_by_courses();
178         $result = external_api::clean_returnvalue(mod_data_external::get_databases_by_courses_returns(), $result);
179         $this->assertEquals($expecteddatabases, $result['databases']);
181         // Call for the second course we unenrolled the user from, expected warning.
182         $result = mod_data_external::get_databases_by_courses(array($course2->id));
183         $this->assertCount(1, $result['warnings']);
184         $this->assertEquals('1', $result['warnings'][0]['warningcode']);
185         $this->assertEquals($course2->id, $result['warnings'][0]['itemid']);
187         // Now, try as a teacher for getting all the additional fields.
188         self::setUser($teacher);
190         $additionalfields = array('scale', 'assessed', 'assesstimestart', 'assesstimefinish', 'editany', 'notification', 'timemodified');
192         foreach ($additionalfields as $field) {
193             if ($field == 'editany') {
194                 $database1->{$field} = (bool) $database1->{$field};
195             }
196             $expecteddatabases[0][$field] = $database1->{$field};
197         }
198         $result = mod_data_external::get_databases_by_courses();
199         $result = external_api::clean_returnvalue(mod_data_external::get_databases_by_courses_returns(), $result);
200         $this->assertEquals($expecteddatabases, $result['databases']);
202         // Admin should get all the information.
203         self::setAdminUser();
205         $result = mod_data_external::get_databases_by_courses(array($course1->id));
206         $result = external_api::clean_returnvalue(mod_data_external::get_databases_by_courses_returns(), $result);
207         $this->assertEquals($expecteddatabases, $result['databases']);
208     }
210     /**
211      * Test view_database invalid id.
212      */
213     public function test_view_database_invalid_id() {
215         // Test invalid instance id.
216         $this->setExpectedException('moodle_exception');
217         mod_data_external::view_database(0);
218     }
220     /**
221      * Test view_database not enrolled user.
222      */
223     public function test_view_database_not_enrolled_user() {
225         $usernotenrolled = self::getDataGenerator()->create_user();
226         $this->setUser($usernotenrolled);
228         $this->setExpectedException('moodle_exception');
229         mod_data_external::view_database(0);
230     }
232     /**
233      * Test view_database no capabilities.
234      */
235     public function test_view_database_no_capabilities() {
236         // Test user with no capabilities.
237         // We need a explicit prohibit since this capability is allowed for students by default.
238         assign_capability('mod/data:viewpage', CAP_PROHIBIT, $this->studentrole->id, $this->context->id);
239         accesslib_clear_all_caches_for_unit_testing();
241         $this->setExpectedException('moodle_exception');
242         mod_data_external::view_database(0);
243     }
245     /**
246      * Test view_database.
247      */
248     public function test_view_database() {
250         // Test user with full capabilities.
251         $this->setUser($this->student1);
253         // Trigger and capture the event.
254         $sink = $this->redirectEvents();
256         $result = mod_data_external::view_database($this->data->id);
257         $result = external_api::clean_returnvalue(mod_data_external::view_database_returns(), $result);
259         $events = $sink->get_events();
260         $this->assertCount(1, $events);
261         $event = array_shift($events);
263         // Checking that the event contains the expected values.
264         $this->assertInstanceOf('\mod_data\event\course_module_viewed', $event);
265         $this->assertEquals($this->context, $event->get_context());
266         $moodledata = new \moodle_url('/mod/data/view.php', array('id' => $this->cm->id));
267         $this->assertEquals($moodledata, $event->get_url());
268         $this->assertEventContextNotUsed($event);
269         $this->assertNotEmpty($event->get_name());
270     }
272     /**
273      * Test get_data_access_information for student.
274      */
275     public function test_get_data_access_information_student() {
276         global $DB;
277         // Modify the database to add access restrictions.
278         $this->data->timeavailablefrom = time() + DAYSECS;
279         $this->data->requiredentries = 2;
280         $this->data->requiredentriestoview = 2;
281         $DB->update_record('data', $this->data);
283         // Test user with full capabilities.
284         $this->setUser($this->student1);
286         $result = mod_data_external::get_data_access_information($this->data->id);
287         $result = external_api::clean_returnvalue(mod_data_external::get_data_access_information_returns(), $result);
289         $this->assertEquals($this->group1->id, $result['groupid']);
291         $this->assertFalse($result['canmanageentries']);
292         $this->assertFalse($result['canapprove']);
293         $this->assertTrue($result['canaddentry']);  // It return true because it doen't check time restrictions.
294         $this->assertFalse($result['timeavailable']);
295         $this->assertFalse($result['inreadonlyperiod']);
296         $this->assertEquals(0, $result['numentries']);
297         $this->assertEquals($this->data->requiredentries, $result['entrieslefttoadd']);
298         $this->assertEquals($this->data->requiredentriestoview, $result['entrieslefttoview']);
299     }
301     /**
302      * Test get_data_access_information for teacher.
303      */
304     public function test_get_data_access_information_teacher() {
305         global $DB;
306         // Modify the database to add access restrictions.
307         $this->data->timeavailablefrom = time() + DAYSECS;
308         $this->data->requiredentries = 2;
309         $this->data->requiredentriestoview = 2;
310         $DB->update_record('data', $this->data);
312         // Test user with full capabilities.
313         $this->setUser($this->teacher);
315         $result = mod_data_external::get_data_access_information($this->data->id);
316         $result = external_api::clean_returnvalue(mod_data_external::get_data_access_information_returns(), $result);
318         $this->assertEquals(0, $result['groupid']);
320         $this->assertTrue($result['canmanageentries']);
321         $this->assertTrue($result['canapprove']);
322         $this->assertTrue($result['canaddentry']);  // It return true because it doen't check time restrictions.
323         $this->assertTrue($result['timeavailable']);
324         $this->assertFalse($result['inreadonlyperiod']);
325         $this->assertEquals(0, $result['numentries']);
326         $this->assertEquals(0, $result['entrieslefttoadd']);
327         $this->assertEquals(0, $result['entrieslefttoview']);
328     }
330     /**
331      * Helper method to populate the database with some entries.
332      *
333      * @return array the entry ids created
334      */
335     public function populate_database_with_entries() {
336         global $DB;
338         // Force approval.
339         $DB->set_field('data', 'approval', 1, array('id' => $this->data->id));
340         $generator = $this->getDataGenerator()->get_plugin_generator('mod_data');
341         $fieldtypes = array('checkbox', 'date', 'menu', 'multimenu', 'number', 'radiobutton', 'text', 'textarea', 'url');
343         $count = 1;
344         // Creating test Fields with default parameter values.
345         foreach ($fieldtypes as $fieldtype) {
346             $fieldname = 'field-' . $count;
347             $record = new StdClass();
348             $record->name = $fieldname;
349             $record->type = $fieldtype;
350             $record->required = 1;
352             $generator->create_field($record, $this->data);
353             $count++;
354         }
355         // Get all the fields created.
356         $fields = $DB->get_records('data_fields', array('dataid' => $this->data->id), 'id');
358         // Populate with contents, creating a new entry.
359         $contents = array();
360         $contents[] = array('opt1', 'opt2', 'opt3', 'opt4');
361         $contents[] = '01-01-2037'; // It should be lower than 2038, to avoid failing on 32-bit windows.
362         $contents[] = 'menu1';
363         $contents[] = array('multimenu1', 'multimenu2', 'multimenu3', 'multimenu4');
364         $contents[] = '12345';
365         $contents[] = 'radioopt1';
366         $contents[] = 'text for testing';
367         $contents[] = '<p>text area testing<br /></p>';
368         $contents[] = array('example.url', 'sampleurl');
369         $count = 0;
370         $fieldcontents = array();
371         foreach ($fields as $fieldrecord) {
372             $fieldcontents[$fieldrecord->id] = $contents[$count++];
373         }
375         $this->setUser($this->student1);
376         $entry11 = $generator->create_entry($this->data, $fieldcontents, $this->group1->id);
377         $this->setUser($this->student2);
378         $entry12 = $generator->create_entry($this->data, $fieldcontents, $this->group1->id);
379         $entry13 = $generator->create_entry($this->data, $fieldcontents, $this->group1->id);
381         $this->setUser($this->student3);
382         $entry21 = $generator->create_entry($this->data, $fieldcontents, $this->group2->id);
384         // Approve all except $entry13.
385         $DB->set_field('data_records', 'approved', 1, ['id' => $entry11]);
386         $DB->set_field('data_records', 'approved', 1, ['id' => $entry12]);
387         $DB->set_field('data_records', 'approved', 1, ['id' => $entry21]);
389         return [$entry11, $entry12, $entry13, $entry21];
390     }
392     /**
393      * Test get_entries
394      */
395     public function test_get_entries() {
396         global $DB;
397         list($entry11, $entry12, $entry13, $entry21) = self::populate_database_with_entries();
399         // First of all, expect to see only my group entries (not other users in other groups ones).
400         $this->setUser($this->student1);
401         $result = mod_data_external::get_entries($this->data->id);
402         $result = external_api::clean_returnvalue(mod_data_external::get_entries_returns(), $result);
403         $this->assertCount(0, $result['warnings']);
404         $this->assertCount(2, $result['entries']);
405         $this->assertEquals(2, $result['totalcount']);
406         $this->assertEquals($entry11, $result['entries'][0]['id']);
407         $this->assertEquals($this->student1->id, $result['entries'][0]['userid']);
408         $this->assertEquals($this->group1->id, $result['entries'][0]['groupid']);
409         $this->assertEquals($this->data->id, $result['entries'][0]['dataid']);
410         $this->assertEquals($entry12, $result['entries'][1]['id']);
411         $this->assertEquals($this->student2->id, $result['entries'][1]['userid']);
412         $this->assertEquals($this->group1->id, $result['entries'][1]['groupid']);
413         $this->assertEquals($this->data->id, $result['entries'][1]['dataid']);
414         // Other user in same group.
415         $this->setUser($this->student2);
416         $result = mod_data_external::get_entries($this->data->id);
417         $result = external_api::clean_returnvalue(mod_data_external::get_entries_returns(), $result);
418         $this->assertCount(0, $result['warnings']);
419         $this->assertCount(3, $result['entries']);  // I can see my entry not approved yet.
420         $this->assertEquals(3, $result['totalcount']);
422         // Now try with the user in the second group that must see only one entry.
423         $this->setUser($this->student3);
424         $result = mod_data_external::get_entries($this->data->id);
425         $result = external_api::clean_returnvalue(mod_data_external::get_entries_returns(), $result);
426         $this->assertCount(0, $result['warnings']);
427         $this->assertCount(1, $result['entries']);
428         $this->assertEquals(1, $result['totalcount']);
429         $this->assertEquals($entry21, $result['entries'][0]['id']);
430         $this->assertEquals($this->student3->id, $result['entries'][0]['userid']);
431         $this->assertEquals($this->group2->id, $result['entries'][0]['groupid']);
432         $this->assertEquals($this->data->id, $result['entries'][0]['dataid']);
434         // Now, as teacher we should see all (we have permissions to view all groups).
435         $this->setUser($this->teacher);
436         $result = mod_data_external::get_entries($this->data->id);
437         $result = external_api::clean_returnvalue(mod_data_external::get_entries_returns(), $result);
438         $this->assertCount(0, $result['warnings']);
439         $this->assertCount(4, $result['entries']);  // I can see the not approved one.
440         $this->assertEquals(4, $result['totalcount']);
442         $entries = $DB->get_records('data_records', array('dataid' => $this->data->id), 'id');
443         $this->assertCount(4, $entries);
444         $count = 0;
445         foreach ($entries as $entry) {
446             $this->assertEquals($entry->id, $result['entries'][$count]['id']);
447             $count++;
448         }
450         // Basic test passing the parameter (instead having to calculate it).
451         $this->setUser($this->student1);
452         $result = mod_data_external::get_entries($this->data->id, $this->group1->id);
453         $result = external_api::clean_returnvalue(mod_data_external::get_entries_returns(), $result);
454         $this->assertCount(0, $result['warnings']);
455         $this->assertCount(2, $result['entries']);
456         $this->assertEquals(2, $result['totalcount']);
458         // Test ordering (reverse).
459         $this->setUser($this->student1);
460         $result = mod_data_external::get_entries($this->data->id, $this->group1->id, false, null, 'DESC');
461         $result = external_api::clean_returnvalue(mod_data_external::get_entries_returns(), $result);
462         $this->assertCount(0, $result['warnings']);
463         $this->assertCount(2, $result['entries']);
464         $this->assertEquals(2, $result['totalcount']);
465         $this->assertEquals($entry12, $result['entries'][0]['id']);
467         // Test pagination.
468         $this->setUser($this->student1);
469         $result = mod_data_external::get_entries($this->data->id, $this->group1->id, false, null, null, 0, 1);
470         $result = external_api::clean_returnvalue(mod_data_external::get_entries_returns(), $result);
471         $this->assertCount(0, $result['warnings']);
472         $this->assertCount(1, $result['entries']);
473         $this->assertEquals(2, $result['totalcount']);
474         $this->assertEquals($entry11, $result['entries'][0]['id']);
476         $result = mod_data_external::get_entries($this->data->id, $this->group1->id, false, null, null, 1, 1);
477         $result = external_api::clean_returnvalue(mod_data_external::get_entries_returns(), $result);
478         $this->assertCount(0, $result['warnings']);
479         $this->assertCount(1, $result['entries']);
480         $this->assertEquals(2, $result['totalcount']);
481         $this->assertEquals($entry12, $result['entries'][0]['id']);
483         // Now test the return contents.
484         data_generate_default_template($this->data, 'listtemplate', 0, false, true); // Generate a default list template.
485         $result = mod_data_external::get_entries($this->data->id, $this->group1->id, true, null, null, 0, 2);
486         $result = external_api::clean_returnvalue(mod_data_external::get_entries_returns(), $result);
487         $this->assertCount(0, $result['warnings']);
488         $this->assertCount(2, $result['entries']);
489         $this->assertEquals(2, $result['totalcount']);
490         $this->assertCount(9, $result['entries'][0]['contents']);
491         $this->assertCount(9, $result['entries'][1]['contents']);
492         // Search for some content.
493         $this->assertTrue(strpos($result['listviewcontents'], 'opt1') !== false);
494         $this->assertTrue(strpos($result['listviewcontents'], 'January') !== false);
495         $this->assertTrue(strpos($result['listviewcontents'], 'menu1') !== false);
496         $this->assertTrue(strpos($result['listviewcontents'], 'text for testing') !== false);
497         $this->assertTrue(strpos($result['listviewcontents'], 'sampleurl') !== false);
498     }
500     /**
501      * Test get_entry_visible_groups.
502      */
503     public function test_get_entry_visible_groups() {
504         global $DB;
506         $DB->set_field('course', 'groupmode', VISIBLEGROUPS, ['id' => $this->course->id]);
507         list($entry11, $entry12, $entry13, $entry21) = self::populate_database_with_entries();
509         // Check I can see my approved group entries.
510         $this->setUser($this->student1);
511         $result = mod_data_external::get_entry($entry11);
512         $result = external_api::clean_returnvalue(mod_data_external::get_entry_returns(), $result);
513         $this->assertCount(0, $result['warnings']);
514         $this->assertEquals($entry11, $result['entry']['id']);
515         $this->assertTrue($result['entry']['approved']);
516         $this->assertTrue($result['entry']['canmanageentry']); // Is mine, I can manage it.
518         // Entry from other group.
519         $result = mod_data_external::get_entry($entry21);
520         $result = external_api::clean_returnvalue(mod_data_external::get_entry_returns(), $result);
521         $this->assertCount(0, $result['warnings']);
522         $this->assertEquals($entry21, $result['entry']['id']);
523     }
525     /**
526      * Test get_entry_separated_groups.
527      */
528     public function test_get_entry_separated_groups() {
529         global $DB;
530         list($entry11, $entry12, $entry13, $entry21) = self::populate_database_with_entries();
532         // Check I can see my approved group entries.
533         $this->setUser($this->student1);
534         $result = mod_data_external::get_entry($entry11);
535         $result = external_api::clean_returnvalue(mod_data_external::get_entry_returns(), $result);
536         $this->assertCount(0, $result['warnings']);
537         $this->assertEquals($entry11, $result['entry']['id']);
538         $this->assertTrue($result['entry']['approved']);
539         $this->assertTrue($result['entry']['canmanageentry']); // Is mine, I can manage it.
541         // Retrieve contents.
542         data_generate_default_template($this->data, 'singletemplate', 0, false, true);
543         $result = mod_data_external::get_entry($entry11, true);
544         $result = external_api::clean_returnvalue(mod_data_external::get_entry_returns(), $result);
545         $this->assertCount(0, $result['warnings']);
546         $this->assertCount(9, $result['entry']['contents']);
547         $this->assertTrue(strpos($result['entryviewcontents'], 'opt1') !== false);
548         $this->assertTrue(strpos($result['entryviewcontents'], 'January') !== false);
549         $this->assertTrue(strpos($result['entryviewcontents'], 'menu1') !== false);
550         $this->assertTrue(strpos($result['entryviewcontents'], 'text for testing') !== false);
551         $this->assertTrue(strpos($result['entryviewcontents'], 'sampleurl') !== false);
553         // This is in my group but I'm not the author.
554         $result = mod_data_external::get_entry($entry12);
555         $result = external_api::clean_returnvalue(mod_data_external::get_entry_returns(), $result);
556         $this->assertCount(0, $result['warnings']);
557         $this->assertEquals($entry12, $result['entry']['id']);
558         $this->assertTrue($result['entry']['approved']);
559         $this->assertFalse($result['entry']['canmanageentry']); // Not mine.
561         $this->setUser($this->student3);
562         $result = mod_data_external::get_entry($entry21);
563         $result = external_api::clean_returnvalue(mod_data_external::get_entry_returns(), $result);
564         $this->assertCount(0, $result['warnings']);
565         $this->assertEquals($entry21, $result['entry']['id']);
566         $this->assertTrue($result['entry']['approved']);
567         $this->assertTrue($result['entry']['canmanageentry']); // Is mine, I can manage it.
569         // As teacher I should be able to see all the entries.
570         $this->setUser($this->teacher);
571         $result = mod_data_external::get_entry($entry11);
572         $result = external_api::clean_returnvalue(mod_data_external::get_entry_returns(), $result);
573         $this->assertEquals($entry11, $result['entry']['id']);
575         $result = mod_data_external::get_entry($entry12);
576         $result = external_api::clean_returnvalue(mod_data_external::get_entry_returns(), $result);
577         $this->assertEquals($entry12, $result['entry']['id']);
578         // This is the not approved one.
579         $result = mod_data_external::get_entry($entry13);
580         $result = external_api::clean_returnvalue(mod_data_external::get_entry_returns(), $result);
581         $this->assertEquals($entry13, $result['entry']['id']);
583         $result = mod_data_external::get_entry($entry21);
584         $result = external_api::clean_returnvalue(mod_data_external::get_entry_returns(), $result);
585         $this->assertEquals($entry21, $result['entry']['id']);
587         // Now, try to get an entry not approved yet.
588         $this->setUser($this->student1);
589         $this->expectException('moodle_exception');
590         $result = mod_data_external::get_entry($entry13);
591     }
593     /**
594      * Test get_entry from other group in separated groups.
595      */
596     public function test_get_entry_other_group_separated_groups() {
597         list($entry11, $entry12, $entry13, $entry21) = self::populate_database_with_entries();
599         // We should not be able to view other gropu entries (in separated groups).
600         $this->setUser($this->student1);
601         $this->expectException('moodle_exception');
602         $result = mod_data_external::get_entry($entry21);
603     }
605     /**
606      * Test get_fields.
607      */
608     public function test_get_fields() {
609         global $DB;
610         list($entry11, $entry12, $entry13, $entry21) = self::populate_database_with_entries();
612         $this->setUser($this->student1);
613         $result = mod_data_external::get_fields($this->data->id);
614         $result = external_api::clean_returnvalue(mod_data_external::get_fields_returns(), $result);
616         // Basically compare we retrieve all the fields and the correct values.
617         $fields = $DB->get_records('data_fields', array('dataid' => $this->data->id), 'id');
618         foreach ($result['fields'] as $field) {
619             $this->assertEquals($field, (array) $fields[$field['id']]);
620         }
621     }
623     /**
624      * Test search_entries.
625      */
626     public function test_search_entries() {
627         global $DB;
628         list($entry11, $entry12, $entry13, $entry21) = self::populate_database_with_entries();
630         // First do a normal text search as student 1. I should see my two group entries.
631         $this->setUser($this->student1);
632         $result = mod_data_external::search_entries($this->data->id, 0, false, 'text');
633         $result = external_api::clean_returnvalue(mod_data_external::search_entries_returns(), $result);
634         $this->assertCount(2, $result['entries']);
635         $this->assertEquals(2, $result['totalcount']);
637         // Now as the other student I should receive my not approved entry. Apply ordering here.
638         $this->setUser($this->student2);
639         $result = mod_data_external::search_entries($this->data->id, 0, false, 'text', [], DATA_APPROVED, 'ASC');
640         $result = external_api::clean_returnvalue(mod_data_external::search_entries_returns(), $result);
641         $this->assertCount(3, $result['entries']);
642         $this->assertEquals(3, $result['totalcount']);
643         // The not approved one should be the first.
644         $this->assertEquals($entry13, $result['entries'][0]['id']);
646         // Now as the other group student.
647         $this->setUser($this->student3);
648         $result = mod_data_external::search_entries($this->data->id, 0, false, 'text');
649         $result = external_api::clean_returnvalue(mod_data_external::search_entries_returns(), $result);
650         $this->assertCount(1, $result['entries']);
651         $this->assertEquals(1, $result['totalcount']);
652         $this->assertEquals($this->student3->id, $result['entries'][0]['userid']);
654         // Same normal text search as teacher.
655         $this->setUser($this->teacher);
656         $result = mod_data_external::search_entries($this->data->id, 0, false, 'text');
657         $result = external_api::clean_returnvalue(mod_data_external::search_entries_returns(), $result);
658         $this->assertCount(4, $result['entries']);  // I can see all groups and non approved.
659         $this->assertEquals(4, $result['totalcount']);
661         // Pagination.
662         $this->setUser($this->teacher);
663         $result = mod_data_external::search_entries($this->data->id, 0, false, 'text', [], DATA_TIMEADDED, 'ASC', 0, 2);
664         $result = external_api::clean_returnvalue(mod_data_external::search_entries_returns(), $result);
665         $this->assertCount(2, $result['entries']);  // Only 2 per page.
666         $this->assertEquals(4, $result['totalcount']);
668         // Now advanced search or not dinamic fields (user firstname for example).
669         $this->setUser($this->student1);
670         $advsearch = [
671             ['name' => 'fn', 'value' => json_encode($this->student2->firstname)]
672         ];
673         $result = mod_data_external::search_entries($this->data->id, 0, false, '', $advsearch);
674         $result = external_api::clean_returnvalue(mod_data_external::search_entries_returns(), $result);
675         $this->assertCount(1, $result['entries']);
676         $this->assertEquals(1, $result['totalcount']);
677         $this->assertEquals($this->student2->id, $result['entries'][0]['userid']);  // I only found mine!
679         // Advanced search for fields.
680         $field = $DB->get_record('data_fields', array('type' => 'url'));
681         $advsearch = [
682             ['name' => 'f_' . $field->id , 'value' => 'sampleurl']
683         ];
684         $result = mod_data_external::search_entries($this->data->id, 0, false, '', $advsearch);
685         $result = external_api::clean_returnvalue(mod_data_external::search_entries_returns(), $result);
686         $this->assertCount(2, $result['entries']);  // Found two entries matching this.
687         $this->assertEquals(2, $result['totalcount']);
689         // Combined search.
690         $field2 = $DB->get_record('data_fields', array('type' => 'number'));
691         $advsearch = [
692             ['name' => 'f_' . $field->id , 'value' => 'sampleurl'],
693             ['name' => 'f_' . $field2->id , 'value' => '12345'],
694             ['name' => 'ln', 'value' => json_encode($this->student2->lastname)]
695         ];
696         $result = mod_data_external::search_entries($this->data->id, 0, false, '', $advsearch);
697         $result = external_api::clean_returnvalue(mod_data_external::search_entries_returns(), $result);
698         $this->assertCount(1, $result['entries']);  // Only one matching everything.
699         $this->assertEquals(1, $result['totalcount']);
701         // Combined search (no results).
702         $field2 = $DB->get_record('data_fields', array('type' => 'number'));
703         $advsearch = [
704             ['name' => 'f_' . $field->id , 'value' => 'sampleurl'],
705             ['name' => 'f_' . $field2->id , 'value' => '98780333'], // Non existent number.
706         ];
707         $result = mod_data_external::search_entries($this->data->id, 0, false, '', $advsearch);
708         $result = external_api::clean_returnvalue(mod_data_external::search_entries_returns(), $result);
709         $this->assertCount(0, $result['entries']);  // Only one matching everything.
710         $this->assertEquals(0, $result['totalcount']);
711     }
713     /**
714      * Test approve_entry.
715      */
716     public function test_approve_entry() {
717         global $DB;
718         list($entry11, $entry12, $entry13, $entry21) = self::populate_database_with_entries();
720         $this->setUser($this->teacher);
721         $this->assertEquals(0, $DB->get_field('data_records', 'approved', array('id' => $entry13)));
722         $result = mod_data_external::approve_entry($entry13);
723         $result = external_api::clean_returnvalue(mod_data_external::approve_entry_returns(), $result);
724         $this->assertEquals(1, $DB->get_field('data_records', 'approved', array('id' => $entry13)));
725     }
727     /**
728      * Test unapprove_entry.
729      */
730     public function test_unapprove_entry() {
731         global $DB;
732         list($entry11, $entry12, $entry13, $entry21) = self::populate_database_with_entries();
734         $this->setUser($this->teacher);
735         $this->assertEquals(1, $DB->get_field('data_records', 'approved', array('id' => $entry11)));
736         $result = mod_data_external::approve_entry($entry11, false);
737         $result = external_api::clean_returnvalue(mod_data_external::approve_entry_returns(), $result);
738         $this->assertEquals(0, $DB->get_field('data_records', 'approved', array('id' => $entry11)));
739     }
741     /**
742      * Test approve_entry missing permissions.
743      */
744     public function test_approve_entry_missing_permissions() {
745         global $DB;
746         list($entry11, $entry12, $entry13, $entry21) = self::populate_database_with_entries();
748         $this->setUser($this->student1);
749         $this->expectException('moodle_exception');
750         mod_data_external::approve_entry($entry13);
751     }
753     /**
754      * Test delete_entry as teacher. Check I can delete any entry.
755      */
756     public function test_delete_entry_as_teacher() {
757         global $DB;
758         list($entry11, $entry12, $entry13, $entry21) = self::populate_database_with_entries();
760         $this->setUser($this->teacher);
761         $result = mod_data_external::delete_entry($entry11);
762         $result = external_api::clean_returnvalue(mod_data_external::delete_entry_returns(), $result);
763         $this->assertEquals(0, $DB->count_records('data_records', array('id' => $entry11)));
765         // Entry in other group.
766         $result = mod_data_external::delete_entry($entry21);
767         $result = external_api::clean_returnvalue(mod_data_external::delete_entry_returns(), $result);
768         $this->assertEquals(0, $DB->count_records('data_records', array('id' => $entry21)));
769     }
771     /**
772      * Test delete_entry as student. Check I can delete my own entries.
773      */
774     public function test_delete_entry_as_student() {
775         global $DB;
776         list($entry11, $entry12, $entry13, $entry21) = self::populate_database_with_entries();
778         $this->setUser($this->student1);
779         $result = mod_data_external::delete_entry($entry11);
780         $result = external_api::clean_returnvalue(mod_data_external::delete_entry_returns(), $result);
781         $this->assertEquals(0, $DB->count_records('data_records', array('id' => $entry11)));
782     }
784     /**
785      * Test delete_entry as student in read only mode period. Check I cannot delete my own entries in that period.
786      */
787     public function test_delete_entry_as_student_in_read_only_period() {
788         global $DB;
789         list($entry11, $entry12, $entry13, $entry21) = self::populate_database_with_entries();
790         // Set a time period.
791         $this->data->timeviewfrom = time() - HOURSECS;
792         $this->data->timeviewto = time() + HOURSECS;
793         $DB->update_record('data', $this->data);
795         $this->setUser($this->student1);
796         $this->expectException('moodle_exception');
797         mod_data_external::delete_entry($entry11);
798     }
800     /**
801      * Test delete_entry with an user missing permissions.
802      */
803     public function test_delete_entry_missing_permissions() {
804         global $DB;
805         list($entry11, $entry12, $entry13, $entry21) = self::populate_database_with_entries();
807         $this->setUser($this->student1);
808         $this->expectException('moodle_exception');
809         mod_data_external::delete_entry($entry21);
810     }
812     /**
813      * Test add_entry.
814      */
815     public function test_add_entry() {
816         global $DB;
817         // First create the record structure and add some entries.
818         list($entry11, $entry12, $entry13, $entry21) = self::populate_database_with_entries();
820         $this->setUser($this->student1);
821         $newentrydata = [];
822         $fields = $DB->get_records('data_fields', array('dataid' => $this->data->id), 'id');
823         // Prepare the new entry data.
824         foreach ($fields as $field) {
825             $subfield = $value = '';
827             switch ($field->type) {
828                 case 'checkbox':
829                     $value = ['opt1', 'opt2'];
830                     break;
831                 case 'date':
832                     // Add two extra.
833                     $newentrydata[] = [
834                         'fieldid' => $field->id,
835                         'subfield' => 'day',
836                         'value' => json_encode('5')
837                     ];
838                     $newentrydata[] = [
839                         'fieldid' => $field->id,
840                         'subfield' => 'month',
841                         'value' => json_encode('1')
842                     ];
843                     $subfield = 'year';
844                     $value = '1981';
845                     break;
846                 case 'menu':
847                     $value = 'menu1';
848                     break;
849                 case 'multimenu':
850                     $value = ['multimenu1', 'multimenu4'];
851                     break;
852                 case 'number':
853                     $value = 6;
854                     break;
855                 case 'radiobutton':
856                     $value = 'radioopt1';
857                     break;
858                 case 'text':
859                     $value = 'some text';
860                     break;
861                 case 'textarea':
862                     $newentrydata[] = [
863                         'fieldid' => $field->id,
864                         'subfield' => 'content1',
865                         'value' => json_encode(FORMAT_MOODLE)
866                     ];
867                     $newentrydata[] = [
868                         'fieldid' => $field->id,
869                         'subfield' => 'itemid',
870                         'value' => json_encode(0)
871                     ];
872                     $value = 'more text';
873                     break;
874                 case 'url':
875                     $value = 'https://moodle.org';
876                     $subfield = 0;
877                     break;
878             }
880             $newentrydata[] = [
881                 'fieldid' => $field->id,
882                 'subfield' => $subfield,
883                 'value' => json_encode($value)
884             ];
885         }
886         $result = mod_data_external::add_entry($this->data->id, 0, $newentrydata);
887         $result = external_api::clean_returnvalue(mod_data_external::add_entry_returns(), $result);
889         $newentryid = $result['newentryid'];
890         $result = mod_data_external::get_entry($newentryid, true);
891         $result = external_api::clean_returnvalue(mod_data_external::get_entry_returns(), $result);
892         $this->assertEquals($this->student1->id, $result['entry']['userid']);
893         $this->assertCount(9, $result['entry']['contents']);
894         foreach ($result['entry']['contents'] as $content) {
895             $field = $fields[$content['fieldid']];
896             // Stored content same that the one retrieved by WS.
897             $dbcontent = $DB->get_record('data_content', array('fieldid' => $field->id, 'recordid' => $newentryid));
898             $this->assertEquals($dbcontent->content, $content['content']);
900             // Now double check everything stored is correct.
901             if ($field->type == 'checkbox') {
902                 $this->assertEquals('opt1##opt2', $content['content']);
903                 continue;
904             }
905             if ($field->type == 'date') {
906                 $this->assertEquals(347500800, $content['content']); // Date in gregorian format.
907                 continue;
908             }
909             if ($field->type == 'menu') {
910                 $this->assertEquals('menu1', $content['content']);
911                 continue;
912             }
913             if ($field->type == 'multimenu') {
914                 $this->assertEquals('multimenu1##multimenu4', $content['content']);
915                 continue;
916             }
917             if ($field->type == 'number') {
918                 $this->assertEquals(6, $content['content']);
919                 continue;
920             }
921             if ($field->type == 'radiobutton') {
922                 $this->assertEquals('radioopt1', $content['content']);
923                 continue;
924             }
925             if ($field->type == 'text') {
926                 $this->assertEquals('some text', $content['content']);
927                 continue;
928             }
929             if ($field->type == 'textarea') {
930                 $this->assertEquals('more text', $content['content']);
931                 $this->assertEquals(FORMAT_MOODLE, $content['content1']);
932                 continue;
933             }
934             if ($field->type == 'url') {
935                 $this->assertEquals('https://moodle.org', $content['content']);
936                 continue;
937             }
938             $this->assertEquals('multimenu1##multimenu4', $content['content']);
939         }
941         // Now, try to add another entry but removing some required data.
942         unset($newentrydata[0]);
943         $result = mod_data_external::add_entry($this->data->id, 0, $newentrydata);
944         $result = external_api::clean_returnvalue(mod_data_external::add_entry_returns(), $result);
945         $this->assertEquals(0, $result['newentryid']);
946         $this->assertCount(0, $result['generalnotifications']);
947         $this->assertCount(1, $result['fieldnotifications']);
948         $this->assertEquals('field-1', $result['fieldnotifications'][0]['fieldname']);
949         $this->assertEquals(get_string('errormustsupplyvalue', 'data'), $result['fieldnotifications'][0]['notification']);
950     }
952     /**
953      * Test add_entry empty_form.
954      */
955     public function test_add_entry_empty_form() {
956         $result = mod_data_external::add_entry($this->data->id, 0, []);
957         $result = external_api::clean_returnvalue(mod_data_external::add_entry_returns(), $result);
958         $this->assertEquals(0, $result['newentryid']);
959         $this->assertCount(1, $result['generalnotifications']);
960         $this->assertCount(0, $result['fieldnotifications']);
961         $this->assertEquals(get_string('emptyaddform', 'data'), $result['generalnotifications'][0]);
962     }
964     /**
965      * Test add_entry read_only_period.
966      */
967     public function test_add_entry_read_only_period() {
968         global $DB;
969         list($entry11, $entry12, $entry13, $entry21) = self::populate_database_with_entries();
970         // Set a time period.
971         $this->data->timeviewfrom = time() - HOURSECS;
972         $this->data->timeviewto = time() + HOURSECS;
973         $DB->update_record('data', $this->data);
975         $this->setUser($this->student1);
976         $this->expectExceptionMessage(get_string('noaccess', 'data'));
977         $this->expectException('moodle_exception');
978         mod_data_external::add_entry($this->data->id, 0, []);
979     }
981     /**
982      * Test add_entry max_num_entries.
983      */
984     public function test_add_entry_max_num_entries() {
985         global $DB;
986         list($entry11, $entry12, $entry13, $entry21) = self::populate_database_with_entries();
987         // Set a time period.
988         $this->data->maxentries = 1;
989         $DB->update_record('data', $this->data);
991         $this->setUser($this->student1);
992         $this->expectExceptionMessage(get_string('noaccess', 'data'));
993         $this->expectException('moodle_exception');
994         mod_data_external::add_entry($this->data->id, 0, []);
995     }
997     /**
998      * Test update_entry.
999      */
1000     public function test_update_entry() {
1001         global $DB;
1002         // First create the record structure and add some entries.
1003         list($entry11, $entry12, $entry13, $entry21) = self::populate_database_with_entries();
1005         $this->setUser($this->student1);
1006         $newentrydata = [];
1007         $fields = $DB->get_records('data_fields', array('dataid' => $this->data->id), 'id');
1008         // Prepare the new entry data.
1009         foreach ($fields as $field) {
1010             $subfield = $value = '';
1012             switch ($field->type) {
1013                 case 'checkbox':
1014                     $value = ['opt1', 'opt2'];
1015                     break;
1016                 case 'date':
1017                     // Add two extra.
1018                     $newentrydata[] = [
1019                         'fieldid' => $field->id,
1020                         'subfield' => 'day',
1021                         'value' => json_encode('5')
1022                     ];
1023                     $newentrydata[] = [
1024                         'fieldid' => $field->id,
1025                         'subfield' => 'month',
1026                         'value' => json_encode('1')
1027                     ];
1028                     $subfield = 'year';
1029                     $value = '1981';
1030                     break;
1031                 case 'menu':
1032                     $value = 'menu1';
1033                     break;
1034                 case 'multimenu':
1035                     $value = ['multimenu1', 'multimenu4'];
1036                     break;
1037                 case 'number':
1038                     $value = 6;
1039                     break;
1040                 case 'radiobutton':
1041                     $value = 'radioopt2';
1042                     break;
1043                 case 'text':
1044                     $value = 'some text';
1045                     break;
1046                 case 'textarea':
1047                     $newentrydata[] = [
1048                         'fieldid' => $field->id,
1049                         'subfield' => 'content1',
1050                         'value' => json_encode(FORMAT_MOODLE)
1051                     ];
1052                     $newentrydata[] = [
1053                         'fieldid' => $field->id,
1054                         'subfield' => 'itemid',
1055                         'value' => json_encode(0)
1056                     ];
1057                     $value = 'more text';
1058                     break;
1059                 case 'url':
1060                     $value = 'https://moodle.org';
1061                     $subfield = 0;
1062                     break;
1063             }
1065             $newentrydata[] = [
1066                 'fieldid' => $field->id,
1067                 'subfield' => $subfield,
1068                 'value' => json_encode($value)
1069             ];
1070         }
1071         $result = mod_data_external::update_entry($entry11, $newentrydata);
1072         $result = external_api::clean_returnvalue(mod_data_external::update_entry_returns(), $result);
1073         $this->assertTrue($result['updated']);
1074         $this->assertCount(0, $result['generalnotifications']);
1075         $this->assertCount(0, $result['fieldnotifications']);
1077         $result = mod_data_external::get_entry($entry11, true);
1078         $result = external_api::clean_returnvalue(mod_data_external::get_entry_returns(), $result);
1079         $this->assertEquals($this->student1->id, $result['entry']['userid']);
1080         $this->assertCount(9, $result['entry']['contents']);
1081         foreach ($result['entry']['contents'] as $content) {
1082             $field = $fields[$content['fieldid']];
1083             // Stored content same that the one retrieved by WS.
1084             $dbcontent = $DB->get_record('data_content', array('fieldid' => $field->id, 'recordid' => $entry11));
1085             $this->assertEquals($dbcontent->content, $content['content']);
1087             // Now double check everything stored is correct.
1088             if ($field->type == 'checkbox') {
1089                 $this->assertEquals('opt1##opt2', $content['content']);
1090                 continue;
1091             }
1092             if ($field->type == 'date') {
1093                 $this->assertEquals(347500800, $content['content']); // Date in gregorian format.
1094                 continue;
1095             }
1096             if ($field->type == 'menu') {
1097                 $this->assertEquals('menu1', $content['content']);
1098                 continue;
1099             }
1100             if ($field->type == 'multimenu') {
1101                 $this->assertEquals('multimenu1##multimenu4', $content['content']);
1102                 continue;
1103             }
1104             if ($field->type == 'number') {
1105                 $this->assertEquals(6, $content['content']);
1106                 continue;
1107             }
1108             if ($field->type == 'radiobutton') {
1109                 $this->assertEquals('radioopt2', $content['content']);
1110                 continue;
1111             }
1112             if ($field->type == 'text') {
1113                 $this->assertEquals('some text', $content['content']);
1114                 continue;
1115             }
1116             if ($field->type == 'textarea') {
1117                 $this->assertEquals('more text', $content['content']);
1118                 $this->assertEquals(FORMAT_MOODLE, $content['content1']);
1119                 continue;
1120             }
1121             if ($field->type == 'url') {
1122                 $this->assertEquals('https://moodle.org', $content['content']);
1123                 continue;
1124             }
1125             $this->assertEquals('multimenu1##multimenu4', $content['content']);
1126         }
1128         // Now, try to update the entry but removing some required data.
1129         unset($newentrydata[0]);
1130         $result = mod_data_external::update_entry($entry11, $newentrydata);
1131         $result = external_api::clean_returnvalue(mod_data_external::update_entry_returns(), $result);
1132         $this->assertFalse($result['updated']);
1133         $this->assertCount(0, $result['generalnotifications']);
1134         $this->assertCount(1, $result['fieldnotifications']);
1135         $this->assertEquals('field-1', $result['fieldnotifications'][0]['fieldname']);
1136         $this->assertEquals(get_string('errormustsupplyvalue', 'data'), $result['fieldnotifications'][0]['notification']);
1137     }
1139     /**
1140      * Test update_entry sending empty data.
1141      */
1142     public function test_update_entry_empty_data() {
1143         list($entry11, $entry12, $entry13, $entry21) = self::populate_database_with_entries();
1145         $this->setUser($this->student1);
1146         $result = mod_data_external::update_entry($entry11, []);
1147         $result = external_api::clean_returnvalue(mod_data_external::update_entry_returns(), $result);
1148         $this->assertFalse($result['updated']);
1149         $this->assertCount(1, $result['generalnotifications']);
1150         $this->assertCount(9, $result['fieldnotifications']);
1151         $this->assertEquals(get_string('emptyaddform', 'data'), $result['generalnotifications'][0]);
1152     }
1154     /**
1155      * Test update_entry in read only period.
1156      */
1157     public function test_update_entry_read_only_period() {
1158         global $DB;
1159         list($entry11, $entry12, $entry13, $entry21) = self::populate_database_with_entries();
1160         // Set a time period.
1161         $this->data->timeviewfrom = time() - HOURSECS;
1162         $this->data->timeviewto = time() + HOURSECS;
1163         $DB->update_record('data', $this->data);
1165         $this->setUser($this->student1);
1166         $this->expectExceptionMessage(get_string('noaccess', 'data'));
1167         $this->expectException('moodle_exception');
1168         mod_data_external::update_entry($entry11, []);
1169     }
1171     /**
1172      * Test update_entry other_user.
1173      */
1174     public function test_update_entry_other_user() {
1175         // Try to update other user entry.
1176         list($entry11, $entry12, $entry13, $entry21) = self::populate_database_with_entries();
1177         $this->setUser($this->student2);
1178         $this->expectExceptionMessage(get_string('noaccess', 'data'));
1179         $this->expectException('moodle_exception');
1180         mod_data_external::update_entry($entry11, []);
1181     }