Passed
Pull Request — master (#15)
by Kevin
17:16
created

can_use_extended_frequency_expression()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 14
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 6
c 0
b 0
f 0
dl 0
loc 14
rs 10
nc 1
nop 0
cc 1
1
<?php
2
3
namespace Zenstruck\ScheduleBundle\Tests\DependencyInjection;
4
5
use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractExtensionTestCase;
6
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
7
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
8
use Zenstruck\ScheduleBundle\Command\ScheduleListCommand;
9
use Zenstruck\ScheduleBundle\Command\ScheduleRunCommand;
10
use Zenstruck\ScheduleBundle\DependencyInjection\ZenstruckScheduleExtension;
11
use Zenstruck\ScheduleBundle\EventListener\ScheduleBuilderSubscriber;
12
use Zenstruck\ScheduleBundle\EventListener\ScheduleExtensionSubscriber;
13
use Zenstruck\ScheduleBundle\EventListener\ScheduleLoggerSubscriber;
14
use Zenstruck\ScheduleBundle\EventListener\ScheduleTimezoneSubscriber;
15
use Zenstruck\ScheduleBundle\EventListener\SelfSchedulingCommandSubscriber;
16
use Zenstruck\ScheduleBundle\EventListener\TaskConfigurationSubscriber;
17
use Zenstruck\ScheduleBundle\Schedule\Extension\EmailExtension;
18
use Zenstruck\ScheduleBundle\Schedule\Extension\EnvironmentExtension;
19
use Zenstruck\ScheduleBundle\Schedule\Extension\ExtensionHandlerRegistry;
20
use Zenstruck\ScheduleBundle\Schedule\Extension\Handler\EmailHandler;
21
use Zenstruck\ScheduleBundle\Schedule\Extension\Handler\EnvironmentHandler;
22
use Zenstruck\ScheduleBundle\Schedule\Extension\Handler\PingHandler;
23
use Zenstruck\ScheduleBundle\Schedule\Extension\Handler\SingleServerHandler;
24
use Zenstruck\ScheduleBundle\Schedule\Extension\Handler\WithoutOverlappingHandler;
25
use Zenstruck\ScheduleBundle\Schedule\Extension\PingExtension;
26
use Zenstruck\ScheduleBundle\Schedule\Extension\SingleServerExtension;
27
use Zenstruck\ScheduleBundle\Schedule\Extension\WithoutOverlappingExtension;
28
use Zenstruck\ScheduleBundle\Schedule\ScheduleRunner;
29
use Zenstruck\ScheduleBundle\Schedule\Task\Runner\CallbackTaskRunner;
30
use Zenstruck\ScheduleBundle\Schedule\Task\Runner\CommandTaskRunner;
31
use Zenstruck\ScheduleBundle\Schedule\Task\Runner\PingTaskRunner;
32
use Zenstruck\ScheduleBundle\Schedule\Task\Runner\ProcessTaskRunner;
33
34
/**
35
 * @author Kevin Bond <[email protected]>
36
 */
37
final class ZenstruckScheduleExtensionTest extends AbstractExtensionTestCase
38
{
39
    /**
40
     * @test
41
     */
42
    public function empty_config_loads_default_services()
43
    {
44
        $this->load([]);
45
46
        $this->assertContainerBuilderHasService(ScheduleListCommand::class);
47
        $this->assertContainerBuilderHasServiceDefinitionWithTag(ScheduleListCommand::class, 'console.command');
48
49
        $this->assertContainerBuilderHasService(ScheduleRunCommand::class);
50
        $this->assertContainerBuilderHasServiceDefinitionWithTag(ScheduleRunCommand::class, 'console.command');
51
52
        $this->assertContainerBuilderHasService(ScheduleRunner::class);
53
54
        $this->assertContainerBuilderHasService(ScheduleBuilderSubscriber::class);
55
        $this->assertContainerBuilderHasServiceDefinitionWithTag(ScheduleBuilderSubscriber::class, 'kernel.event_subscriber');
56
57
        $this->assertContainerBuilderHasService(ScheduleExtensionSubscriber::class);
58
        $this->assertContainerBuilderHasServiceDefinitionWithTag(ScheduleExtensionSubscriber::class, 'kernel.event_subscriber');
59
60
        $this->assertContainerBuilderHasService(SelfSchedulingCommandSubscriber::class);
61
        $this->assertContainerBuilderHasServiceDefinitionWithTag(SelfSchedulingCommandSubscriber::class, 'kernel.event_subscriber');
62
63
        $this->assertContainerBuilderHasService(CommandTaskRunner::class);
64
        $this->assertContainerBuilderHasServiceDefinitionWithTag(CommandTaskRunner::class, 'schedule.task_runner');
65
66
        $this->assertContainerBuilderHasService(ProcessTaskRunner::class);
67
        $this->assertContainerBuilderHasServiceDefinitionWithTag(ProcessTaskRunner::class, 'schedule.task_runner');
68
69
        $this->assertContainerBuilderHasService(CallbackTaskRunner::class);
70
        $this->assertContainerBuilderHasServiceDefinitionWithTag(CallbackTaskRunner::class, 'schedule.task_runner');
71
72
        $this->assertContainerBuilderHasService(ScheduleLoggerSubscriber::class);
73
        $this->assertContainerBuilderHasServiceDefinitionWithTag(ScheduleLoggerSubscriber::class, 'kernel.event_subscriber');
74
        $this->assertContainerBuilderHasServiceDefinitionWithTag(ScheduleLoggerSubscriber::class, 'monolog.logger', ['channel' => 'schedule']);
75
76
        $this->assertContainerBuilderHasService(ExtensionHandlerRegistry::class);
77
78
        $this->assertContainerBuilderHasService(EnvironmentHandler::class);
79
        $this->assertContainerBuilderHasServiceDefinitionWithTag(EnvironmentHandler::class, 'schedule.extension_handler');
80
81
        $this->assertContainerBuilderHasService(TaskConfigurationSubscriber::class);
82
        $this->assertContainerBuilderHasServiceDefinitionWithTag(ScheduleBuilderSubscriber::class, 'kernel.event_subscriber');
83
        $this->assertContainerBuilderHasServiceDefinitionWithArgument(TaskConfigurationSubscriber::class, 0, []);
84
85
        $this->assertContainerBuilderHasServiceDefinitionWithTag(PingHandler::class, 'schedule.extension_handler');
86
        $this->assertEmpty($this->container->findDefinition(PingHandler::class)->getArguments());
87
88
        $this->assertContainerBuilderHasServiceDefinitionWithTag(PingTaskRunner::class, 'schedule.task_runner');
89
        $this->assertEmpty($this->container->findDefinition(PingTaskRunner::class)->getArguments());
90
91
        $this->assertContainerBuilderHasServiceDefinitionWithTag(WithoutOverlappingHandler::class, 'schedule.extension_handler');
92
        $this->assertEmpty($this->container->findDefinition(WithoutOverlappingHandler::class)->getArguments());
93
    }
94
95
    /**
96
     * @test
97
     */
98
    public function can_configure_default_timezone()
99
    {
100
        $this->load(['timezone' => 'UTC']);
101
102
        $this->assertContainerBuilderHasService(ScheduleTimezoneSubscriber::class);
103
        $this->assertContainerBuilderHasServiceDefinitionWithArgument(ScheduleTimezoneSubscriber::class, 0, 'UTC');
104
        $this->assertContainerBuilderHasServiceDefinitionWithTag(ScheduleTimezoneSubscriber::class, 'kernel.event_subscriber');
105
    }
106
107
    /**
108
     * @test
109
     */
110
    public function schedule_timezone_must_be_valid()
111
    {
112
        $this->expectException(InvalidConfigurationException::class);
113
        $this->expectExceptionMessage('Invalid configuration for path "zenstruck_schedule.timezone": Timezone "invalid" is not available');
114
115
        $this->load(['timezone' => 'invalid']);
116
    }
117
118
    /**
119
     * @test
120
     */
121
    public function can_configure_single_server_lock_factory()
122
    {
123
        $this->load(['single_server_lock_factory' => 'my_factory']);
124
125
        $this->assertContainerBuilderHasServiceDefinitionWithArgument(SingleServerHandler::class, 0, 'my_factory');
126
        $this->assertContainerBuilderHasServiceDefinitionWithTag(SingleServerHandler::class, 'schedule.extension_handler');
127
    }
128
129
    /**
130
     * @test
131
     */
132
    public function can_configure_without_overlapping_handler_lock_factory()
133
    {
134
        $this->load(['without_overlapping_lock_factory' => 'my_factory']);
135
136
        $this->assertContainerBuilderHasServiceDefinitionWithArgument(WithoutOverlappingHandler::class, 0, 'my_factory');
137
        $this->assertContainerBuilderHasServiceDefinitionWithTag(WithoutOverlappingHandler::class, 'schedule.extension_handler');
138
    }
139
140
    /**
141
     * @test
142
     */
143
    public function can_configure_http_client()
144
    {
145
        $this->load(['http_client' => 'my_client']);
146
147
        $this->assertContainerBuilderHasServiceDefinitionWithArgument(PingHandler::class, 0, 'my_client');
148
        $this->assertContainerBuilderHasServiceDefinitionWithTag(PingHandler::class, 'schedule.extension_handler');
149
        $this->assertContainerBuilderHasServiceDefinitionWithArgument(PingTaskRunner::class, 0, 'my_client');
150
        $this->assertContainerBuilderHasServiceDefinitionWithTag(PingTaskRunner::class, 'schedule.task_runner');
151
    }
152
153
    /**
154
     * @test
155
     */
156
    public function can_configure_email_handler()
157
    {
158
        $this->load(['mailer' => [
159
            'service' => 'my_mailer',
160
            'default_from' => '[email protected]',
161
            'default_to' => '[email protected]',
162
            'subject_prefix' => '[Acme Inc]',
163
        ]]);
164
165
        $this->assertContainerBuilderHasServiceDefinitionWithArgument(EmailHandler::class, 0, 'my_mailer');
166
        $this->assertContainerBuilderHasServiceDefinitionWithTag(EmailHandler::class, 'schedule.extension_handler');
167
        $this->assertContainerBuilderHasServiceDefinitionWithArgument(EmailHandler::class, 1, '[email protected]');
168
        $this->assertContainerBuilderHasServiceDefinitionWithArgument(EmailHandler::class, 2, '[email protected]');
169
        $this->assertContainerBuilderHasServiceDefinitionWithArgument(EmailHandler::class, 3, '[Acme Inc]');
170
    }
171
172
    /**
173
     * @test
174
     */
175
    public function minimum_email_handler_configuration()
176
    {
177
        $this->load(['mailer' => [
178
            'service' => 'my_mailer',
179
        ]]);
180
181
        $this->assertContainerBuilderHasServiceDefinitionWithArgument(EmailHandler::class, 0, 'my_mailer');
182
        $this->assertContainerBuilderHasServiceDefinitionWithTag(EmailHandler::class, 'schedule.extension_handler');
183
        $this->assertContainerBuilderHasServiceDefinitionWithArgument(EmailHandler::class, 1, null);
184
        $this->assertContainerBuilderHasServiceDefinitionWithArgument(EmailHandler::class, 2, null);
185
        $this->assertContainerBuilderHasServiceDefinitionWithArgument(EmailHandler::class, 3, null);
186
    }
187
188
    /**
189
     * @test
190
     */
191
    public function can_add_schedule_environment_as_string()
192
    {
193
        $this->load(['schedule_extensions' => [
194
            'environments' => 'prod',
195
        ]]);
196
197
        $this->assertContainerBuilderHasService('zenstruck_schedule.extension.environments', EnvironmentExtension::class);
198
        $this->assertContainerBuilderHasServiceDefinitionWithArgument('zenstruck_schedule.extension.environments', 0, ['prod']);
199
        $this->assertContainerBuilderHasServiceDefinitionWithTag('zenstruck_schedule.extension.environments', 'schedule.extension');
200
201
        $extensionIterator = $this->container->getDefinition(ScheduleExtensionSubscriber::class)->getArgument(0);
202
203
        $this->assertInstanceOf(TaggedIteratorArgument::class, $extensionIterator);
204
        $this->assertSame('schedule.extension', $extensionIterator->getTag());
205
    }
206
207
    /**
208
     * @test
209
     */
210
    public function can_add_schedule_environment_as_array()
211
    {
212
        $this->load(['schedule_extensions' => [
213
            'environments' => ['prod', 'stage'],
214
        ]]);
215
216
        $this->assertContainerBuilderHasServiceDefinitionWithArgument('zenstruck_schedule.extension.environments', 0, ['prod', 'stage']);
217
    }
218
219
    /**
220
     * @test
221
     */
222
    public function can_enable_single_server_schedule_extension()
223
    {
224
        $this->load(['schedule_extensions' => [
225
            'on_single_server' => null,
226
        ]]);
227
228
        $this->assertContainerBuilderHasService('zenstruck_schedule.extension.on_single_server', SingleServerExtension::class);
229
        $this->assertContainerBuilderHasServiceDefinitionWithTag('zenstruck_schedule.extension.on_single_server', 'schedule.extension');
230
231
        $extensionIterator = $this->container->getDefinition(ScheduleExtensionSubscriber::class)->getArgument(0);
232
233
        $this->assertInstanceOf(TaggedIteratorArgument::class, $extensionIterator);
234
        $this->assertSame('schedule.extension', $extensionIterator->getTag());
235
    }
236
237
    /**
238
     * @test
239
     */
240
    public function can_enable_email_on_failure_schedule_extension()
241
    {
242
        $this->load(['schedule_extensions' => [
243
            'email_on_failure' => [
244
                'to' => '[email protected]',
245
                'subject' => 'my subject',
246
            ],
247
        ]]);
248
249
        $this->assertContainerBuilderHasService('zenstruck_schedule.extension.email_on_failure', EmailExtension::class);
250
        $this->assertContainerBuilderHasServiceDefinitionWithTag('zenstruck_schedule.extension.email_on_failure', 'schedule.extension');
251
252
        $definition = $this->container->getDefinition('zenstruck_schedule.extension.email_on_failure');
253
254
        $this->assertSame([EmailExtension::class, 'scheduleFailure'], $definition->getFactory());
255
        $this->assertSame(['[email protected]', 'my subject'], $definition->getArguments());
256
257
        $extensionIterator = $this->container->getDefinition(ScheduleExtensionSubscriber::class)->getArgument(0);
258
259
        $this->assertInstanceOf(TaggedIteratorArgument::class, $extensionIterator);
260
        $this->assertSame('schedule.extension', $extensionIterator->getTag());
261
    }
262
263
    /**
264
     * @test
265
     * @dataProvider pingScheduleExtensionProvider
266
     */
267
    public function can_enable_ping_schedule_extensions($key, $method)
268
    {
269
        $this->load(['schedule_extensions' => [
270
            $key => [
271
                'url' => 'example.com',
272
            ],
273
        ]]);
274
275
        $this->assertContainerBuilderHasService('zenstruck_schedule.extension.'.$key, PingExtension::class);
276
        $this->assertContainerBuilderHasServiceDefinitionWithTag('zenstruck_schedule.extension.'.$key, 'schedule.extension');
277
278
        $definition = $this->container->getDefinition('zenstruck_schedule.extension.'.$key);
279
280
        $this->assertSame([PingExtension::class, $method], $definition->getFactory());
281
        $this->assertSame(['example.com', 'GET', []], $definition->getArguments());
282
283
        $extensionIterator = $this->container->getDefinition(ScheduleExtensionSubscriber::class)->getArgument(0);
284
285
        $this->assertInstanceOf(TaggedIteratorArgument::class, $extensionIterator);
286
        $this->assertSame('schedule.extension', $extensionIterator->getTag());
287
    }
288
289
    public static function pingScheduleExtensionProvider()
290
    {
291
        return [
292
            ['ping_before', 'scheduleBefore'],
293
            ['ping_after', 'scheduleAfter'],
294
            ['ping_on_success', 'scheduleSuccess'],
295
            ['ping_on_failure', 'scheduleFailure'],
296
        ];
297
    }
298
299
    /**
300
     * @test
301
     */
302
    public function minimum_task_configuration()
303
    {
304
        $this->load([
305
            'tasks' => [
306
                [
307
                    'task' => 'my:command',
308
                    'frequency' => '0 * * * *',
309
                ],
310
            ],
311
        ]);
312
313
        $config = $this->container->getDefinition(TaskConfigurationSubscriber::class)->getArgument(0)[0];
314
315
        $this->assertSame(['my:command'], $config['task']);
316
        $this->assertSame('0 * * * *', $config['frequency']);
317
        $this->assertNull($config['description']);
318
        $this->assertNull($config['timezone']);
319
        $this->assertFalse($config['without_overlapping']['enabled']);
320
        $this->assertFalse($config['only_between']['enabled']);
321
        $this->assertFalse($config['unless_between']['enabled']);
322
        $this->assertFalse($config['ping_before']['enabled']);
323
        $this->assertFalse($config['ping_after']['enabled']);
324
        $this->assertFalse($config['ping_on_success']['enabled']);
325
        $this->assertFalse($config['ping_on_failure']['enabled']);
326
        $this->assertFalse($config['email_after']['enabled']);
327
        $this->assertFalse($config['email_on_failure']['enabled']);
328
    }
329
330
    /**
331
     * @test
332
     */
333
    public function can_configure_a_compound_task()
334
    {
335
        $this->load([
336
            'tasks' => [
337
                [
338
                    'task' => ['my:command', 'bash:/my-script'],
339
                    'frequency' => '0 * * * *',
340
                ],
341
            ],
342
        ]);
343
344
        $config = $this->container->getDefinition(TaskConfigurationSubscriber::class)->getArgument(0)[0];
345
346
        $this->assertSame(['my:command', 'bash:/my-script'], $config['task']);
347
    }
348
349
    /**
350
     * @test
351
     */
352
    public function can_configure_a_compound_task_with_descriptions()
353
    {
354
        $this->load([
355
            'tasks' => [
356
                [
357
                    'task' => [
358
                        'task1' => 'my:command',
359
                        'task2' => 'bash:/my-script',
360
                    ],
361
                    'frequency' => '0 * * * *',
362
                ],
363
            ],
364
        ]);
365
366
        $config = $this->container->getDefinition(TaskConfigurationSubscriber::class)->getArgument(0)[0];
367
368
        $this->assertSame(['task1' => 'my:command', 'task2' => 'bash:/my-script'], $config['task']);
369
    }
370
371
    /**
372
     * @test
373
     */
374
    public function compound_tasks_cannot_be_an_empty_array()
375
    {
376
        $this->expectException(InvalidConfigurationException::class);
377
        $this->expectExceptionMessage('The path "zenstruck_schedule.tasks.0.task" should have at least 1 element(s) defined.');
378
379
        $this->load([
380
            'tasks' => [
381
                [
382
                    'task' => [],
383
                    'frequency' => 'invalid',
384
                ],
385
            ],
386
        ]);
387
    }
388
389
    /**
390
     * @test
391
     */
392
    public function task_is_required()
393
    {
394
        $this->expectException(InvalidConfigurationException::class);
395
        $this->expectExceptionMessage('The child node "task" at path "zenstruck_schedule.tasks.0" must be configured.');
396
397
        $this->load([
398
            'tasks' => [
399
                [
400
                    'frequency' => '0 * * * *',
401
                ],
402
            ],
403
        ]);
404
    }
405
406
    /**
407
     * @test
408
     */
409
    public function task_cannot_be_null()
410
    {
411
        $this->expectException(InvalidConfigurationException::class);
412
        $this->expectExceptionMessage('Invalid configuration for path "zenstruck_schedule.tasks.0.task": Task cannot be empty value.');
413
414
        $this->load([
415
            'tasks' => [
416
                [
417
                    'task' => null,
418
                    'frequency' => '0 * * * *',
419
                ],
420
            ],
421
        ]);
422
    }
423
424
    /**
425
     * @test
426
     */
427
    public function task_cannot_be_empty()
428
    {
429
        $this->expectException(InvalidConfigurationException::class);
430
        $this->expectExceptionMessage('Invalid configuration for path "zenstruck_schedule.tasks.0.task": Task cannot be empty value.');
431
432
        $this->load([
433
            'tasks' => [
434
                [
435
                    'task' => '',
436
                    'frequency' => '0 * * * *',
437
                ],
438
            ],
439
        ]);
440
    }
441
442
    /**
443
     * @test
444
     */
445
    public function compound_tasks_must_not_contain_null_values()
446
    {
447
        $this->expectException(InvalidConfigurationException::class);
448
        $this->expectExceptionMessage('Invalid configuration for path "zenstruck_schedule.tasks.0.task": Task cannot be empty value.');
449
450
        $this->load([
451
            'tasks' => [
452
                [
453
                    'task' => ['my:command', null],
454
                    'frequency' => 'invalid',
455
                ],
456
            ],
457
        ]);
458
    }
459
460
    /**
461
     * @test
462
     */
463
    public function task_frequency_is_required()
464
    {
465
        $this->expectException(InvalidConfigurationException::class);
466
        $this->expectExceptionMessage('The child node "frequency" at path "zenstruck_schedule.tasks.0" must be configured.');
467
468
        $this->load([
469
            'tasks' => [
470
                [
471
                    'task' => 'my:command',
472
                ],
473
            ],
474
        ]);
475
    }
476
477
    /**
478
     * @test
479
     */
480
    public function task_frequency_cannot_be_null()
481
    {
482
        $this->expectException(InvalidConfigurationException::class);
483
        $this->expectExceptionMessage('The path "zenstruck_schedule.tasks.0.frequency" cannot contain an empty value, but got null.');
484
485
        $this->load([
486
            'tasks' => [
487
                [
488
                    'task' => 'my:command',
489
                    'frequency' => null,
490
                ],
491
            ],
492
        ]);
493
    }
494
495
    /**
496
     * @test
497
     */
498
    public function task_frequency_must_be_valid()
499
    {
500
        $this->expectException(InvalidConfigurationException::class);
501
        $this->expectExceptionMessage('Invalid configuration for path "zenstruck_schedule.tasks.0.frequency": "invalid" is an invalid cron expression.');
502
503
        $this->load([
504
            'tasks' => [
505
                [
506
                    'task' => 'my:command',
507
                    'frequency' => 'invalid',
508
                ],
509
            ],
510
        ]);
511
    }
512
513
    /**
514
     * @test
515
     */
516
    public function can_use_extended_frequency_expression()
517
    {
518
        $this->load([
519
            'tasks' => [
520
                [
521
                    'task' => 'my:command',
522
                    'frequency' => '@daily',
523
                ],
524
            ],
525
        ]);
526
527
        $config = $this->container->getDefinition(TaskConfigurationSubscriber::class)->getArgument(0)[0];
528
529
        $this->assertSame('@daily', $config['frequency']);
530
    }
531
532
    /**
533
     * @test
534
     */
535
    public function can_use_hashed_frequency_expression()
536
    {
537
        $this->load([
538
            'tasks' => [
539
                [
540
                    'task' => 'my:command',
541
                    'frequency' => 'H H * * *',
542
                ],
543
            ],
544
        ]);
545
546
        $config = $this->container->getDefinition(TaskConfigurationSubscriber::class)->getArgument(0)[0];
547
548
        $this->assertSame('H H * * *', $config['frequency']);
549
    }
550
551
    /**
552
     * @test
553
     */
554
    public function can_use_frequency_alias()
555
    {
556
        $this->load([
557
            'tasks' => [
558
                [
559
                    'task' => 'my:command',
560
                    'frequency' => '#midnight',
561
                ],
562
            ],
563
        ]);
564
565
        $config = $this->container->getDefinition(TaskConfigurationSubscriber::class)->getArgument(0)[0];
566
567
        $this->assertSame('#midnight', $config['frequency']);
568
    }
569
570
    /**
571
     * @test
572
     */
573
    public function full_task_configuration()
574
    {
575
        $this->load([
576
            'tasks' => [
577
                [
578
                    'task' => [
579
                        'my:command --option',
580
                        'another:command',
581
                    ],
582
                    'frequency' => '0 0 * * *',
583
                    'description' => 'my description',
584
                    'timezone' => 'UTC',
585
                    'without_overlapping' => null,
586
                    'only_between' => [
587
                        'start' => 9,
588
                        'end' => 17,
589
                    ],
590
                    'unless_between' => [
591
                        'start' => 12,
592
                        'end' => '13:30',
593
                    ],
594
                    'ping_before' => [
595
                        'url' => 'https://example.com/before',
596
                    ],
597
                    'ping_after' => [
598
                        'url' => 'https://example.com/after',
599
                    ],
600
                    'ping_on_success' => [
601
                        'url' => 'https://example.com/success',
602
                    ],
603
                    'ping_on_failure' => [
604
                        'url' => 'https://example.com/failure',
605
                        'method' => 'POST',
606
                    ],
607
                    'email_after' => null,
608
                    'email_on_failure' => [
609
                        'to' => '[email protected]',
610
                        'subject' => 'my subject',
611
                    ],
612
                ],
613
            ],
614
        ]);
615
616
        $config = $this->container->getDefinition(TaskConfigurationSubscriber::class)->getArgument(0)[0];
617
618
        $this->assertSame(['my:command --option', 'another:command'], $config['task']);
619
        $this->assertSame('0 0 * * *', $config['frequency']);
620
        $this->assertSame('my description', $config['description']);
621
        $this->assertSame('UTC', $config['timezone']);
622
        $this->assertTrue($config['without_overlapping']['enabled']);
623
        $this->assertSame(WithoutOverlappingExtension::DEFAULT_TTL, $config['without_overlapping']['ttl']);
624
        $this->assertTrue($config['only_between']['enabled']);
625
        $this->assertSame(9, $config['only_between']['start']);
626
        $this->assertSame(17, $config['only_between']['end']);
627
        $this->assertTrue($config['unless_between']['enabled']);
628
        $this->assertSame(12, $config['unless_between']['start']);
629
        $this->assertSame('13:30', $config['unless_between']['end']);
630
        $this->assertTrue($config['ping_before']['enabled']);
631
        $this->assertSame('https://example.com/before', $config['ping_before']['url']);
632
        $this->assertSame('GET', $config['ping_before']['method']);
633
        $this->assertTrue($config['ping_after']['enabled']);
634
        $this->assertSame('https://example.com/after', $config['ping_after']['url']);
635
        $this->assertSame('GET', $config['ping_after']['method']);
636
        $this->assertTrue($config['ping_on_success']['enabled']);
637
        $this->assertSame('https://example.com/success', $config['ping_on_success']['url']);
638
        $this->assertSame('GET', $config['ping_on_success']['method']);
639
        $this->assertTrue($config['ping_on_failure']['enabled']);
640
        $this->assertSame('https://example.com/failure', $config['ping_on_failure']['url']);
641
        $this->assertSame('POST', $config['ping_on_failure']['method']);
642
        $this->assertTrue($config['email_after']['enabled']);
643
        $this->assertNull($config['email_after']['to']);
644
        $this->assertNull($config['email_after']['subject']);
645
        $this->assertTrue($config['email_on_failure']['enabled']);
646
        $this->assertSame('[email protected]', $config['email_on_failure']['to']);
647
        $this->assertSame('my subject', $config['email_on_failure']['subject']);
648
    }
649
650
    /**
651
     * @test
652
     */
653
    public function email_and_ping_configuration_can_be_shortened()
654
    {
655
        $this->load([
656
            'tasks' => [
657
                [
658
                    'task' => 'my:command --option',
659
                    'frequency' => '0 0 * * *',
660
                    'ping_after' => 'https://example.com/after',
661
                    'email_after' => '[email protected]',
662
                ],
663
            ],
664
        ]);
665
666
        $config = $this->container->getDefinition(TaskConfigurationSubscriber::class)->getArgument(0)[0];
667
668
        $this->assertTrue($config['ping_after']['enabled']);
669
        $this->assertSame('https://example.com/after', $config['ping_after']['url']);
670
        $this->assertSame('GET', $config['ping_after']['method']);
671
        $this->assertSame([], $config['ping_after']['options']);
672
673
        $this->assertTrue($config['email_after']['enabled']);
674
        $this->assertSame('[email protected]', $config['email_after']['to']);
675
        $this->assertNull($config['email_after']['subject']);
676
    }
677
678
    /**
679
     * @test
680
     */
681
    public function between_and_unless_between_config_can_be_shortened()
682
    {
683
        $this->load([
684
            'tasks' => [
685
                [
686
                    'task' => 'my:command --option',
687
                    'frequency' => '0 0 * * *',
688
                    'only_between' => '9-17',
689
                    'unless_between' => '11:30-13:15',
690
                ],
691
            ],
692
        ]);
693
694
        $config = $this->container->getDefinition(TaskConfigurationSubscriber::class)->getArgument(0)[0];
695
696
        $this->assertTrue($config['only_between']['enabled']);
697
        $this->assertSame('9', $config['only_between']['start']);
698
        $this->assertSame('17', $config['only_between']['end']);
699
700
        $this->assertTrue($config['unless_between']['enabled']);
701
        $this->assertSame('11:30', $config['unless_between']['start']);
702
        $this->assertSame('13:15', $config['unless_between']['end']);
703
    }
704
705
    /**
706
     * @test
707
     */
708
    public function task_config_must_be_an_array(): void
709
    {
710
        $this->expectException(InvalidConfigurationException::class);
711
        $this->expectExceptionMessage('Invalid type for path "zenstruck_schedule.tasks.0.config"');
712
713
        $this->load([
714
            'tasks' => [
715
                [
716
                    'task' => 'my:command',
717
                    'frequency' => '0 * * * *',
718
                    'config' => 'not an array',
719
                ],
720
            ],
721
        ]);
722
    }
723
724
    protected function getContainerExtensions(): array
725
    {
726
        return [new ZenstruckScheduleExtension()];
727
    }
728
}
729