Skip to content

Commit 7b3393a

Browse files
committed
More middleware tests
1 parent d844a36 commit 7b3393a

11 files changed

+547
-56
lines changed

app/Http/Middleware/Server/DatabaseBelongsToServer.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class DatabaseBelongsToServer
1212
/**
1313
* @var \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface
1414
*/
15-
protected $repository;
15+
private $repository;
1616

1717
/**
1818
* DatabaseAccess constructor.
@@ -40,7 +40,7 @@ public function handle(Request $request, Closure $next)
4040
$server = $request->attributes->get('server');
4141

4242
$database = $this->repository->find($request->input('database'));
43-
if ($database->server_id !== $server->id) {
43+
if (is_null($database) || $database->server_id !== $server->id) {
4444
throw new NotFoundHttpException;
4545
}
4646

app/Http/Middleware/Server/SubuserBelongsToServer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public function handle(Request $request, Closure $next)
5050

5151
$hash = $request->route()->parameter('subuser', 0);
5252
$subuser = $this->repository->find($this->hashids->decodeFirst($hash, 0));
53-
if (! $subuser || $subuser->server_id !== $server->id) {
53+
if (is_null($subuser) || $subuser->server_id !== $server->id) {
5454
throw new NotFoundHttpException;
5555
}
5656

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
namespace Tests\Assertions;
4+
5+
use PHPUnit\Framework\Assert;
6+
7+
trait MiddlewareAttributeAssertionsTrait
8+
{
9+
/**
10+
* Assert a request has an attribute assigned to it.
11+
*
12+
* @param string $attribute
13+
*/
14+
public function assertRequestHasAttribute(string $attribute)
15+
{
16+
Assert::assertTrue($this->request->attributes->has($attribute), 'Assert that request mock has ' . $attribute . ' attribute.');
17+
}
18+
19+
/**
20+
* Assert a request does not have an attribute assigned to it.
21+
*
22+
* @param string $attribute
23+
*/
24+
public function assertRequestMissingAttribute(string $attribute)
25+
{
26+
Assert::assertFalse($this->request->attributes->has($attribute), 'Assert that request mock does not have ' . $attribute . ' attribute.');
27+
}
28+
29+
/**
30+
* Assert a request attribute matches an expected value.
31+
*
32+
* @param mixed $expected
33+
* @param string $attribute
34+
*/
35+
public function assertRequestAttributeEquals($expected, string $attribute)
36+
{
37+
Assert::assertEquals($expected, $this->request->attributes->get($attribute));
38+
}
39+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
namespace Tests\Traits\Http;
4+
5+
use Closure;
6+
use Illuminate\Http\Request;
7+
use BadFunctionCallException;
8+
9+
trait MocksMiddlewareClosure
10+
{
11+
/**
12+
* @var \Illuminate\Http\Request
13+
*/
14+
protected $request;
15+
16+
/**
17+
* Provide a closure to be used when validating that the response from the middleware
18+
* is the same request object we passed into it.
19+
*/
20+
protected function getClosureAssertions(): Closure
21+
{
22+
if (is_null($this->request)) {
23+
throw new BadFunctionCallException('Calling getClosureAssertions without defining a request object is not supported.');
24+
}
25+
26+
return function ($response) {
27+
$this->assertInstanceOf(Request::class, $response);
28+
$this->assertSame($this->request, $response);
29+
};
30+
}
31+
}

tests/Unit/Http/Middleware/Daemon/DaemonAuthenticateTest.php

Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,21 @@
22

33
namespace Tests\Unit\Http\Middleware\Daemon;
44

5-
use Closure;
65
use Mockery as m;
7-
use Tests\TestCase;
8-
use Illuminate\Http\Request;
96
use Pterodactyl\Models\Node;
10-
use Symfony\Component\HttpFoundation\ParameterBag;
7+
use Tests\Unit\Http\Middleware\MiddlewareTestCase;
118
use Symfony\Component\HttpKernel\Exception\HttpException;
129
use Pterodactyl\Http\Middleware\Daemon\DaemonAuthenticate;
1310
use Pterodactyl\Contracts\Repository\NodeRepositoryInterface;
1411
use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
1512

16-
class DaemonAuthenticateTest extends TestCase
13+
class DaemonAuthenticateTest extends MiddlewareTestCase
1714
{
1815
/**
1916
* @var \Pterodactyl\Contracts\Repository\NodeRepositoryInterface|\Mockery\Mock
2017
*/
2118
private $repository;
2219

23-
/**
24-
* @var \Illuminate\Http\Request|\Mockery\Mock
25-
*/
26-
private $request;
27-
2820
/**
2921
* Setup tests.
3022
*/
@@ -33,8 +25,6 @@ public function setUp()
3325
parent::setUp();
3426

3527
$this->repository = m::mock(NodeRepositoryInterface::class);
36-
$this->request = m::mock(Request::class);
37-
$this->request->attributes = new ParameterBag();
3828
}
3929

