Completed
Push — master ( 465a06...3400f2 )
by Gerrit
01:50
created

shouldThrowExceptionWhenCallArgumentIsMissing()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 40

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 40
rs 9.28
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
/**
3
 * Copyright (C) 2017  Gerrit Addiks.
4
 * This package (including this file) was released under the terms of the GPL-3.0.
5
 * You should have received a copy of the GNU General Public License along with this program.
6
 * If not, see <http://www.gnu.org/licenses/> or send me a mail so i can send you a copy.
7
 * @license GPL-3.0
8
 * @author Gerrit Addiks <[email protected]>
9
 */
10
11
namespace Addiks\SymfonyGenerics\Tests\Unit\Services;
12
13
use PHPUnit\Framework\TestCase;
14
use Addiks\SymfonyGenerics\Services\ArgumentCompiler;
15
use Psr\Container\ContainerInterface;
16
use Addiks\SymfonyGenerics\Services\EntityRepositoryInterface;
17
use ReflectionMethod;
18
use Symfony\Component\HttpFoundation\Request;
19
use ReflectionParameter;
20
use ReflectionType;
21
use stdClass;
22
use InvalidArgumentException;
23
use Serializable;
24
use Addiks\SymfonyGenerics\Tests\Unit\Services\SampleService;
25
26
final class ArgumentCompilerTest extends TestCase
27
{
28
29
    /**
30
     * @var ArgumentCompiler
31
     */
32
    private $argumentCompiler;
33
34
    /**
35
     * @var ContainerInterface
36
     */
37
    private $container;
38
39
    /**
40
     * @var EntityRepositoryInterface
41
     */
42
    private $entityRepository;
43
44
    public function setUp()
45
    {
46
        $this->container = $this->createMock(ContainerInterface::class);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->createMock(\Psr\C...tainerInterface::class) of type object<PHPUnit\Framework\MockObject\MockObject> is incompatible with the declared type object<Psr\Container\ContainerInterface> of property $container.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
47
        $this->entityRepository = $this->createMock(EntityRepositoryInterface::class);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->createMock(\Addik...sitoryInterface::class) of type object<PHPUnit\Framework\MockObject\MockObject> is incompatible with the declared type object<Addiks\SymfonyGen...ityRepositoryInterface> of property $entityRepository.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
48
49
        $this->argumentCompiler = new ArgumentCompiler($this->container, $this->entityRepository);
0 ignored issues
show
Documentation introduced by
$this->container is of type object<PHPUnit\Framework\MockObject\MockObject>, but the function expects a object<Psr\Container\ContainerInterface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
$this->entityRepository is of type object<PHPUnit\Framework\MockObject\MockObject>, but the function expects a object<Addiks\SymfonyGen...ityRepositoryInterface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
50
    }
51
52
    /**
53
     * @test
54
     */
55
    public function shouldBuildArguments()
56
    {
57
        /** @var Request $request */
58
        $request = $this->createMock(Request::class);
59
        $request->method('get')->will($this->returnValueMap([
0 ignored issues
show
Bug introduced by
The method method() does not exist on Symfony\Component\HttpFoundation\Request. Did you maybe mean enableHttpMethodParameterOverride()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
60
            ['reqFoo', null, 'ipsum'],
61
        ]));
62
63
        $someObject = new stdClass();
64
65
        /** @var Serializable $someService */
66
        $someService = $this->createMock(Serializable::class);
67
        $someService->method('serialize')->willReturn($someObject);
68
69
        $this->container->method('get')->will($this->returnValueMap([
0 ignored issues
show
Bug introduced by
The method method() does not seem to exist on object<Psr\Container\ContainerInterface>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
70
            ['some.service', $someService],
71
        ]));
72
73
        /** @var array<string, mixed> $expectedRouteArguments */
74
        $expectedRouteArguments = array(
75
            'foo' => 'ipsum',
76
            'bar' => $someObject
77
        );
78
79
        /** @var array<string, mixed> $actualRouteArguments */
80
        $actualRouteArguments = $this->argumentCompiler->buildArguments([
81
            'foo' => '$reqFoo',
82
            'bar' => '@some.service::serialize',
83
        ], $request);
84
85
        $this->assertEquals($expectedRouteArguments, $actualRouteArguments);
86
    }
87
88
    /**
89
     * @test
90
     */
91
    public function shouldBuildCallArguments()
92
    {
93
        /** @var ReflectionMethod $methodReflection */
94
        $methodReflection = $this->createMock(ReflectionMethod::class);
95
96
        /** @var ReflectionParameter $parameterFooReflection */
97
        $parameterFooReflection = $this->createMock(ReflectionParameter::class);
98
        $parameterFooReflection->method('getName')->willReturn("foo");
99
100
        /** @var ReflectionParameter $parameterBarReflection */
101
        $parameterBarReflection = $this->createMock(ReflectionParameter::class);
102
        $parameterBarReflection->method('getName')->willReturn("bar");
103
104
        /** @var ReflectionParameter $parameterBazReflection */
105
        $parameterBazReflection = $this->createMock(ReflectionParameter::class);
106
        $parameterBazReflection->method('getName')->willReturn("baz");
107
108
        /** @var ReflectionType $parameterType */
109
        $parameterType = $this->createMock(ReflectionType::class);
110
        $parameterType->method('__toString')->willReturn(SampleService::class);
111
112
        /** @var ReflectionParameter $parameterFazReflection */
113
        $parameterFazReflection = $this->createMock(ReflectionParameter::class);
114
        $parameterFazReflection->method('getName')->willReturn("faz");
115
        $parameterFazReflection->method('hasType')->willReturn(true);
116
        $parameterFazReflection->method('getType')->willReturn($parameterType);
117
118
        $methodReflection->method("getParameters")->willReturn([
119
            $parameterFooReflection,
120
            $parameterBarReflection,
121
            $parameterBazReflection,
122
            $parameterFazReflection
123
        ]);
124
125
        /** @var Request $request */
126
        $request = $this->createMock(Request::class);
127
        $request->method('get')->will($this->returnValueMap([
0 ignored issues
show
Bug introduced by
The method method() does not exist on Symfony\Component\HttpFoundation\Request. Did you maybe mean enableHttpMethodParameterOverride()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
128
            ['lorem', null, 'ipsum'],
129
            ['bar', null, 'blah'],
130
        ]));
131
132
        /** @var array<string, mixed> $argumentsConfiguration */
133
        $argumentsConfiguration = array(
134
            "foo" => '$lorem',
135
            "baz" => '@some.service',
136
            "faz" => [
137
                'id' => 'some.service',
138
                'method' => 'someCall',
139
                'arguments' => [
140
                    'foo' => '$lorem'
141
                ]
142
            ]
143
        );
144
145
        $someService = new SampleService();
146
147
        $this->container->method('get')->will($this->returnValueMap([
0 ignored issues
show
Bug introduced by
The method method() does not seem to exist on object<Psr\Container\ContainerInterface>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
148
            ['some.service', $someService],
149
        ]));
150
151
        $this->entityRepository->expects($this->once())->method('findEntity')->with(
0 ignored issues
show
Bug introduced by
The method expects() does not seem to exist on object<Addiks\SymfonyGen...ityRepositoryInterface>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
152
            $this->equalTo(SampleService::class),
153
            $this->equalTo('ipsum')
154
        )->willReturn($someService);
155
156
        /** @var array<int, mixed> $expectedCallArguments */
157
        $expectedCallArguments = array(
158
            'ipsum',
159
            'blah',
160
            $someService,
161
            $someService
162
        );
163
164
        /** @var array<int, mixed> $actualCallArguments */
165
        $actualCallArguments = $this->argumentCompiler->buildCallArguments(
166
            $methodReflection,
167
            $argumentsConfiguration,
168
            $request
169
        );
170
171
        $this->assertEquals($expectedCallArguments, $actualCallArguments);
172
        $this->assertEquals('ipsum', $someService->foo);
173
    }
174
175
    /**
176
     * @test
177
     */
178
    public function shouldRejectInvalidArgumentConfiguration()
179
    {
180
        $this->expectException(InvalidArgumentException::class);
181
182
        /** @var array $argumentsConfiguration */
183
        $argumentsConfiguration = array(
184
            'foo' => false
185
        );
186
187
        /** @var ReflectionParameter $parameterFooReflection */
188
        $parameterFooReflection = $this->createMock(ReflectionParameter::class);
189
        $parameterFooReflection->method('getName')->willReturn("foo");
190
191
        /** @var ReflectionMethod $methodReflection */
192
        $methodReflection = $this->createMock(ReflectionMethod::class);
193
194
        $methodReflection->method("getParameters")->willReturn([
195
            $parameterFooReflection,
196
        ]);
197
198
        /** @var Request $request */
199
        $request = $this->createMock(Request::class);
200
201
        $this->argumentCompiler->buildCallArguments(
202
            $methodReflection,
203
            $argumentsConfiguration,
204
            $request
205
        );
206
207
    }
208
209
    /**
210
     * @test
211
     */
212
    public function shouldRejectNonExistingFactoryMethod()
213
    {
214
        $this->expectException(InvalidArgumentException::class);
215
216
        /** @var Request $request */
217
        $request = $this->createMock(Request::class);
218
219
        /** @var Serializable $someService */
220
        $someService = $this->createMock(Serializable::class);
221
222
        $this->container->method('get')->will($this->returnValueMap([
0 ignored issues
show
Bug introduced by
The method method() does not seem to exist on object<Psr\Container\ContainerInterface>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
223
            ['some.service', $someService],
224
        ]));
225
226
        $this->argumentCompiler->buildArguments([
227
            'foo' => '$reqFoo',
228
            'bar' => '@some.service::doesNotExist',
229
        ], $request);
230
    }
231
232
    /**
233
     * @test
234
     */
235
    public function shouldThrowExceptionWhenFetchingUnknownService()
236
    {
237
        $this->expectException(InvalidArgumentException::class);
238
239
        /** @var ReflectionMethod $methodReflection */
240
        $methodReflection = $this->createMock(ReflectionMethod::class);
241
242
        /** @var ReflectionParameter $parameterFazReflection */
243
        $parameterFazReflection = $this->createMock(ReflectionParameter::class);
244
        $parameterFazReflection->method('getName')->willReturn("faz");
245
246
        $methodReflection->method("getParameters")->willReturn([
247
            $parameterFazReflection
248
        ]);
249
250
        /** @var Request $request */
251
        $request = $this->createMock(Request::class);
252
253
        /** @var array<string, mixed> $argumentsConfiguration */
254
        $argumentsConfiguration = array(
255
            "faz" => [
256
                'id' => 'some.non-existing.service',
257
            ]
258
        );
259
260
        /** @var array<int, mixed> $actualCallArguments */
261
        $actualCallArguments = $this->argumentCompiler->buildCallArguments(
0 ignored issues
show
Unused Code introduced by
$actualCallArguments is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
262
            $methodReflection,
263
            $argumentsConfiguration,
264
            $request
265
        );
266
    }
267
268
    /**
269
     * @test
270
     */
271
    public function shouldThrowExceptionWhenCallArgumentIsMissing()
272
    {
273
        $this->expectException(InvalidArgumentException::class);
274
        $this->expectExceptionMessage("Missing argument 'foo' for this call!");
275
276
        /** @var ReflectionMethod $methodReflection */
277
        $methodReflection = $this->createMock(ReflectionMethod::class);
278
279
        /** @var ReflectionParameter $parameterFazReflection */
280
        $parameterFazReflection = $this->createMock(ReflectionParameter::class);
281
        $parameterFazReflection->method('getName')->willReturn("faz");
282
283
        $methodReflection->method("getParameters")->willReturn([
284
            $parameterFazReflection
285
        ]);
286
287
        /** @var Request $request */
288
        $request = $this->createMock(Request::class);
289
290
        $someService = new SampleService();
291
292
        $this->container->method('get')->will($this->returnValueMap([
0 ignored issues
show
Bug introduced by
The method method() does not seem to exist on object<Psr\Container\ContainerInterface>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
293
            ['some.service', $someService],
294
        ]));
295
296
        /** @var array<string, mixed> $argumentsConfiguration */
297
        $argumentsConfiguration = array(
298
            "faz" => [
299
                'id' => 'some.service',
300
                'method' => 'someCall',
301
            ]
302
        );
303
304
        /** @var array<int, mixed> $actualCallArguments */
305
        $actualCallArguments = $this->argumentCompiler->buildCallArguments(
0 ignored issues
show
Unused Code introduced by
$actualCallArguments is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
306
            $methodReflection,
307
            $argumentsConfiguration,
308
            $request
309
        );
310
    }
311
312
}
313