Completed
Pull Request — master (#125)
by
unknown
16:20
created

ScalarHandler::execute()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
declare(strict_types=1);
3
4
namespace League\Tactician\Bundle\Tests\DependencyInjection\HandlerMapping;
5
6
use DateTime;
7
use League\Tactician\Bundle\DependencyInjection\HandlerMapping\Routing;
8
use League\Tactician\Bundle\DependencyInjection\HandlerMapping\TypeHintMapping;
9
use League\Tactician\Bundle\DependencyInjection\InvalidCommandBusId;
10
use League\Tactician\Bundle\Tests\Fake\FakeCommand;
11
use League\Tactician\Bundle\Tests\Fake\OtherFakeCommand;
12
use PHPUnit\Framework\TestCase;
13
use Symfony\Component\DependencyInjection\ContainerBuilder;
14
use Symfony\Component\DependencyInjection\Definition;
15
16
final class TypeHintMappingTest extends TestCase
17
{
18
    public function test_will_skip_definitions_without_auto_tag()
19
    {
20
        $builder = new ContainerBuilder();
21
        $builder
22
            ->setDefinition('some.handler', new Definition(InvokeHandler::class))
23
            ->addTag('tactician.handler', ['foo' => 'bar']);
24
25
        $routing = (new TypeHintMapping())->build($builder, new Routing(['default']));
26
27
        $this->assertEquals([], $routing->commandToServiceMapping('default'));
28
    }
29
30
    public function test_will_resolve_parameters_in_handler_class()
31
    {
32
        $builder = new ContainerBuilder();
33
        $builder->setParameter('handler_class', InvokeHandler::class);
34
        $builder
35
            ->setDefinition('some.handler', new Definition('%handler_class%'))
36
            ->addTag('tactician.handler', ['typehints' => true]);
37
38
        $routing = (new TypeHintMapping())->build($builder, new Routing(['default']));
39
40
        $this->assertEquals([FakeCommand::class => 'some.handler'], $routing->commandToServiceMapping('default'));
41
    }
42
43
    /**
44
     * @dataProvider simpleTestCases
45
     */
46
    public function test_standard(string $handlerFQCN, array $expectedMapping)
47
    {
48
        $builder = new ContainerBuilder();
49
        $builder
50
            ->setDefinition('some.handler', new Definition($handlerFQCN))
51
            ->addTag('tactician.handler', ['typehints' => true]);
52
53
        $routing = (new TypeHintMapping())->build($builder, new Routing(['default']));
54
55
        $this->assertEquals($expectedMapping, $routing->commandToServiceMapping('default'));
56
    }
57
58
    public function simpleTestCases()
59
    {
60
        $cases = [
61
            'can read __invoke magic method type hint' => [
62
                InvokeHandler::class,
63
                [FakeCommand::class => 'some.handler']
64
            ],
65
            'takes unary methods but not those with multiple parameters' => [
66
                BasicHandler::class,
67
                [FakeCommand::class => 'some.handler', OtherFakeCommand::class => 'some.handler']
68
            ],
69
            'can not exclude built-in objects unfortunately' => [
70
                DateTimeHandler::class,
71
                [DateTime::class => 'some.handler']
72
            ],
73
            'will skip methods with no typehint' => [NoTypehintHandler::class, []],
74
            'will skip methods with an interface typehint' => [InterfaceTypehintHandler::class, []],
75
            'will not try to map scalar typehints' => [ScalarHandler::class, []],
76
            'will not use protected or private methods' => [ProtectedMethodHandler::class, []],
77
            'will not use constructor method' => [ConstructorHandler::class, []],
78
            'will not use static methods' => [StaticHandler::class, []],
79
            'will not use abstract methods' => [AbstractHandler::class, []],
80
            'will not use variadic methods' => [VariadicHandler::class, []]
81
        ];
82
83
        if (version_compare(PHP_VERSION, '8.0.0') >= 0) {
84
            $cases['will not use union type methods'] = [UnionTypeHandler::class, []];
85
        }
86
87
        return $cases;
88
    }
89
90
    public function test_can_bind_to_specific_bus()
91
    {
92
        $builder = new ContainerBuilder();
93
        $builder
94
            ->setDefinition('first.handler', new Definition(BasicHandler::class))
95
            ->addTag('tactician.handler', ['typehints' => true, 'bus' => 'bus.a']);
96
97
        $builder
98
            ->setDefinition('second.handler', new Definition(DateTimeHandler::class))
99
            ->addTag('tactician.handler', ['typehints' => true, 'bus' => 'bus.b']);
100
101
        $routing = (new TypeHintMapping())->build($builder, new Routing(['bus.a', 'bus.b']));
102
103
        $this->assertEquals(
104
            [
105
                FakeCommand::class => 'first.handler',
106
                OtherFakeCommand::class => 'first.handler'
107
            ],
108
            $routing->commandToServiceMapping('bus.a')
109
        );
110
        $this->assertEquals(
111
            [DateTime::class => 'second.handler'],
112
            $routing->commandToServiceMapping('bus.b')
113
        );
114
    }
115
116
    public function test_can_bind_to_multiple_buses()
117
    {
118
        $builder = new ContainerBuilder();
119
        $builder
120
            ->setDefinition('first.handler', new Definition(BasicHandler::class))
121
            ->addTag('tactician.handler', ['typehints' => true, 'bus' => 'bus.a'])
122
            ->addTag('tactician.handler', ['typehints' => true, 'bus' => 'bus.b']);
123
124
        $routing = (new TypeHintMapping())->build($builder, new Routing(['bus.a', 'bus.b']));
125
126
        $expected = [
127
            FakeCommand::class => 'first.handler',
128
            OtherFakeCommand::class => 'first.handler',
129
        ];
130
131
        $this->assertEquals($expected, $routing->commandToServiceMapping('bus.a'));
132
        $this->assertEquals($expected, $routing->commandToServiceMapping('bus.b'));
133
    }
134
135
    public function test_will_error_when_given_invalid_bus()
136
    {
137
        $this->expectException(InvalidCommandBusId::class);
138
139
        $builder = new ContainerBuilder();
140
        $builder
141
            ->setDefinition('first.handler', new Definition(BasicHandler::class))
142
            ->addTag('tactician.handler', ['typehints' => true, 'bus' => 'bus.does.not.exist.mwhahahaha']);
143
144
        (new TypeHintMapping())->build($builder, new Routing(['bus.a', 'bus.b']));
145
    }
146
}
147
148
class BasicHandler
149
{
150
    public function handle(FakeCommand $command)
151
    {
152
    }
153
154
    public function run(OtherFakeCommand $command)
155
    {
156
    }
157
158
    public function notACommand(FakeCommand $cmdA, OtherFakeCommand $cmdB)
159
    {
160
    }
161
}
162
163
class VariadicHandler
164
{
165
    public function handle(FakeCommand ...$commands)
166
    {
167
    }
168
}
169
170
class DateTimeHandler
171
{
172
    public function handle(DateTime $command)
173
    {
174
    }
175
}
176
177
class StaticHandler
178
{
179
    public static function handle(FakeCommand $command)
180
    {
181
    }
182
}
183
184
abstract class AbstractHandler
185
{
186
    abstract public function handle(FakeCommand $command);
187
}
188
189
class ScalarHandler
190
{
191
    public function handle(string $someString)
192
    {
193
    }
194
195
    public function execute(int $foobar)
196
    {
197
    }
198
199
    public function that(callable $thing)
200
    {
201
    }
202
}
203
204
interface ServiceInterface {
205
206
}
207
208
class InterfaceTypehintHandler
209
{
210
    public function interfaced(ServiceInterface $foo)
211
    {
212
    }
213
}
214
215
class NoTypehintHandler
216
{
217
    public function handle($foo)
218
    {
219
    }
220
}
221
222
class InvokeHandler
223
{
224
    public function __invoke(FakeCommand $command)
225
    {
226
    }
227
}
228
229
230
class ProtectedMethodHandler
231
{
232
    protected function handle(FakeCommand $command)
233
    {
234
    }
235
236
    private function execute(OtherFakeCommand $command)
237
    {
238
    }
239
}
240
241
class ConstructorHandler
242
{
243
    public function __construct(SomeDependency $dependency)
244
    {
245
    }
246
}
247
248
class SomeDependency
249
{
250
}
251
252
if (version_compare(PHP_VERSION, '8.0.0') >= 0) {
253
    class UnionTypeHandler
254
    {
255
        public function handle(int|bool $foo)
0 ignored issues
show
Bug introduced by
This code did not parse for me. Apparently, there is an error somewhere around this line:

Syntax error, unexpected '|', expecting T_VARIABLE
Loading history...
256
        {
257
        }
258
    }
259
}
260