feat: cli command to anonymize local database
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Command;
|
||||
|
||||
use App\Command\DbAnonymizeCommand;
|
||||
use App\Service\DatabaseAnonymizer;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Tester\CommandTester;
|
||||
|
||||
class DbAnonymizeCommandTest extends TestCase
|
||||
{
|
||||
public function testCommandRefusesToRunInProdEnvironment(): void
|
||||
{
|
||||
$anonymizer = $this->createMock(DatabaseAnonymizer::class);
|
||||
$anonymizer->expects(self::never())->method('anonymizeAll');
|
||||
|
||||
$tester = new CommandTester($this->createCommand($anonymizer, 'prod'));
|
||||
$tester->execute([]);
|
||||
|
||||
self::assertSame(Command::FAILURE, $tester->getStatusCode());
|
||||
self::assertStringContainsString('disabled in the prod environment', $tester->getDisplay());
|
||||
}
|
||||
|
||||
public function testCommandDelegatesOutsideProdEnvironment(): void
|
||||
{
|
||||
$anonymizer = $this->createMock(DatabaseAnonymizer::class);
|
||||
$anonymizer->expects(self::once())
|
||||
->method('anonymizeAll')
|
||||
->willReturn([
|
||||
'users' => 1,
|
||||
'newsletterConsents' => 2,
|
||||
'newsletterOptInRequests' => 3,
|
||||
'bookingEditDrafts' => 4,
|
||||
]);
|
||||
|
||||
$tester = new CommandTester($this->createCommand($anonymizer, 'dev'));
|
||||
$tester->execute([]);
|
||||
|
||||
self::assertSame(Command::SUCCESS, $tester->getStatusCode());
|
||||
self::assertStringContainsString('Anonymized 1 users, 2 newsletter consents, 3 newsletter opt-in requests, and 4 booking drafts.', $tester->getDisplay());
|
||||
}
|
||||
|
||||
private function createCommand(DatabaseAnonymizer $anonymizer, string $environment): DbAnonymizeCommand
|
||||
{
|
||||
return new DbAnonymizeCommand(
|
||||
$anonymizer,
|
||||
$this->createMock(LoggerInterface::class),
|
||||
$environment,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Service;
|
||||
|
||||
use App\Entity\BookingEditDraft;
|
||||
use App\Entity\NewsletterConsent;
|
||||
use App\Entity\NewsletterOptInRequest;
|
||||
use App\Entity\User;
|
||||
use App\Service\DatabaseAnonymizer;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Log\NullLogger;
|
||||
|
||||
class DatabaseAnonymizerTest extends TestCase
|
||||
{
|
||||
public function testAnonymizeEntityMethodsReuseSharedIdentityAndPreserveDraftTimestamps(): void
|
||||
{
|
||||
$service = new DatabaseAnonymizer(
|
||||
$this->createMock(EntityManagerInterface::class),
|
||||
new NullLogger(),
|
||||
);
|
||||
|
||||
$user = new User('[email protected]');
|
||||
$user->setPassword('plain-text-password');
|
||||
$user->setPersonId(12345);
|
||||
$user->setAddressId(67890);
|
||||
$user->setRoles(['ROLE_ADMIN']);
|
||||
$user->setHotelCodes(['ABC']);
|
||||
$lastLoginAt = new \DateTimeImmutable('2026-06-01 10:00:00');
|
||||
$user->setLastLoginAt($lastLoginAt);
|
||||
$user->setProfileComplete(true);
|
||||
|
||||
$consent = new NewsletterConsent('[email protected]', 42, 'Mia', 'Muster');
|
||||
$consent->markConfirmed(new \DateTimeImmutable('2026-06-02 11:00:00'));
|
||||
|
||||
$request = new NewsletterOptInRequest(
|
||||
'[email protected]',
|
||||
str_repeat('a', 64),
|
||||
new \DateTimeImmutable('2026-07-01 12:00:00'),
|
||||
[11, 22],
|
||||
'Mia',
|
||||
'Muster',
|
||||
);
|
||||
$request->markConfirmed(new \DateTimeImmutable('2026-06-03 13:00:00'));
|
||||
|
||||
$draft = new BookingEditDraft($user, 999, new \DateTimeImmutable('2026-08-15'), [
|
||||
'paymentMethod' => 'debit',
|
||||
'bankAccount' => [
|
||||
'iban' => 'DE02123412341234123412',
|
||||
'accountHolder' => 'Mia Muster',
|
||||
'bankName' => 'Example Bank',
|
||||
'sepaMandateAccepted' => true,
|
||||
],
|
||||
'participants' => [
|
||||
0 => [
|
||||
'personalData' => [
|
||||
'firstName' => 'Mia',
|
||||
'lastName' => 'Muster',
|
||||
'dateOfBirth' => '1990-05-15',
|
||||
'email' => '[email protected]',
|
||||
'mobile' => '+49123456789',
|
||||
'gender' => 'W',
|
||||
'nationality' => 'DE',
|
||||
],
|
||||
'address' => [
|
||||
'street' => 'Main Street 1',
|
||||
'postCode' => '12345',
|
||||
'city' => 'Berlin',
|
||||
'country' => 'DE',
|
||||
'district' => 'Mitte',
|
||||
],
|
||||
'bodyDimensions' => [
|
||||
'height' => '180',
|
||||
'weight' => '70',
|
||||
'shoeSize' => '42',
|
||||
],
|
||||
'roomAssignment' => [
|
||||
'assignedRoomId' => 7,
|
||||
'remarksRoom' => 'Window please',
|
||||
],
|
||||
'licensePlate' => 'B-AB-1234',
|
||||
'services' => [
|
||||
'skiPass' => 10,
|
||||
'courses' => [11],
|
||||
'board' => [12],
|
||||
'rentals' => [13],
|
||||
'rentalInsurance' => 14,
|
||||
'additionalServices' => [15],
|
||||
'transportationOutbound' => 16,
|
||||
'transportationInbound' => 17,
|
||||
'pickup' => 18,
|
||||
'dropOff' => 19,
|
||||
'parking' => true,
|
||||
'insurance' => 20,
|
||||
'bulkInsuranceBooking' => false,
|
||||
],
|
||||
'vouchers' => [
|
||||
'purchaseVoucherCode' => 'PURCHASE-123',
|
||||
'promoVoucherCode' => 'PROMO-456',
|
||||
],
|
||||
],
|
||||
1 => [
|
||||
'personalData' => [
|
||||
'firstName' => 'Mia',
|
||||
'lastName' => 'Muster',
|
||||
'dateOfBirth' => '1990-05-15',
|
||||
'email' => '[email protected]',
|
||||
'mobile' => '+49123456789',
|
||||
'gender' => 'W',
|
||||
'nationality' => 'DE',
|
||||
],
|
||||
'address' => [
|
||||
'street' => 'Main Street 1',
|
||||
'postCode' => '12345',
|
||||
'city' => 'Berlin',
|
||||
'country' => 'DE',
|
||||
],
|
||||
'services' => [
|
||||
'additionalServices' => [99],
|
||||
],
|
||||
],
|
||||
],
|
||||
]);
|
||||
$draft->setBookingNumber(321);
|
||||
$draft->setDateId(55);
|
||||
$draft->setHotelId(77);
|
||||
|
||||
$originalConsentCreatedAt = $consent->getCreatedAt();
|
||||
$originalConsentUpdatedAt = $consent->getUpdatedAt();
|
||||
$originalRequestCreatedAt = $request->getCreatedAt();
|
||||
$originalDraftCreatedAt = $draft->getCreatedAt();
|
||||
$originalDraftUpdatedAt = $draft->getUpdatedAt();
|
||||
|
||||
$service->anonymizeUser($user);
|
||||
$service->anonymizeNewsletterConsent($consent);
|
||||
$service->anonymizeNewsletterOptInRequest($request);
|
||||
$service->anonymizeBookingEditDraft($draft);
|
||||
|
||||
self::assertStringEndsWith('@example.test', $user->getEmail());
|
||||
self::assertSame('plain-text-password', $user->getPassword());
|
||||
self::assertSame(12345, $user->getPersonId());
|
||||
self::assertSame(67890, $user->getAddressId());
|
||||
self::assertSame(['ROLE_USER', 'ROLE_ADMIN'], $user->getRoles());
|
||||
self::assertSame(['ABC'], $user->getHotelCodes());
|
||||
self::assertSame($lastLoginAt, $user->getLastLoginAt());
|
||||
self::assertTrue($user->isProfileComplete());
|
||||
|
||||
self::assertSame($user->getEmail(), $consent->getEmail());
|
||||
self::assertSame($consent->getEmail(), $request->getEmail());
|
||||
self::assertSame($consent->getFirstName(), $request->getFirstName());
|
||||
self::assertSame($consent->getLastName(), $request->getLastName());
|
||||
self::assertSame(42, $consent->getMailjetListId());
|
||||
self::assertSame([11, 22], $request->getMailjetListIds());
|
||||
self::assertSame(str_repeat('a', 64), $request->getTokenHash());
|
||||
self::assertTrue($consent->isConfirmed());
|
||||
self::assertTrue($request->isConfirmed());
|
||||
self::assertSame($originalConsentCreatedAt, $consent->getCreatedAt());
|
||||
self::assertSame($originalConsentUpdatedAt, $consent->getUpdatedAt());
|
||||
self::assertSame($originalRequestCreatedAt, $request->getCreatedAt());
|
||||
|
||||
$formData = $draft->getFormData();
|
||||
self::assertSame('debit', $formData['paymentMethod']);
|
||||
self::assertNotSame('DE02123412341234123412', $formData['bankAccount']['iban']);
|
||||
self::assertSame(
|
||||
$formData['participants'][0]['personalData']['firstName'].' '.$formData['participants'][0]['personalData']['lastName'],
|
||||
$formData['bankAccount']['accountHolder'],
|
||||
);
|
||||
self::assertNotSame('Example Bank', $formData['bankAccount']['bankName']);
|
||||
self::assertSame('DE', $formData['participants'][0]['personalData']['nationality']);
|
||||
self::assertSame('W', $formData['participants'][0]['personalData']['gender']);
|
||||
self::assertSame('1990-05-15', $formData['participants'][0]['personalData']['dateOfBirth']);
|
||||
self::assertSame($formData['participants'][0]['personalData']['email'], $formData['participants'][1]['personalData']['email']);
|
||||
self::assertSame($formData['participants'][0]['personalData']['firstName'], $formData['participants'][1]['personalData']['firstName']);
|
||||
self::assertSame($formData['participants'][0]['personalData']['lastName'], $formData['participants'][1]['personalData']['lastName']);
|
||||
self::assertSame($formData['participants'][0]['address']['street'], $formData['participants'][1]['address']['street']);
|
||||
self::assertSame($formData['participants'][0]['address']['postCode'], $formData['participants'][1]['address']['postCode']);
|
||||
self::assertSame($formData['participants'][0]['address']['city'], $formData['participants'][1]['address']['city']);
|
||||
self::assertSame($formData['participants'][0]['address']['country'], $formData['participants'][1]['address']['country']);
|
||||
self::assertSame(7, $formData['participants'][0]['roomAssignment']['assignedRoomId']);
|
||||
self::assertSame('PURCHASE-123', $formData['participants'][0]['vouchers']['purchaseVoucherCode']);
|
||||
self::assertSame('PROMO-456', $formData['participants'][0]['vouchers']['promoVoucherCode']);
|
||||
self::assertSame(321, $draft->getBookingNumber());
|
||||
self::assertSame(55, $draft->getDateId());
|
||||
self::assertSame(77, $draft->getHotelId());
|
||||
self::assertSame($originalDraftCreatedAt, $draft->getCreatedAt());
|
||||
self::assertSame($originalDraftUpdatedAt, $draft->getUpdatedAt());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user