Skip to content

Commit 68cb2c4

Browse files
committed
Add integration tests for validator constraints
1 parent 8a71e6d commit 68cb2c4

File tree

7 files changed

+125
-4
lines changed

7 files changed

+125
-4
lines changed

app/composer.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"symfony/console": "^7.4 || ^8.0",
1515
"symfony/doctrine-bridge": "^7.4 || ^8.0",
1616
"symfony/dotenv": "^7.4 || ^8.0",
17+
"symfony/form": "^7.4 || ^8.0",
1718
"symfony/framework-bundle": "^7.4 || ^8.0",
1819
"symfony/http-kernel": "^7.4 || ^8.0",
1920
"symfony/mailer": "^7.4 || ^8.0",
@@ -23,6 +24,7 @@
2324
"symfony/security-bundle": "^7.4 || ^8.0",
2425
"symfony/translation": "^7.4 || ^8.0",
2526
"symfony/twig-bundle": "^7.4 || ^8.0",
27+
"symfony/validator": "^7.4 || ^8.0",
2628
"symfony/web-profiler-bundle": "^7.4 || ^8.0",
2729
"symfony/yaml": "^7.4 || ^8.0"
2830
},

app/config/reference.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@
146146
* cookie_name?: scalar|null, // The name of the cookie to use when using stateless protection. // Default: "csrf-token"
147147
* },
148148
* form?: bool|array{ // Form configuration
149-
* enabled?: bool, // Default: false
149+
* enabled?: bool, // Default: true
150150
* csrf_protection?: array{
151151
* enabled?: scalar|null, // Default: null
152152
* token_id?: scalar|null, // Default: null
@@ -325,7 +325,7 @@
325325
* }>,
326326
* },
327327
* validation?: bool|array{ // Validation configuration
328-
* enabled?: bool, // Default: false
328+
* enabled?: bool, // Default: true
329329
* enable_attributes?: bool, // Default: true
330330
* static_method?: list<scalar|null>,
331331
* translation_domain?: scalar|null, // Default: "validators"

app/src/Controller/MembersController.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44

55
namespace App\Controller;
66

7+
use App\Form\TwoFactorFormData;
78
use Scheb\TwoFactorBundle\Model\Google\TwoFactorInterface as GoogleAuthenticatorTwoFactorInterface;
89
use Scheb\TwoFactorBundle\Model\Totp\TwoFactorInterface as TotpTwoFactorInterface;
910
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
11+
use Symfony\Component\HttpFoundation\Request;
1012
use Symfony\Component\HttpFoundation\Response;
1113
use Symfony\Component\Routing\Attribute\Route;
1214
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
@@ -23,4 +25,25 @@ public function membersArea(TokenStorageInterface $tokenStorage): Response
2325
'displayQrCodeTotp' => $user instanceof TotpTwoFactorInterface && $user->isTotpAuthenticationEnabled(),
2426
]);
2527
}
28+
29+
#[Route('/members/validators', name: 'validators_form')]
30+
public function validators(Request $request): Response
31+
{
32+
$formData = new TwoFactorFormData();
33+
$form = $this->createFormBuilder($formData)
34+
->add('googleTotpCode')
35+
->add('totpCode')
36+
->getForm();
37+
38+
$isValid = null;
39+
$form->handleRequest($request);
40+
if ($form->isSubmitted()) {
41+
$isValid = $form->isValid();
42+
}
43+
44+
return $this->render('members/validators.html.twig', [
45+
'form' => $form,
46+
'isValid' => $isValid,
47+
]);
48+
}
2649
}

