Passed
Push — master ( c9ba58...cb7a43 )
by Kevin
02:01
created

can_configure_a_null_task()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 15
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 7
c 0
b 0
f 0
dl 0
loc 15
rs 10
cc 1
nc 1
nop 0
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\SelfHandlingHandler;
24
use Zenstruck\ScheduleBundle\Schedule\Extension\Handler\SingleServerHandler;
25
use Zenstruck\ScheduleBundle\Schedule\Extension\Handler\WithoutOverlappingHandler;
26
use Zenstruck\ScheduleBundle\Schedule\Extension\PingExtension;
27
use Zenstruck\ScheduleBundle\Schedule\Extension\SingleServerExtension;
28
use Zenstruck\ScheduleBundle\Schedule\Extension\WithoutOverlappingExtension;
29
use Zenstruck\ScheduleBundle\Schedule\ScheduleRunner;
30
use Zenstruck\ScheduleBundle\Schedule\Task\Runner\CommandTaskRunner;
31
use Zenstruck\ScheduleBundle\Schedule\Task\Runner\SelfRunningTaskRunner;
32
33
/**
34
 * @author Kevin Bond <[email protected]>
35
 */
36
final class ZenstruckScheduleExtensionTest extends AbstractExtensionTestCase
37
{
38
    /**
39
     * @test
40
     */
41
    public function empty_config_loads_default_services()
42
    {
43
        $this->load([]);
44
45
        $this->assertContainerBuilderHasService(ScheduleListCommand::class);
46
        $this->assertContainerBuilderHasServiceDefinitionWithTag(ScheduleListCommand::class, 'console.command');
47
48
        $this->assertContainerBuilderHasService(ScheduleRunCommand::class);
49
        $this->assertContainerBuilderHasServiceDefinitionWithTag(ScheduleRunCommand::class, 'console.command');
50
51
        $this->assertContainerBuilderHasService(ScheduleRunner::class);
52
53
        $this->assertContainerBuilderHasService(ScheduleBuilderSubscriber::class);
54
        $this->assertContainerBuilderHasServiceDefinitionWithTag(ScheduleBuilderSubscriber::class, 'kernel.event_subscriber');
55
56
        $this->assertContainerBuilderHasService(ScheduleExtensionSubscriber::class);
57
        $this->assertContainerBuilderHasServiceDefinitionWithTag(ScheduleExtensionSubscriber::class, 'kernel.event_subscriber');
58
59
        $this->assertContainerBuilderHasService(SelfSchedulingCommandSubscriber::class);
60
        $this->assertContainerBuilderHasServiceDefinitionWithTag(SelfSchedulingCommandSubscriber::class, 'kernel.event_subscriber');
61
62
        $this->assertContainerBuilderHasService(CommandTaskRunner::class);
63
        $this->assertContainerBuilderHasServiceDefinitionWithTag(CommandTaskRunner::class, 'schedule.task_runner');
64
65
        $this->assertContainerBuilderHasService(SelfRunningTaskRunner::class);
66
        $this->assertContainerBuilderHasServiceDefinitionWithTag(SelfRunningTaskRunner::class, 'schedule.task_runner');
67
68
        $this->assertContainerBuilderHasService(ScheduleLoggerSubscriber::class);
69
        $this->assertContainerBuilderHasServiceDefinitionWithTag(ScheduleLoggerSubscriber::class, 'kernel.event_subscriber');
70
        $this->assertContainerBuilderHasServiceDefinitionWithTag(ScheduleLoggerSubscriber::class, 'monolog.logger', ['channel' => 'schedule']);
71
72
        $this->assertContainerBuilderHasService(ExtensionHandlerRegistry::class);
73
74
        $this->assertContainerBuilderHasService(SelfHandlingHandler::class);
75
        $this->assertContainerBuilderHasServiceDefinitionWithTag(SelfHandlingHandler::class, 'schedule.extension_handler', ['priority' => -100]);
76
77
        $this->assertContainerBuilderHasService(EnvironmentHandler::class);
78
        $this->assertContainerBuilderHasServiceDefinitionWithTag(EnvironmentHandler::class, 'schedule.extension_handler');
79
80
        $this->assertContainerBuilderHasService(TaskConfigurationSubscriber::class);
81
        $this->assertContainerBuilderHasServiceDefinitionWithTag(ScheduleBuilderSubscriber::class, 'kernel.event_subscriber');
82
        $this->assertContainerBuilderHasServiceDefinitionWithArgument(TaskConfigurationSubscriber::class, 0, []);
83
    }
84
85
    /**
86
     * @test
87
     */
88
    public function can_configure_default_timezone()
89
    {
90
        $this->load(['timezone' => 'UTC']);
91
92
        $this->assertContainerBuilderHasService(ScheduleTimezoneSubscriber::class);
93
        $this->assertContainerBuilderHasServiceDefinitionWithArgument(ScheduleTimezoneSubscriber::class, 0, 'UTC');
94
        $this->assertContainerBuilderHasServiceDefinitionWithTag(ScheduleTimezoneSubscriber::class, 'kernel.event_subscriber');
95
    }
96
97
    /**
98
     * @test
99
     */
100
    public function schedule_timezone_must_be_valid()
101
    {
102
        $this->expectException(InvalidConfigurationException::class);
103
        $this->expectExceptionMessage('Invalid configuration for path "zenstruck_schedule.timezone": Timezone "invalid" is not available');
104
105
        $this->load(['timezone' => 'invalid']);
106
    }
107
108
    /**
109
     * @test
110
     */
111
    public function can_configure_single_server_lock_factory()
112
    {
113
        $this->load(['single_server_handler' => 'my_factory']);
114
115
        $this->assertContainerBuilderHasServiceDefinitionWithArgument(SingleServerHandler::class, 0, 'my_factory');
116
        $this->assertContainerBuilderHasServiceDefinitionWithTag(SingleServerHandler::class, 'schedule.extension_handler');
117
    }
118
119
    /**
120
     * @test
121
     */
122
    public function can_configure_without_overlapping_handler_lock_factory()
123
    {
124
        $this->load(['without_overlapping_handler' => 'my_factory']);
125
126
        $this->assertContainerBuilderHasServiceDefinitionWithArgument(WithoutOverlappingHandler::class, 0, 'my_factory');
127
        $this->assertContainerBuilderHasServiceDefinitionWithTag(WithoutOverlappingHandler::class, 'schedule.extension_handler');
128
    }
129
130
    /**
131
     * @test
132
     */
133
    public function can_configure_ping_handler_http_client()
134
    {
135
        $this->load(['ping_handler' => 'my_client']);
136
137
        $this->assertContainerBuilderHasServiceDefinitionWithArgument(PingHandler::class, 0, 'my_client');
138
        $this->assertContainerBuilderHasServiceDefinitionWithTag(PingHandler::class, 'schedule.extension_handler');
139
    }
140
141
    /**
142
     * @test
143
     */
144
    public function can_configure_email_handler()
145
    {
146
        $this->load(['email_handler' => [
147
            'service' => 'my_mailer',
148
            'default_from' => '[email protected]',
149
            'default_to' => '[email protected]',
150
            'subject_prefix' => '[Acme Inc]',
151
        ]]);
152
153
        $this->assertContainerBuilderHasServiceDefinitionWithArgument(EmailHandler::class, 0, 'my_mailer');
154
        $this->assertContainerBuilderHasServiceDefinitionWithTag(EmailHandler::class, 'schedule.extension_handler');
155
        $this->assertContainerBuilderHasServiceDefinitionWithArgument(EmailHandler::class, 1, '[email protected]');
156
        $this->assertContainerBuilderHasServiceDefinitionWithArgument(EmailHandler::class, 2, '[email protected]');
157
        $this->assertContainerBuilderHasServiceDefinitionWithArgument(EmailHandler::class, 3, '[Acme Inc]');
158
    }
159
160
    /**
161
     * @test
162
     */
163
    public function minimum_email_handler_configuration()
164
    {
165
        $this->load(['email_handler' => [
166
            'service' => 'my_mailer',
167
        ]]);
168
169
        $this->assertContainerBuilderHasServiceDefinitionWithArgument(EmailHandler::class, 0, 'my_mailer');
170
        $this->assertContainerBuilderHasServiceDefinitionWithTag(EmailHandler::class, 'schedule.extension_handler');
171
        $this->assertContainerBuilderHasServiceDefinitionWithArgument(EmailHandler::class, 1, null);
172
        $this->assertContainerBuilderHasServiceDefinitionWithArgument(EmailHandler::class, 2, null);
173
        $this->assertContainerBuilderHasServiceDefinitionWithArgument(EmailHandler::class, 3, null);
174
    }
175
176
    /**
177
     * @test
178
     */
179
    public function can_add_schedule_environment_as_string()
180
    {
181
        $this->load(['schedule_extensions' => [
182
            'environments' => 'prod',
183
        ]]);
184
185
        $this->assertContainerBuilderHasService('zenstruck_schedule.extension.environments', EnvironmentExtension::class);
186
        $this->assertContainerBuilderHasServiceDefinitionWithArgument('zenstruck_schedule.extension.environments', 0, ['prod']);
187
        $this->assertContainerBuilderHasServiceDefinitionWithTag('zenstruck_schedule.extension.environments', 'schedule.extension');
188
189
        $extensionIterator = $this->container->getDefinition(ScheduleExtensionSubscriber::class)->getArgument(0);
190
191
        $this->assertInstanceOf(TaggedIteratorArgument::class, $extensionIterator);
192
        $this->assertSame('schedule.extension', $extensionIterator->getTag());
193
    }
194
195
    /**
196
     * @test
197
     */
198
    public function can_add_schedule_environment_as_array()
199
    {
200
        $this->load(['schedule_extensions' => [
201
            'environments' => ['prod', 'stage'],
202
        ]]);
203
204
        $this->assertContainerBuilderHasServiceDefinitionWithArgument('zenstruck_schedule.extension.environments', 0, ['prod', 'stage']);
205
    }
206
207
    /**
208
     * @test
209
     */
210
    public function can_enable_single_server_schedule_extension()
211
    {
212
        $this->load(['schedule_extensions' => [
213
            'on_single_server' => null,
214
        ]]);
215
216
        $this->assertContainerBuilderHasService('zenstruck_schedule.extension.on_single_server', SingleServerExtension::class);
217
        $this->assertContainerBuilderHasServiceDefinitionWithTag('zenstruck_schedule.extension.on_single_server', 'schedule.extension');
218
219
        $extensionIterator = $this->container->getDefinition(ScheduleExtensionSubscriber::class)->getArgument(0);
220
221
        $this->assertInstanceOf(TaggedIteratorArgument::class, $extensionIterator);
222
        $this->assertSame('schedule.extension', $extensionIterator->getTag());
223
    }
224
225
    /**
226
     * @test
227
     */
228
    public function can_enable_email_on_failure_schedule_extension()
229
    {
230
        $this->load(['schedule_extensions' => [
231
            'email_on_failure' => [
232
                'to' => '[email protected]',
233
                'subject' => 'my subject',
234
            ],
235
        ]]);
236
237
        $this->assertContainerBuilderHasService('zenstruck_schedule.extension.email_on_failure', EmailExtension::class);
238
        $this->assertContainerBuilderHasServiceDefinitionWithTag('zenstruck_schedule.extension.email_on_failure', 'schedule.extension');
239
240
        $definition = $this->container->getDefinition('zenstruck_schedule.extension.email_on_failure');
241
242
        $this->assertSame([EmailExtension::class, 'scheduleFailure'], $definition->getFactory());
243
        $this->assertSame(['[email protected]', 'my subject'], $definition->getArguments());
244
245
        $extensionIterator = $this->container->getDefinition(ScheduleExtensionSubscriber::class)->getArgument(0);
246
247
        $this->assertInstanceOf(TaggedIteratorArgument::class, $extensionIterator);
248
        $this->assertSame('schedule.extension', $extensionIterator->getTag());
249
    }
250
251
    /**
252
     * @test
253
     * @dataProvider pingScheduleExtensionProvider
254
     */
255
    public function can_enable_ping_schedule_extensions($key, $method)
256
    {
257
        $this->load(['schedule_extensions' => [
258
            $key => [
259
                'url' => 'example.com',
260
            ],
261
        ]]);
262
263
        $this->assertContainerBuilderHasService('zenstruck_schedule.extension.'.$key, PingExtension::class);
264
        $this->assertContainerBuilderHasServiceDefinitionWithTag('zenstruck_schedule.extension.'.$key, 'schedule.extension');
265
266
        $definition = $this->container->getDefinition('zenstruck_schedule.extension.'.$key);
267
268
        $this->assertSame([PingExtension::class, $method], $definition->getFactory());
269
        $this->assertSame(['example.com', 'GET', []], $definition->getArguments());
270
271
        $extensionIterator = $this->container->getDefinition(ScheduleExtensionSubscriber::class)->getArgument(0);
272
273
        $this->assertInstanceOf(TaggedIteratorArgument::class, $extensionIterator);
274
        $this->assertSame('schedule.extension', $extensionIterator->getTag());
275
    }
276
277
    public static function pingScheduleExtensionProvider()
278
    {
279
        return [
280
            ['ping_before', 'scheduleBefore'],
281
            ['ping_after', 'scheduleAfter'],
282
            ['ping_on_success', 'scheduleSuccess'],
283
            ['ping_on_failure', 'scheduleFailure'],
284
        ];
285
    }
286
287
    /**
288
     * @test
289
     */
290
    public function minimum_task_configuration()
291
    {
292
        $this->load([
293
            'tasks' => [
294
                [
295
                    'command' => 'my:command',
296
                    'frequency' => '0 * * * *',
297
                ],
298
            ],
299
        ]);
300
301
        $config = $this->container->getDefinition(TaskConfigurationSubscriber::class)->getArgument(0)[0];
302
303
        $this->assertSame(['my:command'], $config['command']);
304
        $this->assertSame('0 * * * *', $config['frequency']);
305
        $this->assertNull($config['description']);
306
        $this->assertNull($config['timezone']);
307
        $this->assertFalse($config['without_overlapping']['enabled']);
308
        $this->assertFalse($config['between']['enabled']);
309
        $this->assertFalse($config['unless_between']['enabled']);
310
        $this->assertFalse($config['ping_before']['enabled']);
311
        $this->assertFalse($config['ping_after']['enabled']);
312
        $this->assertFalse($config['ping_on_success']['enabled']);
313
        $this->assertFalse($config['ping_on_failure']['enabled']);
314
        $this->assertFalse($config['email_after']['enabled']);
315
        $this->assertFalse($config['email_on_failure']['enabled']);
316
    }
317
318
    /**
319
     * @test
320
     */
321
    public function can_configure_a_compound_task()
322
    {
323
        $this->load([
324
            'tasks' => [
325
                [
326
                    'command' => ['my:command', 'bash:/my-script'],
327
                    'frequency' => '0 * * * *',
328
                ],
329
            ],
330
        ]);
331
332
        $config = $this->container->getDefinition(TaskConfigurationSubscriber::class)->getArgument(0)[0];
333
334
        $this->assertSame(['my:command', 'bash:/my-script'], $config['command']);
335
    }
336
337
    /**
338
     * @test
339
     */
340
    public function can_configure_a_compound_task_with_descriptions()
341
    {
342
        $this->load([
343
            'tasks' => [
344
                [
345
                    'command' => [
346
                        'task1' => 'my:command',
347
                        'task2' => 'bash:/my-script',
348
                    ],
349
                    'frequency' => '0 * * * *',
350
                ],
351
            ],
352
        ]);
353
354
        $config = $this->container->getDefinition(TaskConfigurationSubscriber::class)->getArgument(0)[0];
355
356
        $this->assertSame(['task1' => 'my:command', 'task2' => 'bash:/my-script'], $config['command']);
357
    }
358
359
    /**
360
     * @test
361
     */
362
    public function compound_tasks_cannot_be_an_empty_array()
363
    {
364
        $this->expectException(InvalidConfigurationException::class);
365
        $this->expectExceptionMessage('The path "zenstruck_schedule.tasks.0.command" should have at least 1 element(s) defined.');
366
367
        $this->load([
368
            'tasks' => [
369
                [
370
                    'command' => [],
371
                    'frequency' => 'invalid',
372
                ],
373
            ],
374
        ]);
375
    }
376
377
    /**
378
     * @test
379
     */
380
    public function can_configure_a_null_task()
381
    {
382
        $this->load([
383
            'tasks' => [
384
                [
385
                    'command' => null,
386
                    'frequency' => '0 * * * *',
387
                    'description' => 'my task',
388
                ],
389
            ],
390
        ]);
391
392
        $config = $this->container->getDefinition(TaskConfigurationSubscriber::class)->getArgument(0)[0];
393
394
        $this->assertSame([null], $config['command']);
395
    }
396
397
    /**
398
     * @test
399
     */
400
    public function null_task_must_have_a_description()
401
    {
402
        $this->expectException(InvalidConfigurationException::class);
403
        $this->expectExceptionMessage('Invalid configuration for path "zenstruck_schedule.tasks.0": "null" tasks must have a description.');
404
405
        $this->load([
406
            'tasks' => [
407
                [
408
                    'command' => null,
409
                    'frequency' => '0 * * * *',
410
                ],
411
            ],
412
        ]);
413
    }
414
415
    /**
416
     * @test
417
     */
418
    public function compound_tasks_must_not_contain_null_tasks()
419
    {
420
        $this->expectException(InvalidConfigurationException::class);
421
        $this->expectExceptionMessage('Invalid configuration for path "zenstruck_schedule.tasks.0.command": "null" tasks cannot be added to compound tasks.');
422
423
        $this->load([
424
            'tasks' => [
425
                [
426
                    'command' => ['my:command', null],
427
                    'frequency' => 'invalid',
428
                ],
429
            ],
430
        ]);
431
    }
432
433
    /**
434
     * @test
435
     */
436
    public function task_frequency_must_be_valid()
437
    {
438
        $this->expectException(InvalidConfigurationException::class);
439
        $this->expectExceptionMessage('Invalid configuration for path "zenstruck_schedule.tasks.0.frequency": "invalid" is an invalid cron expression.');
440
441
        $this->load([
442
            'tasks' => [
443
                [
444
                    'command' => 'my:command',
445
                    'frequency' => 'invalid',
446
                ],
447
            ],
448
        ]);
449
    }
450
451
    /**
452
     * @test
453
     */
454
    public function full_task_configuration()
455
    {
456
        $this->load([
457
            'tasks' => [
458
                [
459
                    'command' => [
460
                        'my:command --option',
461
                        'another:command',
462
                    ],
463
                    'frequency' => '0 0 * * *',
464
                    'description' => 'my description',
465
                    'timezone' => 'UTC',
466
                    'without_overlapping' => null,
467
                    'between' => [
468
                        'start' => 9,
469
                        'end' => 17,
470
                    ],
471
                    'unless_between' => [
472
                        'start' => 12,
473
                        'end' => '13:30',
474
                    ],
475
                    'ping_before' => [
476
                        'url' => 'https://example.com/before',
477
                    ],
478
                    'ping_after' => [
479
                        'url' => 'https://example.com/after',
480
                    ],
481
                    'ping_on_success' => [
482
                        'url' => 'https://example.com/success',
483
                    ],
484
                    'ping_on_failure' => [
485
                        'url' => 'https://example.com/failure',
486
                        'method' => 'POST',
487
                    ],
488
                    'email_after' => null,
489
                    'email_on_failure' => [
490
                        'to' => '[email protected]',
491
                        'subject' => 'my subject',
492
                    ],
493
                ],
494
            ],
495
        ]);
496
497
        $config = $this->container->getDefinition(TaskConfigurationSubscriber::class)->getArgument(0)[0];
498
499
        $this->assertSame(['my:command --option', 'another:command'], $config['command']);
500
        $this->assertSame('0 0 * * *', $config['frequency']);
501
        $this->assertSame('my description', $config['description']);
502
        $this->assertSame('UTC', $config['timezone']);
503
        $this->assertTrue($config['without_overlapping']['enabled']);
504
        $this->assertSame(WithoutOverlappingExtension::DEFAULT_TTL, $config['without_overlapping']['ttl']);
505
        $this->assertTrue($config['between']['enabled']);
506
        $this->assertSame(9, $config['between']['start']);
507
        $this->assertSame(17, $config['between']['end']);
508
        $this->assertTrue($config['unless_between']['enabled']);
509
        $this->assertSame(12, $config['unless_between']['start']);
510
        $this->assertSame('13:30', $config['unless_between']['end']);
511
        $this->assertTrue($config['ping_before']['enabled']);
512
        $this->assertSame('https://example.com/before', $config['ping_before']['url']);
513
        $this->assertSame('GET', $config['ping_before']['method']);
514
        $this->assertTrue($config['ping_after']['enabled']);
515
        $this->assertSame('https://example.com/after', $config['ping_after']['url']);
516
        $this->assertSame('GET', $config['ping_after']['method']);
517
        $this->assertTrue($config['ping_on_success']['enabled']);
518
        $this->assertSame('https://example.com/success', $config['ping_on_success']['url']);
519
        $this->assertSame('GET', $config['ping_on_success']['method']);
520
        $this->assertTrue($config['ping_on_failure']['enabled']);
521
        $this->assertSame('https://example.com/failure', $config['ping_on_failure']['url']);
522
        $this->assertSame('POST', $config['ping_on_failure']['method']);
523
        $this->assertTrue($config['email_after']['enabled']);
524
        $this->assertNull($config['email_after']['to']);
525
        $this->assertNull($config['email_after']['subject']);
526
        $this->assertTrue($config['email_on_failure']['enabled']);
527
        $this->assertSame('[email protected]', $config['email_on_failure']['to']);
528
        $this->assertSame('my subject', $config['email_on_failure']['subject']);
529
    }
530
531
    /**
532
     * @test
533
     */
534
    public function email_and_ping_configuration_can_be_shortened()
535
    {
536
        $this->load([
537
            'tasks' => [
538
                [
539
                    'command' => 'my:command --option',
540
                    'frequency' => '0 0 * * *',
541
                    'ping_after' => 'https://example.com/after',
542
                    'email_after' => '[email protected]',
543
                ],
544
            ],
545
        ]);
546
547
        $config = $this->container->getDefinition(TaskConfigurationSubscriber::class)->getArgument(0)[0];
548
549
        $this->assertTrue($config['ping_after']['enabled']);
550
        $this->assertSame('https://example.com/after', $config['ping_after']['url']);
551
        $this->assertSame('GET', $config['ping_after']['method']);
552
        $this->assertSame([], $config['ping_after']['options']);
553
554
        $this->assertTrue($config['email_after']['enabled']);
555
        $this->assertSame('[email protected]', $config['email_after']['to']);
556
        $this->assertNull($config['email_after']['subject']);
557
    }
558
559
    /**
560
     * @test
561
     */
562
    public function between_and_unless_between_config_can_be_shortened()
563
    {
564
        $this->load([
565
            'tasks' => [
566
                [
567
                    'command' => 'my:command --option',
568
                    'frequency' => '0 0 * * *',
569
                    'between' => '9-17',
570
                    'unless_between' => '11:30-13:15',
571
                ],
572
            ],
573
        ]);
574
575
        $config = $this->container->getDefinition(TaskConfigurationSubscriber::class)->getArgument(0)[0];
576
577
        $this->assertTrue($config['between']['enabled']);
578
        $this->assertSame('9', $config['between']['start']);
579
        $this->assertSame('17', $config['between']['end']);
580
581
        $this->assertTrue($config['unless_between']['enabled']);
582
        $this->assertSame('11:30', $config['unless_between']['start']);
583
        $this->assertSame('13:15', $config['unless_between']['end']);
584
    }
585
586
    protected function getContainerExtensions(): array
587
    {
588
        return [new ZenstruckScheduleExtension()];
589
    }
590
}
591