Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/faker #2

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
5 changes: 5 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,15 @@
"type": "project",
"license": "MIT",
"minimum-stability": "stable",
"scripts": {
"test": "vendor/bin/phpspec run",
"qual": "vendor/bin/grumphp run"
},
"require": {
"php": "^7.3||^8.0",
"ext-ctype": "*",
"ext-iconv": "*",
"fakerphp/faker": "^1.20",
"symfony/validator": "^4.4||^5.3"
},
"require-dev": {
Expand Down
3 changes: 3 additions & 0 deletions grumphp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ grumphp:
ascii:
failed:
succeeded:
fixer:
enabled: true
fix_by_default: true
tasks:
phpcsfixer:
config: '.php-cs-fixer.dist.php'
Expand Down
1 change: 1 addition & 0 deletions pmd-ruleset.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

<rule ref="rulesets/cleancode.xml">
<exclude name="BooleanArgumentFlag"/>
<exclude name="StaticAccess" />
</rule>

<rule ref="rulesets/codesize.xml"/>
Expand Down
6 changes: 6 additions & 0 deletions psalm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
throwExceptionOnError="true"
>
<projectFiles>
<directory name="src" />
Expand All @@ -27,16 +28,21 @@
<file name="src/Constraints/Mig.php" />
<file name="src/Constraints/Nir.php" />
<file name="src/Constraints/Nnp.php" />
<file name="src/Faker/AssureProvider.php" />
<file name="src/Faker/NnpProvider.php" />
</errorLevel>
</PropertyNotSetInConstructor>
<MixedArgumentTypeCoercion>
<errorLevel type="suppress">
<file name="src/Constraints/Nir.php" />
<file name="src/Faker/AssureProvider.php" />
</errorLevel>
</MixedArgumentTypeCoercion>
<MissingParamType>
<errorLevel type="suppress">
<file name="src/Constraints/Nir.php" />
<file name="src/Faker/AssureProvider.php" />
<file name="src/Faker/NnpProvider.php" />
</errorLevel>
</MissingParamType>
</issueHandlers>
Expand Down
23 changes: 11 additions & 12 deletions src/Constraints/NirValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@

class NirValidator extends ConstraintValidator
{
public const NIR_REGEX = '/\A' .
'(?<sexe>[12])' .
'(?<anneeNaissance>\d{2})' .
'(?<moisNaissance>1[0-2]|0[1-9])' .
'(?<departementNaissance>\d{2}|2A|2B)' .
'(?<communeNaissance>\d{3})' .
'(?<rangInscription>\d{3})' .
'(?<cle>9[0-7]|[0-8]\d)?' .
'\z/i';

public function validate($value, Constraint $constraint): void
{
if (!$constraint instanceof Nir) {
Expand Down Expand Up @@ -63,18 +73,7 @@ public function validate($value, Constraint $constraint): void
*/
private function check(string $nir): bool
{
return (bool) preg_match(
'/\A' .
'(?<sexe>[12])' .
'(?<anneeNaissance>\d{2})' .
'(?<moisNaissance>1[0-2]|0[1-9])' .
'(?<departementNaissance>\d{2}|2A|2B)' .
'(?<communeNaissance>\d{3})' .
'(?<rangInscription>\d{3})' .
'(?<cle>9[0-7]|[0-8]\d)?' .
'\z/i',
$nir
);
return (bool) preg_match(self::NIR_REGEX, $nir);
}

private function isNirKeyValid(string $nir, NirKeyInterface $nirKey): bool
Expand Down
91 changes: 91 additions & 0 deletions src/Faker/AssureProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<?php

declare(strict_types=1);

namespace Cnamts\Nir\Faker;

use Faker\Provider\fr_FR\Address;
use Faker\Provider\fr_FR\Person;

class AssureProvider extends Person
{
/* @phpstan-ignore-next-line */
public function nir($gender = null, $formatted = false, \DateTime $birthday = null, string $departmentCode = null): string

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pourquoi ne pas typer gender et formatted ?

{
$departementPart = $this->getDepartmentPart($departmentCode);
$nir = $this->getGenderPart($gender)
. $this->getDateBirthPart($birthday)
. $departementPart
. $this->numerify('###');

$nir .= $this->getKeyPart($nir, $departementPart);
// Format is x xx xx xx xxx xxx xx
if ($formatted) {
$nir = mb_substr($nir, 0, 1) . ' ' . mb_substr($nir, 1, 2) . ' ' . mb_substr($nir, 3, 2) . ' ' . mb_substr($nir, 5, 2) . ' ' . mb_substr($nir, 7, 3) . ' ' . mb_substr($nir, 10, 3) . ' ' . mb_substr($nir, 13, 2);
}

return $nir;
}

/* @phpstan-ignore-next-line */
private function getGenderPart($gender): int
{
// Gender
if ($gender === Person::GENDER_MALE) {
return 1;
}
if ($gender === Person::GENDER_FEMALE) {
return 2;
}

return $this->numberBetween(1, 2);
}

private function getDateBirthPart(?\DateTime $birthday): string
{
if ($birthday === null) {
return
// Year of birth (aa)
$this->numerify('##') .
// Month of birth (mm)
sprintf('%02d', $this->numberBetween(1, 12));
}

return $birthday->format('ym');
}

private function getDepartmentPart(?string $departmentCode): string
{
// Department
$department = $departmentCode ?? (string) key(Address::department());

// Town number, depends on department length
if (mb_strlen($department) === 2) {
$department .= $this->numerify('###');
} elseif (mb_strlen($department) === 3) {
$department .= $this->numerify('##');
}

return $department;
}

private function getKeyPart(string $nir, string $department): string
{
/**
* The key for a given NIR is `97 - 97 % NIR`
* NIR has to be an integer, so we have to do a little replacment
* for departments 2A and 2B
*/
$nirInteger = $nir;
switch ($department) {
case '2A':
$nirInteger = str_replace('2A', '19', $nir);
break;
case '2B':
$nirInteger = str_replace('2B', '18', $nir);
break;
}

return sprintf('%02d', 97 - (int) $nirInteger % 97);
}
}
36 changes: 36 additions & 0 deletions src/Faker/NnpProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

declare(strict_types=1);

namespace Cnamts\Nir\Faker;

use Faker\Provider\Base;
use Faker\Provider\fr_FR\Person;

class NnpProvider extends Base
{
public function nnp(string $gender = null): string
{
return $this->getGenderPart($gender)
// le second chiffre (2ème composant) est la vérification du type de création (1 pour création et 2 pour numéros fictifs générés par le système RFI à ne pas prendre en compte)
. $this->numberBetween(1, 2)
// le 3ème composant est un groupe de trois chiffres correspondant au numéro de caisse
. $this->numerify('###')
// le 4ème composant correspond au numéro d'ordre par caisse
. $this->numerify('########');
}

/* @phpstan-ignore-next-line */
private function getGenderPart($gender): int
{
// Gender
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Du coup même remarque sur le fait de stocker chaque valeur dans une variable et concaténer à la fin

Copy link
Author

@christopheGaon christopheGaon Aug 5, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

donc tu preferes que je stocke les parts dans des var ? car la je renvois direct la concatenation

if ($gender === Person::GENDER_MALE) {
return 7;
}
if ($gender === Person::GENDER_FEMALE) {
return 8;
}

return $this->numberBetween(7, 8);
}
}
2 changes: 1 addition & 1 deletion tests/Spec/Cnamts/Nir/Constraints/MigValidatorSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public function it_does_not_accept_wrong_mig(
public function it_expects_constraint_compatible_type()
{
$this->shouldThrow(UnexpectedTypeException::class)
->during('validate', ['', (new class() extends Constraint {})])
->during('validate', ['', new class() extends Constraint {}])
;
}
}
2 changes: 1 addition & 1 deletion tests/Spec/Cnamts/Nir/Constraints/NirValidatorSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public function it_does_not_accept_wrong_key_of_nir(
public function it_expects_constraint_compatible_type()
{
$this->shouldThrow(UnexpectedTypeException::class)
->during('validate', ['', (new class() extends Constraint {})])
->during('validate', ['', new class() extends Constraint {}])
;
}
}
2 changes: 1 addition & 1 deletion tests/Spec/Cnamts/Nir/Constraints/NnpValidatorSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public function it_does_not_accept_wrong_nnp(
public function it_expects_constraint_compatible_type()
{
$this->shouldThrow(UnexpectedTypeException::class)
->during('validate', ['', (new class() extends Constraint {})])
->during('validate', ['', new class() extends Constraint {}])
;
}
}
78 changes: 78 additions & 0 deletions tests/Spec/Cnamts/Nir/Faker/AssureProviderSpec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<?php

declare(strict_types=1);

namespace Spec\Cnamts\Nir\Faker;

use Cnamts\Nir\Constraints\Nir;
use Cnamts\Nir\Constraints\NirValidator;
use Faker\Factory;
use Faker\Generator;
use Faker\Provider\Person;
use PhpSpec\ObjectBehavior;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Component\Validator\Violation\ConstraintViolationBuilderInterface;

class AssureProviderSpec extends ObjectBehavior
{
/** @var Generator */
private $faker;

public function let(Generator $generator)
{
$this->faker = Factory::create('fr_FR');
$this->beConstructedWith($generator);
}

public function it_generates_valid_nir(
ExecutionContextInterface $context,
ConstraintViolationBuilderInterface $constraintViolationBuilder
) {
$constraintViolationBuilder->addViolation()->shouldNotBeCalled();
$this->validateNir($this->nir(), $context);
}

public function it_generates_different_nir_on_call()
{
$nir = $this->nir();
$nirNext = $this->nir();
$nir->shouldNotEqual($nirNext);
}

public function it_generates_nir_from_params(
ExecutionContextInterface $context,
ConstraintViolationBuilderInterface $constraintViolationBuilder
) {
$gender = $this->faker->randomElement([Person::GENDER_MALE, Person::GENDER_FEMALE]);
$dateNaissance = $this->faker->dateTime();
$departement = $this->faker->departmentNumber();
$nir = $this->nir($gender, false, $dateNaissance, $departement);
$constraintViolationBuilder->addViolation()->shouldNotBeCalled();
$this->validateNir($nir, $context);

$nir->shouldbeNir($dateNaissance, $departement);
}

/** Valide un NIR généré à partir du validateur du package */
private function validateNir($nir, $context)
{
$constraint = new Nir();
$nirValidator = new NirValidator();
$nirValidator->initialize($context->getWrappedObject());
$nirValidator->validate($nir->getWrappedObject(), $constraint);
}

public function getMatchers(): array
{
return [
'beNir' => function (string $value, \DateTime $dateNaissance, string $departement) {
$isNirRegex = preg_match(NirValidator::NIR_REGEX, $value, $matches);
$isSameYear = $matches['anneeNaissance'] === $dateNaissance->format('y');
$isSameMonth = $matches['moisNaissance'] === $dateNaissance->format('m');
$isSameDepartment = mb_strpos($departement, $matches['departementNaissance']) === 0;

return $isNirRegex && $isSameYear && $isSameMonth && $isSameDepartment;
},
];
}
}
44 changes: 44 additions & 0 deletions tests/Spec/Cnamts/Nir/Faker/NnpProviderSpec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

declare(strict_types=1);

namespace Spec\Cnamts\Nir\Faker;

use Cnamts\Nir\Constraints\Nnp;
use Cnamts\Nir\Constraints\NnpValidator;
use Faker\Generator;
use PhpSpec\ObjectBehavior;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Component\Validator\Violation\ConstraintViolationBuilderInterface;

class NnpProviderSpec extends ObjectBehavior
{
public function let(Generator $generator)
{
$this->beConstructedWith($generator);
}

public function it_generates_valid_nnp(
ExecutionContextInterface $context,
ConstraintViolationBuilderInterface $constraintViolationBuilder
) {
$constraintViolationBuilder->addViolation()->shouldNotBeCalled();
$this->validateNnp($this->nnp(), $context);
}

public function it_generates_different_nir_on_call()
{
$nnp = $this->nnp();
$nnpNext = $this->nnp();
$nnp->shouldNotEqual($nnpNext);
}

/** Valide un NNP généré à partir du validateur du package */
private function validateNnp($nnp, $context)
{
$constraint = new Nnp();
$nnpValidator = new NnpValidator();
$nnpValidator->initialize($context->getWrappedObject());
$nnpValidator->validate($nnp->getWrappedObject(), $constraint);
}
}