Proyectos de Subversion Moodle

Rev

| 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
namespace core\moodlenet;
18
 
19
use context_course;
20
use core\http_client;
21
use core\oauth2\issuer;
22
use GuzzleHttp\Exception\ClientException;
23
use GuzzleHttp\Handler\MockHandler;
24
use GuzzleHttp\HandlerStack;
25
use GuzzleHttp\Psr7\Response;
26
use PHPUnit\Framework\MockObject\MockObject;
27
use Psr\Http\Message\ResponseInterface;
28
use ReflectionMethod;
29
use stdClass;
30
use testing_data_generator;
31
 
32
/**
33
 * Test coverage for moodlenet course sender.
34
 *
35
 * @package   core
36
 * @copyright 2023 Safat Shahin <safat.shahin@moodle.com>
37
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
38
 * @coversDefaultClass \core\moodlenet\course_sender
39
 */
40
class course_sender_test extends \advanced_testcase {
41
 
42
    /** @var testing_data_generator Data generator. */
43
    private testing_data_generator $generator;
44
    /** @var stdClass Course object. */
45
    private stdClass $course;
46
 
47
    /** @var context_course Course context instance. */
48
    private context_course $coursecontext;
49
    /** @var issuer $issuer Dummy issuer. */
50
    private issuer $issuer;
51
    /** @var MockObject $mockoauthclient Mock OAuth client. */
52
    private MockObject $mockoauthclient;
53
 
54
    public static function setUpBeforeClass(): void {
55
        parent::setUpBeforeClass();
56
 
57
        require_once(__DIR__ . '/helpers.php');
58
    }
59
 
60
    /**
61
     * Set up function for tests.
62
     */
63
    protected function setUp(): void {
64
        parent::setUp();
65
 
66
        $this->resetAfterTest();
67
        // Get data generator.
68
        $this->generator = $this->getDataGenerator();
69
        // Create course.
70
        $this->course = $this->generator->create_course(['shortname' => 'testcourse']);
71
        $this->coursecontext = context_course::instance($this->course->id);
72
        // Create mock issuer.
73
        $this->issuer = helpers::get_mock_issuer(1);
74
        // Create mock builder for OAuth2 client.
75
        $mockbuilder = $this->getMockBuilder('core\oauth2\client');
76
        $mockbuilder->onlyMethods(['get_issuer', 'is_logged_in', 'get_accesstoken']);
77
        $mockbuilder->setConstructorArgs([$this->issuer, '', '']);
78
        // Get the OAuth2 client mock.
79
        $this->mockoauthclient = $mockbuilder->getMock();
80
    }
81
 
82
    /**
83
     * Test prepare_share_contents method.
84
     *
85
     * @covers ::prepare_share_contents
86
     */
87
    public function test_prepare_share_contents(): void {
88
        global $USER;
89
        $this->setAdminUser();
90
 
91
        // Set get_file method accessibility.
92
        $method = new ReflectionMethod(course_sender::class, 'prepare_share_contents');
93
 
94
        // Test with invalid share format.
95
        $this->expectException(\moodle_exception::class);
96
        $this->expectExceptionMessage(get_string('moodlenet:invalidshareformat', 'error'));
97
 
98
        $httpclient = new http_client();
99
        $moodlenetclient = new moodlenet_client($httpclient, $this->mockoauthclient);
100
        $coursesender = new course_sender(
101
            random_int(5, 30),
102
            $USER->id,
103
            $moodlenetclient,
104
            $this->mockoauthclient,
105
            random_int(5, 30)
106
        );
107
        $coursesender = $method->invoke(new course_sender(
108
            $this->course->id,
109
            $USER->id,
110
            $moodlenetclient,
111
            $this->mockoauthclient,
112
            resource_sender::SHARE_FORMAT_BACKUP
113
        ));
114
 
115
        // Test with valid share format and invalid course module.
116
        $package = $method->invoke(new course_sender(
117
            $this->course->id,
118
            $USER->id,
119
            $moodlenetclient,
120
            $this->mockoauthclient,
121
            resource_sender::SHARE_FORMAT_BACKUP
122
        ));
123
        $this->assertEmpty($package);
124
 
125
        // Test with valid share format and valid course module.
126
        $package = $method->invoke(new course_sender(
127
            $this->course->id,
128
            $USER->id,
129
            $moodlenetclient,
130
            $this->mockoauthclient,
131
            resource_sender::SHARE_FORMAT_BACKUP
132
        ));
133
        $this->assertNotEmpty($package);
134
 
135
        // Confirm the expected stored_file object is returned.
136
        $this->assertInstanceOf(\stored_file::class, $package);
137
    }
138
 
139
    /**
140
     * Test get_resource_description method.
141
     *
142
     * @covers ::get_resource_description
143
     */
144
    public function test_get_resource_description(): void {
145
        global $USER;
146
        $this->setAdminUser();
147
 
148
        $course = $this->generator->create_course([
149
            'summary' => '<p>This is an example Moodle course description.</p>
150
<p>&nbsp;</p>
151
<p>This is a formatted intro</p>
152
<p>&nbsp;</p>
153
<p>This thing has many lines.</p>
154
<p>&nbsp;</p>
155
<p>The last word of this sentence is in <strong>bold</strong></p>'
156
        ]);
157
 
158
        // Set get_resource_description method accessibility.
159
        $method = new ReflectionMethod(course_sender::class, 'get_resource_description');
160
 
161
        // Test the processed description.
162
        $httpclient = new http_client();
163
        $moodlenetclient = new moodlenet_client($httpclient, $this->mockoauthclient);
164
        $processeddescription = $method->invoke(new course_sender(
165
            $course->id,
166
            $USER->id,
167
            $moodlenetclient,
168
            $this->mockoauthclient,
169
            resource_sender::SHARE_FORMAT_BACKUP
170
        ), $this->coursecontext);
171
 
172
        $this->assertEquals('This is an example Moodle course description.
173
 
174
This is a formatted intro
175
 
176
This thing has many lines.
177
 
178
The last word of this sentence is in bold', $processeddescription);
179
    }
180
 
181
    /**
182
     * Test share_resource() method.
183
     *
184
     * @dataProvider share_resource_provider
185
     * @covers ::share_resource
186
     * @covers ::log_event
187
     * @covers \core\moodlenet\moodlenet_client::create_resource_from_stored_file
188
     * @covers \core\moodlenet\moodlenet_client::prepare_file_share_request_data
189
     * @param ResponseInterface $httpresponse
190
     * @param array $expected
191
     */
192
    public function test_share_resource(ResponseInterface $httpresponse, array $expected): void {
193
        global $CFG, $USER;
194
        $this->setAdminUser();
195
 
196
        // Enable the experimental flag.
197
        $CFG->enablesharingtomoodlenet = true;
198
 
199
        // Set OAuth 2 service in the outbound setting to the dummy issuer.
200
        set_config('oauthservice', $this->issuer->get('id'), 'moodlenet');
201
 
202
        // Generate access token for the mock.
203
        $accesstoken = new stdClass();
204
        $accesstoken->token = random_string(64);
205
 
206
        // Get the OAuth2 client mock and set the return value for necessary methods.
207
        $this->mockoauthclient->method('get_issuer')->will($this->returnValue($this->issuer));
208
        $this->mockoauthclient->method('is_logged_in')->will($this->returnValue(true));
209
        $this->mockoauthclient->method('get_accesstoken')->will($this->returnValue($accesstoken));
210
 
211
        // Create Guzzle mock.
212
        $mockguzzlehandler = new MockHandler([$httpresponse]);
213
        $handlerstack = HandlerStack::create($mockguzzlehandler);
214
        $httpclient = new http_client(['handler' => $handlerstack]);
215
 
216
        // Create events sink.
217
        $sink = $this->redirectEvents();
218
 
219
        // Create sender.
220
        $moodlenetclient = new moodlenet_client($httpclient, $this->mockoauthclient);
221
        $coursesender = new course_sender(
222
            $this->course->id,
223
            $USER->id,
224
            $moodlenetclient,
225
            $this->mockoauthclient,
226
            resource_sender::SHARE_FORMAT_BACKUP
227
        );
228
 
229
        if (isset($expected['exception'])) {
230
            $this->expectException(ClientException::class);
231
            $this->expectExceptionMessage($expected['exception']);
232
        }
233
        // Call the API.
234
        $result = $coursesender->share_resource();
235
 
236
        // Verify the result.
237
        $this->assertEquals($expected['response_code'], $result['responsecode']);
238
        $this->assertEquals($expected['resource_url'], $result['drafturl']);
239
 
240
        // Verify the events.
241
        $events = $sink->get_events();
242
 
243
        $event = end($events);
244
        $this->assertInstanceOf('\core\event\moodlenet_resource_exported', $event);
245
        $this->assertEquals($USER->id, $event->userid);
246
 
247
        if ($result['responsecode'] == 201) {
248
            $description = "The user with id '{$USER->id}' successfully shared course to MoodleNet with the " .
249
                "following course id, from context with id '{$this->coursecontext->id}': '{$this->course->id}'.";
250
        } else {
251
            $description = "The user with id '{$USER->id}' failed to share course to MoodleNet with the " .
252
                "following course id, from context with id '{$this->coursecontext->id}': '{$this->course->id}'.";
253
        }
254
        $this->assertEquals($description, $event->get_description());
255
    }
256
 
257
    /**
258
     * Provider for test share_resource().
259
     *
260
     * @return array Test data.
261
     */
262
    public function share_resource_provider(): array {
263
        return [
264
            'Success' => [
265
                'http_response' => new Response(
266
                    201,
267
                    ['Content-Type' => 'application/json'],
268
                    json_encode([
269
                        'homepage' => 'https://moodlenet.example.com/drafts/view/testcourse_backup_1.mbz',
270
                    ]),
271
                ),
272
                'expected' => [
273
                    'response_code' => 201,
274
                    'resource_url' => 'https://moodlenet.example.com/drafts/view/testcourse_backup_1.mbz',
275
                ],
276
            ],
277
            'Fail with 200 status code' => [
278
                'http_response' => new Response(
279
                    200,
280
                    ['Content-Type' => 'application/json'],
281
                    json_encode([
282
                        'homepage' => 'https://moodlenet.example.com/drafts/view/testcourse_backup_2.mbz',
283
                    ]),
284
                ),
285
                'expected' => [
286
                    'response_code' => 200,
287
                    'resource_url' => 'https://moodlenet.example.com/drafts/view/testcourse_backup_2.mbz',
288
                ],
289
            ],
290
            'Fail with 401 status code' => [
291
                'http_response' => new Response(
292
                    401,
293
                ),
294
                'expected' => [
295
                    'response_code' => 401,
296
                    'resource_url' => '',
297
                    'exception' => 'Client error: ' .
298
                        '`POST https://moodlenet.example.com/.pkg/@moodlenet/ed-resource/basic/v1/create` ' .
299
                        'resulted in a `401 Unauthorized` response',
300
                ],
301
            ],
302
            'Fail with 404 status code' => [
303
                'http_response' => new Response(
304
                    404,
305
                ),
306
                'expected' => [
307
                    'response_code' => 404,
308
                    'resource_url' => '',
309
                    'exception' => 'Client error: '.
310
                        '`POST https://moodlenet.example.com/.pkg/@moodlenet/ed-resource/basic/v1/create` ' .
311
                        'resulted in a `404 Not Found` response',
312
                ],
313
            ],
314
        ];
315
    }
316
 
317
 
318
}