Passed
Push — master ( d0187e...7d6d5a )
by Gerrit
62:29
created

shouldRejectNonExistingFactoryMethod()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 19
c 0
b 0
f 0
rs 9.6333
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
use Doctrine\ORM\EntityManagerInterface;
26
27
final class ArgumentCompilerTest extends TestCase
28
{
29
30
    /**
31
     * @var ArgumentCompiler
32
     */
33
    private $argumentCompiler;
34
35
    /**
36
     * @var ContainerInterface
37
     */
38
    private $container;
39
40
    /**
41
     * @var EntityManagerInterface
42
     */
43
    private $entityManager;
44
45
    public function setUp()
46
    {
47
        $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...
48
        $this->entityManager = $this->createMock(EntityManagerInterface::class);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->createMock(\Doctr...anagerInterface::class) of type object<PHPUnit\Framework\MockObject\MockObject> is incompatible with the declared type object<Doctrine\ORM\EntityManagerInterface> of property $entityManager.

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...
49
50
        $this->argumentCompiler = new ArgumentCompiler($this->container, $this->entityManager);
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->entityManager is of type object<PHPUnit\Framework\MockObject\MockObject>, but the function expects a object<Doctrine\ORM\EntityManagerInterface>.

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...
51
    }
52
53
    /**
54
     * @test
55
     */
56
    public function shouldBuildArguments()
57
    {
58
        /** @var Request $request */
59
        $request = $this->createMock(Request::class);
60
        $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...
61
            ['reqFoo', null, 'ipsum'],
62
        ]));
63
64
        $someObject = new stdClass();
65
66
        /** @var Serializable $someService */
67
        $someService = $this->createMock(Serializable::class);
68
        $someService->method('serialize')->willReturn($someObject);
69
70
        $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...
71
            ['some.service', $someService],
72
        ]));
73
74
        /** @var array<string, mixed> $expectedRouteArguments */
75
        $expectedRouteArguments = array(
76
            'foo' => 'ipsum',
77
            'bar' => $someObject
78
        );
79
80
        /** @var array<string, mixed> $actualRouteArguments */
81
        $actualRouteArguments = $this->argumentCompiler->buildArguments([
82
            'foo' => '$reqFoo',
83
            'bar' => '@some.service::serialize',
84
        ], $request);
85
86
        $this->assertEquals($expectedRouteArguments, $actualRouteArguments);
87
    }
88
89
    /**
90
     * @test
91
     */
92
    public function shouldBuildCallArguments()
93
    {
94
        /** @var ReflectionMethod $methodReflection */
95
        $methodReflection = $this->createMock(ReflectionMethod::class);
96
97
        /** @var ReflectionParameter $parameterFooReflection */
98
        $parameterFooReflection = $this->createMock(ReflectionParameter::class);
99
        $parameterFooReflection->method('getName')->willReturn("foo");
100
101
        /** @var ReflectionParameter $parameterBarReflection */
102
        $parameterBarReflection = $this->createMock(ReflectionParameter::class);
103
        $parameterBarReflection->method('getName')->willReturn("bar");
104
105
        /** @var ReflectionParameter $parameterBazReflection */
106
        $parameterBazReflection = $this->createMock(ReflectionParameter::class);
107
        $parameterBazReflection->method('getName')->willReturn("baz");
108
109
        /** @var ReflectionType $parameterType */
110
        $parameterType = $this->createMock(ReflectionType::class);
111
        $parameterType->method('__toString')->willReturn(SampleService::class);
112
113
        /** @var ReflectionParameter $parameterFazReflection */
114
        $parameterFazReflection = $this->createMock(ReflectionParameter::class);
115
        $parameterFazReflection->method('getName')->willReturn("faz");
116
        $parameterFazReflection->method('hasType')->willReturn(true);
117
        $parameterFazReflection->method('getType')->willReturn($parameterType);
118
119
        $methodReflection->method("getParameters")->willReturn([
120
            $parameterFooReflection,
121
            $parameterBarReflection,
122
            $parameterBazReflection,
123
            $parameterFazReflection
124
        ]);
125
126
        /** @var Request $request */
127
        $request = $this->createMock(Request::class);
128
        $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...
129
            ['lorem', null, 'ipsum'],
130
            ['bar', null, 'blah'],
131
        ]));
132
133
        /** @var array<string, mixed> $argumentsConfiguration */
