Subversion-Projekte lars-tiefland.laravel_shop

Revision

Zur aktuellen Revision | Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
namespace Illuminate\Support\Testing\Fakes;
4
 
5
use Closure;
6
use Illuminate\Bus\BatchRepository;
7
use Illuminate\Bus\PendingBatch;
8
use Illuminate\Contracts\Bus\QueueingDispatcher;
9
use Illuminate\Support\Arr;
10
use Illuminate\Support\Collection;
11
use Illuminate\Support\Traits\ReflectsClosures;
12
use PHPUnit\Framework\Assert as PHPUnit;
13
 
14
class BusFake implements QueueingDispatcher
15
{
16
    use ReflectsClosures;
17
 
18
    /**
19
     * The original Bus dispatcher implementation.
20
     *
21
     * @var \Illuminate\Contracts\Bus\QueueingDispatcher
22
     */
23
    protected $dispatcher;
24
 
25
    /**
26
     * The job types that should be intercepted instead of dispatched.
27
     *
28
     * @var array
29
     */
30
    protected $jobsToFake = [];
31
 
32
    /**
33
     * The job types that should be dispatched instead of faked.
34
     *
35
     * @var array
36
     */
37
    protected $jobsToDispatch = [];
38
 
39
    /**
40
     * The fake repository to track batched jobs.
41
     *
42
     * @var \Illuminate\Bus\BatchRepository
43
     */
44
    protected $batchRepository;
45
 
46
    /**
47
     * The commands that have been dispatched.
48
     *
49
     * @var array
50
     */
51
    protected $commands = [];
52
 
53
    /**
54
     * The commands that have been dispatched synchronously.
55
     *
56
     * @var array
57
     */
58
    protected $commandsSync = [];
59
 
60
    /**
61
     * The commands that have been dispatched after the response has been sent.
62
     *
63
     * @var array
64
     */
65
    protected $commandsAfterResponse = [];
66
 
67
    /**
68
     * The batches that have been dispatched.
69
     *
70
     * @var array
71
     */
72
    protected $batches = [];
73
 
74
    /**
75
     * Create a new bus fake instance.
76
     *
77
     * @param  \Illuminate\Contracts\Bus\QueueingDispatcher  $dispatcher
78
     * @param  array|string  $jobsToFake
79
     * @param  \Illuminate\Bus\BatchRepository|null  $batchRepository
80
     * @return void
81
     */
82
    public function __construct(QueueingDispatcher $dispatcher, $jobsToFake = [], BatchRepository $batchRepository = null)
83
    {
84
        $this->dispatcher = $dispatcher;
85
        $this->jobsToFake = Arr::wrap($jobsToFake);
86
        $this->batchRepository = $batchRepository ?: new BatchRepositoryFake;
87
    }
88
 
89
    /**
90
     * Specify the jobs that should be dispatched instead of faked.
91
     *
92
     * @param  array|string  $jobsToDispatch
93
     * @return void
94
     */
95
    public function except($jobsToDispatch)
96
    {
97
        $this->jobsToDispatch = array_merge($this->jobsToDispatch, Arr::wrap($jobsToDispatch));
98
 
99
        return $this;
100
    }
101
 
102
    /**
103
     * Assert if a job was dispatched based on a truth-test callback.
104
     *
105
     * @param  string|\Closure  $command
106
     * @param  callable|int|null  $callback
107
     * @return void
108
     */
109
    public function assertDispatched($command, $callback = null)
110
    {
111
        if ($command instanceof Closure) {
112
            [$command, $callback] = [$this->firstClosureParameterType($command), $command];
113
        }
114
 
115
        if (is_numeric($callback)) {
116
            return $this->assertDispatchedTimes($command, $callback);
117
        }
118
 
119
        PHPUnit::assertTrue(
120
            $this->dispatched($command, $callback)->count() > 0 ||
121
            $this->dispatchedAfterResponse($command, $callback)->count() > 0 ||
122
            $this->dispatchedSync($command, $callback)->count() > 0,
123
            "The expected [{$command}] job was not dispatched."
124
        );
125
    }
126
 
127
    /**
128
     * Assert if a job was pushed a number of times.
129
     *
130
     * @param  string  $command
131
     * @param  int  $times
132
     * @return void
133
     */
134
    public function assertDispatchedTimes($command, $times = 1)
135
    {
136
        $count = $this->dispatched($command)->count() +
137
                 $this->dispatchedAfterResponse($command)->count() +
138
                 $this->dispatchedSync($command)->count();
139
 
140
        PHPUnit::assertSame(
141
            $times, $count,
142
            "The expected [{$command}] job was pushed {$count} times instead of {$times} times."
143
        );
144
    }
145
 
146
    /**
147
     * Determine if a job was dispatched based on a truth-test callback.
148
     *
149
     * @param  string|\Closure  $command
150
     * @param  callable|null  $callback
151
     * @return void
152
     */
153
    public function assertNotDispatched($command, $callback = null)
154
    {
155
        if ($command instanceof Closure) {
156
            [$command, $callback] = [$this->firstClosureParameterType($command), $command];
157
        }
158
 
159
        PHPUnit::assertTrue(
160
            $this->dispatched($command, $callback)->count() === 0 &&
161
            $this->dispatchedAfterResponse($command, $callback)->count() === 0 &&
162
            $this->dispatchedSync($command, $callback)->count() === 0,
163
            "The unexpected [{$command}] job was dispatched."
164
        );
165
    }
166
 
167
    /**
168
     * Assert that no jobs were dispatched.
169
     *
170
     * @return void
171
     */
172
    public function assertNothingDispatched()
173
    {
174
        PHPUnit::assertEmpty($this->commands, 'Jobs were dispatched unexpectedly.');
175
    }
176
 
177
    /**
178
     * Assert if a job was explicitly dispatched synchronously based on a truth-test callback.
179
     *
180
     * @param  string|\Closure  $command
181
     * @param  callable|int|null  $callback
182
     * @return void
183
     */
184
    public function assertDispatchedSync($command, $callback = null)
185
    {
186
        if ($command instanceof Closure) {
187
            [$command, $callback] = [$this->firstClosureParameterType($command), $command];
188
        }
189
 
190
        if (is_numeric($callback)) {
191
            return $this->assertDispatchedSyncTimes($command, $callback);
192
        }
193
 
194
        PHPUnit::assertTrue(
195
            $this->dispatchedSync($command, $callback)->count() > 0,
196
            "The expected [{$command}] job was not dispatched synchronously."
197
        );
198
    }
199
 
200
    /**
201
     * Assert if a job was pushed synchronously a number of times.
202
     *
203
     * @param  string  $command
204
     * @param  int  $times
205
     * @return void
206
     */
207
    public function assertDispatchedSyncTimes($command, $times = 1)
208
    {
209
        $count = $this->dispatchedSync($command)->count();
210
 
211
        PHPUnit::assertSame(
212
            $times, $count,
213
            "The expected [{$command}] job was synchronously pushed {$count} times instead of {$times} times."
214
        );
215
    }
216
 
217
    /**
218
     * Determine if a job was dispatched based on a truth-test callback.
219
     *
220
     * @param  string|\Closure  $command
221
     * @param  callable|null  $callback
222
     * @return void
223
     */
224
    public function assertNotDispatchedSync($command, $callback = null)
225
    {
226
        if ($command instanceof Closure) {
227
            [$command, $callback] = [$this->firstClosureParameterType($command), $command];
228
        }
229
 
230
        PHPUnit::assertCount(
231
            0, $this->dispatchedSync($command, $callback),
232
            "The unexpected [{$command}] job was dispatched synchronously."
233
        );
234
    }
235
 
236
    /**
237
     * Assert if a job was dispatched after the response was sent based on a truth-test callback.
238
     *
239
     * @param  string|\Closure  $command
240
     * @param  callable|int|null  $callback
241
     * @return void
242
     */
243
    public function assertDispatchedAfterResponse($command, $callback = null)
244
    {
245
        if ($command instanceof Closure) {
246
            [$command, $callback] = [$this->firstClosureParameterType($command), $command];
247
        }
248
 
249
        if (is_numeric($callback)) {
250
            return $this->assertDispatchedAfterResponseTimes($command, $callback);
251
        }
252
 
253
        PHPUnit::assertTrue(
254
            $this->dispatchedAfterResponse($command, $callback)->count() > 0,
255
            "The expected [{$command}] job was not dispatched after sending the response."
256
        );
257
    }
258
 
259
    /**
260
     * Assert if a job was pushed after the response was sent a number of times.
261
     *
262
     * @param  string  $command
263
     * @param  int  $times
264
     * @return void
265
     */
266
    public function assertDispatchedAfterResponseTimes($command, $times = 1)
267
    {
268
        $count = $this->dispatchedAfterResponse($command)->count();
269
 
270
        PHPUnit::assertSame(
271
            $times, $count,
272
            "The expected [{$command}] job was pushed {$count} times instead of {$times} times."
273
        );
274
    }
275
 
276
    /**
277
     * Determine if a job was dispatched based on a truth-test callback.
278
     *
279
     * @param  string|\Closure  $command
280
     * @param  callable|null  $callback
281
     * @return void
282
     */
283
    public function assertNotDispatchedAfterResponse($command, $callback = null)
284
    {
285
        if ($command instanceof Closure) {
286
            [$command, $callback] = [$this->firstClosureParameterType($command), $command];
287
        }
288
 
289
        PHPUnit::assertCount(
290
            0, $this->dispatchedAfterResponse($command, $callback),
291
            "The unexpected [{$command}] job was dispatched after sending the response."
292
        );
293
    }
294
 
295
    /**
296
     * Assert if a chain of jobs was dispatched.
297
     *
298
     * @param  array  $expectedChain
299
     * @return void
300
     */
301
    public function assertChained(array $expectedChain)
302
    {
303
        $command = $expectedChain[0];
304
 
305
        $expectedChain = array_slice($expectedChain, 1);
306
 
307
        $callback = null;
308
 
309
        if ($command instanceof Closure) {
310
            [$command, $callback] = [$this->firstClosureParameterType($command), $command];
311
        } elseif (! is_string($command)) {
312
            $instance = $command;
313
 
314
            $command = get_class($instance);
315
 
316
            $callback = function ($job) use ($instance) {
317
                return serialize($this->resetChainPropertiesToDefaults($job)) === serialize($instance);
318
            };
319
        }
320
 
321
        PHPUnit::assertTrue(
322
            $this->dispatched($command, $callback)->isNotEmpty(),
323
            "The expected [{$command}] job was not dispatched."
324
        );
325
 
326
        PHPUnit::assertTrue(
327
            collect($expectedChain)->isNotEmpty(),
328
            'The expected chain can not be empty.'
329
        );
330
 
331
        $this->isChainOfObjects($expectedChain)
332
            ? $this->assertDispatchedWithChainOfObjects($command, $expectedChain, $callback)
333
            : $this->assertDispatchedWithChainOfClasses($command, $expectedChain, $callback);
334
    }
335
 
336
    /**
337
     * Reset the chain properties to their default values on the job.
338
     *
339
     * @param  mixed  $job
340
     * @return mixed
341
     */
342
    protected function resetChainPropertiesToDefaults($job)
343
    {
344
        return tap(clone $job, function ($job) {
345
            $job->chainConnection = null;
346
            $job->chainQueue = null;
347
            $job->chainCatchCallbacks = null;
348
            $job->chained = [];
349
        });
350
    }
351
 
352
    /**
353
     * Assert if a job was dispatched with an empty chain based on a truth-test callback.
354
     *
355
     * @param  string|\Closure  $command
356
     * @param  callable|null  $callback
357
     * @return void
358
     */
359
    public function assertDispatchedWithoutChain($command, $callback = null)
360
    {
361
        if ($command instanceof Closure) {
362
            [$command, $callback] = [$this->firstClosureParameterType($command), $command];
363
        }
364
 
365
        PHPUnit::assertTrue(
366
            $this->dispatched($command, $callback)->isNotEmpty(),
367
            "The expected [{$command}] job was not dispatched."
368
        );
369
 
370
        $this->assertDispatchedWithChainOfClasses($command, [], $callback);
371
    }
372
 
373
    /**
374
     * Assert if a job was dispatched with chained jobs based on a truth-test callback.
375
     *
376
     * @param  string  $command
377
     * @param  array  $expectedChain
378
     * @param  callable|null  $callback
379
     * @return void
380
     */
381
    protected function assertDispatchedWithChainOfObjects($command, $expectedChain, $callback)
382
    {
383
        $chain = collect($expectedChain)->map(fn ($job) => serialize($job))->all();
384
 
385
        PHPUnit::assertTrue(
386
            $this->dispatched($command, $callback)->filter(
387
                fn ($job) => $job->chained == $chain
388
            )->isNotEmpty(),
389
            'The expected chain was not dispatched.'
390
        );
391
    }
392
 
393
    /**
394
     * Assert if a job was dispatched with chained jobs based on a truth-test callback.
395
     *
396
     * @param  string  $command
397
     * @param  array  $expectedChain
398
     * @param  callable|null  $callback
399
     * @return void
400
     */
401
    protected function assertDispatchedWithChainOfClasses($command, $expectedChain, $callback)
402
    {
403
        $matching = $this->dispatched($command, $callback)->map->chained->map(function ($chain) {
404
            return collect($chain)->map(
405
                fn ($job) => get_class(unserialize($job))
406
            );
407
        })->filter(
408
            fn ($chain) => $chain->all() === $expectedChain
409
        );
410
 
411
        PHPUnit::assertTrue(
412
            $matching->isNotEmpty(), 'The expected chain was not dispatched.'
413
        );
414
    }
415
 
416
    /**
417
     * Determine if the given chain is entirely composed of objects.
418
     *
419
     * @param  array  $chain
420
     * @return bool
421
     */
422
    protected function isChainOfObjects($chain)
423
    {
424
        return ! collect($chain)->contains(fn ($job) => ! is_object($job));
425
    }
426
 
427
    /**
428
     * Assert if a batch was dispatched based on a truth-test callback.
429
     *
430
     * @param  callable  $callback
431
     * @return void
432
     */
433
    public function assertBatched(callable $callback)
434
    {
435
        PHPUnit::assertTrue(
436
            $this->batched($callback)->count() > 0,
437
            'The expected batch was not dispatched.'
438
        );
439
    }
440
 
441
    /**
442
     * Assert the number of batches that have been dispatched.
443
     *
444
     * @param  int  $count
445
     * @return void
446
     */
447
    public function assertBatchCount($count)
448
    {
449
        PHPUnit::assertCount(
450
            $count, $this->batches,
451
        );
452
    }
453
 
454
    /**
455
     * Assert that no batched jobs were dispatched.
456
     *
457
     * @return void
458
     */
459
    public function assertNothingBatched()
460
    {
461
        PHPUnit::assertEmpty($this->batches, 'Batched jobs were dispatched unexpectedly.');
462
    }
463
 
464
    /**
465
     * Get all of the jobs matching a truth-test callback.
466
     *
467
     * @param  string  $command
468
     * @param  callable|null  $callback
469
     * @return \Illuminate\Support\Collection
470
     */
471
    public function dispatched($command, $callback = null)
472
    {
473
        if (! $this->hasDispatched($command)) {
474
            return collect();
475
        }
476
 
477
        $callback = $callback ?: fn () => true;
478
 
479
        return collect($this->commands[$command])->filter(fn ($command) => $callback($command));
480
    }
481
 
482
    /**
483
     * Get all of the jobs dispatched synchronously matching a truth-test callback.
484
     *
485
     * @param  string  $command
486
     * @param  callable|null  $callback
487
     * @return \Illuminate\Support\Collection
488
     */
489
    public function dispatchedSync(string $command, $callback = null)
490
    {
491
        if (! $this->hasDispatchedSync($command)) {
492
            return collect();
493
        }
494
 
495
        $callback = $callback ?: fn () => true;
496
 
497
        return collect($this->commandsSync[$command])->filter(fn ($command) => $callback($command));
498
    }
499
 
500
    /**
501
     * Get all of the jobs dispatched after the response was sent matching a truth-test callback.
502
     *
503
     * @param  string  $command
504
     * @param  callable|null  $callback
505
     * @return \Illuminate\Support\Collection
506
     */
507
    public function dispatchedAfterResponse(string $command, $callback = null)
508
    {
509
        if (! $this->hasDispatchedAfterResponse($command)) {
510
            return collect();
511
        }
512
 
513
        $callback = $callback ?: fn () => true;
514
 
515
        return collect($this->commandsAfterResponse[$command])->filter(fn ($command) => $callback($command));
516
    }
517
 
518
    /**
519
     * Get all of the pending batches matching a truth-test callback.
520
     *
521
     * @param  callable  $callback
522
     * @return \Illuminate\Support\Collection
523
     */
524
    public function batched(callable $callback)
525
    {
526
        if (empty($this->batches)) {
527
            return collect();
528
        }
529
 
530
        return collect($this->batches)->filter(fn ($batch) => $callback($batch));
531
    }
532
 
533
    /**
534
     * Determine if there are any stored commands for a given class.
535
     *
536
     * @param  string  $command
537
     * @return bool
538
     */
539
    public function hasDispatched($command)
540
    {
541
        return isset($this->commands[$command]) && ! empty($this->commands[$command]);
542
    }
543
 
544
    /**
545
     * Determine if there are any stored commands for a given class.
546
     *
547
     * @param  string  $command
548
     * @return bool
549
     */
550
    public function hasDispatchedSync($command)
551
    {
552
        return isset($this->commandsSync[$command]) && ! empty($this->commandsSync[$command]);
553
    }
554
 
555
    /**
556
     * Determine if there are any stored commands for a given class.
557
     *
558
     * @param  string  $command
559
     * @return bool
560
     */
561
    public function hasDispatchedAfterResponse($command)
562
    {
563
        return isset($this->commandsAfterResponse[$command]) && ! empty($this->commandsAfterResponse[$command]);
564
    }
565
 
566
    /**
567
     * Dispatch a command to its appropriate handler.
568
     *
569
     * @param  mixed  $command
570
     * @return mixed
571
     */
572
    public function dispatch($command)
573
    {
574
        if ($this->shouldFakeJob($command)) {
575
            $this->commands[get_class($command)][] = $command;
576
        } else {
577
            return $this->dispatcher->dispatch($command);
578
        }
579
    }
580
 
581
    /**
582
     * Dispatch a command to its appropriate handler in the current process.
583
     *
584
     * Queueable jobs will be dispatched to the "sync" queue.
585
     *
586
     * @param  mixed  $command
587
     * @param  mixed  $handler
588
     * @return mixed
589
     */
590
    public function dispatchSync($command, $handler = null)
591
    {
592
        if ($this->shouldFakeJob($command)) {
593
            $this->commandsSync[get_class($command)][] = $command;
594
        } else {
595
            return $this->dispatcher->dispatchSync($command, $handler);
596
        }
597
    }
598
 
599
    /**
600
     * Dispatch a command to its appropriate handler in the current process.
601
     *
602
     * @param  mixed  $command
603
     * @param  mixed  $handler
604
     * @return mixed
605
     */
606
    public function dispatchNow($command, $handler = null)
607
    {
608
        if ($this->shouldFakeJob($command)) {
609
            $this->commands[get_class($command)][] = $command;
610
        } else {
611
            return $this->dispatcher->dispatchNow($command, $handler);
612
        }
613
    }
614
 
615
    /**
616
     * Dispatch a command to its appropriate handler behind a queue.
617
     *
618
     * @param  mixed  $command
619
     * @return mixed
620
     */
621
    public function dispatchToQueue($command)
622
    {
623
        if ($this->shouldFakeJob($command)) {
624
            $this->commands[get_class($command)][] = $command;
625
        } else {
626
            return $this->dispatcher->dispatchToQueue($command);
627
        }
628
    }
629
 
630
    /**
631
     * Dispatch a command to its appropriate handler.
632
     *
633
     * @param  mixed  $command
634
     * @return mixed
635
     */
636
    public function dispatchAfterResponse($command)
637
    {
638
        if ($this->shouldFakeJob($command)) {
639
            $this->commandsAfterResponse[get_class($command)][] = $command;
640
        } else {
641
            return $this->dispatcher->dispatch($command);
642
        }
643
    }
644
 
645
    /**
646
     * Create a new chain of queueable jobs.
647
     *
648
     * @param  \Illuminate\Support\Collection|array  $jobs
649
     * @return \Illuminate\Foundation\Bus\PendingChain
650
     */
651
    public function chain($jobs)
652
    {
653
        $jobs = Collection::wrap($jobs);
654
 
655
        return new PendingChainFake($this, $jobs->shift(), $jobs->toArray());
656
    }
657
 
658
    /**
659
     * Attempt to find the batch with the given ID.
660
     *
661
     * @param  string  $batchId
662
     * @return \Illuminate\Bus\Batch|null
663
     */
664
    public function findBatch(string $batchId)
665
    {
666
        return $this->batchRepository->find($batchId);
667
    }
668
 
669
    /**
670
     * Create a new batch of queueable jobs.
671
     *
672
     * @param  \Illuminate\Support\Collection|array  $jobs
673
     * @return \Illuminate\Bus\PendingBatch
674
     */
675
    public function batch($jobs)
676
    {
677
        return new PendingBatchFake($this, Collection::wrap($jobs));
678
    }
679
 
680
    /**
681
     * Dispatch an empty job batch for testing.
682
     *
683
     * @param  string  $name
684
     * @return \Illuminate\Bus\Batch
685
     */
686
    public function dispatchFakeBatch($name = '')
687
    {
688
        return $this->batch([])->name($name)->dispatch();
689
    }
690
 
691
    /**
692
     * Record the fake pending batch dispatch.
693
     *
694
     * @param  \Illuminate\Bus\PendingBatch  $pendingBatch
695
     * @return \Illuminate\Bus\Batch
696
     */
697
    public function recordPendingBatch(PendingBatch $pendingBatch)
698
    {
699
        $this->batches[] = $pendingBatch;
700
 
701
        return $this->batchRepository->store($pendingBatch);
702
    }
703
 
704
    /**
705
     * Determine if a command should be faked or actually dispatched.
706
     *
707
     * @param  mixed  $command
708
     * @return bool
709
     */
710
    protected function shouldFakeJob($command)
711
    {
712
        if ($this->shouldDispatchCommand($command)) {
713
            return false;
714
        }
715
 
716
        if (empty($this->jobsToFake)) {
717
            return true;
718
        }
719
 
720
        return collect($this->jobsToFake)
721
            ->filter(function ($job) use ($command) {
722
                return $job instanceof Closure
723
                            ? $job($command)
724
                            : $job === get_class($command);
725
            })->isNotEmpty();
726
    }
727
 
728
    /**
729
     * Determine if a command should be dispatched or not.
730
     *
731
     * @param  mixed  $command
732
     * @return bool
733
     */
734
    protected function shouldDispatchCommand($command)
735
    {
736
        return collect($this->jobsToDispatch)
737
            ->filter(function ($job) use ($command) {
738
                return $job instanceof Closure
739
                    ? $job($command)
740
                    : $job === get_class($command);
741
            })->isNotEmpty();
742
    }
743
 
744
    /**
745
     * Set the pipes commands should be piped through before dispatching.
746
     *
747
     * @param  array  $pipes
748
     * @return $this
749
     */
750
    public function pipeThrough(array $pipes)
751
    {
752
        $this->dispatcher->pipeThrough($pipes);
753
 
754
        return $this;
755
    }
756
 
757
    /**
758
     * Determine if the given command has a handler.
759
     *
760
     * @param  mixed  $command
761
     * @return bool
762
     */
763
    public function hasCommandHandler($command)
764
    {
765
        return $this->dispatcher->hasCommandHandler($command);
766
    }
767
 
768
    /**
769
     * Retrieve the handler for a command.
770
     *
771
     * @param  mixed  $command
772
     * @return mixed
773
     */
774
    public function getCommandHandler($command)
775
    {
776
        return $this->dispatcher->getCommandHandler($command);
777
    }
778
 
779
    /**
780
     * Map a command to a handler.
781
     *
782
     * @param  array  $map
783
     * @return $this
784
     */
785
    public function map(array $map)
786
    {
787
        $this->dispatcher->map($map);
788
 
789
        return $this;
790
    }
791
}