Skip to content

Commit e915793

Browse files
authoredOct 13, 2020
Merge pull request #3 from diwms/1.1.x
Psalm Integration
2 parents 3e420e2 + ddea187 commit e915793

9 files changed

+93
-43
lines changed
 

‎.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/clover.xml
22
/composer.lock
3+
/.phpunit.result.cache
34
/coveralls-upload.json
45
/docs/html/
56
/laminas-mkdoc-theme.tgz

‎.travis.yml

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ install:
4848
script:
4949
- if [[ $TEST_COVERAGE == 'true' ]]; then composer test-coverage ; else composer test ; fi
5050
- if [[ $CS_CHECK == 'true' ]]; then composer cs-check ; fi
51+
- if [[ $TEST_COVERAGE == 'true' ]]; then composer static-analysis ; fi
5152

5253
after_script:
5354
- if [[ $TEST_COVERAGE == 'true' ]]; then vendor/bin/php-coveralls -v ; fi

‎composer.json

+4-2
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
"laminas/laminas-coding-standard": "~1.0.0",
3737
"phpspec/prophecy": "^1.12",
3838
"phpspec/prophecy-phpunit": "^2.0",
39+
"psalm/plugin-phpunit": "^0.12.2",
3940
"phpunit/phpunit": "^9.3"
4041
},
4142
"autoload": {
@@ -55,8 +56,9 @@
5556
],
5657
"cs-check": "phpcs",
5758
"cs-fix": "phpcbf",
58-
"test": "phpunit --colors=always",
59-
"test-coverage": "phpunit --colors=always --coverage-clover clover.xml"
59+
"test": "phpunit --colors=always --dont-report-useless-tests",
60+
"test-coverage": "phpunit --colors=always --coverage-clover clover.xml",
61+
"static-analysis": "psalm --shepherd --stats"
6062
},
6163
"replace": {
6264
"zendframework/zend-expressive-authentication-basic": "^1.0.0"

‎psalm.xml.dist

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?xml version="1.0"?>
2+
<psalm
3+
totallyTyped="true"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xmlns="https://getpsalm.org/schema/config"
6+
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
7+
>
8+
<projectFiles>
9+
<directory name="src"/>
10+
<directory name="test"/>
11+
<ignoreFiles>
12+
<directory name="vendor"/>
13+
</ignoreFiles>
14+
</projectFiles>
15+
<plugins>
16+
<pluginClass class="Psalm\PhpUnitPlugin\Plugin"/>
17+
</plugins>
18+
</psalm>

‎src/BasicAccess.php

+2-5
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class BasicAccess implements AuthenticationInterface
3232
protected $realm;
3333

3434
/**
35-
* @var callable
35+
* @var callable():ResponseInterface
3636
*/
3737
protected $responseFactory;
3838

@@ -46,6 +46,7 @@ public function __construct(
4646

4747
// Ensures type safety of the composed factory
4848
$this->responseFactory = function () use ($responseFactory) : ResponseInterface {
49+
/** @var ResponseInterface */
4950
return $responseFactory();
5051
};
5152
}
@@ -72,10 +73,6 @@ public function authenticate(ServerRequestInterface $request) : ?UserInterface
7273

7374
$credentialParts = explode(':', $decodedCredentials, 2);
7475

75-
if (false === $credentialParts) {
76-
return null;
77-
}
78-
7976
if (2 !== count($credentialParts)) {
8077
return null;
8178
}

‎src/BasicAccessFactory.php

+14-3
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@ class BasicAccessFactory
1717
{
1818
public function __invoke(ContainerInterface $container) : BasicAccess
1919
{
20+
/** @var UserRepositoryInterface|\Mezzio\Authentication\UserRepositoryInterface|null $userRegister */
2021
$userRegister = $container->has(UserRepositoryInterface::class)
2122
? $container->get(UserRepositoryInterface::class)
22-
: ($container->has(\Zend\Expressive\Authentication\UserRepositoryInterface::class)
23-
? $container->get(\Zend\Expressive\Authentication\UserRepositoryInterface::class)
23+
: ($container->has(\Mezzio\Authentication\UserRepositoryInterface::class)
24+
? $container->get(\Mezzio\Authentication\UserRepositoryInterface::class)
2425
: null);
2526

2627
if (null === $userRegister) {
@@ -29,6 +30,7 @@ public function __invoke(ContainerInterface $container) : BasicAccess
2930
);
3031
}
3132

33+
/** @var string|null $realm */
3234
$realm = $container->get('config')['authentication']['realm'] ?? null;
3335

3436
if (null === $realm) {
@@ -37,10 +39,19 @@ public function __invoke(ContainerInterface $container) : BasicAccess
3739
);
3840
}
3941

42+
/** @var callable|null $responseFactory */
43+
$responseFactory = $container->get(ResponseInterface::class) ?? null;
44+
45+
if (null === $responseFactory || ! is_callable($responseFactory)) {
46+
throw new Exception\InvalidConfigException(
47+
'ResponseInterface value is not present in authentication config or not callable'
48+
);
49+
}
50+
4051
return new BasicAccess(
4152
$userRegister,
4253
$realm,
43-
$container->get(ResponseInterface::class)
54+
$responseFactory
4455
);
4556
}
4657
}