134
        $argumentsConfiguration = array(
135
            "foo" => '$lorem',
136
            "baz" => '@some.service',
137
            "faz" => [
138
                'id' => 'some.service',
139
                'method' => 'someCall',
140
                'arguments' => [
141
                    'foo' => '$lorem'
142
                ]
143
            ]
144
        );
145
146
        $someService = new SampleService();
147
148
        $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...
149
            ['some.service', $someService],
150
        ]));
151
152
        $this->entityManager->expects($this->once())->method('find')->with(
0 ignored issues
show
Bug introduced by
The method expects() does not seem to exist on object<Doctrine\ORM\EntityManagerInterface>.

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...
153
            $this->equalTo(SampleService::class),
154
            $this->equalTo('ipsum')
155
        )->willReturn($someService);
156
157
        /** @var array<int, mixed> $expectedCallArguments */
158
        $expectedCallArguments = array(
159
            'ipsum',
160
            'blah',
161
            $someService,
162
            $someService
163
        );
164
165
        /** @var array<int, mixed> $actualCallArguments */
166
        $actualCallArguments = $this->argumentCompiler->buildCallArguments(
167
            $methodReflection,
168
            $argumentsConfiguration,
169
            $request
170
        );
171
172
        $this->assertEquals($expectedCallArguments, $actualCallArguments);
173
        $this->assertEquals('ipsum', $someService->foo);
174
    }
175
176
    /**
177
     * @test
178
     */
179
    public function shouldRejectNonExistingFactoryMethod()
180
    {
181
        $this->expectException(InvalidArgumentException::class);
182
183
        /** @var Request $request */
184
        $request = $this->createMock(Request::class);
185
186
        /** @var Serializable $someService */
187
        $someService = $this->createMock(Serializable::class);
188
189
        $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...
190
            ['some.service', $someService],
191
        ]));
192
193
        $this->argumentCompiler->buildArguments([
194
            'foo' => '$reqFoo',
195
            'bar' => '@some.service::doesNotExist',
196
        ], $request);
197
    }
198
199
    /**
200
     * @test
201
     */
202
    public function shouldThrowExceptionWhenFetchingUnknownService()
203
    {
204
        $this->expectException(InvalidArgumentException::class);
205
206
        /** @var ReflectionMethod $methodReflection */
207
        $methodReflection = $this->createMock(ReflectionMethod::class);
208
209
        /** @var ReflectionParameter $parameterFazReflection */
210
        $parameterFazReflection = $this->createMock(ReflectionParameter::class);
211
        $parameterFazReflection->method('getName')->willReturn("faz");
212
213
        $methodReflection->method("getParameters")->willReturn([
214
            $parameterFazReflection
215
        ]);
216
217
        /** @var Request $request */
218
        $request = $this->createMock(Request::class);
219
220
        /** @var array<string, mixed> $argumentsConfiguration */
221
        $argumentsConfiguration = array(
222
            "faz" => [
223
                'id' => 'some.non-existing.service',
224
            ]
225
        );
226
227
        /** @var array<int, mixed> $actualCallArguments */
228
        $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...
229
            $methodReflection,
230
            $argumentsConfiguration,
231
            $request
232
        );
233
    }
234
235
    /**
236
     * @test
237
     */
238
    public function shouldThrowExceptionWhenCallArgumentIsMissing()
239
    {
240
        $this->expectException(InvalidArgumentException::class);
241
        $this->expectExceptionMessage("Missing argument 'foo' for this call!");
242
243
        /** @var ReflectionMethod $methodReflection */
244
        $methodReflection = $this->createMock(ReflectionMethod::class);
245
246
        /** @var ReflectionParameter $parameterFazReflection */
247
        $parameterFazReflection = $this->createMock(ReflectionParameter::class);
248
        $parameterFazReflection->method('getName')->willReturn("faz");
249
250
        $methodReflection->method("getParameters")->willReturn([
251
            $parameterFazReflection
252
        ]);
253
254
        /** @var Request $request */
255
        $request = $this->createMock(Request::class);
256
257
        $someService = new SampleService();
258
259
        $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...
260
            ['some.service', $someService],
261
        ]));
262
263
        /** @var array<string, mixed> $argumentsConfiguration */
264
        $argumentsConfiguration = array(
265
            "faz" => [
266
                'id' => 'some.service',
267
                'method' => 'someCall',
268
            ]
269
        );
270
271
        /** @var array<int, mixed> $actualCallArguments */
272
        $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...
273
            $methodReflection,
274
            $argumentsConfiguration,
275
            $request
276
        );
277
    }
278
279
}
280