4030
/**
@@ -98,8 +88,8 @@ public function testSuccessfulMiddlewareProcess()
9888
$this->repository->shouldReceive('findFirstWhere')->with([['daemonSecret', '=', $model->daemonSecret]])->once()->andReturn($model);
9989

10090
$this->getMiddleware()->handle($this->request, $this->getClosureAssertions());
101-
$this->assertTrue($this->request->attributes->has('node'), 'Assert request attributes contains node.');
102-
$this->assertSame($model, $this->request->attributes->get('node'));
91+
$this->assertRequestHasAttribute('node');
92+
$this->assertRequestAttributeEquals($model, 'node');
10393
}
10494

10595
/**
@@ -111,16 +101,4 @@ private function getMiddleware(): DaemonAuthenticate
111101
{
112102
return new DaemonAuthenticate($this->repository);
113103
}
114-
115-
/**
116-
* Provide a closure to be used when validating that the response from the middleware
117-
* is the same request object we passed into it.
118-
*/
119-
private function getClosureAssertions(): Closure
120-
{
121-
return function ($response) {
122-
$this->assertInstanceOf(Request::class, $response);
123-
$this->assertSame($this->request, $response);
124-
};
125-
}
126104
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php
2+
3+
namespace Tests\Unit\Http\Middleware;
4+
5+
use Mockery as m;
6+
use Tests\TestCase;
7+
use Illuminate\Http\Request;
8+
use Pterodactyl\Models\User;
9+
use Tests\Traits\Http\MocksMiddlewareClosure;
10+
use Symfony\Component\HttpFoundation\ParameterBag;
11+
use Tests\Assertions\MiddlewareAttributeAssertionsTrait;
12+
13+
abstract class MiddlewareTestCase extends TestCase
14+
{
15+
use MiddlewareAttributeAssertionsTrait, MocksMiddlewareClosure;
16+
17+
/**
18+
* @var \Illuminate\Http\Request|\Mockery\Mock
19+
*/
20+
protected $request;
21+
22+
/**
23+
* Setup tests with a mocked request object and normal attributes.
24+
*/
25+
public function setUp()
26+
{
27+
parent::setUp();
28+
29+
$this->request = m::mock(Request::class);
30+
$this->request->attributes = new ParameterBag();
31+
}
32+
33+
/**
34+
* Set a request attribute on the mock object.
35+
*
36+
* @param string $attribute
37+
* @param mixed $value
38+
*/
39+
protected function setRequestAttribute(string $attribute, $value)
40+
{
41+
$this->request->attributes->set($attribute, $value);
42+
}
43+
44+
/**
45+
* Sets the mocked request user. If a user model is not provided, a factory model
46+
* will be created and returned.
47+
*
48+
* @param \Pterodactyl\Models\User|null $user
49+
* @return \Pterodactyl\Models\User
50+
*/
51+
protected function setRequestUser(User $user = null): User
52+
{
53+
$user = $user instanceof User ? $user : factory(User::class)->make();
54+
$this->request->shouldReceive('user')->withNoArgs()->andReturn($user);
55+
56+
return $user;
57+
}
58+
}

tests/Unit/Http/Middleware/Server/AccessingValidServerTest.php

Lines changed: 4 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,16 @@
22

33
namespace Tests\Unit\Http\Middleware\Server;
44

5-
use Closure;
65
use Mockery as m;
7-
use Tests\TestCase;
8-
use Illuminate\View\View;
9-
use Illuminate\Http\Request;
106
use Illuminate\Http\Response;
117
use Pterodactyl\Models\Server;
128
use Illuminate\Contracts\Session\Session;
139
use Illuminate\Contracts\Config\Repository;
14-
use Symfony\Component\HttpFoundation\ParameterBag;
10+
use Tests\Unit\Http\Middleware\MiddlewareTestCase;
1511
use Pterodactyl\Http\Middleware\AccessingValidServer;
1612
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
1713

