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