‎test/BasicAccessFactoryTest.php

+10-10
Original file line numberDiff line numberDiff line change
@@ -24,19 +24,19 @@ class BasicAccessFactoryTest extends TestCase
2424
{
2525
use ProphecyTrait;
2626

27-
/** @var ContainerInterface|ObjectProphecy */
27+
/** @var ObjectProphecy<ContainerInterface> */
2828
private $container;
2929

3030
/** @var BasicAccessFactory */
3131
private $factory;
3232

33-
/** @var UserRepositoryInterface|ObjectProphecy */
33+
/** @var ObjectProphecy<UserRepositoryInterface> */
3434
private $userRegister;
3535

36-
/** @var ResponseInterface|ObjectProphecy */
36+
/** @var ObjectProphecy<ResponseInterface> */
3737
private $responsePrototype;
3838

39-
/** @var callback */
39+
/** @var callable */
4040
private $responseFactory;
4141

4242
protected function setUp(): void
@@ -45,18 +45,18 @@ protected function setUp(): void
4545
$this->factory = new BasicAccessFactory();
4646
$this->userRegister = $this->prophesize(UserRepositoryInterface::class);
4747
$this->responsePrototype = $this->prophesize(ResponseInterface::class);
48-
$this->responseFactory = function () {
48+
$this->responseFactory = function (): ResponseInterface {
4949
return $this->responsePrototype->reveal();
5050
};
5151
}
5252

53-
public function testInvokeWithEmptyContainer()
53+
public function testInvokeWithEmptyContainer(): void
5454
{
5555
$this->expectException(InvalidConfigException::class);
5656
($this->factory)($this->container->reveal());
5757
}
5858

59-
public function testInvokeWithContainerEmptyConfig()
59+
public function testInvokeWithContainerEmptyConfig(): void
6060
{
6161
$this->container
6262
->has(UserRepositoryInterface::class)
@@ -78,7 +78,7 @@ public function testInvokeWithContainerEmptyConfig()
7878
($this->factory)($this->container->reveal());
7979
}
8080

81-
public function testInvokeWithContainerAndConfig()
81+
public function testInvokeWithContainerAndConfig(): void
8282
{
8383
$this->container
8484
->has(UserRepositoryInterface::class)
@@ -99,14 +99,14 @@ public function testInvokeWithContainerAndConfig()
9999
]);
100100

101101
$basicAccess = ($this->factory)($this->container->reveal());
102-
$this->assertInstanceOf(BasicAccess::class, $basicAccess);
103102
$this->assertResponseFactoryReturns($this->responsePrototype->reveal(), $basicAccess);
104103
}
105104

