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