18-
class AccessingValidServerTest extends TestCase
14+
class AccessingValidServerTest extends MiddlewareTestCase
1915
{
2016
/**
2117
* @var \Illuminate\Contracts\Config\Repository|\Mockery\Mock
@@ -27,11 +23,6 @@ class AccessingValidServerTest extends TestCase
2723
*/
2824
private $repository;
2925

30-
/**
31-
* @var \Illuminate\Http\Request|\Mockery\Mock
32-
*/
33-
private $request;
34-
3526
/**
3627
* @var \Illuminate\Contracts\Session\Session|\Mockery\Mock
3728
*/
@@ -46,8 +37,6 @@ public function setUp()
4637

4738
$this->config = m::mock(Repository::class);
4839
$this->repository = m::mock(ServerRepositoryInterface::class);
49-
$this->request = m::mock(Request::class);
50-
$this->request->attributes = new ParameterBag();
5140
$this->session = m::mock(Session::class);
5241
}
5342

@@ -139,8 +128,8 @@ public function testValidServerProcess()
139128
$this->session->shouldReceive('now')->with('server_data.model', $model)->once()->andReturnNull();
140129

141130
$this->getMiddleware()->handle($this->request, $this->getClosureAssertions());
142-
$this->assertTrue($this->request->attributes->has('server'), 'Assert request attributes contains server.');
143-
$this->assertSame($model, $this->request->attributes->get('server'));
131+
$this->assertRequestHasAttribute('server');
132+
$this->assertRequestAttributeEquals($model, 'server');
144133
}
145134

146135
/**
@@ -170,16 +159,4 @@ private function getMiddleware(): AccessingValidServer
170159
{
171160
return new AccessingValidServer($this->config, $this->repository, $this->session);
172161
}
173-
174-
/**
175-
* Provide a closure to be used when validating that the response from the middleware
176-
* is the same request object we passed into it.
177-
*/
178-
private function getClosureAssertions(): Closure
179-
{
180-
return function ($response) {
181-
$this->assertInstanceOf(Request::class, $response);
182-
$this->assertSame($this->request, $response);
183-
};
184-
}
185162
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?php
2+
3+
namespace Tests\Unit\Http\Middleware\Server;
4+
5+
use Mockery as m;
6+
use Pterodactyl\Models\Server;
7+
use Illuminate\Contracts\Session\Session;
8+
use Tests\Unit\Http\Middleware\MiddlewareTestCase;
9+
use Pterodactyl\Http\Middleware\Server\AuthenticateAsSubuser;
10+
use Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService;
11+
use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
12+
13+
class AuthenticateAsSubuserTest extends MiddlewareTestCase
14+
{
15+
/**
16+
* @var \Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService|\Mockery\Mock
17+
*/
18+
private $keyProviderService;
19+
20+
/**
21+
* @var \Illuminate\Contracts\Session\Session|\Mockery\Mock
22+
*/
23+
private $session;
24+
25+
/**
26+
* Setup tests.
27+
*/
28+
public function setUp()
29+
{
30+
parent::setUp();
31+
32+
$this->keyProviderService = m::mock(DaemonKeyProviderService::class);
33+
$this->session = m::mock(Session::class);
34+
}
35+
36+
/**
37+
* Test a successful instance of the middleware.
38+
*/
39+
public function testSuccessfulMiddleware()
40+
{
41+
$model = factory(Server::class)->make();
42+
$user = $this->setRequestUser();
43+
$this->setRequestAttribute('server', $model);
44+
45+
$this->keyProviderService->shouldReceive('handle')->with($model->id, $user->id)->once()->andReturn('abc123');
46+
$this->session->shouldReceive('now')->with('server_data.token', 'abc123')->once()->andReturnNull();
47+
48+
$this->getMiddleware()->handle($this->request, $this->getClosureAssertions());
49+
$this->assertRequestHasAttribute('server_token');
50+
$this->assertRequestAttributeEquals('abc123', 'server_token');
51+
}
52+
53+
/**
54+
* Test middleware handles missing token exception.
55+
*
56+
* @expectedException \Illuminate\Auth\AuthenticationException
57+
* @expectedExceptionMessage This account does not have permission to access this server.
58+
*/
59+
public function testExceptionIsThrownIfNoTokenIsFound()
60+
{
61+
$model = factory(Server::class)->make();
62+
$user = $this->setRequestUser();
63+
$this->setRequestAttribute('server', $model);
64+
65+
$this->keyProviderService->shouldReceive('handle')->with($model->id, $user->id)->once()->andThrow(new RecordNotFoundException);
66+
67+
$this->getMiddleware()->handle($this->request, $this->getClosureAssertions());
68+
}
69+
70+
/**
71+
* Return an instance of the middleware using mocked dependencies.
72+
*
73+
* @return \Pterodactyl\Http\Middleware\Server\AuthenticateAsSubuser
74+
*/
75+
public function getMiddleware(): AuthenticateAsSubuser
76+
{
77+
return new AuthenticateAsSubuser($this->keyProviderService, $this->session);
78+
}
79+
}

0 commit comments

Comments
 (0)