106-
public static function assertResponseFactoryReturns(ResponseInterface $expected, BasicAccess $service) : void
105+
public static function assertResponseFactoryReturns(ResponseInterface $expected, BasicAccess $service): void
107106
{
108107
$r = new ReflectionProperty($service, 'responseFactory');
109108
$r->setAccessible(true);
109+
/** @var callable $responseFactory */
110110
$responseFactory = $r->getValue($service);
111111
Assert::assertSame($expected, $responseFactory());
112112
}

‎test/BasicAccessTest.php

+37-15
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,16 @@ class BasicAccessTest extends TestCase
2323
{
2424
use ProphecyTrait;
2525

26-
/** @var ServerRequestInterface|ObjectProphecy */
26+
/** @var ObjectProphecy<ServerRequestInterface> */
2727
private $request;
2828

29-
/** @var UserRepositoryInterface|ObjectProphecy */
29+
/** @var ObjectProphecy<UserRepositoryInterface> */
3030
private $userRepository;
3131

32-
/** @var UserInterface|ObjectProphecy */
32+
/** @var ObjectProphecy<UserInterface> */
3333
private $authenticatedUser;
3434

35-
/** @var ResponseInterface|ObjectProphecy */
35+
/** @var ObjectProphecy<ResponseInterface> */
3636
private $responsePrototype;
3737

3838
/** @var callable */
@@ -44,12 +44,12 @@ protected function setUp(): void
4444
$this->userRepository = $this->prophesize(UserRepositoryInterface::class);
4545
$this->authenticatedUser = $this->prophesize(UserInterface::class);
4646
$this->responsePrototype = $this->prophesize(ResponseInterface::class);
47-
$this->responseFactory = function () {
47+
$this->responseFactory = function (): ResponseInterface {
4848
return $this->responsePrototype->reveal();
4949
};
5050
}
5151

52-
public function testConstructor()
52+
public function testConstructor(): void
5353
{
5454
$basicAccess = new BasicAccess(
5555
$this->userRepository->reveal(),
@@ -59,12 +59,10 @@ public function testConstructor()
5959
$this->assertInstanceOf(AuthenticationInterface::class, $basicAccess);
6060
}
6161

62-
6362
/**
64-
* @param array $authHeader
6563
* @dataProvider provideInvalidAuthenticationHeader
6664
*/
67-
public function testIsAuthenticatedWithInvalidData(array $authHeader)
65+
public function testIsAuthenticatedWithInvalidData(array $authHeader): void
6866
{
6967
$this->request
7068
->getHeader('Authorization')
@@ -77,16 +75,14 @@ public function testIsAuthenticatedWithInvalidData(array $authHeader)
7775
'test',
7876
$this->responseFactory
7977
);
78+
8079
$this->assertNull($basicAccess->authenticate($this->request->reveal()));
8180
}
8281

8382
/**
84-
* @param string $username
85-
* @param string $password
86-
* @param array $authHeader
8783
* @dataProvider provideValidAuthentication
8884
*/
89-
public function testIsAuthenticatedWithValidCredential(string $username, string $password, array $authHeader)
85+
public function testIsAuthenticatedWithValidCredential(string $username, string $password, array $authHeader): void
9086
{
9187
$this->request
9288
->getHeader('Authorization')
@@ -112,7 +108,7 @@ public function testIsAuthenticatedWithValidCredential(string $username, string
112108
$this->assertInstanceOf(UserInterface::class, $user);
113109
}
114110

115-
public function testIsAuthenticatedWithNoCredential()
111+
public function testIsAuthenticatedWithNoCredential(): void
116112
{
117113
$this->request
118114
->getHeader('Authorization')
@@ -131,7 +127,7 @@ public function testIsAuthenticatedWithNoCredential()
131127
$this->assertNull($basicAccess->authenticate($this->request->reveal()));
132128
}
133129

134-
public function testGetUnauthenticatedResponse()
130+
public function testGetUnauthenticatedResponse(): void
135131
{
136132
$this->responsePrototype
137133
->getHeader('WWW-Authenticate')
@@ -154,6 +150,19 @@ public function testGetUnauthenticatedResponse()
154150
$this->assertEquals(['Basic realm="test"'], $response->getHeader('WWW-Authenticate'));
155151
}
156152

153+
/**
154+
* @psalm-return array{
155+
* empty-header: array{0: array<empty, empty>},
156+
* missing-basic-prefix: array{0: array{0: string}},
157+
* only-username-without-colon: array{0: array{0: string}},
158+
* base64-encoded-pile-of-poo-emoji: array{0: array{0: string}},
159+
* pile-of-poo-emoji: array{0: array{0: string}},
160+
* only-pile-of-poo-emoji: array{0: array{0: string}},
161+
* basic-prefix-without-content: array{0: array{0: string}},
162+
* only-basic: array{0: array{0: string}},
163+
* multiple-auth-headers: array{0: array{0: array{0: string}, 1: array{0: string}}}
164+
* }
165+
*/
157166
public function provideInvalidAuthenticationHeader(): array
158167
{
159168
return [
@@ -174,6 +183,19 @@ public function provideInvalidAuthenticationHeader(): array
174183
];
175184
}
176185

186+
/**
187+
* @psalm-return array{
188+
* aladdin: array{0: string, 1: string, 2: array{0: string}},
189+
* aladdin-with-nonzero-array-index: array{0: string, 1: string, 2: array{-200: string}},
190+
* passwords-with-colon: array{0: string, 1: string, 2: array{0: string}},
191+
* username-without-password: array{0: string, 1: string, 2: array{0: string}},
192+
* password-without-username: array{0: string, 1: string, 2: array{0: string}},
193+
* passwords-with-multiple-colons: array{0: string, 1: string, 2: array{0: string}},
194+
* no-username-or-password: array{0: string, 1: string, 2: array{0: string}},
195+
* no-username-password-only-colons: array{0: string, 1: string, 2: array{0: string}},
196+
* unicode-username-and-password: array{0: string, 1: string, 2: array{0: string}}
197+
* }
198+
*/
177199
public function provideValidAuthentication(): array
178200
{
179201
return [

‎test/ConfigProviderTest.php

+6-8
Original file line numberDiff line numberDiff line change
@@ -10,28 +10,26 @@
1010

1111
use Mezzio\Authentication\Basic\ConfigProvider;
1212
use PHPUnit\Framework\TestCase;
13-
use Prophecy\PhpUnit\ProphecyTrait;
1413

1514
class ConfigProviderTest extends TestCase
1615
{
17-
use ProphecyTrait;
16+
/** @var ConfigProvider */
17+
private $provider;
1818

1919
public function setUp(): void
2020
{
2121
$this->provider = new ConfigProvider();
2222
}
2323

24-
public function testInvocationReturnsArray()
24+
public function testInvocationReturnsArray(): array
2525
{
26-
$config = ($this->provider)();
27-
$this->assertIsArray($config);
28-
return $config;
26+
return ($this->provider)();
2927
}
3028

3129
/**
3230
* @depends testInvocationReturnsArray
3331
*/
34-
public function testReturnedArrayContainsDependencies(array $config)
32+
public function testReturnedArrayContainsDependencies(array $config): void
3533
{
3634
$this->assertArrayHasKey('dependencies', $config);
3735
$this->assertIsArray($config['dependencies']);
@@ -40,7 +38,7 @@ public function testReturnedArrayContainsDependencies(array $config)
4038
/**
4139
* @depends testInvocationReturnsArray
4240
*/
43-
public function testReturnedArrayContainsAuthenticationConfig(array $config)
41+
public function testReturnedArrayContainsAuthenticationConfig(array $config): void
4442
{
4543
$this->assertArrayHasKey('authentication', $config);
4644
$this->assertIsArray($config['authentication']);

0 commit comments

Comments
 (0)
Please sign in to comment.