[AdminBundle] Add QuestionFactory

This commit is contained in:
Wojdylak 2024-01-03 14:49:07 +01:00
parent 7385cab236
commit 4b94f51140
No known key found for this signature in database
GPG key ID: 7509E560A6821ABE
13 changed files with 294 additions and 128 deletions

View file

@ -1,4 +1,4 @@
@change_admin_password @cli
@change_admin_password
Feature: Changing an administrator's password via CLI
In order to login to my administrator account when I forget my password
As a administrator
@ -12,4 +12,6 @@ Feature: Changing an administrator's password via CLI
When I want to change password
And I specify email as "sylius_change_password@example.com"
And I specify my new password as "newp@ssw0rd"
Then I should be able to log in as "sylius_change_password@example.com" authenticated by "newp@ssw0rd" password
And I run command
Then I should be informed that password has been changed successfully
And I should be able to log in as "sylius_change_password@example.com" authenticated by "newp@ssw0rd" password

View file

@ -85,20 +85,6 @@ parameters:
count: 1
path: src/Sylius/Bundle/AdminBundle/Action/ResendShipmentConfirmationEmailAction.php
-
message: "#^Method Sylius\\\\Bundle\\\\AdminBundle\\\\Command\\\\CreateAdminUserCommand\\:\\:askAdminUserData\\(\\) return type has no value type specified in iterable type array\\.$#"
count: 1
path: src/Sylius/Bundle/AdminBundle/Command/CreateAdminUserCommand.php
-
message: "#^Method Sylius\\\\Bundle\\\\AdminBundle\\\\Command\\\\CreateAdminUserCommand\\:\\:showSummary\\(\\) has parameter \\$adminUserData with no value type specified in iterable type array\\.$#"
count: 1
path: src/Sylius/Bundle/AdminBundle/Command/CreateAdminUserCommand.php
-
message: "#^Method Sylius\\\\Bundle\\\\AdminBundle\\\\Command\\\\ChangeAdminUserPasswordCommand\\:\\:__construct\\(\\) has parameter \\$adminUserRepository with generic interface Sylius\\\\Component\\\\User\\\\Repository\\\\UserRepositoryInterface but does not specify its types\\: T$#"
count: 1
path: src/Sylius/Bundle/AdminBundle/Command/ChangeAdminUserPasswordCommand.php
-
message: "#^Method Sylius\\\\Bundle\\\\AdminBundle\\\\Controller\\\\CustomerStatisticsController\\:\\:__construct\\(\\) has parameter \\$customerRepository with generic interface Sylius\\\\Component\\\\Resource\\\\Repository\\\\RepositoryInterface but does not specify its types\\: T$#"
count: 1

View file

@ -14,6 +14,9 @@ declare(strict_types=1);
namespace Sylius\Behat\Context\Cli;
use Behat\Behat\Context\Context;
use Sylius\Component\Core\Model\AdminUserInterface;
use Sylius\Component\User\Repository\UserRepositoryInterface;
use Sylius\Component\User\Security\UserPasswordHasherInterface;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Tester\CommandTester;
use Symfony\Component\HttpKernel\KernelInterface;
@ -29,8 +32,11 @@ final class ChangeAdminPasswordContext implements Context
private $input = [];
/** @param UserRepositoryInterface<AdminUserInterface> $adminUserRepository */
public function __construct(
KernelInterface $kernel
KernelInterface $kernel,
private UserRepositoryInterface $adminUserRepository,
private UserPasswordHasherInterface $userPasswordHasher,
) {
$this->application = new Application($kernel);
}
@ -61,14 +67,31 @@ final class ChangeAdminPasswordContext implements Context
$this->input['password'] = $password;
}
/**
* @When I run command
*/
public function iRunCommand(): void
{
$this->commandTester->setInputs($this->input);
$this->commandTester->execute(['command' => self::ADMIN_USER_CHANGE_PASSWORD]);
}
/**
* @Then I should be informed that password has been changed successfully
*/
public function iShouldBeInformedThatPasswordHasBeenChangedSuccessfully(): void
{
Assert::contains($this->commandTester->getDisplay(), 'Admin user password has been changed successfully.');
}
/**
* @Then I should be able to log in as :email authenticated by :password password
*/
public function iShouldBeAbleToLoginWithEmailAndPassword(string $email = '', string $password = ''): void
{
$this->commandTester->setInputs($this->input);
$this->commandTester->execute(['command' => self::ADMIN_USER_CHANGE_PASSWORD]);
Assert::contains($this->commandTester->getDisplay(), 'Admin user password has been changed successfully.');
/** @var AdminUserInterface|null $adminUser */
$adminUser = $this->adminUserRepository->findOneByEmail($email);
$adminUser->setPlainPassword($password);
Assert::same($adminUser->getPassword(), $this->userPasswordHasher->hash($adminUser));
}
}

