270 lines
8.8 KiB
PHP
270 lines
8.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Engelsystem\Test\Unit\Controllers;
|
|
|
|
use DMS\PHPUnitExtensions\ArraySubset\ArraySubsetAsserts;
|
|
use Engelsystem\Config\Config;
|
|
use Engelsystem\Controllers\NotificationType;
|
|
use Engelsystem\Controllers\PasswordResetController;
|
|
use Engelsystem\Helpers\Authenticator;
|
|
use Engelsystem\Http\Exceptions\HttpNotFound;
|
|
use Engelsystem\Http\Exceptions\ValidationException;
|
|
use Engelsystem\Http\Request;
|
|
use Engelsystem\Http\Response;
|
|
use Engelsystem\Http\Validation\Validator;
|
|
use Engelsystem\Mail\EngelsystemMailer;
|
|
use Engelsystem\Models\Session as SessionModel;
|
|
use Engelsystem\Models\User\PasswordReset;
|
|
use Engelsystem\Models\User\User;
|
|
use Engelsystem\Renderer\Renderer;
|
|
use Engelsystem\Test\Unit\HasDatabase;
|
|
use PHPUnit\Framework\MockObject\MockObject;
|
|
use Psr\Log\Test\TestLogger;
|
|
use Symfony\Component\HttpFoundation\Session\Session;
|
|
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
|
|
|
|
class PasswordResetControllerTest extends ControllerTest
|
|
{
|
|
use ArraySubsetAsserts;
|
|
use HasDatabase;
|
|
|
|
protected array $args = [];
|
|
|
|
/**
|
|
* @covers \Engelsystem\Controllers\PasswordResetController::reset
|
|
* @covers \Engelsystem\Controllers\PasswordResetController::__construct
|
|
*/
|
|
public function testReset(): void
|
|
{
|
|
$controller = $this->getController('pages/password/reset');
|
|
$response = $controller->reset();
|
|
|
|
$this->assertEquals(200, $response->getStatusCode());
|
|
}
|
|
|
|
/**
|
|
* @covers \Engelsystem\Controllers\PasswordResetController::postReset
|
|
*/
|
|
public function testPostReset(): void
|
|
{
|
|
$this->initDatabase();
|
|
$request = new Request([], ['email' => 'foo@bar.batz']);
|
|
$user = $this->createUser();
|
|
|
|
$controller = $this->getController(
|
|
'pages/password/reset-success',
|
|
['type' => 'email']
|
|
);
|
|
/** @var TestLogger $log */
|
|
$log = $this->args['log'];
|
|
/** @var EngelsystemMailer|MockObject $mailer */
|
|
$mailer = $this->args['mailer'];
|
|
$this->setExpects($mailer, 'sendViewTranslated');
|
|
|
|
$controller->postReset($request);
|
|
|
|
$this->assertNotEmpty((new PasswordReset())->find($user->id)->first());
|
|
$this->assertTrue($log->hasInfoThatContains($user->name));
|
|
$this->assertHasNoNotifications();
|
|
}
|
|
|
|
/**
|
|
* @covers \Engelsystem\Controllers\PasswordResetController::postReset
|
|
*/
|
|
public function testPostResetInvalidRequest(): void
|
|
{
|
|
$request = new Request();
|
|
|
|
$controller = $this->getController();
|
|
|
|
$this->expectException(ValidationException::class);
|
|
$controller->postReset($request);
|
|
}
|
|
|
|
/**
|
|
* @covers \Engelsystem\Controllers\PasswordResetController::postReset
|
|
*/
|
|
public function testPostResetNoUser(): void
|
|
{
|
|
$this->initDatabase();
|
|
$request = new Request([], ['email' => 'foo@bar.batz']);
|
|
|
|
$controller = $this->getController(
|
|
'pages/password/reset-success',
|
|
['type' => 'email']
|
|
);
|
|
|
|
$controller->postReset($request);
|
|
$this->assertHasNoNotifications();
|
|
}
|
|
|
|
/**
|
|
* @covers \Engelsystem\Controllers\PasswordResetController::resetPassword
|
|
* @covers \Engelsystem\Controllers\PasswordResetController::requireToken
|
|
*/
|
|
public function testResetPassword(): void
|
|
{
|
|
$this->initDatabase();
|
|
|
|
$this->app->instance('config', new Config(['min_password_length' => 3]));
|
|
$user = $this->createUser();
|
|
$token = $this->createToken($user);
|
|
$request = new Request([], [], ['token' => $token->token]);
|
|
|
|
$controller = $this->getController('pages/password/reset-form');
|
|
|
|
$controller->resetPassword($request);
|
|
}
|
|
|
|
/**
|
|
* @covers \Engelsystem\Controllers\PasswordResetController::resetPassword
|
|
* @covers \Engelsystem\Controllers\PasswordResetController::requireToken
|
|
*/
|
|
public function testResetPasswordNoToken(): void
|
|
{
|
|
$this->initDatabase();
|
|
$controller = $this->getController();
|
|
|
|
$this->expectException(HttpNotFound::class);
|
|
$controller->resetPassword(new Request());
|
|
}
|
|
|
|
/**
|
|
* @covers \Engelsystem\Controllers\PasswordResetController::postResetPassword
|
|
*/
|
|
public function testPostResetPassword(): void
|
|
{
|
|
$this->initDatabase();
|
|
|
|
$this->app->instance('config', new Config(['min_password_length' => 3]));
|
|
$user = $this->createUser();
|
|
$token = $this->createToken($user);
|
|
$password = 'SomeRandomPasswordForAmazingSecurity';
|
|
$request = new Request(
|
|
[],
|
|
['password' => $password, 'password_confirmation' => $password],
|
|
['token' => $token->token]
|
|
);
|
|
SessionModel::factory()->create(); // Some other session
|
|
SessionModel::factory(3)->create(['user_id' => $user->id]);
|
|
|
|
$controller = $this->getController(
|
|
'pages/password/reset-success',
|
|
['type' => 'reset']
|
|
);
|
|
|
|
$auth = new Authenticator($request, $this->args['session'], $user);
|
|
$this->app->instance('authenticator', $auth);
|
|
|
|
$response = $controller->postResetPassword($request);
|
|
$this->assertEquals(200, $response->getStatusCode());
|
|
|
|
$this->assertEmpty((new PasswordReset())->find($user->id));
|
|
$this->assertNotNull(auth()->authenticate($user->name, $password));
|
|
$this->assertHasNoNotifications();
|
|
|
|
$this->assertEmpty(
|
|
SessionModel::whereUserId($user->id)->get(),
|
|
'All user sessions should be deleted after successful password reset'
|
|
);
|
|
$this->assertCount(1, SessionModel::all()); // Another session should be still there
|
|
}
|
|
|
|
/**
|
|
* @covers \Engelsystem\Controllers\PasswordResetController::postResetPassword
|
|
* @covers \Engelsystem\Controllers\PasswordResetController::showView
|
|
*/
|
|
public function testPostResetPasswordNotMatching(): void
|
|
{
|
|
$this->initDatabase();
|
|
|
|
$this->app->instance('config', new Config(['min_password_length' => 3]));
|
|
$user = $this->createUser();
|
|
$token = $this->createToken($user);
|
|
$password = 'SomeRandomPasswordForAmazingSecurity';
|
|
$request = new Request(
|
|
[],
|
|
['password' => $password, 'password_confirmation' => $password . 'OrNot'],
|
|
['token' => $token->token]
|
|
);
|
|
|
|
$controller = $this->getController('pages/password/reset-form');
|
|
|
|
$controller->postResetPassword($request);
|
|
$this->assertHasNotification('validation.password.confirmed', NotificationType::ERROR);
|
|
}
|
|
|
|
protected function getControllerArgs(): array
|
|
{
|
|
$response = new Response();
|
|
$session = new Session(new MockArraySessionStorage());
|
|
/** @var EngelsystemMailer|MockObject $mailer */
|
|
$mailer = $this->createMock(EngelsystemMailer::class);
|
|
$log = new TestLogger();
|
|
$renderer = $this->createMock(Renderer::class);
|
|
$response->setRenderer($renderer);
|
|
|
|
$this->app->instance('session', $session);
|
|
|
|
$this->session = $session;
|
|
$this->response = $response;
|
|
$this->log = $log;
|
|
|
|
return $this->args = [
|
|
'response' => $response,
|
|
'session' => $session,
|
|
'mailer' => $mailer,
|
|
'log' => $log,
|
|
'renderer' => $renderer,
|
|
];
|
|
}
|
|
|
|
protected function getController(?string $view = null, ?array $data = null): PasswordResetController
|
|
{
|
|
/** @var Response $response */
|
|
/** @var Session $session */
|
|
/** @var EngelsystemMailer|MockObject $mailer */
|
|
/** @var TestLogger $log */
|
|
/** @var Renderer|MockObject $renderer */
|
|
list($response, $session, $mailer, $log, $renderer) = array_values($this->getControllerArgs());
|
|
$controller = new PasswordResetController($response, $session, $mailer, $log);
|
|
$controller->setValidator(new Validator());
|
|
|
|
if ($view) {
|
|
/** @var array|mixed[] $args */
|
|
$args = [$view];
|
|
if ($data) {
|
|
$args[] = $data;
|
|
}
|
|
|
|
$renderer->expects($this->atLeastOnce())
|
|
->method('render')
|
|
->willReturnCallback(function ($template, $data = []) use ($args) {
|
|
$this->assertEquals($args[0], $template);
|
|
if (isset($args[1])) {
|
|
$this->assertArraySubset($args[1], $data);
|
|
}
|
|
|
|
return 'Foo';
|
|
});
|
|
}
|
|
|
|
return $controller;
|
|
}
|
|
|
|
protected function createUser(): User
|
|
{
|
|
return User::factory()->create(['email' => 'foo@bar.batz']);
|
|
}
|
|
|
|
protected function createToken(User $user): PasswordReset
|
|
{
|
|
$reset = new PasswordReset(['user_id' => $user->id, 'token' => 'SomeTestToken123']);
|
|
$reset->save();
|
|
|
|
return $reset;
|
|
}
|
|
}
|