Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1441 ariadna 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_sms;
18
 
19
use core_sms\task\send_sms_task;
20
 
21
/**
22
 * Tests for sms manager
23
 *
24
 * @package    core_sms
25
 * @category   test
26
 * @copyright  2024 Andrew Lyons <andrew@nicols.co.uk>
27
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
28
 * @covers \core_sms\manager
29
 * @covers \core_sms\message
30
 * @covers \core_sms\gateway
31
 */
32
final class manager_test extends \advanced_testcase {
33
    public static function setUpBeforeClass(): void {
34
        require_once(__DIR__ . "/fixtures/dummy_gateway.php");
35
        parent::setUpBeforeClass();
36
    }
37
 
38
    public function test_gateway_manipulation(): void {
39
        $this->resetAfterTest();
40
        $config = (object) [
41
            'data' => 'goeshere',
42
        ];
43
 
44
        $dummy = $this->getMockBuilder(\core_sms\gateway::class)
45
            ->setConstructorArgs([
46
                'enabled' => true,
47
                'name' => 'dummy',
48
                'config' => json_encode($config),
49
            ])
50
            ->onlyMethods(['get_send_priority', 'send'])
51
            ->getMock();
52
        $dummygw = get_class($dummy);
53
 
54
        $manager = \core\di::get(\core_sms\manager::class);
55
        $gateway = $manager->create_gateway_instance(
56
            classname: $dummygw,
57
            name: 'dummy',
58
            enabled: true,
59
            config: $config,
60
        );
61
 
62
        $this->assertIsInt($gateway->id);
63
        $this->assertTrue($gateway->enabled);
64
        $this->assertEquals('goeshere', $gateway->config->data);
65
 
66
        // Disable the gateway.
67
        $disabled = $manager->disable_gateway($gateway);
68
        $this->assertFalse($disabled->enabled);
69
        $this->assertEquals($gateway->id, $disabled->id);
70
        $this->assertEquals($gateway->config, $disabled->config);
71
        $this->assertTrue($gateway->enabled);
72
 
73
        // Enable the gateway.
74
        $enabled = $manager->enable_gateway($disabled);
75
        $this->assertTrue($enabled->enabled);
76
        $this->assertEquals($disabled->id, $enabled->id);
77
        $this->assertEquals($gateway->config, $enabled->config);
78
        $this->assertFalse($disabled->enabled);
79
 
80
        // Enabling an enabled gateway should return an identical object.
81
        // Note: Whether the object is identical is not guaranteed, and is internal logic we should not be concerned with.
82
        $reenabled = $manager->enable_gateway($enabled);
83
        $this->assertEquals($enabled, $reenabled);
84
    }
85
 
86
    public function test_create_gateway_instance_unknown_class(): void {
87
        $manager = \core\di::get(\core_sms\manager::class);
88
 
89
        $this->expectException(\coding_exception::class);
90
        $manager->create_gateway_instance(
91
            classname: \no\class\name\here::class,
92
            name: 'dummy',
93
            enabled: true,
94
            config: (object) [
95
                'data' => 'goeshere',
96
            ],
97
        );
98
    }
99
 
100
    public function test_create_gateway_instance_valid_but_wrong_class(): void {
101
        $manager = \core\di::get(\core_sms\manager::class);
102
 
103
        $this->expectException(\coding_exception::class);
104
        $manager->create_gateway_instance(
105
            classname: self::class,
106
            name: 'dummy',
107
            enabled: true,
108
            config: (object) [
109
                'data' => 'goeshere',
110
            ],
111
        );
112
    }
113
 
114
    /**
115
     * Test that uninstalled gateways do not cause failures in the workflow.
116
     */
117
    public function test_uninstalled_gateway(): void {
118
        // We should prevent removal of gateways which hold any data, but if one has been removed, we should not fail.
119
        $this->resetAfterTest();
120
 
121
        $config = (object) [
122
            'data' => 'goeshere',
123
        ];
124
 
125
        $dummy = $this->getMockBuilder(\core_sms\gateway::class)
126
            ->setConstructorArgs([
127
                'enabled' => true,
128
                'name' => 'dummy',
129
                'config' => json_encode($config),
130
            ])
131
            ->onlyMethods(['get_send_priority', 'send'])
132
            ->getMock();
133
        $dummygw = get_class($dummy);
134
 
135
        $manager = \core\di::get(\core_sms\manager::class);
136
        $gateway = $manager->create_gateway_instance(
137
            classname: $dummygw,
138
            name: 'dummy',
139
            enabled: true,
140
            config: $config,
141
        );
142
        $uninstalledgateway = $manager->create_gateway_instance(
143
            classname: $dummygw,
144
            name: 'dummy',
145
            enabled: true,
146
            config: $config,
147
        );
148
 
149
        $db = \core\di::get(\moodle_database::class);
150
        $db->set_field('sms_gateways', 'gateway', 'uninstalled', ['id' => $uninstalledgateway->id]);
151
 
152
        $instances = $manager->get_gateway_instances();
153
        $this->assertDebuggingCalled();
154
        $this->assertCount(1, $instances);
155
        $this->assertArrayHasKey($gateway->id, $instances);
156
        $this->assertArrayNotHasKey($uninstalledgateway->id, $instances);
157
    }
158
 
159
    /**
160
     * Test that multiple instances of the same gateway can be created.
161
     */
162
    public function test_multiple_gateway_instances(): void {
163
        $this->resetAfterTest();
164
 
165
        $config = (object) [
166
            'data' => 'goeshere',
167
        ];
168
 
169
        $dummy = $this->getMockBuilder(\core_sms\gateway::class)
170
            ->setConstructorArgs([
171
                'enabled' => true,
172
                'name' => 'dummy',
173
                'config' => json_encode($config),
174
            ])
175
            ->onlyMethods(['get_send_priority', 'send'])
176
            ->setMockClassName('dummygateway')
177
            ->getMock();
178
        $dummygw = get_class($dummy);
179
        $otherdummy = $this->getMockBuilder(\core_sms\gateway::class)
180
            ->setConstructorArgs([
181
                'enabled' => true,
182
                'name' => 'dummy',
183
                'config' => json_encode($config),
184
            ])
185
            ->onlyMethods(['get_send_priority', 'send'])
186
            ->setMockClassName('otherdummygw')
187
            ->getMock();
188
        $otherdummygw = get_class($otherdummy);
189
 
190
        $manager = \core\di::get(\core_sms\manager::class);
191
        $gatewaya = $manager->create_gateway_instance(
192
            classname: $dummygw,
193
            name: 'dummy',
194
            enabled: true,
195
            config: $config,
196
        );
197
        $gatewayb = $manager->create_gateway_instance(
198
            classname: $otherdummygw,
199
            name: 'dummy',
200
            enabled: true,
201
            config: $config,
202
        );
203
        $gatewayc = $manager->create_gateway_instance(
204
            classname: $dummygw,
205
            name: 'dummy',
206
            config: $config,
207
        );
208
 
209
        $this->assertNotEquals($gatewaya->id, $gatewayb->id);
210
        $this->assertNotEquals($gatewaya->id, $gatewayc->id);
211
        $this->assertNotEquals($gatewayb->id, $gatewayc->id);
212
 
213
        $instances = $manager->get_gateway_instances();
214
        $this->assertCount(3, $instances);
215
        $this->assertArrayHasKey($gatewaya->id, $instances);
216
        $this->assertArrayHasKey($gatewayb->id, $instances);
217
        $this->assertArrayHasKey($gatewayc->id, $instances);
218
 
219
        $enabled = $manager->get_enabled_gateway_instances();
220
        $this->assertCount(2, $enabled);
221
        $this->assertArrayHasKey($gatewaya->id, $enabled);
222
        $this->assertArrayHasKey($gatewayb->id, $enabled);
223
        $this->assertArrayNotHasKey($gatewayc->id, $enabled);
224
 
225
        $dummygwinstances = $manager->get_gateway_instances(['gateway' => $dummygw]);
226
        $this->assertCount(2, $dummygwinstances);
227
        $this->assertArrayHasKey($gatewaya->id, $dummygwinstances);
228
        $this->assertArrayNotHasKey($gatewayb->id, $dummygwinstances);
229
        $this->assertArrayHasKey($gatewayc->id, $dummygwinstances);
230
    }
231
 
232
    /**
233
     * Test that the manager can get gateways for a message.
234
     *
235
     * @dataProvider gateway_priority_provider
236
     * @param string $recipientnumber
237
     * @param int $matchcount
238
     * @param ?string $gw
239
     */
240
    public function test_get_gateways_for_message(
241
        string $recipientnumber,
242
        int $matchcount,
243
        ?string $gw,
244
    ): void {
245
        $this->resetAfterTest();
246
 
247
        $manager = \core\di::get(\core_sms\manager::class);
248
        $ukgw = $manager->create_gateway_instance(\smsgateway_dummy\gateway::class, 'dummy', true, (object) [
249
            'startswith' => (object) [
250
                '+44' => 100,
251
                '+61' => 1,
252
            ],
253
            'priority' => 0,
254
        ]);
255
        $augw = $manager->create_gateway_instance(\smsgateway_dummy\gateway::class, 'dummy', true, (object) [
256
            'startswith' => (object) [
257
                '+44' => 1,
258
                '+61' => 100,
259
            ],
260
            'priority' => 0,
261
        ]);
262
 
263
        $message = new message(
264
            recipientnumber: $recipientnumber,
265
            content: 'Hello, world!',
266
            component: 'core',
267
            messagetype: 'test',
268
            recipientuserid: null,
269
            issensitive: false,
270
        );
271
 
272
        $gateways = $manager->get_possible_gateways_for_message($message);
273
        $this->assertCount($matchcount, $gateways);
274
 
275
        $preferredgw = $manager->get_gateway_for_message($message);
276
        if ($gw === null) {
277
            $this->assertNull($preferredgw);
278
            $this->assertFalse($ukgw->can_send($message));
279
            $this->assertFalse($augw->can_send($message));
280
        } else {
281
            $this->assertEquals(${$gw}->id, $preferredgw->id);
282
            $this->assertTrue(${$gw}->can_send($message));
283
        }
284
    }
285
 
286
    /**
287
     * Data provider for test_get_gateways_for_message tests.
288
     *
289
     * @return array
290
     */
291
    public static function gateway_priority_provider(): array {
292
        return [
293
            'uk' => [
294
                '+447123456789',
295
                2,
296
                'ukgw',
297
            ],
298
            'au' => [
299
                '+61987654321',
300
                2,
301
                'augw',
302
            ],
303
            'us' => [
304
                '+1987654321',
305
                0,
306
                null,
307
            ],
308
        ];
309
    }
310
 
311
    public function test_save_message(): void {
312
        $this->resetAfterTest();
313
 
314
        $manager = \core\di::get(\core_sms\manager::class);
315
        $message = new message(
316
            recipientnumber: '+447123456789',
317
            content: 'Hello, world!',
318
            component: 'core',
319
            messagetype: 'test',
320
            recipientuserid: null,
321
            issensitive: false,
322
        );
323
 
324
        $saved = $manager->save_message($message);
325
 
326
        $this->assertFalse(isset($message->id));
327
        $this->assertTrue(isset($saved->id));
328
 
329
        $storedmessage = $manager->get_message(['id' => $saved->id]);
330
        $this->assertEquals($saved, $storedmessage);
331
 
332
        $updatedmessage = $manager->save_message($saved->with(status: message_status::GATEWAY_SENT));
333
        $this->assertEquals($saved->id, $updatedmessage->id);
334
        $this->assertEquals(message_status::GATEWAY_SENT, $updatedmessage->status);
335
        $this->assertEquals($saved->recipientnumber, $updatedmessage->recipientnumber);
336
        $this->assertEquals($saved->content, $updatedmessage->content);
337
        $this->assertEquals($saved->component, $updatedmessage->component);
338
        $this->assertEquals($saved->messagetype, $updatedmessage->messagetype);
339
        $this->assertEquals($saved->recipientuserid, $updatedmessage->recipientuserid);
340
        $this->assertEquals($saved->issensitive, $updatedmessage->issensitive);
341
    }
342
 
343
    public function test_send(): void {
344
        $this->resetAfterTest();
345
 
346
        $config = new \stdClass();
347
        $config->priority = 50;
348
 
349
        $manager = \core\di::get(\core_sms\manager::class);
350
        $gw = $manager->create_gateway_instance(
351
            classname: \smsgateway_dummy\gateway::class,
352
            name: 'dummy',
353
            enabled: true,
354
            config: $config,
355
        );
356
 
357
        $message = $manager->send(
358
            recipientnumber: '+447123456789',
359
            content: 'Hello, world!',
360
            component: 'core',
361
            messagetype: 'test',
362
            recipientuserid: null,
363
            async: false,
364
        );
365
 
366
        $this->assertInstanceOf(message::class, $message);
367
 
368
        $this->assertIsInt($message->id);
369
        $this->assertEquals(message_status::GATEWAY_SENT, $message->status);
370
        $this->assertEquals($gw->id, $message->gatewayid);
371
 
372
        $this->assertEquals('Hello, world!', $message->content);
373
 
374
        $storedmessage = $manager->get_message(['id' => $message->id]);
375
        $this->assertEquals($message, $storedmessage);
376
    }
377
 
378
    public function test_send_issensitive(): void {
379
        $this->resetAfterTest();
380
 
381
        $manager = \core\di::get(\core_sms\manager::class);
382
        $config = new \stdClass();
383
        $config->priority = 50;
384
 
385
        $gw = $manager->create_gateway_instance(\smsgateway_dummy\gateway::class, 'dummy', true, $config);
386
 
387
        $message = $manager->send(
388
            recipientnumber: '+447123456789',
389
            content: 'Hello, world!',
390
            component: 'core',
391
            messagetype: 'test',
392
            recipientuserid: null,
393
            issensitive: true,
394
            async: false,
395
        );
396
 
397
        $this->assertInstanceOf(message::class, $message);
398
 
399
        $this->assertIsInt($message->id);
400
        $this->assertEquals(message_status::GATEWAY_SENT, $message->status);
401
        $this->assertEquals($gw->id, $message->gatewayid);
402
        $this->assertNull($message->content);
403
 
404
        $storedmessage = $manager->get_message(['id' => $message->id]);
405
        $this->assertEquals($message, $storedmessage);
406
    }
407
 
408
    public function test_send_issensitive_async(): void {
409
        $this->resetAfterTest();
410
 
411
        $manager = \core\di::get(\core_sms\manager::class);
412
 
413
        $this->expectException(\coding_exception::class);
414
        $this->expectExceptionMessage('Sensitive messages cannot be sent asynchronously');
415
        $manager->send(
416
            recipientnumber: '+447123456789',
417
            content: 'Hello, world!',
418
            component: 'core',
419
            messagetype: 'test',
420
            recipientuserid: null,
421
            issensitive: true,
422
            async: true,
423
        );
424
    }
425
 
426
    /**
427
     * Test sending SMS asynchronously.
428
     */
429
    public function test_send_async(): void {
430
        $this->resetAfterTest();
431
 
432
        $config = new \stdClass();
433
        $config->priority = 50;
434
 
435
        $manager = \core\di::get(\core_sms\manager::class);
436
        $manager->create_gateway_instance(
437
            classname: \smsgateway_dummy\gateway::class,
438
            name: 'dummy',
439
            enabled: true,
440
            config: $config,
441
        );
442
 
443
        $message = $manager->send(
444
            recipientnumber: '+447123456789',
445
            content: 'Hello, world!',
446
            component: 'core',
447
            messagetype: 'test',
448
            recipientuserid: null,
449
        );
450
 
451
        $this->assertInstanceOf(message::class, $message);
452
        $this->assertIsInt($message->id);
453
        $this->assertEquals(message_status::GATEWAY_QUEUED, $message->status);
454
        $this->assertEquals('Hello, world!', $message->content);
455
 
456
        $messagedbrecords = $manager->get_messages();
457
        $this->assertInstanceOf(\Generator::class, $messagedbrecords);
458
        $messages = iterator_to_array($messagedbrecords);
459
        $this->assertCount(1, $messages);
460
 
461
        $storedmessage = $manager->get_message(['id' => $message->id]);
462
        $this->assertEquals($message, $storedmessage);
463
 
464
        $adhoctask = \core\task\manager::get_adhoc_tasks(send_sms_task::class);
465
        $this->assertCount(1, $adhoctask);
466
 
467
        // Now lets run the task and check if SMS is sent.
468
        $this->run_all_adhoc_tasks();
469
 
470
        $message = $manager->get_message(['id' => $message->id]);
471
        $this->assertEquals(message_status::GATEWAY_SENT, $message->status);
472
    }
473
 
474
    public function test_send_no_gateway(): void {
475
        $this->resetAfterTest();
476
 
477
        $manager = \core\di::get(\core_sms\manager::class);
478
 
479
        $message = $manager->send(
480
            recipientnumber: '+447123456789',
481
            content: 'Hello, world!',
482
            component: 'core',
483
            messagetype: 'test',
484
            recipientuserid: null,
485
            async: false,
486
        );
487
 
488
        $this->assertInstanceOf(message::class, $message);
489
 
490
        $this->assertIsInt($message->id);
491
        $this->assertEquals(message_status::GATEWAY_NOT_AVAILABLE, $message->status);
492
        $this->assertEmpty($message->gatewayid);
493
    }
494
 
495
    /**
496
     * Test the truncate content process while sending the SMS.
497
     */
498
    public function test_send_truncate_content(): void {
499
        $this->resetAfterTest();
500
 
501
        $config = new \stdClass();
502
        $config->priority = 50;
503
 
504
        $manager = \core\di::get(\core_sms\manager::class);
505
        $manager->create_gateway_instance(
506
            classname: \smsgateway_dummy\gateway::class,
507
            name: 'dummy',
508
            enabled: true,
509
            config: $config,
510
        );
511
 
512
        $message = $manager->send(
513
            recipientnumber: '+447123456789',
514
            content: str_repeat('a', 161),
515
            component: 'core',
516
            messagetype: 'test',
517
            recipientuserid: null,
518
            async: false,
519
        );
520
 
521
        // Expected the message to be truncated with the length limit.
522
        $this->assertEquals(
523
            expected: str_repeat('a', 160), // 160 is the limit of the dummy gateway.
524
            actual: $message->content,
525
        );
526
    }
527
 
528
    public function test_get_messages(): void {
529
        $db = $this->createStub(\moodle_database::class);
530
        $db->method('get_records')->willReturn([
531
            (object) [
532
                'id' => 1,
533
                'recipientnumber' => '+447123456789',
534
                'content' => 'Hello, world!',
535
                'component' => 'core',
536
                'messagetype' => 'test',
537
                'recipientuserid' => null,
538
                'issensitive' => false,
539
                'status' => message_status::GATEWAY_SENT->value,
540
                'gatewayid' => 1,
541
                'timecreated' => time(),
542
            ],
543
        ]);
544
        \core\di::set(\moodle_database::class, $db);
545
 
546
        $manager = \core\di::get(\core_sms\manager::class);
547
        $result = $manager->get_messages();
548
        $this->assertInstanceOf(\Generator::class, $result);
549
 
550
        $messages = iterator_to_array($result);
551
        $this->assertCount(1, $messages);
552
        array_walk($messages, fn ($message) => $this->assertInstanceOf(message::class, $message));
553
    }
554
}