app/src/Form/TwoFactorFormData.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Form;
6+
7+
use Scheb\TwoFactorBundle\Security\TwoFactor\Validator\Constraints\UserGoogleTotpCode;
8+
use Scheb\TwoFactorBundle\Security\TwoFactor\Validator\Constraints\UserTotpCode;
9+
10+
class TwoFactorFormData
11+
{
12+
#[UserGoogleTotpCode]
13+
public string $googleTotpCode;
14+
15+
#[UserTotpCode]
16+
public string $totpCode;
17+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{% extends "base.html.twig" %}
2+
3+
{% block body %}
4+
<h2>Form Validators</h2>
5+
{% if isValid %}
6+
<p class="notice">All codes valid!</p>
7+
{% endif %}
8+
{{ form_start(form) }}
9+
{{ form_widget(form) }}
10+
<p><button type="submit" name="submit-button">Check Codes</button></p>
11+
{{ form_end(form) }}
12+
{% endblock %}

app/tests/TestCase.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ abstract class TestCase extends WebTestCase
3232
private const TRUSTED_DEVICE_COOKIE_NAME = 'trusted_device';
3333
private const REMEMBER_ME_COOKIE_NAME = 'REMEMBERME';
3434

35-
private KernelBrowser $client;
35+
protected KernelBrowser $client;
3636

3737
// //////////////////// CONFIGURATION
3838

@@ -224,6 +224,11 @@ protected function navigateTo2faForm(): Crawler
224224
return $this->client->request('GET', '/2fa');
225225
}
226226

227+
protected function navigateToValidatorsForm(): Crawler
228+
{
229+
return $this->client->request('GET', '/members/validators');
230+
}
231+
227232
// //////////////////// ASSERTS
228233

229234
protected function assertLoggerHasInfo(string $message): void
@@ -337,7 +342,7 @@ protected function assertHasTrustedDeviceCookieSet(): void
337342
$this->assertNotNull($this->client->getCookieJar()->get(self::TRUSTED_DEVICE_COOKIE_NAME), 'Trusted device cookie must be set');
338343
}
339344

340-
private function assertResponseStatusCode(int $code): void
345+
protected function assertResponseStatusCode(int $code): void
341346
{
342347
$this->assertEquals(
343348
$code,
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use App\Tests\TestCase;
6+
use Symfony\Component\DomCrawler\Crawler;
7+
8+
class ValidatorConstraintsTest extends TestCase
9+
{
10+
public function testValidatorConstraintsWithWrongCodes(): void
11+
{
12+
// Login
13+
$this->setAll2faProvidersEnabled(false);
14+
$this->performLogin();
15+
16+
$submittedPage = $this->submitValidatorForm('wrongCode', 'wrongCode');
17+
18+
$this->assertValidatedWrongCodes($submittedPage);
19+
}
20+
21+
public function testValidatorConstraintsWithCorrectCodes(): void
22+
{
23+
// Login
24+
$this->setAll2faProvidersEnabled(false);
25+
$this->performLogin();
26+
27+
$submittedPage = $this->submitValidatorForm($this->getGoogleAuthenticatorCode(), $this->getTotpCode());
28+
29+
$this->assertValidatedCorrectCodes($submittedPage);
30+
}
31+
32+
private function submitValidatorForm(string $googleTotpCode, string $totpCode): Crawler
33+
{
34+
$formPage = $this->navigateToValidatorsForm();
35+
36+
$form = $formPage->selectButton('submit-button')->form();
37+
$form['form[googleTotpCode]'] = $googleTotpCode;
38+
$form['form[totpCode]'] = $totpCode;
39+
40+
return $this->client->submit($form);
41+
}
42+
43+
private function assertValidatedWrongCodes(Crawler $submitPage): void
44+
{
45+
$this->assertResponseStatusCode(422); // Unprocessable Entity
46+
$this->assertStringContainsString(
47+
'The verification code is not valid',
48+
$submitPage->html(),
49+
'The page must show an error message'
50+
);
51+
}
52+
53+
private function assertValidatedCorrectCodes(Crawler $submitPage): void
54+
{
55+
$this->assertResponseStatusCode(200); // Unprocessable Entity
56+
$this->assertStringContainsString(
57+
'All codes valid!',
58+
$submitPage->html(),
59+
'The page must show the success message'
60+
);
61+
}
62+
}

0 commit comments

Comments
 (0)