Passed
Pull Request — 1.x (#6)
by Kevin
02:07
created

EnvelopeCollection::assertCount()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 5
rs 10
c 1
b 0
f 1
1
<?php
2
3
namespace Zenstruck\Messenger\Test;
4
5
use PHPUnit\Framework\Assert as PHPUnit;
6
use Symfony\Component\Messenger\Envelope;
7
8
/**
9
 * @author Kevin Bond <[email protected]>
10
 */
11
final class EnvelopeCollection implements \IteratorAggregate, \Countable
12
{
13
    private array $envelopes;
14
15
    /**
16
     * @internal
17
     */
18
    public function __construct(Envelope ...$envelopes)
19
    {
20
        $this->envelopes = $envelopes;
21
    }
22
23
    public function assertEmpty(): self
24
    {
25
        return $this->assertCount(0);
26
    }
27
28
    public function assertNotEmpty(): self
29
    {
30
        PHPUnit::assertNotEmpty($this, 'Expected some messages but found none.');
31
32
        return $this;
33
    }
34
35
    public function assertCount(int $count): self
36
    {
37
        PHPUnit::assertCount($count, $this->envelopes, \sprintf('Expected %d messages, but %d messages found.', $count, \count($this->envelopes)));
38
39
        return $this;
40
    }
41
42
    public function assertContains(string $messageClass, ?int $times = null): self
43
    {
44
        $actual = $this->messages($messageClass);
45
46
        PHPUnit::assertNotEmpty($actual, "Message \"{$messageClass}\" not found.");
47
48
        if (null !== $times) {
49
            PHPUnit::assertCount($times, $actual, \sprintf('Expected to find message "%s" %d times but found %d times.', $messageClass, $times, \count($actual)));
50
        }
51
52
        return $this;
53
    }
54
55
    public function assertNotContains(string $messageClass): self
56
    {
57
        $actual = $this->messages($messageClass);
58
59
        PHPUnit::assertEmpty($actual, "Found message \"{$messageClass}\" but should not.");
60
61
        return $this;
62
    }
63
64
    /**
65
     * @param string|callable|null $filter
66
     */
67
    public function first($filter = null): TestEnvelope
68
    {
69
        if (null === $filter) {
70
            // just the first envelope
71
            return $this->first(fn() => true);
72
        }
73
74
        if (!\is_callable($filter)) {
75
            // first envelope for message class
76
            return $this->first(fn(Envelope $e) => $filter === \get_class($e->getMessage()));
77
        }
78
79
        $filter = self::normalizeFilter($filter);
80
81
        foreach ($this->envelopes as $envelope) {
82
            if ($filter($envelope)) {
83
                return new TestEnvelope($envelope);
84
            }
85
        }
86
87
        throw new \RuntimeException('No envelopes found.');
88
    }
89
90
    /**
91
     * The messages extracted from envelopes.
92
     *
93
     * @param string|null $class Only messages of this class
94
     *
95
     * @return object[]
96
     */
97
    public function messages(?string $class = null): array
98
    {
99
        $messages = \array_map(static fn(Envelope $envelope) => $envelope->getMessage(), $this->envelopes);
100
101
        if (!$class) {
102
            return $messages;
103
        }
104
105
        return \array_values(\array_filter($messages, static fn(object $message) => $class === \get_class($message)));
106
    }
107
108
    /**
109
     * @return TestEnvelope[]
110
     */
111
    public function all(): array
112
    {
113
        return \iterator_to_array($this);
114
    }
115
116
    public function getIterator(): \Iterator
117
    {
118
        foreach ($this->envelopes as $envelope) {
119
            yield new TestEnvelope($envelope);
120
        }
121
    }
122
123
    public function count(): int
124
    {
125
        return \count($this->envelopes);
126
    }
127
128
    private static function normalizeFilter(callable $filter): callable
129
    {
130
        $function = new \ReflectionFunction(\Closure::fromCallable($filter));
131
132
        if (!$parameter = $function->getParameters()[0] ?? null) {
133
            return $filter;
134
        }
135
136
        if (!$type = $parameter->getType()) {
137
            return $filter;
138
        }
139
140
        if (!$type instanceof \ReflectionNamedType || $type->isBuiltin() || Envelope::class === $type->getName()) {
141
            return $filter;
142
        }
143
144
        // user used message class name as type-hint
145
        return function(Envelope $envelope) use ($filter, $type) {
146
            if ($type->getName() !== \get_class($envelope->getMessage())) {
147
                return false;
148
            }
149
150
            return $filter($envelope->getMessage());
151
        };
152
    }
153
}
154