diff --git a/app/AppKernel.php b/app/AppKernel.php index 8ea2a2fa7..59c74d4d1 100644 --- a/app/AppKernel.php +++ b/app/AppKernel.php @@ -35,7 +35,7 @@ public function registerBundles() new Knp\Bundle\GaufretteBundle\KnpGaufretteBundle(), new Vich\UploaderBundle\VichUploaderBundle(), - new JMS\DiExtraBundle\JMSDiExtraBundle($this), + new JMS\DiExtraBundle\JMSDiExtraBundle(), new JMS\AopBundle\JMSAopBundle(), new JMS\SecurityExtraBundle\JMSSecurityExtraBundle(), new Nelmio\ApiDocBundle\NelmioApiDocBundle(), @@ -76,6 +76,7 @@ public function registerBundles() new Circle\RestClientBundle\CircleRestClientBundle(), new PROCERGS\SmsServiceBundle\PROCERGSSmsServiceBundle(), new PROCERGS\LoginCidadao\PhoneVerificationBundle\PROCERGSPhoneVerificationBundle(), + new PROCERGS\LoginCidadao\CpfVerificationBundle\PROCERGSLoginCidadaoCpfVerificationBundle(), ); if (in_array($this->getEnvironment(), array('dev', 'test'))) { diff --git a/app/config/routing.yml b/app/config/routing.yml index 0510f4e79..a4a4a2df5 100644 --- a/app/config/routing.yml +++ b/app/config/routing.yml @@ -1,3 +1,7 @@ +procergs_login_cidadao_cpf_verification: + resource: "@PROCERGSLoginCidadaoCpfVerificationBundle/Resources/config/routing.yml" + prefix: / + procergs_login_cidadao_accounting: resource: "@PROCERGSLoginCidadaoAccountingBundle/Resources/config/routing.yml" prefix: / diff --git a/src/PROCERGS/LoginCidadao/CpfVerificationBundle/DependencyInjection/Configuration.php b/src/PROCERGS/LoginCidadao/CpfVerificationBundle/DependencyInjection/Configuration.php new file mode 100644 index 000000000..6b1e7a070 --- /dev/null +++ b/src/PROCERGS/LoginCidadao/CpfVerificationBundle/DependencyInjection/Configuration.php @@ -0,0 +1,29 @@ +root('procergs_login_cidadao_cpf_verification'); + + // Here you should define the parameters that are allowed to + // configure your bundle. See the documentation linked above for + // more information on that topic. + + return $treeBuilder; + } +} diff --git a/src/PROCERGS/LoginCidadao/CpfVerificationBundle/DependencyInjection/PROCERGSLoginCidadaoCpfVerificationExtension.php b/src/PROCERGS/LoginCidadao/CpfVerificationBundle/DependencyInjection/PROCERGSLoginCidadaoCpfVerificationExtension.php new file mode 100644 index 000000000..c4246d466 --- /dev/null +++ b/src/PROCERGS/LoginCidadao/CpfVerificationBundle/DependencyInjection/PROCERGSLoginCidadaoCpfVerificationExtension.php @@ -0,0 +1,28 @@ +processConfiguration($configuration, $configs); + + $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + $loader->load('services.yml'); + } +} diff --git a/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Exception/CpfNotSubscribedToNfgException.php b/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Exception/CpfNotSubscribedToNfgException.php new file mode 100644 index 000000000..0cb0e76b9 --- /dev/null +++ b/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Exception/CpfNotSubscribedToNfgException.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PROCERGS\LoginCidadao\CpfVerificationBundle\Exception; + +use Throwable; + +class CpfNotSubscribedToNfgException extends \RuntimeException +{ + public const ERROR_CODE = 'cpf_not_subscribed_to_nfg'; + + /** @var string */ + private $cpf; + + public function __construct(string $cpf, string $message = "", int $code = 0, Throwable $previous = null) + { + $this->cpf = $cpf; + parent::__construct($message, $code, $previous); + } + + /** + * @return string + */ + public function getCpf(): string + { + return $this->cpf; + } +} diff --git a/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Exception/WrongAnswerException.php b/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Exception/WrongAnswerException.php new file mode 100644 index 000000000..20be17ecf --- /dev/null +++ b/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Exception/WrongAnswerException.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PROCERGS\LoginCidadao\CpfVerificationBundle\Exception; + +use PROCERGS\LoginCidadao\CpfVerificationBundle\Model\ChallengeInterface; +use Throwable; + +class WrongAnswerException extends \RuntimeException +{ + /** @var ChallengeInterface */ + private $challenge; + + /** + * @inheritDoc + */ + public function __construct( + ChallengeInterface $challenge, + string $message = "", + int $code = 0, + Throwable $previous = null + ) { + $this->challenge = $challenge; + parent::__construct($message, $code, $previous); + } + + /** + * @return ChallengeInterface + */ + public function getChallenge(): ChallengeInterface + { + return $this->challenge; + } +} diff --git a/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Model/AbstractChallenge.php b/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Model/AbstractChallenge.php new file mode 100644 index 000000000..b8529bd31 --- /dev/null +++ b/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Model/AbstractChallenge.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PROCERGS\LoginCidadao\CpfVerificationBundle\Model; + +abstract class AbstractChallenge implements ChallengeInterface +{ + /** @var string */ + private $cpf; + + /** @var int */ + private $attemptsLeft; + + /** + * Challenge constructor. + * @param string $cpf + * @param int $attemptsLeft + */ + public function __construct(int $attemptsLeft, string $cpf) + { + $this->cpf = $cpf; + $this->attemptsLeft = $attemptsLeft; + } + + /** + * @return string + */ + public function getCpf(): string + { + return $this->cpf; + } + + /** + * @return int + */ + public function getAttemptsLeft(): int + { + return $this->attemptsLeft; + } +} diff --git a/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Model/ChallengeFactory.php b/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Model/ChallengeFactory.php new file mode 100644 index 000000000..5b51cae61 --- /dev/null +++ b/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Model/ChallengeFactory.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PROCERGS\LoginCidadao\CpfVerificationBundle\Model; + +class ChallengeFactory +{ + public static function create( + string $challengeName, + int $attemptsLeft, + string $cpf, + array $choices = null + ): ChallengeInterface { + switch ($challengeName) { + case SelectMotherInitialsChallenge::CHALLENGE_NAME: + return new SelectMotherInitialsChallenge($attemptsLeft, $cpf, $choices ?? []); + case TypeBirthdayChallenge::CHALLENGE_NAME: + return new TypeBirthdayChallenge($attemptsLeft, $cpf); + case TypeMotherInitialsChallenge::CHALLENGE_NAME: + return new TypeMotherInitialsChallenge($attemptsLeft, $cpf); + case TypePostalCodeChallenge::CHALLENGE_NAME: + return new TypePostalCodeChallenge($attemptsLeft, $cpf); + case TypeVoterRegistrationChallenge::CHALLENGE_NAME: + return new TypeVoterRegistrationChallenge($attemptsLeft, $cpf); + default: + throw new \InvalidArgumentException("Unsupported challenge '{$challengeName}'"); + } + } +} diff --git a/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Model/ChallengeInterface.php b/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Model/ChallengeInterface.php new file mode 100644 index 000000000..a7146fe34 --- /dev/null +++ b/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Model/ChallengeInterface.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PROCERGS\LoginCidadao\CpfVerificationBundle\Model; + +interface ChallengeInterface +{ + /** + * @return string + */ + public function getName(): string; + + /** + * @return string + */ + public function getCpf(): string; + + /** + * @return int + */ + public function getAttemptsLeft(): int; +} diff --git a/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Model/SelectMotherInitialsChallenge.php b/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Model/SelectMotherInitialsChallenge.php new file mode 100644 index 000000000..6822167d9 --- /dev/null +++ b/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Model/SelectMotherInitialsChallenge.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PROCERGS\LoginCidadao\CpfVerificationBundle\Model; + +class SelectMotherInitialsChallenge extends AbstractChallenge +{ + public const CHALLENGE_NAME = 'select_mother_initials'; + + /** @var string[] */ + private $choices; + + /** + * SelectMotherInitialsChallenge constructor. + * @param int $attemptsLeft + * @param string $cpf + * @param string[] $choices + */ + public function __construct(int $attemptsLeft, string $cpf, array $choices) + { + $this->choices = $choices; + parent::__construct($attemptsLeft, $cpf); + } + + /** + * @inheritDoc + */ + public function getName(): string + { + return self::CHALLENGE_NAME; + } + + /** + * @return string[] + */ + public function getChoices(): array + { + return $this->choices; + } +} diff --git a/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Model/TypeBirthdayChallenge.php b/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Model/TypeBirthdayChallenge.php new file mode 100644 index 000000000..18b4449e7 --- /dev/null +++ b/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Model/TypeBirthdayChallenge.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PROCERGS\LoginCidadao\CpfVerificationBundle\Model; + +class TypeBirthdayChallenge extends AbstractChallenge +{ + public const CHALLENGE_NAME = 'type_birthday'; + + /** + * @inheritDoc + */ + public function getName(): string + { + return self::CHALLENGE_NAME; + } +} diff --git a/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Model/TypeMotherInitialsChallenge.php b/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Model/TypeMotherInitialsChallenge.php new file mode 100644 index 000000000..cdcd05831 --- /dev/null +++ b/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Model/TypeMotherInitialsChallenge.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PROCERGS\LoginCidadao\CpfVerificationBundle\Model; + +class TypeMotherInitialsChallenge extends AbstractChallenge +{ + public const CHALLENGE_NAME = 'type_mother_initials'; + + /** + * @inheritDoc + */ + public function getName(): string + { + return self::CHALLENGE_NAME; + } +} diff --git a/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Model/TypePostalCodeChallenge.php b/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Model/TypePostalCodeChallenge.php new file mode 100644 index 000000000..6d87b9ca6 --- /dev/null +++ b/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Model/TypePostalCodeChallenge.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PROCERGS\LoginCidadao\CpfVerificationBundle\Model; + +class TypePostalCodeChallenge extends AbstractChallenge +{ + public const CHALLENGE_NAME = 'type_postal_code'; + + /** + * @inheritDoc + */ + public function getName(): string + { + return self::CHALLENGE_NAME; + } +} diff --git a/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Model/TypeVoterRegistrationChallenge.php b/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Model/TypeVoterRegistrationChallenge.php new file mode 100644 index 000000000..f270667f6 --- /dev/null +++ b/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Model/TypeVoterRegistrationChallenge.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PROCERGS\LoginCidadao\CpfVerificationBundle\Model; + +class TypeVoterRegistrationChallenge extends AbstractChallenge +{ + public const CHALLENGE_NAME = 'type_voter_registration'; + + /** + * @inheritDoc + */ + public function getName(): string + { + return self::CHALLENGE_NAME; + } +} diff --git a/src/PROCERGS/LoginCidadao/CpfVerificationBundle/PROCERGSLoginCidadaoCpfVerificationBundle.php b/src/PROCERGS/LoginCidadao/CpfVerificationBundle/PROCERGSLoginCidadaoCpfVerificationBundle.php new file mode 100644 index 000000000..735c5e6c6 --- /dev/null +++ b/src/PROCERGS/LoginCidadao/CpfVerificationBundle/PROCERGSLoginCidadaoCpfVerificationBundle.php @@ -0,0 +1,9 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PROCERGS\LoginCidadao\CpfVerificationBundle\Parser; + +use PROCERGS\LoginCidadao\CpfVerificationBundle\Model\ChallengeFactory; +use PROCERGS\LoginCidadao\CpfVerificationBundle\Model\ChallengeInterface; + +class ChallengeParser +{ + public static function parseJson(string $json, string $cpf = null): ChallengeInterface + { + $decoded = json_decode($json, true); + + return self::parseArray($decoded, $cpf); + } + + public static function parseArray(array $serialized, string $cpf = null): ChallengeInterface + { + if (!array_key_exists('challenge', $serialized)) { + throw new \RuntimeException(); + } + if (!array_key_exists('attempts_left', $serialized)) { + throw new \RuntimeException(); + } + + $challengeName = $serialized['challenge']; + $attemptsLeft = $serialized['attempts_left']; + $cpf = $cpf ?? $serialized['cpf'] ?? null; + $choices = $serialized['choices'] ?? []; + + return ChallengeFactory::create($challengeName, $attemptsLeft, $cpf, $choices); + } +} diff --git a/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Resources/config/routing.yml b/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Resources/config/routing.yml new file mode 100644 index 000000000..4af17c2c5 --- /dev/null +++ b/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Resources/config/routing.yml @@ -0,0 +1,4 @@ +lc_cpf_verification: + resource: "@PROCERGSLoginCidadaoCpfVerificationBundle/Controller/" + type: annotation + prefix: / diff --git a/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Resources/config/services.yml b/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Resources/config/services.yml new file mode 100644 index 000000000..8287f32b5 --- /dev/null +++ b/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Resources/config/services.yml @@ -0,0 +1,2 @@ +services: + diff --git a/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Service/CpfVerificationService.php b/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Service/CpfVerificationService.php new file mode 100644 index 000000000..2fb8cf019 --- /dev/null +++ b/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Service/CpfVerificationService.php @@ -0,0 +1,147 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PROCERGS\LoginCidadao\CpfVerificationBundle\Service; + +use GuzzleHttp\Client; +use PROCERGS\LoginCidadao\CpfVerificationBundle\Exception\CpfNotSubscribedToNfgException; +use PROCERGS\LoginCidadao\CpfVerificationBundle\Model\ChallengeInterface; +use PROCERGS\LoginCidadao\CpfVerificationBundle\Model\SelectMotherInitialsChallenge; +use PROCERGS\LoginCidadao\CpfVerificationBundle\Model\TypeBirthdayChallenge; +use PROCERGS\LoginCidadao\CpfVerificationBundle\Model\TypeMotherInitialsChallenge; +use PROCERGS\LoginCidadao\CpfVerificationBundle\Model\TypePostalCodeChallenge; +use PROCERGS\LoginCidadao\CpfVerificationBundle\Model\TypeVoterRegistrationChallenge; +use PROCERGS\LoginCidadao\CpfVerificationBundle\Parser\ChallengeParser; +use Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException; + +class CpfVerificationService +{ + /** @var Client */ + private $client; + + /** + * CpfVerificationService constructor. + * @param Client $client + */ + public function __construct(Client $client) + { + $this->client = $client; + } + + /** + * @param string $cpf + * @return ChallengeInterface[] + * @throws CpfNotSubscribedToNfgException + */ + public function listAvailableChallenges(string $cpf): array + { + $challenges = $this->getAvailableChallengesFromApi($cpf); + + return $challenges; + + return [ + new TypeMotherInitialsChallenge(1, $cpf), + new SelectMotherInitialsChallenge(2, $cpf, ['ABC', 'DEF', 'XYZ']), + new TypePostalCodeChallenge(3, $cpf), + new TypeBirthdayChallenge(4, $cpf), + new TypeVoterRegistrationChallenge(5, $cpf), + ]; + } + + /** + * @param ChallengeInterface $challenge + * @return ChallengeInterface + * @throws CpfNotSubscribedToNfgException + */ + public function selectChallenge(ChallengeInterface $challenge): ChallengeInterface + { + return $this->selectChallengeFromApi($challenge); + + return $challenge; + } + + /** + * @param ChallengeInterface $challenge + * @param $answer + * @return bool + * @throws CpfNotSubscribedToNfgException + */ + public function answerChallenge(ChallengeInterface $challenge, $answer): bool + { + // TODO: implement actual method + + return true; + } + + private function getAvailableChallengesFromApi(string $cpf): array + { + $response = $this->client->get("cpf/{$cpf}/challenges"); + $statusCode = $response->getStatusCode(); + $body = (string)$response->getBody(); + if ($statusCode === 200) { + return $this->parseChallengesList($body); + } + + if ($statusCode === 403) { + $response = json_decode($body); + if ($response['error'] === CpfNotSubscribedToNfgException::ERROR_CODE) { + throw new CpfNotSubscribedToNfgException($cpf, $response['message'] ?? null); + } + } + + if ($statusCode === 429) { + throw new TooManyRequestsHttpException(); + } + + throw new \LogicException("Invalid response code {$statusCode} with body {$body}"); + } + + private function selectChallengeFromApi(ChallengeInterface $challenge): ChallengeInterface + { + $response = $this->client->get("cpf/{$challenge->getCpf()}/challenges/{$challenge->getName()}"); + $statusCode = $response->getStatusCode(); + $body = (string)$response->getBody(); + + if ($statusCode === 200) { + return ChallengeParser::parseJson((string)$response->getBody()); + } + + if ($statusCode === 403) { + $response = json_decode($body); + if ($response['error'] === CpfNotSubscribedToNfgException::ERROR_CODE) { + throw new CpfNotSubscribedToNfgException($challenge->getCpf(), $response['message'] ?? null); + } + } + + if ($statusCode === 429) { + throw new TooManyRequestsHttpException(); + } + + throw new \LogicException("Invalid response code {$statusCode} with body {$body}"); + } + + /** + * @param string $json + * @return ChallengeInterface[] + */ + private function parseChallengesList(string $json): array + { + $response = json_decode($json, true); + $challenges = []; + if (array_key_exists('challenges', $response)) { + $cpf = $response['cpf']; + foreach ($response['challenges'] as $challenge) { + $challenges[] = ChallengeParser::parseArray($challenge, $cpf); + } + } + + return $challenges; + } +} diff --git a/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Tests/Exception/CpfNotSubscribedToNfgExceptionTest.php b/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Tests/Exception/CpfNotSubscribedToNfgExceptionTest.php new file mode 100644 index 000000000..eedb1b7b1 --- /dev/null +++ b/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Tests/Exception/CpfNotSubscribedToNfgExceptionTest.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PROCERGS\LoginCidadao\CpfVerificationBundle\Tests\Exception; + +use PHPUnit\Framework\TestCase; +use PROCERGS\LoginCidadao\CpfVerificationBundle\Exception\CpfNotSubscribedToNfgException; + +class CpfNotSubscribedToNfgExceptionTest extends TestCase +{ + public function testException() + { + $e = new CpfNotSubscribedToNfgException($cpf = '12345678901'); + $this->assertSame($cpf, $e->getCpf()); + } +} diff --git a/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Tests/Exception/WrongAnswerExceptionTest.php b/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Tests/Exception/WrongAnswerExceptionTest.php new file mode 100644 index 000000000..21d2c3644 --- /dev/null +++ b/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Tests/Exception/WrongAnswerExceptionTest.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PROCERGS\LoginCidadao\CpfVerificationBundle\Tests\Exception; + +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use PROCERGS\LoginCidadao\CpfVerificationBundle\Exception\WrongAnswerException; +use PROCERGS\LoginCidadao\CpfVerificationBundle\Model\ChallengeInterface; + +class WrongAnswerExceptionTest extends TestCase +{ + public function testException() + { + /** @var ChallengeInterface|MockObject $challenge */ + $challenge = $this->createMock(ChallengeInterface::class); + + $e = new WrongAnswerException($challenge); + $this->assertSame($challenge, $e->getChallenge()); + } +} diff --git a/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Tests/Model/ChallengeFactoryTest.php b/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Tests/Model/ChallengeFactoryTest.php new file mode 100644 index 000000000..779a6eb8a --- /dev/null +++ b/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Tests/Model/ChallengeFactoryTest.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PROCERGS\LoginCidadao\CpfVerificationBundle\Tests\Model; + +use PHPUnit\Framework\TestCase; +use PROCERGS\LoginCidadao\CpfVerificationBundle\Model\ChallengeFactory; +use PROCERGS\LoginCidadao\CpfVerificationBundle\Model\SelectMotherInitialsChallenge; +use PROCERGS\LoginCidadao\CpfVerificationBundle\Model\TypeBirthdayChallenge; +use PROCERGS\LoginCidadao\CpfVerificationBundle\Model\TypeMotherInitialsChallenge; +use PROCERGS\LoginCidadao\CpfVerificationBundle\Model\TypePostalCodeChallenge; +use PROCERGS\LoginCidadao\CpfVerificationBundle\Model\TypeVoterRegistrationChallenge; + +class ChallengeFactoryTest extends TestCase +{ + public function testSelectMotherInitialsChallenge() + { + $class = SelectMotherInitialsChallenge::class; + $this->assertInstanceOf($class, ChallengeFactory::create($class::CHALLENGE_NAME, 1, 'cpf')); + } + + public function testTypeMotherInitialsChallenge() + { + $class = TypeMotherInitialsChallenge::class; + $this->assertInstanceOf($class, ChallengeFactory::create($class::CHALLENGE_NAME, 1, 'cpf')); + } + + public function testTypeBirthdayChallenge() + { + $class = TypeBirthdayChallenge::class; + $this->assertInstanceOf($class, ChallengeFactory::create($class::CHALLENGE_NAME, 1, 'cpf')); + } + + public function testTypePostalCodeChallenge() + { + $class = TypePostalCodeChallenge::class; + $this->assertInstanceOf($class, ChallengeFactory::create($class::CHALLENGE_NAME, 1, 'cpf')); + } + + public function testTypeVoterRegistrationChallenge() + { + $class = TypeVoterRegistrationChallenge::class; + $this->assertInstanceOf($class, ChallengeFactory::create($class::CHALLENGE_NAME, 1, 'cpf')); + } + + public function testUnsupportedChallenge() + { + $this->expectException(\InvalidArgumentException::class); + + ChallengeFactory::create('unsupported_challenge_here', 1, 'cpf'); + } +} diff --git a/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Tests/Model/ChallengesTest.php b/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Tests/Model/ChallengesTest.php new file mode 100644 index 000000000..c613a528a --- /dev/null +++ b/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Tests/Model/ChallengesTest.php @@ -0,0 +1,71 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PROCERGS\LoginCidadao\CpfVerificationBundle\Tests\Model; + +use PHPUnit\Framework\TestCase; +use PROCERGS\LoginCidadao\CpfVerificationBundle\Model\SelectMotherInitialsChallenge; +use PROCERGS\LoginCidadao\CpfVerificationBundle\Model\TypeBirthdayChallenge; +use PROCERGS\LoginCidadao\CpfVerificationBundle\Model\TypeMotherInitialsChallenge; +use PROCERGS\LoginCidadao\CpfVerificationBundle\Model\TypePostalCodeChallenge; +use PROCERGS\LoginCidadao\CpfVerificationBundle\Model\TypeVoterRegistrationChallenge; + +class ChallengesTest extends TestCase +{ + public function testSelectMotherInitialsChallenge() + { + $challenge = new SelectMotherInitialsChallenge( + $attempts = 3, + $cpf = '12345678901', + $choices = ['ABC', 'DEF'] + ); + + $this->assertSame('select_mother_initials', $challenge->getName()); + $this->assertSame($attempts, $challenge->getAttemptsLeft()); + $this->assertSame($cpf, $challenge->getCpf()); + $this->assertSame($choices, $challenge->getChoices()); + } + + public function testTypeBirthdayChallenge() + { + $challenge = new TypeBirthdayChallenge($attempts = 3, $cpf = '12345678901'); + + $this->assertSame('type_birthday', $challenge->getName()); + $this->assertSame($attempts, $challenge->getAttemptsLeft()); + $this->assertSame($cpf, $challenge->getCpf()); + } + + public function testTypeMotherInitialsChallenge() + { + $challenge = new TypeMotherInitialsChallenge($attempts = 3, $cpf = '12345678901'); + + $this->assertSame('type_mother_initials', $challenge->getName()); + $this->assertSame($attempts, $challenge->getAttemptsLeft()); + $this->assertSame($cpf, $challenge->getCpf()); + } + + public function testTypePostalCodeChallenge() + { + $challenge = new TypePostalCodeChallenge($attempts = 3, $cpf = '12345678901'); + + $this->assertSame('type_postal_code', $challenge->getName()); + $this->assertSame($attempts, $challenge->getAttemptsLeft()); + $this->assertSame($cpf, $challenge->getCpf()); + } + + public function testTypeVoterRegistrationChallenge() + { + $challenge = new TypeVoterRegistrationChallenge($attempts = 3, $cpf = '12345678901'); + + $this->assertSame('type_voter_registration', $challenge->getName()); + $this->assertSame($attempts, $challenge->getAttemptsLeft()); + $this->assertSame($cpf, $challenge->getCpf()); + } +} diff --git a/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Tests/Parser/ChallengeParserTest.php b/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Tests/Parser/ChallengeParserTest.php new file mode 100644 index 000000000..a48e5788d --- /dev/null +++ b/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Tests/Parser/ChallengeParserTest.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PROCERGS\LoginCidadao\CpfVerificationBundle\Tests\Parser; + +use PHPUnit\Framework\TestCase; +use PROCERGS\LoginCidadao\CpfVerificationBundle\Model\SelectMotherInitialsChallenge; +use PROCERGS\LoginCidadao\CpfVerificationBundle\Model\TypeMotherInitialsChallenge; +use PROCERGS\LoginCidadao\CpfVerificationBundle\Model\TypeVoterRegistrationChallenge; +use PROCERGS\LoginCidadao\CpfVerificationBundle\Parser\ChallengeParser; + +class ChallengeParserTest extends TestCase +{ + public function testParseWithChoices() + { + $json = json_encode([ + 'challenge' => $name = 'select_mother_initials', + 'cpf' => $cpf = '12345678901', + 'choices' => $choices = [], + 'attempts_left' => $attemptsLeft = 3, + ]); + + /** @var SelectMotherInitialsChallenge $challenge */ + $challenge = ChallengeParser::parseJson($json); + + $this->assertInstanceOf(SelectMotherInitialsChallenge::class, $challenge); + $this->assertSame($name, $challenge->getName()); + $this->assertSame($cpf, $challenge->getCpf()); + $this->assertSame($choices, $challenge->getChoices()); + $this->assertSame($attemptsLeft, $challenge->getAttemptsLeft()); + } + + public function testParseWithoutChoices() + { + $json = json_encode([ + 'challenge' => $name = 'type_mother_initials', + 'cpf' => $cpf = '12345678901', + 'attempts_left' => $attemptsLeft = 3, + ]); + + $challenge = ChallengeParser::parseJson($json); + + $this->assertInstanceOf(TypeMotherInitialsChallenge::class, $challenge); + $this->assertSame($name, $challenge->getName()); + $this->assertSame($cpf, $challenge->getCpf()); + $this->assertSame($attemptsLeft, $challenge->getAttemptsLeft()); + } + + public function testParseInvalidJson() + { + $this->expectException(\RuntimeException::class); + + ChallengeParser::parseJson('{"invalid":true}'); + } + + public function testMissingAttemptsLeft() + { + $this->expectException(\RuntimeException::class); + + ChallengeParser::parseJson('{"challenge":"dummy"}'); + } + + public function testMissingCpf() + { + $json = json_encode([ + 'challenge' => $name = TypeVoterRegistrationChallenge::CHALLENGE_NAME, + 'attempts_left' => $attemptsLeft = 3, + ]); + + $challenge = ChallengeParser::parseJson($json, $cpf = '12345678901'); + + $this->assertInstanceOf(TypeVoterRegistrationChallenge::class, $challenge); + $this->assertSame($name, $challenge->getName()); + $this->assertSame($cpf, $challenge->getCpf()); + $this->assertSame($attemptsLeft, $challenge->getAttemptsLeft()); + } +} diff --git a/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Tests/Service/CpfVerificationServiceTest.php b/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Tests/Service/CpfVerificationServiceTest.php new file mode 100644 index 000000000..3b63c9a5e --- /dev/null +++ b/src/PROCERGS/LoginCidadao/CpfVerificationBundle/Tests/Service/CpfVerificationServiceTest.php @@ -0,0 +1,82 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace PROCERGS\LoginCidadao\CpfVerificationBundle\Tests\Service; + +use GuzzleHttp\Client; +use GuzzleHttp\Handler\MockHandler; +use GuzzleHttp\HandlerStack; +use GuzzleHttp\Psr7\Response; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use PROCERGS\LoginCidadao\CpfVerificationBundle\Model\ChallengeInterface; +use PROCERGS\LoginCidadao\CpfVerificationBundle\Model\TypeMotherInitialsChallenge; +use PROCERGS\LoginCidadao\CpfVerificationBundle\Service\CpfVerificationService; + +class CpfVerificationServiceTest extends TestCase +{ + public function testListAvailableChallenges() + { + $cpf = '12345678901'; + $response = json_encode([ + 'cpf' => $cpf, + 'challenges' => [ + ['challenge' => 'type_mother_initials', 'attempts_left' => 3], + ['challenge' => 'select_mother_initials', 'attempts_left' => 2], + ], + ]); + $client = $this->getHttpClient([ + new Response(200, [], $response), + ]); + $service = new CpfVerificationService($client); + $challenges = $service->listAvailableChallenges($cpf); + + foreach ($challenges as $challenge) { + $this->assertInstanceOf(ChallengeInterface::class, $challenge); + $this->assertSame($cpf, $challenge->getCpf()); + } + } + + public function testSelectChallenge() + { + $cpf = '12345678901'; + $response = json_encode(['challenge' => 'type_mother_initials', 'attempts_left' => 3, 'cpf' => $cpf]); + $client = $this->getHttpClient([ + new Response(200, [], $response), + ]); + + /** @var ChallengeInterface|MockObject $challenge */ + $challenge = $this->createMock(ChallengeInterface::class); + + $service = new CpfVerificationService($client); + $this->assertInstanceOf(TypeMotherInitialsChallenge::class, $service->selectChallenge($challenge)); + } + + public function testAnswerChallenge() + { + /** @var ChallengeInterface|MockObject $challenge */ + $challenge = $this->createMock(ChallengeInterface::class); + + $service = new CpfVerificationService($this->getHttpClient([new Response(204)])); + $this->assertTrue($service->answerChallenge($challenge, 'answer')); + } + + /** + * @param array $responses + * @return Client + */ + private function getHttpClient(array $responses = []): Client + { + $mock = new MockHandler($responses); + $handler = HandlerStack::create($mock); + + return new Client(['handler' => $handler]); + } +}