Passed
Pull Request — master (#3)
by Alexander
01:26
created

testScalarVariadicArgumentAnsNamedParam()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 9
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Injector\Tests;
6
7
use PHPUnit\Framework\TestCase;
8
use Psr\Container\NotFoundExceptionInterface;
9
use Yiisoft\Di\Container;
10
use Yiisoft\Injector\Injector;
11
use Yiisoft\Injector\InvalidParameterException;
12
use Yiisoft\Injector\MissingRequiredArgumentException;
13
use Yiisoft\Injector\Tests\Support\ColorInterface;
14
use Yiisoft\Injector\Tests\Support\EngineInterface;
15
use Yiisoft\Injector\Tests\Support\EngineMarkTwo;
16
use Yiisoft\Injector\Tests\Support\EngineZIL130;
17
use Yiisoft\Injector\Tests\Support\EngineVAZ2101;
18
use Yiisoft\Injector\Tests\Support\LightEngine;
19
20
class InjectorTest extends TestCase
21
{
22
    public function testInvokeClosure(): void
23
    {
24
        $container = new Container([EngineInterface::class => EngineMarkTwo::class]);
25
26
        $getEngineName = fn (EngineInterface $engine) => $engine->getName();
27
28
        $engineName = (new Injector($container))->invoke($getEngineName);
29
30
        $this->assertSame('Mark Two', $engineName);
31
    }
32
33
    public function testInvokeCallableArray(): void
34
    {
35
        $container = new Container([]);
36
37
        $object = new EngineVAZ2101();
38
39
        $engine = (new Injector($container))->invoke([$object, 'rust'], ['index' => 5.0]);
40
41
        $this->assertInstanceOf(EngineVAZ2101::class, $engine);
42
    }
43
44
    /**
45
     * Injector should be able to invoke static method
46
     */
47
    public function testInvokeStatic(): void
48
    {
49
        $container = new Container([]);
50
51
        $result = (new Injector($container))->invoke([EngineVAZ2101::class, 'isWroomWroom']);
52
53
        $this->assertIsBool($result);
54
    }
55
56
    /**
57
     * Injector should be able to invoke method without arguments
58
     */
59
    public function testInvokeWithoutArguments(): void
60
    {
61
        $container = new Container([]);
62
63
        $true = fn () => true;
64
65
        $result = (new Injector($container))->invoke($true);
66
67
        $this->assertTrue($result);
68
    }
69
70
    /**
71
     * Nullable arguments should be searched in container
72
     */
73
    public function testWithNullableArgument(): void
74
    {
75
        $container = new Container([EngineInterface::class => EngineMarkTwo::class]);
76
77
        $nullable = fn (?EngineInterface $engine) => $engine;
78
79
        $result = (new Injector($container))->invoke($nullable);
80
81
        $this->assertNotNull($result);
82
    }
83
84
    /**
85
     * Nullable arguments not found in container should be passed as `null`
86
     */
87
    public function testWithNullableArgumentAndEmptyContainer(): void
88
    {
89
        $container = new Container([]);
90
91
        $nullable = fn (?EngineInterface $engine) => $engine;
92
93
        $result = (new Injector($container))->invoke($nullable);
94
95
        $this->assertNull($result);
96
    }
97
98
    /**
99
     * Nullable scalars should be set with `null` if not specified by name explicitly
100
     */
101
    public function testWithNullableScalarArgument(): void
102
    {
103
        $container = new Container([]);
104
105
        $nullableInt = fn (?int $number) => $number;
106
107
        $result = (new Injector($container))->invoke($nullableInt);
108
109
        $this->assertNull($result);
110
    }
111
112
    public function testWithNullableOptionalArgument(): void
113
    {
114
        $container = new Container([]);
115
116
        $nullableInt = fn (?int $number = 6) => $number;
117
118
        $result = (new Injector($container))->invoke($nullableInt);
119
120
        $this->assertSame(6, $result);
121
    }
122
123
    public function testWithNullableOptionalArgumentThatNull(): void
124
    {
125
        $container = new Container([EngineInterface::class => EngineMarkTwo::class]);
126
127
        $callable = fn (EngineInterface $engine = null) => $engine;
128
129
        $result = (new Injector($container))->invoke($callable);
130
131
        $this->assertNotNull($result);
132
    }
133
134
    public function testCustomDependency(): void
135
    {
136
        $container = new Container([EngineInterface::class => EngineMarkTwo::class]);
137
        $needleEngine = new EngineZIL130();
138
139
        $getEngineName = fn (EngineInterface $engine) => $engine->getName();
140
141
        $engineName = (new Injector($container))->invoke($getEngineName, [$needleEngine]);
142
143
        $this->assertSame(EngineZIL130::NAME, $engineName);
144
    }
145
146
    public function testTwoEqualCustomArgumentsWithOneCustom(): void
147
    {
148
        $container = new Container([EngineInterface::class => EngineMarkTwo::class]);
149
150
        $compareEngines = static function (EngineInterface $engine1, EngineInterface $engine2) {
151
            return $engine1->getPower() <=> $engine2->getPower();
152
        };
153
        $zilEngine = new EngineZIL130();
154
155
        $result = (new Injector($container))->invoke($compareEngines, [$zilEngine]);
156
157
        $this->assertSame(-1, $result);
158
    }
159
160
    public function testTwoEqualCustomArgumentsWithOneCustomNamedParameter(): void
161
    {
162
        $container = new Container([EngineInterface::class => EngineMarkTwo::class]);
163
164
        $compareEngines = static function (EngineInterface $engine1, EngineInterface $engine2) {
165
            return $engine1->getPower() <=> $engine2->getPower();
166
        };
167
        $zilEngine = new EngineZIL130();
168
169
        $result = (new Injector($container))->invoke($compareEngines, ['engine1' => $zilEngine]);
170
171
        $this->assertSame(-1, $result);
172
    }
173
174
    public function testTwoEqualCustomArgumentsWithOneCustomNamedParameter2(): void
175
    {
176
        $container = new Container([EngineInterface::class => EngineMarkTwo::class]);
177
178
        $compareEngines = static function (EngineInterface $engine1, EngineInterface $engine2) {
179
            return $engine1->getPower() <=> $engine2->getPower();
180
        };
181
        $zilEngine = new EngineZIL130();
182
183
        $result = (new Injector($container))->invoke($compareEngines, ['engine2' => $zilEngine]);
184
185
        $this->assertSame(1, $result);
186
    }
187
188
    public function testExtendedArgumentsWithOneCustomNamedParameter2(): void
189
    {
190
        $container = new Container(
191
            [
192
                EngineInterface::class => EngineZIL130::class,
193
                LightEngine::class => EngineVAZ2101::class,
194
            ]
195
        );
196
197
        $concatEngineNames = static function (EngineInterface $engine1, LightEngine $engine2) {
198
            return $engine1->getName() . $engine2->getName();
199
        };
200
        $zilEngine = new EngineMarkTwo();
201
202
        $result = (new Injector($container))->invoke($concatEngineNames, [$zilEngine]);
203
204
        $this->assertSame(EngineMarkTwo::NAME . EngineVAZ2101::NAME, $result);
205
    }
206
207
    public function testMissingRequiredTypedParameter(): void
208
    {
209
        $container = new Container([EngineInterface::class => EngineMarkTwo::class]);
210
211
        $getEngineName = static function (EngineInterface $engine, string $two) {
0 ignored issues
show
Unused Code introduced by
The parameter $two is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

211
        $getEngineName = static function (EngineInterface $engine, /** @scrutinizer ignore-unused */ string $two) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
212
            return $engine->getName();
213
        };
214
215
        $injector = new Injector($container);
216
217
        $this->expectException(MissingRequiredArgumentException::class);
218
        $injector->invoke($getEngineName);
219
    }
220
221
    public function testMissingRequiredNotTypedParameter(): void
222
    {
223
        $container = new Container([EngineInterface::class => EngineMarkTwo::class]);
224
225
        $getEngineName = static function (EngineInterface $engine, $two) {
0 ignored issues
show
Unused Code introduced by
The parameter $two is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

225
        $getEngineName = static function (EngineInterface $engine, /** @scrutinizer ignore-unused */ $two) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
226
            return $engine->getName();
227
        };
228
        $injector = new Injector($container);
229
230
        $this->expectException(MissingRequiredArgumentException::class);
231
232
        $injector->invoke($getEngineName);
233
    }
234
235
    public function testNotFoundException(): void
236
    {
237
        $container = new Container([EngineInterface::class => EngineMarkTwo::class]);
238
239
        $getEngineName = static function (EngineInterface $engine, ColorInterface $color) {
0 ignored issues
show
Unused Code introduced by
The parameter $color is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

239
        $getEngineName = static function (EngineInterface $engine, /** @scrutinizer ignore-unused */ ColorInterface $color) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
240
            return $engine->getName();
241
        };
242
243
        $injector = new Injector($container);
244
245
        $this->expectException(NotFoundExceptionInterface::class);
246
        $injector->invoke($getEngineName);
247
    }
248
249
    public function testAloneScalarVariadicArgumentAnsNamedParam(): void
250
    {
251
        $container = new Container([]);
252
253
        $callable = fn (...$var) => array_sum($var);
254
255
        $result = (new Injector($container))->invoke($callable, ['var' => [1, 2, 3]]);
256
257
        $this->assertSame(6, $result);
258
    }
259
260
    public function testScalarVariadicArgumentAnsNamedParam(): void
261
    {
262
        $container = new Container([EngineInterface::class => EngineMarkTwo::class]);
263
264
        $callable = fn (EngineInterface $engine, int ...$var) => array_sum($var);
265
266
        $result = (new Injector($container))->invoke($callable, ['var' => [1, 2, 3]]);
267
268
        $this->assertSame(6, $result);
269
    }
270
271
    public function testVariadicArgumentUnnamedParams(): void
272
    {
273
        $container = new Container([\DateTimeInterface::class => new \DateTimeImmutable()]);
274
275
        $callable = fn (\DateTimeInterface $dateTime, EngineInterface ...$engines) => count($engines);
276
277
        $result = (new Injector($container))->invoke(
278
            $callable,
279
            [new EngineZIL130(), new EngineVAZ2101(), new EngineMarkTwo()]
280
        );
281
282
        $this->assertSame(3, $result);
283
    }
284
285
    public function testVariadicArgumentUnnamedParamsWithIncorrectItem(): void
286
    {
287
        $container = new Container([\DateTimeInterface::class => new \DateTimeImmutable()]);
288
289
        $callable = fn (\DateTimeInterface $dateTime, EngineInterface ...$engines) => count($engines);
290
291
        $result = (new Injector($container))->invoke(
292
            $callable,
293
            [new EngineZIL130(), new EngineVAZ2101(), new EngineMarkTwo(), new \stdClass()]
294
        );
295
296
        // stdClass should be ignored
297
        $this->assertSame(3, $result);
298
    }
299
300
    public function testVariadicMixedArgumentWithMixedParams(): void
301
    {
302
        $container = new Container([\DateTimeInterface::class => new \DateTimeImmutable()]);
303
304
        $callable = fn (...$engines) => $engines;
305
306
        $result = (new Injector($container))->invoke(
307
            $callable,
308
            [new EngineZIL130(), new EngineVAZ2101(), new EngineMarkTwo(), new \stdClass()]
309
        );
310
311
        $this->assertCount(4, $result);
312
    }
313
314
    public function testVariadicStringArgumentWithUnnamedStringsParams(): void
315
    {
316
        $container = new Container([\DateTimeInterface::class => new \DateTimeImmutable()]);
317
318
        $callable = fn (string ...$engines) => $engines;
319
320
        $this->expectException(\Exception::class);
321
322
        (new Injector($container))->invoke($callable, ['str 1', 'str 2', 'str 3']);
323
    }
324
325
    public function testNullableVariadicArgument(): void
326
    {
327
        $container = new Container([]);
328
329
        $callable = fn (?EngineInterface ...$engines) => $engines;
330
331
        $result = (new Injector($container))->invoke($callable, []);
332
333
        $this->assertSame([], $result);
334
    }
335
336
    public function testAppendingUnusedParams(): void
337
    {
338
        $container = new Container([EngineInterface::class => EngineMarkTwo::class]);
339
340
        $callable = fn (?EngineInterface $engine, $id = 'test') => func_num_args();
0 ignored issues
show
Unused Code introduced by
The parameter $engine is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

340
        $callable = fn (/** @scrutinizer ignore-unused */ ?EngineInterface $engine, $id = 'test') => func_num_args();

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $id is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

340
        $callable = fn (?EngineInterface $engine, /** @scrutinizer ignore-unused */ $id = 'test') => func_num_args();

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
341
342
        $result = (new Injector($container))->invoke($callable, [new \DateTimeImmutable(), new \DateTimeImmutable()]);
343
344
        $this->assertSame(4, $result);
345
    }
346
347
    public function testWrongNamedParam(): void
348
    {
349
        $container = new Container([EngineInterface::class => EngineMarkTwo::class]);
350
351
        $callable = fn (EngineInterface $engine) => $engine;
352
353
        $this->expectException(\Throwable::class);
354
355
        (new Injector($container))->invoke($callable, ['engine' => new \DateTimeImmutable()]);
356
    }
357
358
    public function testArrayArgumentWithUnnamedType(): void
359
    {
360
        $container = new Container([EngineInterface::class => EngineMarkTwo::class]);
361
362
        $callable = fn (array $arg) => $arg;
363
364
        $this->expectException(MissingRequiredArgumentException::class);
365
366
        (new Injector($container))->invoke($callable, [['test']]);
367
    }
368
369
    public function testCallableArgumentWithUnnamedType(): void
370
    {
371
        $container = new Container([EngineInterface::class => EngineMarkTwo::class]);
372
373
        $callable = fn (callable $arg) => $arg();
374
375
        $this->expectException(MissingRequiredArgumentException::class);
376
377
        (new Injector($container))->invoke($callable, [fn () => true]);
378
    }
379
380
    public function testIterableArgumentWithUnnamedType(): void
381
    {
382
        $container = new Container([EngineInterface::class => EngineMarkTwo::class]);
383
384
        $callable = fn (iterable $arg) => $arg;
385
386
        $this->expectException(MissingRequiredArgumentException::class);
387
388
        (new Injector($container))->invoke($callable, [new \SplStack()]);
389
    }
390
391
    public function testUnnamedScalarParam(): void
392
    {
393
        $container = new Container([]);
394
395
        $getEngineName = fn () => 42;
396
397
        $this->expectException(InvalidParameterException::class);
398
399
        (new Injector($container))->invoke($getEngineName, ['test']);
400
    }
401
}
402