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 get state webservice. 35 * 36 * @package core_xapi 37 * @covers \core_xapi\external\get_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 get_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 $this->resetAfterTest(); 61 62 // Scenario. 63 $this->setAdminUser(); 64 65 // Add, at least, one xAPI state record to database. 66 $data = test_helper::create_state([], true); 67 68 // Perform test. 69 $this->get_state_data($component, $data, $expected); 70 } 71 72 /** 73 * Data provider for the test_component_names tests. 74 * 75 * @return array 76 */ 77 public function components_provider() : array { 78 return [ 79 'Inexistent component' => [ 80 'component' => 'inexistent_component', 81 'expected' => null, 82 ], 83 'Compatible component' => [ 84 'component' => 'fake_component', 85 'expected' => 'true', 86 ], 87 'Incompatible component' => [ 88 'component' => 'core_xapi', 89 'expected' => null, 90 ], 91 ]; 92 } 93 94 /** 95 * Testing invalid agent for get_state. 96 * 97 */ 98 public function test_invalid_agent(): void { 99 $this->resetAfterTest(); 100 101 // Scenario. 102 $this->setAdminUser(); 103 $other = $this->getDataGenerator()->create_user(); 104 105 // Invalid agent (use different user, instead of the current one). 106 $info = [ 107 'agent' => item_agent::create_from_user($other), 108 ]; 109 $data = test_helper::create_state($info, true); 110 $this->get_state_data('fake_component', $data, null); 111 } 112 113 /** 114 * Testing valid/invalid state. 115 * 116 * @dataProvider states_provider 117 * @param array $info The xAPI state information (to override default values). 118 * @param string $expected Expected results. 119 */ 120 public function test_get_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 $component = $info['component'] ?? 'fake_component'; 145 $this->get_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 get_state the external class 182 */ 183 private function get_external_class(): get_state { 184 $ws = new class extends get_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 get_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 get_state 209 * @param string $expected expected results (if null an exception is expected) 210 */ 211 private function get_state_data(string $component, state $data, ?string $expected): void { 212 // When no result is expected, an exception will be incurred. 213 if (is_null($expected)) { 214 $this->expectException(xapi_exception::class); 215 } 216 217 $external = $this->get_external_class(); 218 $result = $external::execute( 219 $component, 220 iri::generate($data->get_activity_id(), 'activity'), 221 json_encode($data->get_agent()), 222 $data->get_state_id(), 223 $data->get_registration() 224 ); 225 $result = external_api::clean_returnvalue($external::execute_returns(), $result); 226 227 // Check the returned state has the expected values. 228 if ($expected === 'true') { 229 $this->assertEquals(json_encode($data->jsonSerialize()), $result); 230 } else { 231 $this->assertNull($result); 232 } 233 } 234 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body