Skip to content

Commit

Permalink
Added response body stream assertions [SLE-192]
Browse files Browse the repository at this point in the history
  • Loading branch information
samuelgfeller committed Jan 4, 2024
1 parent bc5ba10 commit af90d1f
Show file tree
Hide file tree
Showing 16 changed files with 110 additions and 147 deletions.
2 changes: 1 addition & 1 deletion LICENCE.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Copyright (c) 2023 Samuel Gfeller
Copyright (c) 2024 Samuel Gfeller

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the Software without restriction, including without limitation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public function __invoke(ServerRequest $request, Response $response): Response
} catch (InvalidTokenException $ite) {
$this->templateRenderer->addPhpViewAttribute(
'formErrorMessage',
__(
__( // Message below asserted in PasswordResetSubmitActionTest
'<b>Invalid, used or expired link. <br> Please request a new link below and make
sure to click on the most recent email we send you</a>.</b>'
)
Expand Down
2 changes: 0 additions & 2 deletions src/Application/Action/User/Ajax/UserCreateAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

use App\Application\Responder\JsonResponder;
use App\Domain\User\Service\UserCreator;
use Odan\Session\SessionInterface;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as ServerRequest;
use Psr\Log\LoggerInterface;
Expand All @@ -16,7 +15,6 @@ public function __construct(
private LoggerInterface $logger,
private JsonResponder $jsonResponder,
private UserCreator $userCreator,
private SessionInterface $session,
) {
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ class InvalidCredentialsException extends AuthenticationException
// Voluntarily not more information
private string $userEmail;

// Invalid credentials asserted in LoginSubmitActionTest
public function __construct(string $email, string $message = 'Invalid credentials')
{
parent::__construct($message);
Expand Down
2 changes: 1 addition & 1 deletion src/Infrastructure/Console/SqlSchemaGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ private function query(string $sql): PDOStatement
$statement = $this->pdo->query($sql);

if (!$statement) {
throw new UnexpectedValueException('Query failed: ' . $sql . ' Error: ' . $this->pdo->errorInfo()[2] ?? '');
throw new UnexpectedValueException('Query failed: ' . $sql . ' Error: ' . $this->pdo->errorInfo()[2]);
}

return $statement;
Expand Down
5 changes: 5 additions & 0 deletions tests/Fixture/FixtureInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

namespace App\Test\Fixture;

/**
* Fixture classes contain the properties $table and $records.
* @property string $table
* @property array $records
*/
interface FixtureInterface
{
// Attributes are public but php doesn't support class properties in interfaces so getters are needed
Expand Down
3 changes: 1 addition & 2 deletions tests/Fixture/NoteFixture.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
namespace App\Test\Fixture;

/**
* Post values that can be inserted into the database
* UserFixture HAS to be inserted first.
* Note values that can be inserted into the database
*/
class NoteFixture implements FixtureInterface
{
Expand Down
19 changes: 18 additions & 1 deletion tests/Integration/Authentication/LoginSubmitActionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,14 @@ public function testLoginSubmitActionWrongPassword(): void

// Assert that session user_id is not set
self::assertNull($this->container->get(SessionInterface::class)->get('user_id'));

// Get response body as string from stream
$stream = $response->getBody();
$stream->rewind();
$body = $stream->getContents();

// Assert that response body contains validation error
self::assertStringContainsString('Invalid credentials', $body);
}

/**
Expand All @@ -104,8 +112,9 @@ public function testLoginSubmitActionWrongPassword(): void
* @dataProvider \App\Test\Provider\Authentication\AuthenticationProvider::invalidLoginCredentialsProvider()
*
* @param array $invalidLoginValues valid credentials
* @param string $errorMessage validation message that should be in response body
*/
public function testLoginSubmitActionInvalidValues(array $invalidLoginValues): void
public function testLoginSubmitActionInvalidValues(array $invalidLoginValues, string $errorMessage): void
{
$this->insertFixturesWithAttributes([], new UserFixture());

Expand All @@ -119,6 +128,14 @@ public function testLoginSubmitActionInvalidValues(array $invalidLoginValues): v
$session = $this->container->get(SessionInterface::class);
// Assert that session user_id is not set
self::assertNull($session->get('user_id'));

// Get response body as string from stream
$stream = $response->getBody();
$stream->rewind();
$body = $stream->getContents();

// Assert that response body contains validation error
self::assertStringContainsString($errorMessage, $body);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,13 @@ public function testPasswordForgottenEmailSubmit(): void
// Simulate logged-in user with id 2
$this->container->get(SessionInterface::class)->set('user_id', $userId);

$request = $this->createFormRequest('POST', // Request to change password
$this->urlFor('password-forgotten-email-submit'), [
$request = $this->createFormRequest(
'POST', // Request to change password
$this->urlFor('password-forgotten-email-submit'),
[
'email' => $email,
]);
]
);

$response = $this->app->handle($request);

Expand Down Expand Up @@ -108,10 +111,13 @@ public function testPasswordForgottenEmailSubmitUserNotExisting(): void
{
// Not inserting user as it shouldn't exist

$request = $this->createFormRequest('POST', // Request to change password
$this->urlFor('password-forgotten-email-submit'), [
$request = $this->createFormRequest(
'POST', // Request to change password
$this->urlFor('password-forgotten-email-submit'),
[
'email' => '[email protected]',
]);
]
);

$response = $this->app->handle($request);

Expand All @@ -135,17 +141,25 @@ public function testPasswordForgottenEmailSubmitInvalidData(): void
// Simulate logged-in user with id 2
$this->container->get(SessionInterface::class)->set('user_id', $userRow['id']);

$request = $this->createFormRequest('POST', // Request to change password
$this->urlFor('password-forgotten-email-submit'), [
$request = $this->createFormRequest(
'POST', // Request to change password
$this->urlFor('password-forgotten-email-submit'),
[
'email' => 'inval$d@ema$l.com',
]);
]
);

$response = $this->app->handle($request);

// Assert that response has error status 422
self::assertSame(StatusCodeInterface::STATUS_UNPROCESSABLE_ENTITY, $response->getStatusCode());

// As form is directly rendered with validation errors it's not possible to test them as response is a stream
// There is a visual test in insomnia for this, but I couldn't manage to keep the login session
// Get response body as string from stream
$stream = $response->getBody();
$stream->rewind();
$body = $stream->getContents();

// Assert that response body contains validation error
self::assertStringContainsString('Invalid email', $body);
}
}
24 changes: 18 additions & 6 deletions tests/Integration/Authentication/PasswordResetSubmitActionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,17 +107,22 @@ public function testResetPasswordSubmitInvalidToken(
self::assertSame(StatusCodeInterface::STATUS_OK, $response->getStatusCode());

// Assert that token had NOT been used (except if already used)
self::assertSame(
$verification->usedAt,
$this->getTableRowById('user_verification', (int)$verification->id, ['used_at'])['used_at']
);
$this->assertTableRowValue($verification->usedAt, 'user_verification', $verification->id, 'used_at');

// Assert that password was not changed to the new one
// Assert that the password was not changed to the new one
$this->assertTableRowValue(UserStatus::Unverified->value, 'user', $userRow['id'], 'status');

// Assert that password was NOT changed
$dbPasswordHash = $this->getTableRowById('user', $userRow['id'])['password_hash'];
self::assertFalse(password_verify($newPassword, $dbPasswordHash));

// Get response body as string from stream
$stream = $response->getBody();
$stream->rewind();
$body = $stream->getContents();

// Assert that response body contains validation error
self::assertStringContainsString('Invalid, used or expired link', $body);
}

/**
Expand Down Expand Up @@ -154,7 +159,14 @@ public function testResetPasswordSubmitInvalidData(

// Assert that response has error status 422
self::assertSame(StatusCodeInterface::STATUS_UNPROCESSABLE_ENTITY, $response->getStatusCode());
// As form is directly rendered with validation errors it's not possible to test them as response is a stream

// Get response body as string from stream
$stream = $response->getBody();
$stream->rewind();
$body = $stream->getContents();

// Assert that response body contains validation error
self::assertStringContainsString('Minimum length is 3', $body);
}

/**
Expand Down
7 changes: 1 addition & 6 deletions tests/Integration/Client/ClientReadPageActionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
use Fig\Http\Message\StatusCodeInterface;
use Odan\Session\SessionInterface;
use PHPUnit\Framework\TestCase;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Selective\TestTrait\Traits\DatabaseTestTrait;
use Selective\TestTrait\Traits\HttpTestTrait;
use Selective\TestTrait\Traits\RouteTestTrait;
Expand All @@ -32,12 +30,9 @@ class ClientReadPageActionTest extends TestCase
/**
* Normal page action while being authenticated.
*
* @throws NotFoundExceptionInterface
* @throws ContainerExceptionInterface
*
* @return void
*/
public function testClientReadPageActionAuthorization(): void
public function testClientReadPageActionAuthenticated(): void
{
// Insert linked and authenticated user
$userId = $this->insertFixturesWithAttributes([], new UserFixture())['id'];
Expand Down
11 changes: 6 additions & 5 deletions tests/Provider/Authentication/AuthenticationProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,26 +81,27 @@ public static function authenticationSecurityCases(): array
public static function invalidLoginCredentialsProvider(): array
{
return [
[
'Invalid email' => [
[
// Invalid email
'email' => 'admin@exam$ple.com',
'password' => '12345678',
],
'validationErrorMessage' => 'Invalid email',
],
[
'Missing email' => [
[
// Missing email
'email' => '',
'password' => '12345678',
],
'validationErrorMessage' => 'Invalid email',
],
[
'Missing password' => [
[
// Missing password
'email' => '[email protected]',
'password' => '',
],
'validationErrorMessage' => 'Invalid password',
],
];
}
Expand Down
2 changes: 1 addition & 1 deletion tests/Provider/Note/NoteProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public static function noteListUserAttributesAndExpectedResultProvider(): array
*/
public static function noteListWithFilterProvider(): array
{
// Users linked to notes to insert (authenticated user not relevant for this test, he is inserted in test case)
// Users linked to notes to insert (authenticated user not relevant for this test, inserted in test case)
$usersToInsert = [
['id' => 10],
['id' => 11],
Expand Down
3 changes: 0 additions & 3 deletions tests/Provider/Security/LoginRequestProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@

class LoginRequestProvider
{
// ! This must be the same as the mocked config values in the unit test case
private const userLoginThrottleThresholds = [4 => 10, 9 => 120, 12 => 'captcha'];

private const securitySettings = [
'throttle_login' => true,
'login_throttle_rule' => [4 => 10, 9 => 120, 12 => 'captcha'],
Expand Down
2 changes: 1 addition & 1 deletion tests/Traits/DatabaseExtensionTestTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
use Selective\TestTrait\Traits\DatabaseTestTrait;

/**
* Slim App Route Test Trait.
* Extension of DatabaseTestTrait to add more functionality.
*/
trait DatabaseExtensionTestTrait
{
Expand Down
Loading

0 comments on commit af90d1f

Please sign in to comment.