2 // This file is part of Moodle - http://moodle.org/
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
17 namespace core_xapi\privacy
;
19 use core_privacy\tests\provider_testcase
;
20 use core_privacy\local\request\transform
;
21 use core_xapi\privacy\provider
;
22 use core_xapi\local\statement\item_activity
;
23 use core_xapi\test_helper
;
26 * Privacy tests for core_xapi.
30 * @copyright 2023 Sara Arjona (sara@moodle.com)
31 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
32 * @covers \core_xapi\privacy\provider
34 class provider_test
extends provider_testcase
{
37 * Setup to ensure that fixtures are loaded.
39 public static function setUpBeforeClass(): void
{
41 require_once($CFG->dirroot
.'/lib/xapi/tests/helper.php');
45 * Helper to set up some sample data.
47 * @return array Array with the users that have been created.
49 protected function set_up_data(): array {
50 $user1 = self
::getDataGenerator()->create_user();
51 $user2 = self
::getDataGenerator()->create_user();
52 $user3 = self
::getDataGenerator()->create_user();
54 // Add a few xAPI state records to database.
55 $context = \context_system
::instance();
57 $this->setUser($user1);
58 test_helper
::create_state(['activity' => item_activity
::create_from_id($context->id
)], true);
59 test_helper
::create_state(['activity' => item_activity
::create_from_id('2')], true);
60 test_helper
::create_state(['activity' => item_activity
::create_from_id('3'), 'component' => 'mod_h5pactivity'], true);
61 $this->setUser($user2);
62 test_helper
::create_state(['activity' => item_activity
::create_from_id($context->id
)], true);
63 test_helper
::create_state(['activity' => item_activity
::create_from_id('2')], true);
64 test_helper
::create_state(['activity' => item_activity
::create_from_id('4')], true);
65 test_helper
::create_state(['activity' => item_activity
::create_from_id('5')], true);
66 $this->setUser($user3);
67 test_helper
::create_state(['activity' => item_activity
::create_from_id($cid), 'component' => 'mod_h5pactivity'], true);
69 return [$user1, $user2, $user3];
73 * Test confirming that contexts of xapi items can be added to the contextlist.
75 public function test_add_contexts_for_userid(): void
{
76 $this->resetAfterTest();
79 list($user1, $user2) = $this->set_up_data();
81 // Ask the xapi privacy api to export contexts for xapi of the type we just created, for user1.
82 $contextlist = new \core_privacy\local\request\
contextlist();
83 provider
::add_contexts_for_userid($contextlist, $user1->id
, 'fake_component');
84 $this->assertCount(2, $contextlist->get_contextids());
86 $contextlist = new \core_privacy\local\request\
contextlist();
87 provider
::add_contexts_for_userid($contextlist, $user1->id
, 'mod_h5pactivity');
88 $this->assertCount(1, $contextlist->get_contextids());
90 // Ask the xapi privacy api to export contexts for xapi of the type we just created, for user2.
91 $contextlist = new \core_privacy\local\request\
contextlist();
92 provider
::add_contexts_for_userid($contextlist, $user2->id
, 'fake_component');
93 $this->assertCount(4, $contextlist->get_contextids());
95 $contextlist = new \core_privacy\local\request\
contextlist();
96 provider
::add_contexts_for_userid($contextlist, $user2->id
, 'mod_h5pactivity');
97 $this->assertCount(0, $contextlist->get_contextids());
101 * Test confirming that user ID's of xapi states can be added to the userlist.
103 public function test_add_userids_for_context() {
106 $this->resetAfterTest();
109 list($user1, $user2, $user3) = $this->set_up_data();
110 $this->assertEquals(3, $DB->count_records('xapi_states', ['userid' => $user1->id
]));
111 $this->assertEquals(4, $DB->count_records('xapi_states', ['userid' => $user2->id
]));
112 $this->assertEquals(1, $DB->count_records('xapi_states', ['userid' => $user3->id
]));
113 $systemcontext = \context_system
::instance();
115 // Ask the xapi privacy api to export userids for xapi states of the type we just created, in the system context.
116 $userlist = new \core_privacy\local\request\
userlist($systemcontext, 'fake_component');
117 provider
::add_userids_for_context($userlist, 'fake_component');
118 // Only user1 and user2 should be returned, because user3 has a different component for the system context.
119 $this->assertCount(2, $userlist->get_userids());
124 $this->assertEqualsCanonicalizing($expected, $userlist->get_userids());
126 // Ask the xapi privacy api to export userids for xapi states of the type we just created for a different component.
127 $userlist = new \core_privacy\local\request\
userlist($systemcontext, 'mod_h5pactivity');
128 provider
::add_userids_for_context($userlist, 'mod_h5pactivity');
129 // Only user3 should be returned, because the others have a different component for the system context.
130 $this->assertCount(1, $userlist->get_userids());
131 $expected = [$user3->id
];
132 $this->assertEqualsCanonicalizing($expected, $userlist->get_userids());
134 // Ask the xapi privacy api to export userids xapi states for an empty component.
135 $userlist = new \core_privacy\local\request\
userlist($systemcontext, 'empty_component');
136 provider
::add_userids_for_context($userlist, 'empty_component');
137 $this->assertCount(0, $userlist->get_userids());
141 * Test fetching the xapi state data for a specified user in a specified component and itemid.
143 public function test_get_xapi_states_for_user() {
146 $this->resetAfterTest();
149 list($user1, $user2, $user3) = $this->set_up_data();
150 $this->assertEquals(3, $DB->count_records('xapi_states', ['userid' => $user1->id
]));
151 $this->assertEquals(4, $DB->count_records('xapi_states', ['userid' => $user2->id
]));
152 $this->assertEquals(1, $DB->count_records('xapi_states', ['userid' => $user3->id
]));
153 $systemcontext = \context_system
::instance();
155 // Get the states info for user1 in the system context.
156 $result = provider
::get_xapi_states_for_user($user1->id
, 'fake_component', $systemcontext->id
);
157 $info = (object) reset($result);
158 // Ensure the correct data has been returned.
159 $this->assertNotEmpty($info->statedata
);
160 $this->assertNotEmpty(transform
::datetime($info->timecreated
));
161 $this->assertNotEmpty(transform
::datetime($info->timemodified
));
163 // Get the states info for user2 in the system context.
164 $result = provider
::get_xapi_states_for_user($user2->id
, 'fake_component', $systemcontext->id
);
165 $info = (object) reset($result);
166 // Ensure the correct data has been returned.
167 $this->assertNotEmpty($info->statedata
);
168 $this->assertNotEmpty(transform
::datetime($info->timecreated
));
169 $this->assertNotEmpty(transform
::datetime($info->timemodified
));
171 // Get the states info for user3 in the system context (it should be empty).
172 $info = provider
::get_xapi_states_for_user($user3->id
, 'fake_component', $systemcontext->id
);
173 // Ensure the correct data has been returned.
174 $this->assertEmpty($info);
178 * Test deletion of user xapi states based on an approved_contextlist and component area.
180 public function test_delete_states_for_user(): void
{
183 $this->resetAfterTest();
186 list($user1, $user2, $user3) = $this->set_up_data();
187 $this->assertEquals(3, $DB->count_records('xapi_states', ['userid' => $user1->id
]));
188 $this->assertEquals(4, $DB->count_records('xapi_states', ['userid' => $user2->id
]));
189 $this->assertEquals(1, $DB->count_records('xapi_states', ['userid' => $user3->id
]));
191 // Now, delete the xapistates for user1 only.
192 $user1context = \context_user
::instance($user1->id
);
193 $approvedcontextlist = new \core_privacy\local\request\approved_contextlist
($user1, 'fake_component', [$user1context->id
]);
194 provider
::delete_states_for_user($approvedcontextlist, 'fake_component');
196 // Verify that we have no xapi states for user1 for the fake_component but that the rest of records are intact.
197 $this->assertEquals(0, $DB->count_records('xapi_states', ['userid' => $user1->id
, 'component' => 'fake_component']));
198 $this->assertEquals(1, $DB->count_records('xapi_states', ['userid' => $user1->id
, 'component' => 'mod_h5pactivity']));
199 $this->assertEquals(4, $DB->count_records('xapi_states', ['userid' => $user2->id
]));
200 $this->assertEquals(1, $DB->count_records('xapi_states', ['userid' => $user3->id
]));
204 * Test deletion of all user xapi states.
206 public function test_delete_states_for_all_users(): void
{
209 $this->resetAfterTest();
212 list($user1, $user2, $user3) = $this->set_up_data();
213 $this->assertEquals(3, $DB->count_records('xapi_states', ['userid' => $user1->id
]));
214 $this->assertEquals(4, $DB->count_records('xapi_states', ['userid' => $user2->id
]));
215 $this->assertEquals(1, $DB->count_records('xapi_states', ['userid' => $user3->id
]));
217 // Now, delete all course module xapi states in the 'fake_component' context only.
218 provider
::delete_states_for_all_users(\context_system
::instance(), 'fake_component');
220 // Verify that only content with the context_system for the fake_component have been removed.
221 $this->assertEquals(2, $DB->count_records('xapi_states', ['userid' => $user1->id
]));
222 $this->assertEquals(3, $DB->count_records('xapi_states', ['userid' => $user2->id
]));
223 $this->assertEquals(1, $DB->count_records('xapi_states', ['userid' => $user3->id
]));
227 * Test deletion of user xapi states based on an approved_userlist and component area.
229 public function test_delete_states_for_userlist() {
232 $this->resetAfterTest();
235 list($user1, $user2, $user3) = $this->set_up_data();
236 $this->assertEquals(3, $DB->count_records('xapi_states', ['userid' => $user1->id
]));
237 $this->assertEquals(4, $DB->count_records('xapi_states', ['userid' => $user2->id
]));
238 $this->assertEquals(1, $DB->count_records('xapi_states', ['userid' => $user3->id
]));
239 $systemcontext = \context_system
::instance();
241 // Ask the xapi privacy api to export userids for states of the type we just created, in the system context.
242 $userlist1 = new \core_privacy\local\request\
userlist($systemcontext, 'fake_component');
243 provider
::add_userids_for_context($userlist1);
244 // Verify we have two userids in the list for system context.
245 $this->assertCount(2, $userlist1->get_userids());
247 // Now, delete the states for user1 only in the system context.
248 $approveduserlist = new \core_privacy\local\request\approved_userlist
($systemcontext, 'fake_component', [$user1->id
]);
249 provider
::delete_states_for_userlist($approveduserlist);
250 // Ensure user1's data was deleted and user2 is still returned for system context.
251 $userlist1 = new \core_privacy\local\request\
userlist($systemcontext, 'fake_component');
252 provider
::add_userids_for_context($userlist1);
253 $this->assertCount(1, $userlist1->get_userids());
254 // Verify that user2 is still in the list for system context.
255 $expected = [$user2->id
];
256 $this->assertEquals($expected, $userlist1->get_userids());
257 // Verify that the data of user1 in other contexts was not deleted.
258 $this->assertEquals(1, $DB->count_records('xapi_states', ['userid' => $user3->id
]));
259 $this->assertEquals(1, $DB->count_records('xapi_states', ['userid' => $user1->id
]));
260 $this->assertEquals(2, $DB->count_records('xapi_states', ['itemid' => $systemcontext->id
]));
262 // Verify that no data is removed if the component is empty.
263 $userlist3 = new \core_privacy\local\request\
userlist($systemcontext, 'empty_component');
264 provider
::add_userids_for_context($userlist3);
265 $this->assertCount(0, $userlist3->get_userids());
266 $this->assertEquals(2, $DB->count_records('xapi_states', ['itemid' => $systemcontext->id
]));