Proyectos de Subversion Moodle

Rev

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

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
// This file is part of Moodle - http://moodle.org/
3
//
4
// Moodle is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// Moodle is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
 
17
/**
18
 * Unit tests for privacy.
19
 *
20
 * @package   core_analytics
21
 * @copyright 2018 David Monllaó {@link http://www.davidmonllao.com}
22
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23
 */
24
namespace core_analytics\privacy;
25
 
26
use core_analytics\privacy\provider;
27
use core_privacy\local\request\transform;
28
use core_privacy\local\request\writer;
29
use core_privacy\local\request\approved_contextlist;
30
use core_privacy\local\request\approved_userlist;
31
 
32
defined('MOODLE_INTERNAL') || die();
33
 
34
require_once(__DIR__ . '/../fixtures/test_indicator_max.php');
35
require_once(__DIR__ . '/../fixtures/test_indicator_min.php');
36
require_once(__DIR__ . '/../fixtures/test_target_site_users.php');
37
require_once(__DIR__ . '/../fixtures/test_target_course_users.php');
38
 
39
/**
40
 * Unit tests for privacy.
41
 *
42
 * @package   core_analytics
43
 * @copyright 2018 David Monllaó {@link http://www.davidmonllao.com}
44
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
45
 */
46
class provider_test extends \core_privacy\tests\provider_testcase {
47
 
48
    /** @var \core_analytics\model Store Model 1. */
49
    protected $model1;
50
 
51
    /** @var \core_analytics\model Store Model 2. */
52
    protected $model2;
53
 
54
    /** @var \stdClass $modelobj1 Store Model 1 object. */
55
    protected $modelobj1;
56
 
57
    /** @var \stdClass $modelobj2 Store Model 2 object. */
58
    protected $modelobj2;
59
 
60
    /** @var \stdClass $u1 User 1 record. */
61
    protected $u1;
62
 
63
    /** @var \stdClass $u2 User 2 record. */
64
    protected $u2;
65
 
66
    /** @var \stdClass $u3 User 3 record. */
67
    protected $u3;
68
 
69
    /** @var \stdClass $u4 User 4 record. */
70
    protected $u4;
71
 
72
    /** @var \stdClass $u5 User 5 record. */
73
    protected $u5;
74
 
75
    /** @var \stdClass $u6 User 6 record. */
76
    protected $u6;
77
 
78
    /** @var \stdClass $u7 User 7 record. */
79
    protected $u7;
80
 
81
    /** @var \stdClass $u8 User 8 record. */
82
    protected $u8;
83
 
84
    /** @var \stdClass $c1 Course 1 record. */
85
    protected $c1;
86
 
87
    /** @var \stdClass $c2 Course 2 record. */
88
    protected $c2;
89
 
90
    public function setUp(): void {
91
 
92
        $this->resetAfterTest(true);
93
        $this->setAdminUser();
94
 
95
        $timesplittingid = '\core\analytics\time_splitting\single_range';
96
        $target = \core_analytics\manager::get_target('test_target_site_users');
97
        $indicators = array('test_indicator_max');
98
        foreach ($indicators as $key => $indicator) {
99
            $indicators[$key] = \core_analytics\manager::get_indicator($indicator);
100
        }
101
        $this->model1 = \core_analytics\model::create($target, $indicators, $timesplittingid);
102
        $this->modelobj1 = $this->model1->get_model_obj();
103
 
104
        $target = \core_analytics\manager::get_target('test_target_course_users');
105
        $indicators = array('test_indicator_min');
106
        foreach ($indicators as $key => $indicator) {
107
            $indicators[$key] = \core_analytics\manager::get_indicator($indicator);
108
        }
109
        $this->model2 = \core_analytics\model::create($target, $indicators, $timesplittingid);
110
        $this->modelobj2 = $this->model1->get_model_obj();
111
 
112
        $this->u1 = $this->getDataGenerator()->create_user(['firstname' => 'a111111111111', 'lastname' => 'a']);
113
        $this->u2 = $this->getDataGenerator()->create_user(['firstname' => 'a222222222222', 'lastname' => 'a']);
114
        $this->u3 = $this->getDataGenerator()->create_user(['firstname' => 'b333333333333', 'lastname' => 'b']);
115
        $this->u4 = $this->getDataGenerator()->create_user(['firstname' => 'b444444444444', 'lastname' => 'b']);
116
        $this->u5 = $this->getdatagenerator()->create_user(['firstname' => 'a555555555555', 'lastname' => 'a']);
117
        $this->u6 = $this->getdatagenerator()->create_user(['firstname' => 'a666666666666', 'lastname' => 'a']);
118
        $this->u7 = $this->getdatagenerator()->create_user(['firstname' => 'b777777777777', 'lastname' => 'b']);
119
        $this->u8 = $this->getDataGenerator()->create_user(['firstname' => 'b888888888888', 'lastname' => 'b']);
120
 
121
        $this->c1 = $this->getDataGenerator()->create_course(['visible' => false]);
122
        $this->c2 = $this->getDataGenerator()->create_course();
123
 
124
        $this->getDataGenerator()->enrol_user($this->u1->id, $this->c1->id, 'student');
125
        $this->getDataGenerator()->enrol_user($this->u2->id, $this->c1->id, 'student');
126
        $this->getDataGenerator()->enrol_user($this->u3->id, $this->c1->id, 'student');
127
        $this->getDataGenerator()->enrol_user($this->u4->id, $this->c1->id, 'student');
128
        $this->getDataGenerator()->enrol_user($this->u5->id, $this->c1->id, 'student');
129
        $this->getDataGenerator()->enrol_user($this->u6->id, $this->c1->id, 'student');
130
        $this->getDataGenerator()->enrol_user($this->u7->id, $this->c1->id, 'student');
131
        $this->getDataGenerator()->enrol_user($this->u8->id, $this->c1->id, 'student');
132
        $this->getDataGenerator()->enrol_user($this->u1->id, $this->c2->id, 'student');
133
        $this->getDataGenerator()->enrol_user($this->u2->id, $this->c2->id, 'student');
134
        $this->getDataGenerator()->enrol_user($this->u3->id, $this->c2->id, 'student');
135
        $this->getDataGenerator()->enrol_user($this->u4->id, $this->c2->id, 'student');
136
        $this->getDataGenerator()->enrol_user($this->u5->id, $this->c2->id, 'student');
137
        $this->getDataGenerator()->enrol_user($this->u6->id, $this->c2->id, 'student');
138
        $this->getDataGenerator()->enrol_user($this->u7->id, $this->c2->id, 'student');
139
        $this->getDataGenerator()->enrol_user($this->u8->id, $this->c2->id, 'student');
140
 
141
        $this->setAdminUser();
142
 
143
        $this->model1->enable();
144
        $this->model1->train();
145
        $this->model1->predict();
146
        $this->model2->enable();
147
        $this->model2->train();
148
        $this->model2->predict();
149
 
150
        list($total, $predictions) = $this->model2->get_predictions(\context_course::instance($this->c1->id));
151
 
152
        $this->setUser($this->u3);
153
        $prediction = reset($predictions);
154
        $prediction->action_executed(\core_analytics\prediction::ACTION_INCORRECTLY_FLAGGED, $this->model2->get_target());
155
 
156
        $this->setAdminUser();
157
    }
158
 
159
    /**
160
     * Test fetching users within a context.
161
     */
11 efrain 162
    public function test_get_users_in_context(): void {
1 efrain 163
        global $CFG;
164
 
165
        $component = 'core_analytics';
166
        $course1context = \context_course::instance($this->c1->id);
167
        $course2context = \context_course::instance($this->c2->id);
168
        $systemcontext = \context_system::instance();
169
        $expected = [$this->u1->id, $this->u2->id, $this->u3->id, $this->u4->id, $this->u5->id, $this->u6->id,
170
            $this->u7->id, $this->u8->id];
171
 
172
        // Check users exist in the relevant contexts.
173
        $userlist = new \core_privacy\local\request\userlist($course1context, $component);
174
        provider::get_users_in_context($userlist);
175
        $actual = $userlist->get_userids();
176
        sort($actual);
177
        $this->assertEquals($expected, $actual);
178
 
179
        $userlist = new \core_privacy\local\request\userlist($course2context, $component);
180
        provider::get_users_in_context($userlist);
181
        $actual = $userlist->get_userids();
182
        sort($actual);
183
        $this->assertEquals($expected, $actual);
184
 
185
        // System context will also find guest and admin user, add to expected before testing.
186
        $expected = array_merge($expected, [$CFG->siteguest, get_admin()->id]);
187
        sort($expected);
188
 
189
        $userlist = new \core_privacy\local\request\userlist($systemcontext, $component);
190
        provider::get_users_in_context($userlist);
191
        $actual = $userlist->get_userids();
192
        sort($actual);
193
        $this->assertEquals($expected, $actual);
194
    }
195
 
196
    /**
197
     * Test delete a context.
198
     *
199
     * @return null
200
     */
11 efrain 201
    public function test_delete_context_data(): void {
1 efrain 202
        global $DB;
203
 
204
        // We have 4 predictions for model1 and 8 predictions for model2.
205
        $this->assertEquals(12, $DB->count_records('analytics_predictions'));
206
        $this->assertEquals(26, $DB->count_records('analytics_indicator_calc'));
207
 
208
        // We have 1 prediction action.
209
        $this->assertEquals(1, $DB->count_records('analytics_prediction_actions'));
210
 
211
        $coursecontext = \context_course::instance($this->c1->id);
212
 
213
        // Delete the course that was used for prediction.
214
        provider::delete_data_for_all_users_in_context($coursecontext);
215
 
216
        // The course1 predictions are deleted.
217
        $this->assertEquals(8, $DB->count_records('analytics_predictions'));
218
 
219
        // Calculations related to that context are deleted.
220
        $this->assertEmpty($DB->count_records('analytics_indicator_calc', ['contextid' => $coursecontext->id]));
221
 
222
        // The deleted context prediction actions are deleted as well.
223
        $this->assertEquals(0, $DB->count_records('analytics_prediction_actions'));
224
    }
225
 
226
    /**
227
     * Test delete a user.
228
     *
229
     * @return null
230
     */
11 efrain 231
    public function test_delete_user_data(): void {
1 efrain 232
        global $DB;
233
 
234
        $usercontexts = provider::get_contexts_for_userid($this->u3->id);
235
        $contextlist = new \core_privacy\local\request\approved_contextlist($this->u3, 'core_analytics',
236
                                                                            $usercontexts->get_contextids());
237
        provider::delete_data_for_user($contextlist);
238
 
239
        // The site level prediction for u3 was deleted.
240
        $this->assertEquals(9, $DB->count_records('analytics_predictions'));
241
        $this->assertEquals(0, $DB->count_records('analytics_prediction_actions'));
242
 
243
        $usercontexts = provider::get_contexts_for_userid($this->u1->id);
244
        $contextlist = new \core_privacy\local\request\approved_contextlist($this->u1, 'core_analytics',
245
                                                                            $usercontexts->get_contextids());
246
        provider::delete_data_for_user($contextlist);
247
        // We have nothing for u1.
248
        $this->assertEquals(9, $DB->count_records('analytics_predictions'));
249
 
250
        $usercontexts = provider::get_contexts_for_userid($this->u4->id);
251
        $contextlist = new \core_privacy\local\request\approved_contextlist($this->u4, 'core_analytics',
252
                                                                            $usercontexts->get_contextids());
253
        provider::delete_data_for_user($contextlist);
254
        $this->assertEquals(6, $DB->count_records('analytics_predictions'));
255
    }
256
 
257
    /**
258
     * Test deleting multiple users in a context.
259
     */
11 efrain 260
    public function test_delete_data_for_users(): void {
1 efrain 261
        global $DB;
262
 
263
        $component = 'core_analytics';
264
        $course1context = \context_course::instance($this->c1->id);
265
        $course2context = \context_course::instance($this->c2->id);
266
        $systemcontext = \context_system::instance();
267
 
268
        // Ensure all records exist in expected contexts.
269
        $expectedcontexts = [$course1context->id, $course2context->id, $systemcontext->id];
270
        sort($expectedcontexts);
271
 
272
        $actualcontexts = [
273
            $this->u1->id => provider::get_contexts_for_userid($this->u1->id)->get_contextids(),
274
            $this->u2->id => provider::get_contexts_for_userid($this->u2->id)->get_contextids(),
275
            $this->u3->id => provider::get_contexts_for_userid($this->u3->id)->get_contextids(),
276
            $this->u4->id => provider::get_contexts_for_userid($this->u4->id)->get_contextids(),
277
            $this->u5->id => provider::get_contexts_for_userid($this->u5->id)->get_contextids(),
278
            $this->u6->id => provider::get_contexts_for_userid($this->u6->id)->get_contextids(),
279
            $this->u7->id => provider::get_contexts_for_userid($this->u7->id)->get_contextids(),
280
            $this->u8->id => provider::get_contexts_for_userid($this->u8->id)->get_contextids(),
281
        ];
282
 
283
        foreach ($actualcontexts as $userid => $unused) {
284
            sort($actualcontexts[$userid]);
285
            $this->assertEquals($expectedcontexts, $actualcontexts[$userid]);
286
        }
287
 
288
        // Test initial record counts are as expected.
289
        $this->assertEquals(12, $DB->count_records('analytics_predictions'));
290
        $this->assertEquals(1, $DB->count_records('analytics_prediction_actions'));
291
        $this->assertEquals(26, $DB->count_records('analytics_indicator_calc'));
292
 
293
        // Delete u1 and u3 from system context.
294
        $approveduserids = [$this->u1->id, $this->u3->id];
295
        $approvedlist = new approved_userlist($systemcontext, $component, $approveduserids);
296
        provider::delete_data_for_users($approvedlist);
297
 
298
        // Ensure u1 and u3 system context data deleted only.
299
        $expectedcontexts = [
300
            $this->u1->id => [$course1context->id, $course2context->id],
301
            $this->u2->id => [$systemcontext->id, $course1context->id, $course2context->id],
302
            $this->u3->id => [$course1context->id, $course2context->id],
303
            $this->u4->id => [$systemcontext->id, $course1context->id, $course2context->id],
304
            $this->u5->id => [$systemcontext->id, $course1context->id, $course2context->id],
305
            $this->u6->id => [$systemcontext->id, $course1context->id, $course2context->id],
306
            $this->u7->id => [$systemcontext->id, $course1context->id, $course2context->id],
307
            $this->u8->id => [$systemcontext->id, $course1context->id, $course2context->id],
308
        ];
309
 
310
        $actualcontexts = [
311
            $this->u1->id => provider::get_contexts_for_userid($this->u1->id)->get_contextids(),
312
            $this->u2->id => provider::get_contexts_for_userid($this->u2->id)->get_contextids(),
313
            $this->u3->id => provider::get_contexts_for_userid($this->u3->id)->get_contextids(),
314
            $this->u4->id => provider::get_contexts_for_userid($this->u4->id)->get_contextids(),
315
            $this->u5->id => provider::get_contexts_for_userid($this->u5->id)->get_contextids(),
316
            $this->u6->id => provider::get_contexts_for_userid($this->u6->id)->get_contextids(),
317
            $this->u7->id => provider::get_contexts_for_userid($this->u7->id)->get_contextids(),
318
            $this->u8->id => provider::get_contexts_for_userid($this->u8->id)->get_contextids(),
319
        ];
320
 
321
        foreach ($actualcontexts as $userid => $unused) {
322
            sort($expectedcontexts[$userid]);
323
            sort($actualcontexts[$userid]);
324
            $this->assertEquals($expectedcontexts[$userid], $actualcontexts[$userid]);
325
        }
326
 
327
        // Test expected number of records have been deleted.
328
        $this->assertEquals(11, $DB->count_records('analytics_predictions'));
329
        $this->assertEquals(1, $DB->count_records('analytics_prediction_actions'));
330
        $this->assertEquals(24, $DB->count_records('analytics_indicator_calc'));
331
 
332
        // Delete for all 8 users in course 2 context.
333
        $approveduserids = [$this->u1->id, $this->u2->id, $this->u3->id, $this->u4->id, $this->u5->id, $this->u6->id,
334
            $this->u7->id, $this->u8->id];
335
        $approvedlist = new approved_userlist($course2context, $component, $approveduserids);
336
        provider::delete_data_for_users($approvedlist);
337
 
338
        // Ensure all course 2 context data deleted for all 4 users.
339
        $expectedcontexts = [
340
            $this->u1->id => [$course1context->id],
341
            $this->u2->id => [$systemcontext->id, $course1context->id],
342
            $this->u3->id => [$course1context->id],
343
            $this->u4->id => [$systemcontext->id, $course1context->id],
344
            $this->u5->id => [$systemcontext->id, $course1context->id],
345
            $this->u6->id => [$systemcontext->id, $course1context->id],
346
            $this->u7->id => [$systemcontext->id, $course1context->id],
347
            $this->u8->id => [$systemcontext->id, $course1context->id],
348
        ];
349
 
350
        $actualcontexts = [
351
            $this->u1->id => provider::get_contexts_for_userid($this->u1->id)->get_contextids(),
352
            $this->u2->id => provider::get_contexts_for_userid($this->u2->id)->get_contextids(),
353
            $this->u3->id => provider::get_contexts_for_userid($this->u3->id)->get_contextids(),
354
            $this->u4->id => provider::get_contexts_for_userid($this->u4->id)->get_contextids(),
355
            $this->u5->id => provider::get_contexts_for_userid($this->u5->id)->get_contextids(),
356
            $this->u6->id => provider::get_contexts_for_userid($this->u6->id)->get_contextids(),
357
            $this->u7->id => provider::get_contexts_for_userid($this->u7->id)->get_contextids(),
358
            $this->u8->id => provider::get_contexts_for_userid($this->u8->id)->get_contextids(),
359
        ];
360
 
361
        foreach ($actualcontexts as $userid => $unused) {
362
            sort($actualcontexts[$userid]);
363
            sort($expectedcontexts[$userid]);
364
            $this->assertEquals($expectedcontexts[$userid], $actualcontexts[$userid]);
365
        }
366
 
367
        // Test expected number of records have been deleted.
368
        $this->assertEquals(7, $DB->count_records('analytics_predictions'));
369
        $this->assertEquals(1, $DB->count_records('analytics_prediction_actions'));
370
        $this->assertEquals(16, $DB->count_records('analytics_indicator_calc'));
371
 
372
        $approveduserids = [$this->u3->id];
373
        $approvedlist = new approved_userlist($course1context, $component, $approveduserids);
374
        provider::delete_data_for_users($approvedlist);
375
 
376
        // Ensure all course 1 context data deleted for u3.
377
        $expectedcontexts = [
378
            $this->u1->id => [$course1context->id],
379
            $this->u2->id => [$systemcontext->id, $course1context->id],
380
            $this->u3->id => [],
381
            $this->u4->id => [$systemcontext->id, $course1context->id],
382
            $this->u5->id => [$systemcontext->id, $course1context->id],
383
            $this->u6->id => [$systemcontext->id, $course1context->id],
384
            $this->u7->id => [$systemcontext->id, $course1context->id],
385
            $this->u8->id => [$systemcontext->id, $course1context->id],
386
        ];
387
 
388
        $actualcontexts = [
389
            $this->u1->id => provider::get_contexts_for_userid($this->u1->id)->get_contextids(),
390
            $this->u2->id => provider::get_contexts_for_userid($this->u2->id)->get_contextids(),
391
            $this->u3->id => provider::get_contexts_for_userid($this->u3->id)->get_contextids(),
392
            $this->u4->id => provider::get_contexts_for_userid($this->u4->id)->get_contextids(),
393
            $this->u5->id => provider::get_contexts_for_userid($this->u5->id)->get_contextids(),
394
            $this->u6->id => provider::get_contexts_for_userid($this->u6->id)->get_contextids(),
395
            $this->u7->id => provider::get_contexts_for_userid($this->u7->id)->get_contextids(),
396
            $this->u8->id => provider::get_contexts_for_userid($this->u8->id)->get_contextids(),
397
        ];
398
        foreach ($actualcontexts as $userid => $unused) {
399
            sort($actualcontexts[$userid]);
400
            sort($expectedcontexts[$userid]);
401
            $this->assertEquals($expectedcontexts[$userid], $actualcontexts[$userid]);
402
        }
403
 
404
        // Test expected number of records have been deleted.
405
        $this->assertEquals(6, $DB->count_records('analytics_predictions'));
406
        $this->assertEquals(0, $DB->count_records('analytics_prediction_actions'));
407
        $this->assertEquals(15, $DB->count_records('analytics_indicator_calc'));
408
    }
409
 
410
    /**
411
     * Test export user data.
412
     *
413
     * @return null
414
     */
11 efrain 415
    public function test_export_data(): void {
1 efrain 416
        global $DB;
417
 
418
        $system = \context_system::instance();
419
        list($total, $predictions) = $this->model1->get_predictions($system);
420
        foreach ($predictions as $key => $prediction) {
421
            if ($prediction->get_prediction_data()->sampleid !== $this->u3->id) {
422
                $otheruserprediction = $prediction;
423
                break;
424
            }
425
        }
426
        $this->setUser($this->u3);
427
        $otheruserprediction->action_executed(\core_analytics\prediction::ACTION_INCORRECTLY_FLAGGED, $this->model1->get_target());
428
        $this->setAdminUser();
429
 
430
        $this->export_context_data_for_user($this->u3->id, $system, 'core_analytics');
431
        $writer = \core_privacy\local\request\writer::with_context($system);
432
        $this->assertTrue($writer->has_any_data());
433
 
434
        $u3prediction = $DB->get_record('analytics_predictions', ['contextid' => $system->id, 'sampleid' => $this->u3->id]);
435
        $data = $writer->get_data([get_string('analytics', 'analytics'),
436
            get_string('privacy:metadata:analytics:predictions', 'analytics'), $u3prediction->id]);
437
        $this->assertEquals(get_string('adminhelplogs'), $data->target);
438
        $this->assertEquals(get_string('coresystem'), $data->context);
439
        $this->assertEquals('firstname first char is not A', $data->prediction);
440
 
441
        $u3calculation = $DB->get_record('analytics_indicator_calc', ['contextid' => $system->id, 'sampleid' => $this->u3->id]);
442
        $data = $writer->get_data([get_string('analytics', 'analytics'),
443
            get_string('privacy:metadata:analytics:indicatorcalc', 'analytics'), $u3calculation->id]);
444
        $this->assertEquals('Allow stealth activities', $data->indicator);
445
        $this->assertEquals(get_string('coresystem'), $data->context);
446
        $this->assertEquals(get_string('yes'), $data->calculation);
447
 
448
        $sql = "SELECT apa.id FROM {analytics_prediction_actions} apa
449
                  JOIN {analytics_predictions} ap ON ap.id = apa.predictionid
450
                 WHERE ap.contextid = :contextid AND apa.userid = :userid AND ap.modelid = :modelid";
451
        $params = ['contextid' => $system->id, 'userid' => $this->u3->id, 'modelid' => $this->model1->get_id()];
452
        $u3action = $DB->get_record_sql($sql, $params);
453
        $data = $writer->get_data([get_string('analytics', 'analytics'),
454
            get_string('privacy:metadata:analytics:predictionactions', 'analytics'), $u3action->id]);
455
        $this->assertEquals(get_string('adminhelplogs'), $data->target);
456
        $this->assertEquals(get_string('coresystem'), $data->context);
457
        $this->assertEquals(\core_analytics\prediction::ACTION_INCORRECTLY_FLAGGED, $data->action);
458
 
459
    }
460
}