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 namespace core_xapi\external; 18 19 use core_xapi\xapi_exception; 20 use core_xapi\local\statement\item_agent; 21 use externallib_advanced_testcase; 22 use core_external\external_api; 23 use core_xapi\iri; 24 use core_xapi\local\state; 25 use core_xapi\local\statement\item_activity; 26 use core_xapi\test_helper; 27 28 defined('MOODLE_INTERNAL') || die(); 29 30 global $CFG; 31 require_once($CFG->dirroot . '/webservice/tests/helpers.php'); 32 33 /** 34 * Unit tests for xAPI delete state webservice. 35 * 36 * @package core_xapi 37 * @covers \core_xapi\external\delete_state 38 * @since Moodle 4.2 39 * @copyright 2023 Sara Arjona (sara@moodle.com) 40 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 41 */ 42 class delete_state_test extends externallib_advanced_testcase { 43 44 /** 45 * Setup to ensure that fixtures are loaded. 46 */ 47 public static function setUpBeforeClass(): void { 48 global $CFG; 49 require_once($CFG->dirroot . '/lib/xapi/tests/helper.php'); 50 } 51 52 /** 53 * Testing different component names on valid states. 54 * 55 * @dataProvider components_provider 56 * @param string $component component name 57 * @param string|null $expected expected results 58 */ 59 public function test_component_names(string $component, ?string $expected): void { 60 61 $this->resetAfterTest(); 62 63 // Scenario. 64 $this->setAdminUser(); 65 66 // Perform test. 67 $data = test_helper::create_state([], true); 68 $this->delete_state_data($component, $data, $expected); 69 } 70 71 /** 72 * Data provider for the test_component_names tests. 73 * 74 * @return array 75 */ 76 public function components_provider() : array { 77 return [ 78 'Inexistent component' => [ 79 'component' => 'inexistent_component', 80 'expected' => null, 81 ], 82 'Compatible component' => [ 83 'component' => 'fake_component', 84 'expected' => 'true', 85 ], 86 'Incompatible component' => [ 87 'component' => 'core_xapi', 88 'expected' => null, 89 ], 90 ]; 91 } 92 93 /** 94 * Testing invalid agent. 95 * 96 */ 97 public function test_invalid_agent(): void { 98 $this->resetAfterTest(); 99 100 // Scenario. 101 $this->setAdminUser(); 102 $other = $this->getDataGenerator()->create_user(); 103 104 // Invalid agent (use different user, instead of the current one). 105 $info = [ 106 'agent' => item_agent::create_from_user($other), 107 ]; 108 $data = test_helper::create_state($info, true); 109 $this->delete_state_data('fake_component', $data, null); 110 } 111 112 /** 113 * Testing valid/invalid state. 114 * 115 * @dataProvider states_provider 116 * @param array $info array of overriden state data. 117 * @param string|null $expected Expected results. 118 * @return void 119 */ 120 public function test_delete_state(array $info, ?string $expected): void { 121 $this->resetAfterTest(); 122 123 // Scenario. 124 $this->setAdminUser(); 125 $component = $info['component'] ?? 'fake_component'; 126 $params = []; 127 if ($component === 'mod_h5pactivity') { 128 // For the mod_h5pactivity component, the activity needs to be created too. 129 $course = $this->getDataGenerator()->create_course(); 130 $user = $this->getDataGenerator()->create_and_enrol($course, 'student'); 131 $activity = $this->getDataGenerator()->create_module('h5pactivity', ['course' => $course]); 132 133 $activitycontext = \context_module::instance($activity->cmid); 134 $info['activity'] = item_activity::create_from_id($activitycontext->id); 135 $params['activity'] = $info['activity']; 136 $this->setUser($user); 137 } 138 139 // Add, at least, one xAPI state record to database (with the default values). 140 test_helper::create_state($params, true); 141 142 // Perform test. 143 $data = test_helper::create_state($info); 144 145 $this->delete_state_data($component, $data, $expected); 146 } 147 148 /** 149 * Data provider for the test_get_state tests. 150 * 151 * @return array 152 */ 153 public function states_provider() : array { 154 return [ 155 'Existing and valid state' => [ 156 'info' => [], 157 'expected' => 'true', 158 ], 159 'No state (wrong activityid)' => [ 160 'info' => ['activity' => item_activity::create_from_id('1')], 161 'expected' => 'false', 162 ], 163 'No state (wrong stateid)' => [ 164 'info' => ['stateid' => 'food'], 165 'expected' => 'false', 166 ], 167 'No state (wrong component)' => [ 168 'info' => ['component' => 'mod_h5pactivity'], 169 'expected' => 'false', 170 ], 171 ]; 172 } 173 174 /** 175 * Return a xAPI external webservice class to operate. 176 * 177 * The test needs to fake a component in order to test without 178 * using a real one. This way if in the future any component 179 * implement it's xAPI handler this test will continue working. 180 * 181 * @return delete_state the external class 182 */ 183 private function get_external_class(): delete_state { 184 $ws = new class extends delete_state { 185 /** 186 * Method to override validate_component. 187 * 188 * @param string $component The component name in frankenstyle. 189 */ 190 protected static function validate_component(string $component): void { 191 if ($component != 'fake_component') { 192 parent::validate_component($component); 193 } 194 } 195 }; 196 return $ws; 197 } 198 199 /** 200 * This function do all checks from a standard delete_state request. 201 * 202 * The reason for this function is because states crafting (special in error 203 * scenarios) is complicated to do via data providers because every test need a specific 204 * testing conditions. For this reason alls tests creates a scenario and then uses this 205 * function to check the results. 206 * 207 * @param string $component component name 208 * @param state $data data to encode and send to delete_state 209 * @param string $expected expected results (if null an exception is expected) 210 */ 211 private function delete_state_data(string $component, state $data, ?string $expected): void { 212 global $DB; 213 214 // Get current states in database. 215 $currentstates = $DB->count_records('xapi_states'); 216 217 // When no result is expected, an exception will be incurred. 218 if (is_null($expected)) { 219 $this->expectException(xapi_exception::class); 220 } 221 222 $external = $this->get_external_class(); 223 $result = $external::execute( 224 $component, 225 iri::generate($data->get_activity_id(), 'activity'), 226 json_encode($data->get_agent()), 227 $data->get_state_id(), 228 $data->get_registration() 229 ); 230 $result = external_api::clean_returnvalue($external::execute_returns(), $result); 231 232 // Check the state has been removed. 233 $records = $DB->get_records('xapi_states'); 234 $this->assertTrue($result); 235 if ($expected === 'true') { 236 $this->assertCount($currentstates - 1, $records); 237 } else if ($expected === 'false') { 238 $this->assertCount($currentstates, $records); 239 } 240 } 241 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body