View file

@ -30,6 +30,8 @@
<service id="sylius.behat.context.cli.change_admin_password" class="Sylius\Behat\Context\Cli\ChangeAdminPasswordContext">
<argument type="service" id="kernel" />
<argument type="service" id="sylius.repository.admin_user" />
<argument type="service" id="sylius.security.password_hasher" />
</service>
</services>
</container>

View file

@ -5,7 +5,10 @@ default:
suites:
change_admin_password:
contexts:
- sylius.behat.context.hook.doctrine_orm
- sylius.behat.context.setup.admin_user
- sylius.behat.context.cli.change_admin_password
filters:

View file

@ -13,6 +13,7 @@ declare(strict_types=1);
namespace Sylius\Bundle\AdminBundle\Command;
use Sylius\Bundle\AdminBundle\Command\Factory\QuestionFactoryInterface;
use Sylius\Component\Core\Model\AdminUserInterface;
use Sylius\Component\User\Repository\UserRepositoryInterface;
use Sylius\Component\User\Security\PasswordUpdaterInterface;
@ -20,20 +21,30 @@ use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
#[AsCommand(
name: 'sylius:admin-user:change-password',
description: 'Change password of admin user'
)]
final class ChangeAdminUserPasswordCommand extends AbstractAdminUserCommand
final class ChangeAdminUserPasswordCommand extends Command
{
protected SymfonyStyle $io;
/** @param UserRepositoryInterface<AdminUserInterface> $adminUserRepository */
public function __construct(
private UserRepositoryInterface $adminUserRepository,
private PasswordUpdaterInterface $passwordUpdater
private PasswordUpdaterInterface $passwordUpdater,
private QuestionFactoryInterface $questionFactory,
) {
parent::__construct();
}
protected function initialize(InputInterface $input, OutputInterface $output): void
{
$this->io = new SymfonyStyle($input, $output);
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
if (!$input->isInteractive()) {
@ -44,7 +55,7 @@ final class ChangeAdminUserPasswordCommand extends AbstractAdminUserCommand
$this->io->title('Change admin user password');
$email = $this->io->askQuestion($this->createEmailQuestion());
$email = $this->io->askQuestion($this->questionFactory->createEmail());
/** @var AdminUserInterface|null $adminUser */
$adminUser = $this->adminUserRepository->findOneByEmail($email);
@ -54,7 +65,7 @@ final class ChangeAdminUserPasswordCommand extends AbstractAdminUserCommand
return Command::INVALID;
}
$password = $this->io->askQuestion($this->createQuestionWithNonBlankValidator('New password', true));
$password = $this->io->askQuestion($this->questionFactory->createWithNotNullValidator('New password', true));
$adminUser->setPlainPassword($password);
$this->passwordUpdater->updatePassword($adminUser);

View file

@ -13,12 +13,14 @@ declare(strict_types=1);
namespace Sylius\Bundle\AdminBundle\Command;
use Sylius\Bundle\AdminBundle\Command\Factory\QuestionFactoryInterface;
use Sylius\Bundle\AdminBundle\Exception\CreateAdminUserFailedException;
use Sylius\Bundle\AdminBundle\Message\CreateAdminUser;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Intl\Locales;
use Symfony\Component\Messenger\Exception\HandlerFailedException;
use Symfony\Component\Messenger\HandleTrait;
@ -28,17 +30,27 @@ use Symfony\Component\Messenger\MessageBusInterface;
name: 'sylius:admin-user:create',
description: 'Create a new admin user',
)]
final class CreateAdminUserCommand extends AbstractAdminUserCommand
final class CreateAdminUserCommand extends Command
{
use HandleTrait;
public function __construct(MessageBusInterface $messageBus, private string $defaultLocaleCode)
{
protected SymfonyStyle $io;
public function __construct(
MessageBusInterface $messageBus,
private string $defaultLocaleCode,
private QuestionFactoryInterface $questionFactory,
) {
$this->messageBus = $messageBus;
parent::__construct();
}
protected function initialize(InputInterface $input, OutputInterface $output): void
{
$this->io = new SymfonyStyle($input, $output);
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
if (!$input->isInteractive()) {
@ -76,18 +88,19 @@ final class CreateAdminUserCommand extends AbstractAdminUserCommand
return Command::SUCCESS;
}
/** @return array<array-key, mixed> */
private function askAdminUserData(): array
{
$adminUserData = [];
$adminUserData['email'] = $this->io->askQuestion($this->createEmailQuestion());
$adminUserData['email'] = $this->io->askQuestion($this->questionFactory->createEmail());
$adminUserData['username'] = $this->io->askQuestion(
$this->createQuestionWithNonBlankValidator('Username'),
$this->questionFactory->createWithNotNullValidator('Username'),
);
$adminUserData['first_name'] = $this->io->ask('First name');
$adminUserData['last_name'] = $this->io->ask('Last name');
$adminUserData['plain_password'] = $this->io->askQuestion(
$this->createQuestionWithNonBlankValidator('Password', true),
$this->questionFactory->createWithNotNullValidator('Password', true),
);
$localeCodes = Locales::getNames();
@ -98,6 +111,7 @@ final class CreateAdminUserCommand extends AbstractAdminUserCommand
return $adminUserData;
}
/** @param array<array-key, mixed> $adminUserData */
private function showSummary(array $adminUserData): void
{
$this->io->writeln('The following admin user will be created:');

View file

@ -11,24 +11,13 @@
declare(strict_types=1);
namespace Sylius\Bundle\AdminBundle\Command;
namespace Sylius\Bundle\AdminBundle\Command\Factory;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Style\SymfonyStyle;
abstract class AbstractAdminUserCommand extends Command
final class QuestionFactory implements QuestionFactoryInterface
{
protected SymfonyStyle $io;
protected function initialize(InputInterface $input, OutputInterface $output): void
{
$this->io = new SymfonyStyle($input, $output);
}
protected function createEmailQuestion(): Question
public function createEmail(): Question
{
$question = new Question('Email');
$question->setValidator(function (?string $email) {
@ -43,7 +32,7 @@ abstract class AbstractAdminUserCommand extends Command
return $question;
}
protected function createQuestionWithNonBlankValidator(string $askedQuestion, bool $hidden = false): Question
public function createWithNotNullValidator(string $askedQuestion, bool $hidden = false): Question
{
$question = new Question($askedQuestion);
$question->setValidator(function (?string $value) {
@ -54,10 +43,7 @@ abstract class AbstractAdminUserCommand extends Command
return $value;
});
$question->setMaxAttempts(3);
if ($hidden) {
$question->setHidden(true);
}
$question->setHidden($hidden);
return $question;
}

View file

@ -0,0 +1,12 @@
<?php
namespace Sylius\Bundle\AdminBundle\Command\Factory;
use Symfony\Component\Console\Question\Question;
interface QuestionFactoryInterface
{
public function createEmail(): Question;
public function createWithNotNullValidator(string $askedQuestion, bool $hidden = false): Question;
}

View file

@ -38,12 +38,14 @@
<service id="Sylius\Bundle\AdminBundle\Command\CreateAdminUserCommand">
<argument type="service" id="sylius.command_bus" />
<argument>%sylius_locale.locale%</argument>
<argument type="service" id="sylius.command_factory.question" />
<tag name="console.command" />
</service>
<service id="Sylius\Bundle\AdminBundle\Command\ChangeAdminUserPasswordCommand">
<argument type="service" id="sylius.repository.admin_user" />
<argument type="service" id="sylius.security.password_updater" />
<argument type="service" id="sylius.command_factory.question" />
<tag name="console.command" />
</service>
@ -57,6 +59,10 @@
<tag name="messenger.message_handler" bus="sylius_default.bus" />
</service>
<service id="sylius.command_factory.question" class="Sylius\Bundle\AdminBundle\Command\Factory\QuestionFactory" />
<service id="Sylius\Bundle\AdminBundle\Command\Factory\QuestionFactoryInterface" alias="sylius.command_factory.question" />
<service id="sylius.context.locale.admin_based" class="Sylius\Bundle\AdminBundle\Context\AdminBasedLocaleContext">
<argument type="service" id="security.token_storage" />
<tag name="sylius.context.locale" priority="128" />

View file

@ -13,23 +13,31 @@ declare(strict_types=1);
namespace Sylius\Bundle\AdminBundle\Tests\Command;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Sylius\Bundle\AdminBundle\Command\ChangeAdminUserPasswordCommand;
use Sylius\Bundle\AdminBundle\Command\Factory\QuestionFactoryInterface;
use Sylius\Component\Core\Model\AdminUser;
use Sylius\Component\User\Repository\UserRepositoryInterface;
use Sylius\Component\User\Security\PasswordUpdaterInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Tester\CommandTester;
final class ChangeAdminUserPasswordCommandTest extends TestCase
{
private const EMAIL = 'sylius@example.com';
private const PASSWORD = 'Password';
private CommandTester $command;
private UserRepositoryInterface $userRepository;
private PasswordUpdaterInterface $passwordUpdater;
private QuestionFactoryInterface $questionFactory;
protected function setUp(): void
{
parent::setUp();
@ -38,8 +46,10 @@ final class ChangeAdminUserPasswordCommandTest extends TestCase
$this->passwordUpdater = $this->createMock(PasswordUpdaterInterface::class);
$this->questionFactory = $this->createMock(QuestionFactoryInterface::class);
$this->command = new CommandTester(
new ChangeAdminUserPasswordCommand($this->userRepository, $this->passwordUpdater),
new ChangeAdminUserPasswordCommand($this->userRepository, $this->passwordUpdater, $this->questionFactory),
);
}
@ -54,6 +64,12 @@ final class ChangeAdminUserPasswordCommandTest extends TestCase
/** @test */
public function it_does_not_change_password_when_admin_user_is_not_found(): void
{
$this
->questionFactory
->expects($this->once())
->method('createEmail')
->willReturn($this->createQuestionMock('Email'));
$this
->userRepository
->expects($this->once())
@ -74,7 +90,24 @@ final class ChangeAdminUserPasswordCommandTest extends TestCase
/** @test */
public function it_changes_password_for_existing_admin_user(): void
{
$adminUser = new AdminUser();
$adminUser = $this->createMock(AdminUser::class);
$adminUser
->expects($this->once())
->method('setPlainPassword')
->with(self::PASSWORD);
$this
->questionFactory
->expects($this->once())
->method('createEmail')
->willReturn($this->createQuestionMock('Email'));
$this
->questionFactory
->expects($this->once())
->method('createWithNotNullValidator')
->with('New password', true)
->willReturn($this->createQuestionMock('New password'));
$this
->userRepository
@ -104,4 +137,17 @@ final class ChangeAdminUserPasswordCommandTest extends TestCase
self::assertSame(Command::SUCCESS, $this->command->getStatusCode());
}
private function createQuestionMock(string $askedQuestion): MockObject
{
$question = $this->createMock(Question::class);
$question
->method('isTrimmable')
->willReturn(true);
$question
->method('getQuestion')
->willReturn($askedQuestion);
return $question;
}
}

View file

@ -16,9 +16,11 @@ namespace Sylius\Bundle\AdminBundle\Tests\Command;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Sylius\Bundle\AdminBundle\Command\CreateAdminUserCommand;
use Sylius\Bundle\AdminBundle\Command\Factory\QuestionFactoryInterface;
use Sylius\Bundle\AdminBundle\Exception\CreateAdminUserFailedException;
use Sylius\Bundle\AdminBundle\Message\CreateAdminUser;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Tester\CommandTester;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\Exception\HandlerFailedException;
@ -47,83 +49,53 @@ final class CreateAdminUserCommandTest extends TestCase
private MockObject $messageBus;
private QuestionFactoryInterface $questionFactory;
protected function setUp(): void
{
parent::setUp();
$this->messageBus = $this->createMock(MessageBusInterface::class);
$this->questionFactory = $this->createMock(QuestionFactoryInterface::class);
$this->command = new CommandTester(
new CreateAdminUserCommand($this->messageBus, self::LOCALE_CODE),
new CreateAdminUserCommand($this->messageBus, self::LOCALE_CODE, $this->questionFactory),
);
}
/** @test */
public function it_creates_an_admin_user_if_accepted_in_the_summary(): void
{
$adminUserData = $this->getDefaultAdminUserDataSetup();
$this
->questionFactory
->expects($this->once())
->method('createEmail')
->willReturn($this->createQuestionMock('Email'));
$commandInputs = $this->getDefaultCommandInputsSetup();
$this
->questionFactory
->expects($this->exactly(2))
->method('createWithNotNullValidator')
->willReturnOnConsecutiveCalls(
$this->createQuestionMock('Username'),
$this->createQuestionMock('New password'),
);
$this->assertSuccessfulCommandExecution($adminUserData, $commandInputs);
}
$this->command->setInputs($this->getDefaultCommandInputsSetup());
/** @test */
public function it_has_set_up_three_attempts_to_write_a_valid_email(): void
{
$adminUserData = $this->getDefaultAdminUserDataSetup();
$message = new CreateAdminUser(...array_values($this->getDefaultAdminUserDataSetup()));
$commandInputs = array_merge(
[
'first_email_entry' => 'invalid-email',
'second_email_entry' => 'still-invalid-email',
],
$this->getDefaultCommandInputsSetup(),
);
$this->messageBus->expects($this->once())
->method('dispatch')
->with($message)
->willReturn(new Envelope($message, [new HandledStamp(self::anything(), 'handler')]))
;
$this->assertSuccessfulCommandExecution($adminUserData, $commandInputs);
}
$this->command->execute([]);
/** @test */
public function it_has_set_up_three_attempts_to_write_a_non_blank_username(): void
{
$adminUserData = $this->getDefaultAdminUserDataSetup();
$commandInputs = [
'email' => self::EMAIL,
'first_username_entry' => '',
'second_username_entry' => '',
'username' => self::USERNAME,
'first_name' => self::FIRST_NAME,
'last_name' => self::LAST_NAME,
'password' => self::PASSWORD,
'locale_code' => self::LOCALE_CODE,
'admin_user_enabled' => self::YES,
'creation_confirmation' => self::YES,
];
$this->assertSuccessfulCommandExecution($adminUserData, $commandInputs);
}
/** @test */
public function it_has_set_up_three_attempts_to_write_a_non_blank_password(): void
{
$adminUserData = $this->getDefaultAdminUserDataSetup();
$commandInputs = [
'email' => self::EMAIL,
'username' => self::USERNAME,
'first_name' => self::FIRST_NAME,
'last_name' => self::LAST_NAME,
'first_password_entry' => '',
'second_password_entry' => '',
'password' => self::PASSWORD,
'locale_code' => self::LOCALE_CODE,
'admin_user_enabled' => self::YES,
'creation_confirmation' => self::YES,
];
$this->assertSuccessfulCommandExecution($adminUserData, $commandInputs);
$this->command->assertCommandIsSuccessful();
self::assertStringContainsString('Admin user has been successfully created.', $this->command->getDisplay());
}
/** @test */
@ -140,6 +112,21 @@ final class CreateAdminUserCommandTest extends TestCase
'creation_confirmation' => self::NO,
]);
$this
->questionFactory
->expects($this->once())
->method('createEmail')
->willReturn($this->createQuestionMock('Email'));
$this
->questionFactory
->expects($this->exactly(2))
->method('createWithNotNullValidator')
->willReturnOnConsecutiveCalls(
$this->createQuestionMock('Username'),
$this->createQuestionMock('New password'),
);
$this->messageBus->expects($this->never())->method('dispatch');
self::assertSame(Command::INVALID, $this->command->execute([]));
@ -155,6 +142,21 @@ final class CreateAdminUserCommandTest extends TestCase
$message = new CreateAdminUser(...array_values($adminUserData));
$this
->questionFactory
->expects($this->once())
->method('createEmail')
->willReturn($this->createQuestionMock('Email'));
$this
->questionFactory
->expects($this->exactly(2))
->method('createWithNotNullValidator')
->willReturnOnConsecutiveCalls(
$this->createQuestionMock('Username'),
$this->createQuestionMock('New password'),
);
$this->messageBus->expects($this->once())
->method('dispatch')
->with($message)
@ -175,24 +177,6 @@ final class CreateAdminUserCommandTest extends TestCase
self::assertSame(Command::FAILURE, $this->command->execute([], ['interactive' => false]));
}
private function assertSuccessfulCommandExecution(array $adminUserData, array $commandInputs): void
{
$this->command->setInputs($commandInputs);
$message = new CreateAdminUser(...array_values($adminUserData));
$this->messageBus->expects($this->once())
->method('dispatch')
->with($message)
->willReturn(new Envelope($message, [new HandledStamp(self::anything(), 'handler')]))
;
$this->command->execute([]);
$this->command->assertCommandIsSuccessful();
self::assertStringContainsString('Admin user has been successfully created.', $this->command->getDisplay());
}
private function getDefaultCommandInputsSetup(): array
{
return [
@ -219,4 +203,17 @@ final class CreateAdminUserCommandTest extends TestCase
'admin_user_enabled' => true,
];
}
private function createQuestionMock(string $askedQuestion): MockObject
{
$question = $this->createMock(Question::class);
$question
->method('isTrimmable')
->willReturn(true);
$question
->method('getQuestion')
->willReturn($askedQuestion);
return $question;
}
}

View file

@ -0,0 +1,78 @@
<?php
/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Sylius\Bundle\AdminBundle\Tests\Command\Factory;
use PHPUnit\Framework\TestCase;
use Sylius\Bundle\AdminBundle\Command\Factory\QuestionFactory;
final class QuestionFactoryTest extends TestCase
{
/** @test */
public function it_creates_email_question(): void
{
$questionFactory = new QuestionFactory();
$question = $questionFactory->createEmail();
$this->assertSame('Email', $question->getQuestion());
$this->assertSame(3, $question->getMaxAttempts());
$this->assertEquals('test@example.com', $question->getValidator()('test@example.com'));
}
/** @test */
public function it_creates_email_question_with_invalid_email(): void
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('The email address provided is invalid. Please try again.');
$questionFactory = new QuestionFactory();
$emailQuestion = $questionFactory->createEmail();
$emailQuestion->getValidator()('invalid-email');
}
/** @test */
public function it_creates_email_question_with_null_email(): void
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('The email address provided is invalid. Please try again.');
$questionFactory = new QuestionFactory();
$emailQuestion = $questionFactory->createEmail();
$emailQuestion->getValidator()(null);
}
/** @test */
public function it_creates_question_with_not_null_validator(): void
{
$questionFactory = new QuestionFactory();
$question = $questionFactory->createWithNotNullValidator('Question');
$this->assertSame('Question', $question->getQuestion());
$this->assertSame(3, $question->getMaxAttempts());
$this->assertEquals('test', $question->getValidator()('test'));
}
/** @test */
public function it_creates_question_with_not_null_validator_with_null_value(): void
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('The value cannot be empty.');
$questionFactory = new QuestionFactory();
$question = $questionFactory->createWithNotNullValidator('Question');
$question->getValidator()(null);
}
}