diff --git a/config/DoctrineClassMapping/WMDE.Fundraising.DonationContext.DataAccess.DoctrineEntities.Donation.dcm.xml b/config/DoctrineClassMapping/WMDE.Fundraising.DonationContext.DataAccess.DoctrineEntities.Donation.dcm.xml index a6e7ef3c..3555fc9d 100644 --- a/config/DoctrineClassMapping/WMDE.Fundraising.DonationContext.DataAccess.DoctrineEntities.Donation.dcm.xml +++ b/config/DoctrineClassMapping/WMDE.Fundraising.DonationContext.DataAccess.DoctrineEntities.Donation.dcm.xml @@ -15,7 +15,7 @@ - + diff --git a/config/DoctrineClassMapping/WMDE.Fundraising.DonationContext.Domain.Model.DonationId.dcm.xml b/config/DoctrineClassMapping/WMDE.Fundraising.DonationContext.Domain.Model.DonationId.dcm.xml new file mode 100644 index 00000000..27527325 --- /dev/null +++ b/config/DoctrineClassMapping/WMDE.Fundraising.DonationContext.Domain.Model.DonationId.dcm.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 00267c32..d8e52ff5 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,35 +1,10 @@ parameters: ignoreErrors: - - - message: "#^Parameter \\#1 \\$donationId of method WMDE\\\\Fundraising\\\\DonationContext\\\\Domain\\\\Repositories\\\\CommentWithAmount\\:\\:setDonationId\\(\\) expects int, int\\|null given\\.$#" - count: 1 - path: src/DataAccess/DoctrineCommentFinder.php - - message: "#^Method WMDE\\\\Fundraising\\\\DonationContext\\\\DataAccess\\\\DoctrineDonationPrePersistSubscriber\\:\\:prePersist\\(\\) has parameter \\$args with generic class Doctrine\\\\Persistence\\\\Event\\\\LifecycleEventArgs but does not specify its types\\: TObjectManager$#" count: 1 path: src/DataAccess/DoctrineDonationPrePersistSubscriber.php - - - message: "#^Parameter \\#1 \\$id of method WMDE\\\\Fundraising\\\\DonationContext\\\\DataAccess\\\\DoctrineDonationRepository\\:\\:getDoctrineDonationById\\(\\) expects int, int\\|null given\\.$#" - count: 1 - path: src/DataAccess/DoctrineDonationRepository.php - - - - message: "#^Parameter \\#1 \\$id of method WMDE\\\\Fundraising\\\\DonationContext\\\\Domain\\\\Model\\\\Donation\\:\\:assignId\\(\\) expects int, int\\|null given\\.$#" - count: 1 - path: src/DataAccess/DoctrineDonationRepository.php - - - - message: "#^Cannot call method getAuthorDisplayName\\(\\) on WMDE\\\\Fundraising\\\\DonationContext\\\\Domain\\\\Model\\\\DonationComment\\|null\\.$#" - count: 1 - path: src/Domain/Model/Donation.php - - - - message: "#^Cannot call method getCommentText\\(\\) on WMDE\\\\Fundraising\\\\DonationContext\\\\Domain\\\\Model\\\\DonationComment\\|null\\.$#" - count: 1 - path: src/Domain/Model/Donation.php - - message: "#^Method WMDE\\\\Fundraising\\\\DonationContext\\\\Domain\\\\Model\\\\DonorType\\:\\:__callStatic\\(\\) has parameter \\$arguments with no type specified\\.$#" count: 1 @@ -39,195 +14,3 @@ parameters: message: "#^Method WMDE\\\\Fundraising\\\\DonationContext\\\\Domain\\\\Model\\\\DonorType\\:\\:__callStatic\\(\\) has parameter \\$name with no type specified\\.$#" count: 1 path: src/Domain/Model/DonorType.php - - - - message: "#^Method WMDE\\\\Fundraising\\\\DonationContext\\\\Infrastructure\\\\DonationMailer\\:\\:getAdminTemplateArguments\\(\\) should return array\\{id\\: int, moderationFlags\\: array\\, amount\\: float\\} but returns array\\{id\\: int\\|null, moderationFlags\\: array\\, amount\\: float\\}\\.$#" - count: 1 - path: src/Infrastructure/DonationMailer.php - - - - message: "#^Parameter \\#1 \\$donationId of method WMDE\\\\Fundraising\\\\DonationContext\\\\Authorization\\\\DonationTokenFetcher\\:\\:getTokens\\(\\) expects int, int\\|null given\\.$#" - count: 1 - path: src/Infrastructure/HttpDonationNotifier.php - - - - message: "#^Parameter \\#1 \\$donationId of class WMDE\\\\Fundraising\\\\DonationContext\\\\Domain\\\\Event\\\\DonationCreatedEvent constructor expects int, int\\|null given\\.$#" - count: 1 - path: src/UseCases/AddDonation/AddDonationUseCase.php - - - - message: "#^Parameter \\#1 \\$donationId of method WMDE\\\\Fundraising\\\\DonationContext\\\\Authorization\\\\DonationTokenFetcher\\:\\:getTokens\\(\\) expects int, int\\|null given\\.$#" - count: 1 - path: src/UseCases/AddDonation/AddDonationUseCase.php - - - - message: "#^Parameter \\#1 \\$itemId of class WMDE\\\\Fundraising\\\\PaymentContext\\\\Domain\\\\PaymentUrlGenerator\\\\RequestContext constructor expects int, int\\|null given\\.$#" - count: 1 - path: src/UseCases/AddDonation/AddDonationUseCase.php - - - - message: "#^Parameter \\#1 \\$donationId of method WMDE\\\\Fundraising\\\\DonationContext\\\\Authorization\\\\DonationAuthorizer\\:\\:systemCanModifyDonation\\(\\) expects int, int\\|null given\\.$#" - count: 1 - path: src/UseCases/BookDonationUseCase/BookDonationUseCase.php - - - - message: "#^Parameter \\#1 \\$donationId of method WMDE\\\\Fundraising\\\\DonationContext\\\\Infrastructure\\\\DonationEventLogger\\:\\:log\\(\\) expects int, int\\|null given\\.$#" - count: 1 - path: src/UseCases/BookDonationUseCase/BookDonationUseCase.php - - - - message: "#^Parameter \\#1 \\$donationId of method WMDE\\\\Fundraising\\\\DonationContext\\\\Infrastructure\\\\DonationEventLogger\\:\\:log\\(\\) expects int, int\\|null given\\.$#" - count: 1 - path: src/UseCases/CancelDonation/CancelDonationUseCase.php - - - - message: "#^Parameter \\#1 \\$donationId of method WMDE\\\\Fundraising\\\\DonationContext\\\\Infrastructure\\\\DonationEventLogger\\:\\:log\\(\\) expects int, int\\|null given\\.$#" - count: 1 - path: src/UseCases/HandlePaypalPaymentWithoutDonation/HandlePaypalPaymentWithoutDonationUseCase.php - - - - message: "#^Parameter \\#1 \\$donationId of method WMDE\\\\Fundraising\\\\DonationContext\\\\UseCases\\\\ModerateDonation\\\\NotificationLog\\:\\:hasSentConfirmationFor\\(\\) expects int, int\\|null given\\.$#" - count: 1 - path: src/UseCases/ModerateDonation/ModerateDonationUseCase.php - - - - message: "#^Parameter \\#1 \\$donationId of method WMDE\\\\Fundraising\\\\DonationContext\\\\UseCases\\\\ModerateDonation\\\\NotificationLog\\:\\:logConfirmationSent\\(\\) expects int, int\\|null given\\.$#" - count: 1 - path: src/UseCases/ModerateDonation/ModerateDonationUseCase.php - - - - message: "#^Parameter \\#1 \\$donationId of method WMDE\\\\Fundraising\\\\DonationContext\\\\Authorization\\\\DonationAuthorizer\\:\\:canAccessDonation\\(\\) expects int, int\\|null given\\.$#" - count: 3 - path: tests/Integration/DataAccess/DoctrineDonationAuthorizerTest.php - - - - message: "#^Parameter \\#1 \\$donationId of method WMDE\\\\Fundraising\\\\DonationContext\\\\Authorization\\\\DonationAuthorizer\\:\\:systemCanModifyDonation\\(\\) expects int, int\\|null given\\.$#" - count: 1 - path: tests/Integration/DataAccess/DoctrineDonationAuthorizerTest.php - - - - message: "#^Parameter \\#1 \\$donationId of method WMDE\\\\Fundraising\\\\DonationContext\\\\Authorization\\\\DonationAuthorizer\\:\\:userCanModifyDonation\\(\\) expects int, int\\|null given\\.$#" - count: 4 - path: tests/Integration/DataAccess/DoctrineDonationAuthorizerTest.php - - - - message: "#^Parameter \\#1 \\$donationId of method WMDE\\\\Fundraising\\\\DonationContext\\\\DataAccess\\\\DoctrineDonationEventLogger\\:\\:log\\(\\) expects int, int\\|null given\\.$#" - count: 1 - path: tests/Integration/DataAccess/DoctrineDonationEventLoggerTest.php - - - - message: "#^Parameter \\#1 \\$donationId of method WMDE\\\\Fundraising\\\\DonationContext\\\\Tests\\\\Integration\\\\DataAccess\\\\DoctrineDonationEventLoggerTest\\:\\:getDonationById\\(\\) expects int, int\\|null given\\.$#" - count: 2 - path: tests/Integration/DataAccess/DoctrineDonationEventLoggerTest.php - - - - message: "#^Parameter \\#1 \\$donationId of static method WMDE\\\\Fundraising\\\\DonationContext\\\\Tests\\\\Data\\\\ValidDonation\\:\\:newBookedAnonymousPayPalDonationUpdate\\(\\) expects int, int\\|null given\\.$#" - count: 1 - path: tests/Integration/DataAccess/DoctrineDonationRepositoryTest.php - - - - message: "#^Parameter \\#1 \\$id of method WMDE\\\\Fundraising\\\\DonationContext\\\\DataAccess\\\\DoctrineDonationRepository\\:\\:getDonationById\\(\\) expects int, int\\|null given\\.$#" - count: 4 - path: tests/Integration/DataAccess/DoctrineDonationRepositoryTest.php - - - - message: "#^Parameter \\#1 \\$id of method WMDE\\\\Fundraising\\\\DonationContext\\\\Domain\\\\Model\\\\Donation\\:\\:assignId\\(\\) expects int, int\\|null given\\.$#" - count: 2 - path: tests/Integration/DataAccess/DoctrineDonationRepositoryTest.php - - - - message: "#^Parameter \\#1 \\$id of method WMDE\\\\Fundraising\\\\DonationContext\\\\Tests\\\\Integration\\\\DataAccess\\\\DoctrineDonationRepositoryTest\\:\\:getDoctrineDonationById\\(\\) expects int, int\\|null given\\.$#" - count: 1 - path: tests/Integration/DataAccess/DoctrineDonationRepositoryTest.php - - - - message: "#^Call to an undefined method WMDE\\\\Fundraising\\\\DonationContext\\\\Authorization\\\\DonationAuthorizer\\:\\:hasAuthorizedAsAdmin\\(\\)\\.$#" - count: 2 - path: tests/Integration/UseCases/CancelDonation/CancelDonationUseCaseTest.php - - - - message: "#^Call to an undefined method WMDE\\\\Fundraising\\\\DonationContext\\\\Authorization\\\\DonationAuthorizer\\:\\:hasAuthorizedAsUser\\(\\)\\.$#" - count: 2 - path: tests/Integration/UseCases/CancelDonation/CancelDonationUseCaseTest.php - - - - message: "#^Call to an undefined method WMDE\\\\Fundraising\\\\DonationContext\\\\Domain\\\\Repositories\\\\DonationRepository\\:\\:getStoreDonationCalls\\(\\)\\.$#" - count: 1 - path: tests/Integration/UseCases/CancelDonation/CancelDonationUseCaseTest.php - - - - message: "#^Call to an undefined method WMDE\\\\Fundraising\\\\DonationContext\\\\Domain\\\\Repositories\\\\DonationRepository\\:\\:throwOnRead\\(\\)\\.$#" - count: 1 - path: tests/Integration/UseCases/CancelDonation/CancelDonationUseCaseTest.php - - - - message: "#^Call to an undefined method WMDE\\\\Fundraising\\\\DonationContext\\\\Domain\\\\Repositories\\\\DonationRepository\\:\\:throwOnWrite\\(\\)\\.$#" - count: 1 - path: tests/Integration/UseCases/CancelDonation/CancelDonationUseCaseTest.php - - - - message: "#^Call to an undefined method WMDE\\\\Fundraising\\\\DonationContext\\\\Infrastructure\\\\TemplateMailerInterface\\:\\:assertCalledOnceWith\\(\\)\\.$#" - count: 1 - path: tests/Integration/UseCases/CancelDonation/CancelDonationUseCaseTest.php - - - - message: "#^Parameter \\#1 \\$donationId of class WMDE\\\\Fundraising\\\\DonationContext\\\\UseCases\\\\CancelDonation\\\\CancelDonationRequest constructor expects int, int\\|null given\\.$#" - count: 11 - path: tests/Integration/UseCases/CancelDonation/CancelDonationUseCaseTest.php - - - - message: "#^Parameter \\#1 \\$id of method WMDE\\\\Fundraising\\\\DonationContext\\\\Domain\\\\Repositories\\\\DonationRepository\\:\\:getDonationById\\(\\) expects int, int\\|null given\\.$#" - count: 1 - path: tests/Integration/UseCases/CancelDonation/CancelDonationUseCaseTest.php - - - - message: "#^Parameter \\#1 \\$donationId of method WMDE\\\\Fundraising\\\\DonationContext\\\\Tests\\\\Integration\\\\UseCases\\\\UpdateDonor\\\\UpdateDonorUseCaseTest\\:\\:newUpdateDonorRequestForCompany\\(\\) expects int, int\\|null given\\.$#" - count: 1 - path: tests/Integration/UseCases/UpdateDonor/UpdateDonorUseCaseTest.php - - - - message: "#^Parameter \\#1 \\$donationId of method WMDE\\\\Fundraising\\\\DonationContext\\\\Tests\\\\Integration\\\\UseCases\\\\UpdateDonor\\\\UpdateDonorUseCaseTest\\:\\:newUpdateDonorRequestForPerson\\(\\) expects int, int\\|null given\\.$#" - count: 7 - path: tests/Integration/UseCases/UpdateDonor/UpdateDonorUseCaseTest.php - - - - message: "#^Parameter \\#1 \\$id of method WMDE\\\\Fundraising\\\\DonationContext\\\\Domain\\\\Repositories\\\\DonationRepository\\:\\:getDonationById\\(\\) expects int, int\\|null given\\.$#" - count: 2 - path: tests/Integration/UseCases/UpdateDonor/UpdateDonorUseCaseTest.php - - - - message: "#^Parameter \\#2 \\$address of class WMDE\\\\Fundraising\\\\DonationContext\\\\Domain\\\\Model\\\\Donor\\\\PersonDonor constructor expects WMDE\\\\Fundraising\\\\DonationContext\\\\Domain\\\\Model\\\\Donor\\\\Address\\\\PostalAddress, WMDE\\\\Fundraising\\\\DonationContext\\\\Domain\\\\Model\\\\Address given\\.$#" - count: 1 - path: tests/Unit/DataAccess/DonorFieldMapperTest.php - - - - message: "#^Parameter \\#1 \\$donationId of static method WMDE\\\\Fundraising\\\\DonationContext\\\\Tests\\\\Data\\\\ValidPayPalNotificationRequest\\:\\:newDuplicatePayment\\(\\) expects int, int\\|null given\\.$#" - count: 2 - path: tests/Unit/UseCases/HandlePayPalPaymentNotification/HandlePayPalPaymentCompletionNotificationUseCaseTest.php - - - - message: "#^Parameter \\#1 \\$donationId of static method WMDE\\\\Fundraising\\\\DonationContext\\\\Tests\\\\Data\\\\ValidPayPalNotificationRequest\\:\\:newInstantPayment\\(\\) expects int, int\\|null given\\.$#" - count: 4 - path: tests/Unit/UseCases/HandlePayPalPaymentNotification/HandlePayPalPaymentCompletionNotificationUseCaseTest.php - - - - message: "#^Parameter \\#2 \\$donationId of method WMDE\\\\Fundraising\\\\DonationContext\\\\Tests\\\\Unit\\\\UseCases\\\\HandlePayPalPaymentNotification\\\\HandlePayPalPaymentCompletionNotificationUseCaseTest\\:\\:assertEventLogContainsExpression\\(\\) expects int, int\\|null given\\.$#" - count: 1 - path: tests/Unit/UseCases/HandlePayPalPaymentNotification/HandlePayPalPaymentCompletionNotificationUseCaseTest.php - - - - message: "#^Parameter \\#1 \\$donationId of method WMDE\\\\Fundraising\\\\DonationContext\\\\UseCases\\\\ModerateDonation\\\\ModerateDonationUseCase\\:\\:approveDonation\\(\\) expects int, int\\|null given\\.$#" - count: 7 - path: tests/Unit/UseCases/ModerateDonation/ModerateDonationUseCaseTest.php - - - - message: "#^Parameter \\#1 \\$donationId of method WMDE\\\\Fundraising\\\\DonationContext\\\\UseCases\\\\ModerateDonation\\\\ModerateDonationUseCase\\:\\:markDonationAsModerated\\(\\) expects int, int\\|null given\\.$#" - count: 5 - path: tests/Unit/UseCases/ModerateDonation/ModerateDonationUseCaseTest.php - - - - message: "#^Parameter \\#1 \\$donationId of method WMDE\\\\Fundraising\\\\DonationContext\\\\UseCases\\\\RestoreDonation\\\\RestoreDonationUseCase\\:\\:restoreCancelledDonation\\(\\) expects int, int\\|null given\\.$#" - count: 3 - path: tests/Unit/UseCases/RestoreDonation/RestoreDonationUseCaseTest.php - - diff --git a/src/DataAccess/DoctrineCommentFinder.php b/src/DataAccess/DoctrineCommentFinder.php index 8026e3c3..ffbd8dd7 100644 --- a/src/DataAccess/DoctrineCommentFinder.php +++ b/src/DataAccess/DoctrineCommentFinder.php @@ -39,7 +39,7 @@ static function ( Donation $donation ) { ->setCommentText( $donation->getComment() ) ->setDonationAmount( (float)$donation->getAmount() ) ->setDonationTime( $donation->getCreationTime() ) - ->setDonationId( $donation->getId() ) + ->setDonationId( $donation->getId() ?? 0 ) ->freeze() ->assertNoNullFields(); }, diff --git a/src/DataAccess/DoctrineDonationExistsChecker.php b/src/DataAccess/DoctrineDonationExistsChecker.php new file mode 100644 index 00000000..003b0bd0 --- /dev/null +++ b/src/DataAccess/DoctrineDonationExistsChecker.php @@ -0,0 +1,25 @@ +entityManager->getConnection(); + $count = $connection->prepare( 'SELECT count(*) FROM spenden WHERE id=?' ) + ->executeQuery( [ $donationId ] ) + ->fetchOne(); + + return intval( $count ) === 1; + } +} diff --git a/src/DataAccess/DoctrineDonationIdRepository.php b/src/DataAccess/DoctrineDonationIdRepository.php new file mode 100644 index 00000000..b357e339 --- /dev/null +++ b/src/DataAccess/DoctrineDonationIdRepository.php @@ -0,0 +1,44 @@ +entityManager = $entityManager; + } + + public function getNewId(): int { + $connection = $this->entityManager->getConnection(); + + return $connection->transactional( function ( Connection $connection ): int { + $this->updateDonationId( $connection ); + $result = $this->getCurrentIdResult( $connection ); + $id = $result->fetchOne(); + + if ( $id === false ) { + throw new \RuntimeException( 'The ID generator needs a row with initial donation_id set to 0.' ); + } + + return intval( $id ); + } ); + } + + private function updateDonationId( Connection $connection ): void { + $statement = $connection->prepare( 'UPDATE last_generated_donation_id SET donation_id = donation_id + 1' ); + $statement->executeStatement(); + } + + private function getCurrentIdResult( Connection $connection ): Result { + $statement = $connection->prepare( 'SELECT donation_id FROM last_generated_donation_id LIMIT 1' ); + return $statement->executeQuery(); + } +} diff --git a/src/DataAccess/DoctrineDonationRepository.php b/src/DataAccess/DoctrineDonationRepository.php index c05b2be2..6d5f4f91 100644 --- a/src/DataAccess/DoctrineDonationRepository.php +++ b/src/DataAccess/DoctrineDonationRepository.php @@ -11,6 +11,7 @@ use WMDE\Fundraising\DonationContext\DataAccess\LegacyConverters\LegacyToDomainConverter; use WMDE\Fundraising\DonationContext\Domain\Model\Donation; use WMDE\Fundraising\DonationContext\Domain\Model\ModerationReason; +use WMDE\Fundraising\DonationContext\Domain\Repositories\DonationExistsChecker; use WMDE\Fundraising\DonationContext\Domain\Repositories\DonationRepository; use WMDE\Fundraising\DonationContext\Domain\Repositories\GetDonationException; use WMDE\Fundraising\DonationContext\Domain\Repositories\StoreDonationException; @@ -22,9 +23,10 @@ class DoctrineDonationRepository implements DonationRepository { public function __construct( - private EntityManager $entityManager, - private GetPaymentUseCase $getPaymentUseCase, - private ModerationReasonRepository $moderationReasonRepository + private readonly EntityManager $entityManager, + private readonly DonationExistsChecker $donationExistsChecker, + private readonly GetPaymentUseCase $getPaymentUseCase, + private readonly ModerationReasonRepository $moderationReasonRepository ) { } @@ -32,7 +34,7 @@ public function storeDonation( Donation $donation ): void { $existingModerationReasons = $this->moderationReasonRepository->getModerationReasonsThatAreAlreadyPersisted( ...$donation->getModerationReasons() ); // doctrine will persist the moderation reasons that are not yet found in the database // and create relation entries to donation automatically - if ( $donation->getId() == null ) { + if ( !$this->donationExistsChecker->donationExists( $donation->getId() ) ) { $this->insertDonation( $donation, $existingModerationReasons ); } else { $this->updateDonation( $donation, $existingModerationReasons ); @@ -58,8 +60,6 @@ private function insertDonation( Donation $donation, array $existingModerationRe } catch ( ORMException $ex ) { throw new StoreDonationException( $ex ); } - - $donation->assignId( $doctrineDonation->getId() ); } /** @@ -109,6 +109,10 @@ public function getDonationById( int $id ): ?Donation { } $converter = new LegacyToDomainConverter(); - return $converter->createFromLegacyObject( $doctrineDonation ); + try { + return $converter->createFromLegacyObject( $doctrineDonation ); + } catch ( \InvalidArgumentException $ex ) { + throw new GetDonationException( $ex ); + } } } diff --git a/src/DataAccess/LegacyConverters/LegacyToDomainConverter.php b/src/DataAccess/LegacyConverters/LegacyToDomainConverter.php index ba7b4523..caf33902 100644 --- a/src/DataAccess/LegacyConverters/LegacyToDomainConverter.php +++ b/src/DataAccess/LegacyConverters/LegacyToDomainConverter.php @@ -13,6 +13,10 @@ class LegacyToDomainConverter { public function createFromLegacyObject( DoctrineDonation $doctrineDonation ): Donation { + if ( $doctrineDonation->getId() === null ) { + throw new \InvalidArgumentException( "Doctrine donation ID must not be null" ); + } + $donor = $this->getDonor( $doctrineDonation ); $donation = new Donation( $doctrineDonation->getId(), diff --git a/src/DataAccess/Migrations/Version20230613080717.php b/src/DataAccess/Migrations/Version20230613080717.php new file mode 100644 index 00000000..b0a2919e --- /dev/null +++ b/src/DataAccess/Migrations/Version20230613080717.php @@ -0,0 +1,32 @@ +createTable( 'last_generated_donation_id' ); + $table->addColumn( 'donation_id', 'integer' ); + $table->setPrimaryKey( [ 'donation_id' ] ); + + $this->addSql( + 'INSERT INTO last_generated_payment_id (donation_id) VALUES ((SELECT MAX(id) + 1 FROM spenden))' + ); + } + + public function down( Schema $schema ): void { + $schema->dropTable( 'last_generated_donation_id' ); + } +} diff --git a/src/Domain/Model/Donation.php b/src/Domain/Model/Donation.php index 604c3635..b4d9cebf 100644 --- a/src/Domain/Model/Donation.php +++ b/src/Domain/Model/Donation.php @@ -19,7 +19,7 @@ class Donation { private array $moderationReasons; private bool $cancelled; - private ?int $id; + private int $id; private Donor $donor; private int $paymentId; @@ -35,7 +35,7 @@ class Donation { private DonationTrackingInfo $trackingInfo; /** - * @param int|null $id + * @param int $id * @param Donor $donor * @param int $paymentId * @param DonationTrackingInfo $trackingInfo @@ -43,7 +43,7 @@ class Donation { * * @throws \InvalidArgumentException */ - public function __construct( ?int $id, Donor $donor, int $paymentId, DonationTrackingInfo $trackingInfo, DonationComment $comment = null ) { + public function __construct( int $id, Donor $donor, int $paymentId, DonationTrackingInfo $trackingInfo, DonationComment $comment = null ) { $this->id = $id; $this->donor = $donor; $this->paymentId = $paymentId; @@ -54,23 +54,10 @@ public function __construct( ?int $id, Donor $donor, int $paymentId, DonationTra $this->moderationReasons = []; } - public function getId(): ?int { + public function getId(): int { return $this->id; } - /** - * @param int $id - * - * @throws \RuntimeException - */ - public function assignId( int $id ): void { - if ( $this->id !== null && $this->id !== $id ) { - throw new \RuntimeException( 'Id cannot be changed after initial assignment' ); - } - - $this->id = $id; - } - /** * @deprecated Use a payment instead * @return Euro @@ -124,12 +111,15 @@ public function revokeCancellation(): void { } public function confirmBooked(): void { - if ( $this->hasComment() && ( $this->isMarkedForModeration() || $this->isCancelled() ) ) { + if ( $this->isMarkedForModeration() || $this->isCancelled() ) { $this->makeCommentPrivate(); } } private function makeCommentPrivate(): void { + if ( $this->comment === null ) { + return; + } $this->comment = new DonationComment( $this->comment->getCommentText(), false, @@ -206,9 +196,9 @@ public function donorIsAnonymous(): bool { return $this->donor instanceof AnonymousDonor; } - public function createFollowupDonationForPayment( int $paymentId ): self { + public function createFollowupDonationForPayment( int $donationId, int $paymentId ): self { return new Donation( - null, + $donationId, $this->getDonor(), $paymentId, $this->getTrackingInfo(), diff --git a/src/Domain/Model/DonationId.php b/src/Domain/Model/DonationId.php new file mode 100644 index 00000000..42ab69a4 --- /dev/null +++ b/src/Domain/Model/DonationId.php @@ -0,0 +1,36 @@ +persist( new DonationId() ); + * $entityManager->flush(); + * ``` + * + * @codeCoverageIgnore + */ +class DonationId { + private ?int $id = null; + private int $donationId; + + public function __construct( int $donationId = 0 ) { + $this->donationId = $donationId; + } + + public function getId(): ?int { + return $this->id; + } + + public function getDonationId(): int { + return $this->donationId; + } +} diff --git a/src/Domain/Repositories/DonationExistsChecker.php b/src/Domain/Repositories/DonationExistsChecker.php new file mode 100644 index 00000000..5fdefdae --- /dev/null +++ b/src/Domain/Repositories/DonationExistsChecker.php @@ -0,0 +1,7 @@ +donationRepository = $donationRepository; - $this->donationValidator = $donationValidator; - $this->policyValidator = $policyValidator; - $this->notifier = $notifier; - $this->tokenFetcher = $tokenFetcher; - $this->eventEmitter = $eventEmitter; - $this->paymentService = $paymentService; + public function __construct( + private readonly DonationIdRepository $idGenerator, + private readonly DonationRepository $donationRepository, + private readonly AddDonationValidator $donationValidator, + private readonly ModerationService $policyValidator, + private readonly DonationNotifier $notifier, + private readonly DonationTokenFetcher $tokenFetcher, + private readonly EventEmitter $eventEmitter, + private readonly CreatePaymentService $paymentService + ) { } public function addDonation( AddDonationRequest $donationRequest ): AddDonationResponse { @@ -102,7 +94,7 @@ private function newDonationFromRequest( AddDonationRequest $donationRequest, in $donor = $this->getPersonalInfoFromRequest( $donationRequest ); $this->processNewsletterAndReceiptOptions( $donationRequest, $donor ); return new Donation( - null, + $this->idGenerator->getNewId(), $donor, $paymentId, $this->newTrackingInfoFromRequest( $donationRequest ) diff --git a/src/UseCases/BookDonationUseCase/BookDonationUseCase.php b/src/UseCases/BookDonationUseCase/BookDonationUseCase.php index b9ea13d9..e628b8ca 100644 --- a/src/UseCases/BookDonationUseCase/BookDonationUseCase.php +++ b/src/UseCases/BookDonationUseCase/BookDonationUseCase.php @@ -6,6 +6,7 @@ use WMDE\Fundraising\DonationContext\Authorization\DonationAuthorizer; use WMDE\Fundraising\DonationContext\Domain\Model\Donation; +use WMDE\Fundraising\DonationContext\Domain\Repositories\DonationIdRepository; use WMDE\Fundraising\DonationContext\Domain\Repositories\DonationRepository; use WMDE\Fundraising\DonationContext\Infrastructure\DonationEventLogger; use WMDE\Fundraising\DonationContext\Services\PaymentBookingService; @@ -17,20 +18,14 @@ class BookDonationUseCase { - private DonationRepository $repository; - private DonationAuthorizer $authorizationService; - private DonationNotifier $notifier; - private PaymentBookingService $paymentBookingService; - private DonationEventLogger $eventLogger; - - public function __construct( DonationRepository $repository, DonationAuthorizer $authorizationService, - DonationNotifier $notifier, PaymentBookingService $paymentBookingService, - DonationEventLogger $eventLogger ) { - $this->repository = $repository; - $this->authorizationService = $authorizationService; - $this->notifier = $notifier; - $this->paymentBookingService = $paymentBookingService; - $this->eventLogger = $eventLogger; + public function __construct( + private readonly DonationIdRepository $idGenerator, + private readonly DonationRepository $repository, + private readonly DonationAuthorizer $authorizationService, + private readonly DonationNotifier $notifier, + private readonly PaymentBookingService $paymentBookingService, + private readonly DonationEventLogger $eventLogger + ) { } public function handleNotification( NotificationRequest $request ): NotificationResponse { @@ -56,7 +51,7 @@ private function handleRequestForDonation( NotificationRequest $request, Donatio return $this->createFailureResponseFromPaymentServiceResponse( $result ); } if ( $result instanceof FollowUpSuccessResponse ) { - $donation = $donation->createFollowupDonationForPayment( $result->childPaymentId ); + $donation = $donation->createFollowupDonationForPayment( $this->idGenerator->getNewId(), $result->childPaymentId ); $isFollowupPayment = true; } $donation->confirmBooked(); diff --git a/src/UseCases/HandlePaypalPaymentWithoutDonation/HandlePaypalPaymentWithoutDonationUseCase.php b/src/UseCases/HandlePaypalPaymentWithoutDonation/HandlePaypalPaymentWithoutDonationUseCase.php index 3e96d69f..4b9ecb15 100644 --- a/src/UseCases/HandlePaypalPaymentWithoutDonation/HandlePaypalPaymentWithoutDonationUseCase.php +++ b/src/UseCases/HandlePaypalPaymentWithoutDonation/HandlePaypalPaymentWithoutDonationUseCase.php @@ -7,6 +7,7 @@ use WMDE\Fundraising\DonationContext\Domain\Model\Donation; use WMDE\Fundraising\DonationContext\Domain\Model\DonationTrackingInfo; use WMDE\Fundraising\DonationContext\Domain\Model\Donor\AnonymousDonor; +use WMDE\Fundraising\DonationContext\Domain\Repositories\DonationIdRepository; use WMDE\Fundraising\DonationContext\Domain\Repositories\DonationRepository; use WMDE\Fundraising\DonationContext\Infrastructure\DonationEventLogger; use WMDE\Fundraising\DonationContext\Services\PaypalBookingService; @@ -19,8 +20,9 @@ class HandlePaypalPaymentWithoutDonationUseCase { public function __construct( private readonly PaypalBookingService $paypalBookingService, private readonly DonationRepository $donationRepository, - private DonationNotifier $notifier, - private DonationEventLogger $eventLogger, + private readonly DonationIdRepository $idGenerator, + private readonly DonationNotifier $notifier, + private readonly DonationEventLogger $eventLogger, ) { } @@ -38,7 +40,7 @@ public function handleNotification( int $amountInCents, array $bookingData ): No } $donation = new Donation( - null, + $this->idGenerator->getNewId(), new AnonymousDonor(), $result->paymentId, DonationTrackingInfo::newBlankTrackingInfo(), diff --git a/tests/Data/IncompleteDoctrineDonation.php b/tests/Data/IncompleteDoctrineDonation.php index 21f0141a..e7e82371 100644 --- a/tests/Data/IncompleteDoctrineDonation.php +++ b/tests/Data/IncompleteDoctrineDonation.php @@ -9,6 +9,7 @@ class IncompleteDoctrineDonation { + private const DONATION_ID = 1; private const PAYMENT_ID = 7; public static function newPaypalDonationWithMissingTrackingData(): Donation { @@ -47,6 +48,7 @@ private function createPaypalDonationWithMissingFields(): Donation { private function createPaypalDonationWithMissingTrackingData(): Donation { $donation = new Donation(); + $donation->setId( self::DONATION_ID ); $this->setPaymentData( $donation ); $this->setDonorData( $donation ); $donation->setPaymentType( PaymentType::Paypal->value ); diff --git a/tests/Data/ValidDoctrineDonation.php b/tests/Data/ValidDoctrineDonation.php index 679f00ff..b76a98d4 100644 --- a/tests/Data/ValidDoctrineDonation.php +++ b/tests/Data/ValidDoctrineDonation.php @@ -7,6 +7,7 @@ use WMDE\Fundraising\DonationContext\DataAccess\DoctrineEntities\Donation; class ValidDoctrineDonation { + private const DONATION_ID = 1; private const PAYMENT_PAYPAL = 'PPL'; private const PAYMENT_BANK_TRANSFER = 'UEB'; private const PAYMENT_SOFORT = 'SUB'; @@ -130,7 +131,7 @@ public static function newAnyonymizedDonation(): Donation { private function createDonation(): Donation { $donation = new Donation(); - + $donation->setId( self::DONATION_ID ); $donation->setStatus( Donation::STATUS_NEW ); $donation->setAmount( (string)ValidDonation::DONATION_AMOUNT ); diff --git a/tests/Data/ValidDonation.php b/tests/Data/ValidDonation.php index ea00a976..e0cce914 100644 --- a/tests/Data/ValidDonation.php +++ b/tests/Data/ValidDonation.php @@ -17,7 +17,6 @@ use WMDE\Fundraising\PaymentContext\Domain\Model\Payment; class ValidDonation { - public const DONOR_FIRST_NAME = 'Jeroen'; public const DONOR_LAST_NAME = 'De Dauw'; public const DONOR_SALUTATION = 'nyan'; @@ -48,121 +47,135 @@ class ValidDonation { public const COMMENT_AUTHOR_DISPLAY_NAME = 'Such a tomato'; - public static function newBankTransferDonation(): Donation { + public static function newBankTransferDonation( int $donationId = 1 ): Donation { return self::createDonation( + $donationId, ValidPayments::newBankTransferPayment(), ); } - public static function newSofortDonation(): Donation { + public static function newSofortDonation( int $donationId = 1 ): Donation { return self::createDonation( + $donationId, ValidPayments::newSofortPayment(), ); } - public static function newDirectDebitDonation(): Donation { + public static function newDirectDebitDonation( int $donationId = 1 ): Donation { return self::createDonation( + $donationId, ValidPayments::newDirectDebitPayment(), ); } - public static function newBookedPayPalDonation( string $transactionId = ValidPayments::PAYPAL_TRANSACTION_ID ): Donation { + public static function newBookedPayPalDonation( int $donationId = 1, string $transactionId = ValidPayments::PAYPAL_TRANSACTION_ID ): Donation { return self::createDonation( + $donationId, ValidPayments::newBookedPayPalPayment( $transactionId ), ); } - public static function newIncompletePayPalDonation(): Donation { + public static function newIncompletePayPalDonation( int $donationId = 1 ): Donation { return self::createDonation( + $donationId, ValidPayments::newPayPalPayment(), ); } - public static function newIncompleteSofortDonation(): Donation { - return self::newSofortDonation(); + public static function newIncompleteSofortDonation( int $donationId = 1 ): Donation { + return self::newSofortDonation( $donationId ); } - public static function newCompletedSofortDonation(): Donation { + public static function newCompletedSofortDonation( int $donationId = 1 ): Donation { $payment = ValidPayments::newCompletedSofortPayment(); return self::createDonation( + $donationId, $payment, ); } - public static function newIncompleteAnonymousPayPalDonation(): Donation { + public static function newIncompleteAnonymousPayPalDonation( int $donationId = 1 ): Donation { return self::createAnonymousDonation( + $donationId, ValidPayments::newPayPalPayment(), ); } - public static function newBookedAnonymousPayPalDonation(): Donation { + public static function newBookedAnonymousPayPalDonation( int $donationId = 1 ): Donation { return self::createAnonymousDonation( + $donationId, ValidPayments::newBookedPayPalPayment(), ); } public static function newBookedAnonymousPayPalDonationUpdate( int $donationId ): Donation { - return self::createAnonymousDonationWithId( + return self::createAnonymousDonation( $donationId, ValidPayments::newBookedPayPalPayment(), ); } - public static function newBookedCreditCardDonation(): Donation { + public static function newBookedCreditCardDonation( int $donationId = 1 ): Donation { $payment = ValidPayments::newBookedCreditCardPayment(); return self::createDonation( + $donationId, $payment, ); } - public static function newIncompleteCreditCardDonation(): Donation { + public static function newIncompleteCreditCardDonation( int $donationId = 1 ): Donation { return self::createDonation( + $donationId, ValidPayments::newCreditCardPayment(), ); } - public static function newIncompleteAnonymousCreditCardDonation(): Donation { + public static function newIncompleteAnonymousCreditCardDonation( int $donationId = 1 ): Donation { return self::createAnonymousDonation( + $donationId, ValidPayments::newCreditCardPayment(), ); } - public static function newCancelledPayPalDonation(): Donation { + public static function newCancelledPayPalDonation( int $donationId = 1 ): Donation { return self::createCancelledDonation( + $donationId, ValidPayments::newPayPalPayment() ); } - public static function newCancelledBankTransferDonation(): Donation { + public static function newCancelledBankTransferDonation( int $donationId = 1 ): Donation { return self::createCancelledDonation( + $donationId, ValidPayments::newBankTransferPayment() ); } - public static function newCompanyBankTransferDonation(): Donation { + public static function newCompanyBankTransferDonation( int $donationId = 1 ): Donation { $donation = self::createDonation( + $donationId, ValidPayments::newBankTransferPayment(), ); $donation->setDonor( self::newCompanyDonor() ); return $donation; } - private static function createDonation( Payment $payment ): Donation { + private static function createDonation( int $donationId, Payment $payment ): Donation { $donor = self::newDonor(); $donor->subscribeToNewsletter(); return new Donation( - null, + $donationId, $donor, $payment->getId(), self::newTrackingInfo() ); } - private static function createCancelledDonation( Payment $payment ): Donation { + private static function createCancelledDonation( int $donationId, Payment $payment ): Donation { $donor = self::newDonor(); $donor->subscribeToNewsletter(); $donation = new Donation( - null, + $donationId, $donor, $payment->getId(), self::newTrackingInfo() @@ -171,16 +184,7 @@ private static function createCancelledDonation( Payment $payment ): Donation { return $donation; } - private static function createAnonymousDonation( Payment $payment ): Donation { - return new Donation( - null, - new AnonymousDonor(), - $payment->getId(), - self::newTrackingInfo() - ); - } - - private static function createAnonymousDonationWithId( int $donationId, Payment $payment ): Donation { + private static function createAnonymousDonation( int $donationId, Payment $payment ): Donation { return new Donation( $donationId, new AnonymousDonor(), diff --git a/tests/Fixtures/FailingDonationExistsChecker.php b/tests/Fixtures/FailingDonationExistsChecker.php new file mode 100644 index 00000000..eae88ce3 --- /dev/null +++ b/tests/Fixtures/FailingDonationExistsChecker.php @@ -0,0 +1,14 @@ + */ class FakeDonationRepository implements DonationRepository { - - private int $calls = 0; - /** * @var Donation[] */ @@ -48,13 +45,9 @@ public function storeDonation( Donation $donation ): void { throw new StoreDonationException(); } - if ( $donation->getId() === null ) { - $donation->assignId( ++$this->calls ); - } - - $this->donations[$donation->getId()] = $donation; + $this->donations[ $donation->getId() ] = $donation; // guard against memory-modification after store - $this->donationClones[$donation->getId()] = clone $donation; + $this->donationClones[ $donation->getId() ] = clone $donation; } /** diff --git a/tests/Fixtures/StaticDonationIdRepository.php b/tests/Fixtures/StaticDonationIdRepository.php new file mode 100644 index 00000000..d5b6a1b8 --- /dev/null +++ b/tests/Fixtures/StaticDonationIdRepository.php @@ -0,0 +1,16 @@ +persistFirstDonationWithComment(); - $this->persistSecondDonationWithComment(); - $this->persistThirdDonationWithComment(); + $this->givenStoredDonationWithComment( donationId: 1, date: '1984-01-01' ); + $this->givenStoredDonationWithComment( donationId: 2, date: '1984-01-02' ); + $this->givenStoredDonationWithComment( donationId: 3, date: '1984-01-03' ); $this->entityManager->flush(); $repository = $this->newDbalCommentRepository(); $this->assertEquals( [ - $this->getThirdComment( 3 ), - $this->getSecondComment(), - $this->getFirstComment(), + $this->getComment( donationId: 3, date: '1984-01-03' ), + $this->getComment( donationId: 2, date: '1984-01-02' ), + $this->getComment( donationId: 1, date: '1984-01-01' ), ], $repository->getPublicComments( 10 ) ); } public function testWhenThereAreMoreCommentsThanTheLimit_aLimitedNumberAreReturned(): void { - $this->persistFirstDonationWithComment(); - $this->persistSecondDonationWithComment(); - $this->persistThirdDonationWithComment(); + $this->givenStoredDonationWithComment( donationId: 1, date: '1984-01-01' ); + $this->givenStoredDonationWithComment( donationId: 2, date: '1984-01-02' ); + $this->givenStoredDonationWithComment( donationId: 3, date: '1984-01-03' ); $this->entityManager->flush(); $repository = $this->newDbalCommentRepository(); $this->assertEquals( [ - $this->getThirdComment( 3 ), - $this->getSecondComment(), + $this->getComment( donationId: 3, date: '1984-01-03' ), + $this->getComment( donationId: 2, date: '1984-01-02' ), ], $repository->getPublicComments( 2 ) ); } public function testOnlyPublicCommentsGetReturned(): void { - $this->persistFirstDonationWithComment(); - $this->persistSecondDonationWithComment(); - $this->persistDonationWithPrivateComment(); - $this->persistThirdDonationWithComment(); + $this->givenStoredDonationWithComment( donationId: 1, date: '1984-01-01' ); + $this->givenStoredDonationWithComment( donationId: 2, date: '1984-01-02' ); + $this->givenStoredDonationWithPrivateComment( donationId: 3, date: '1984-01-03' ); + $this->givenStoredDonationWithComment( donationId: 4, date: '1984-01-04' ); $this->entityManager->flush(); $repository = $this->newDbalCommentRepository(); $this->assertEquals( [ - $this->getThirdComment( 4 ), - $this->getSecondComment(), - $this->getFirstComment(), + $this->getComment( donationId: 4, date: '1984-01-04' ), + $this->getComment( donationId: 2, date: '1984-01-02' ), + $this->getComment( donationId: 1, date: '1984-01-01' ), ], $repository->getPublicComments( 10 ) ); } public function testOnlyNonDeletedCommentsGetReturned(): void { - $this->persistFirstDonationWithComment(); - $this->persistSecondDonationWithComment(); - $this->persistDeletedDonationWithComment(); - $this->persistThirdDonationWithComment(); - $this->persistDeletedDonationWithoutDeletedTimestamp(); + $this->givenStoredDonationWithComment( donationId: 1, date: '1984-01-01' ); + $this->givenStoredDonationWithComment( donationId: 2, date: '1984-01-02' ); + $this->givenDeletedTimeStoredDonationWithComment( donationId: 3, createdDate: '1984-01-03', deletedDate: '2000-01-01' ); + $this->givenStoredDonationWithComment( donationId: 4, date: '1984-01-04' ); + $this->givenDeletedStatusStoredDonationWithComment( donationId: 5, createdDate: '1984-01-05' ); $this->entityManager->flush(); $repository = $this->newDbalCommentRepository(); $this->assertEquals( [ - $this->getThirdComment( 4 ), - $this->getSecondComment(), - $this->getFirstComment(), + $this->getComment( donationId: 4, date: '1984-01-04' ), + $this->getComment( donationId: 2, date: '1984-01-02' ), + $this->getComment( donationId: 1, date: '1984-01-01' ), ], $repository->getPublicComments( 10 ) ); } - private function persistFirstDonationWithComment(): void { - $firstDonation = new Donation(); - $firstDonation->setPaymentId( self::DUMMY_PAYMENT_ID ); - $firstDonation->setPublicRecord( 'First name' ); - $firstDonation->setComment( 'First comment' ); - $firstDonation->setAmount( '100' ); - $firstDonation->setCreationTime( new DateTime( '1984-01-01' ) ); - $firstDonation->setIsPublic( true ); - $this->entityManager->persist( $firstDonation ); + public function testDoctrineThrowsException_getPublicCommentsRethrowsAsDomainException(): void { + $repository = new DoctrineCommentFinder( $this->newThrowingEntityManager() ); + + $this->expectException( CommentListingException::class ); + $repository->getPublicComments( 10 ); } - private function persistSecondDonationWithComment(): void { - $secondDonation = new Donation(); - $secondDonation->setPaymentId( self::DUMMY_PAYMENT_ID + 1 ); - $secondDonation->setPublicRecord( 'Second name' ); - $secondDonation->setComment( 'Second comment' ); - $secondDonation->setAmount( '200' ); - $secondDonation->setCreationTime( new DateTime( '1984-02-02' ) ); - $secondDonation->setIsPublic( true ); - $this->entityManager->persist( $secondDonation ); + public function testGivenOffsetOfOneCausesOneCommentToBeSkipped(): void { + $this->givenStoredDonationWithComment( donationId: 1, date: '1984-01-01' ); + $this->givenStoredDonationWithComment( donationId: 2, date: '1984-01-02' ); + $this->givenStoredDonationWithComment( donationId: 3, date: '1984-01-03' ); + $this->entityManager->flush(); + + $this->assertEquals( + [ + $this->getComment( donationId: 2, date: '1984-01-02' ), + $this->getComment( donationId: 1, date: '1984-01-01' ), + ], + $this->newDbalCommentRepository()->getPublicComments( 10, 1 ) + ); } - private function persistThirdDonationWithComment(): void { - $thirdDonation = new Donation(); - $thirdDonation->setPaymentId( self::DUMMY_PAYMENT_ID + 2 ); - $thirdDonation->setPublicRecord( 'Third name' ); - $thirdDonation->setComment( 'Third comment' ); - $thirdDonation->setAmount( '300' ); - $thirdDonation->setCreationTime( new DateTime( '1984-03-03' ) ); - $thirdDonation->setIsPublic( true ); - $this->entityManager->persist( $thirdDonation ); + public function testGivenOffsetBeyondResultSetCausesEmptyResult(): void { + $this->givenStoredDonationWithComment( donationId: 1, date: '1984-01-01' ); + $this->givenStoredDonationWithComment( donationId: 2, date: '1984-01-02' ); + $this->givenStoredDonationWithComment( donationId: 3, date: '1984-01-03' ); + $this->entityManager->flush(); + + $this->assertEquals( + [], + $this->newDbalCommentRepository()->getPublicComments( 10, 10 ) + ); } - private function persistDonationWithPrivateComment(): void { - $privateDonation = new Donation(); - $privateDonation->setPaymentId( self::DUMMY_PAYMENT_ID ); - $privateDonation->setPublicRecord( 'Private name' ); - $privateDonation->setComment( 'Private comment' ); - $privateDonation->setAmount( '1337' ); - $privateDonation->setCreationTime( new DateTime( '1984-12-12' ) ); - $privateDonation->setIsPublic( false ); - $this->entityManager->persist( $privateDonation ); + private function givenDonation( int $donationId, string $date ): Donation { + $donation = new Donation(); + $donation->setId( $donationId ); + $donation->setPaymentId( $donationId ); + $donation->setPublicRecord( self::COMMENT_NAME ); + $donation->setComment( self::COMMENT ); + $donation->setAmount( self::DONATION_AMOUNT ); + $donation->setCreationTime( new DateTime( $date ) ); + $donation->setIsPublic( true ); + return $donation; } - private function persistDeletedDonationWithComment(): void { - $deletedDonation = new Donation(); - $deletedDonation->setPaymentId( self::DUMMY_PAYMENT_ID ); - $deletedDonation->setPublicRecord( 'Deleted name' ); - $deletedDonation->setComment( 'Deleted comment' ); - $deletedDonation->setAmount( '31337' ); - $deletedDonation->setCreationTime( new DateTime( '1984-11-11' ) ); - $deletedDonation->setIsPublic( true ); - $deletedDonation->setDeletionTime( new DateTime( '2000-01-01' ) ); - $this->entityManager->persist( $deletedDonation ); + private function givenStoredDonationWithComment( int $donationId, string $date ): void { + $this->entityManager->persist( $this->givenDonation( $donationId, $date ) ); } - private function persistDeletedDonationWithoutDeletedTimestamp(): void { - $deletedDonation = new Donation(); - $deletedDonation->setPaymentId( self::DUMMY_PAYMENT_ID ); - $deletedDonation->setPublicRecord( 'Deleted name' ); - $deletedDonation->setComment( 'Deleted comment' ); - $deletedDonation->setAmount( '31337' ); - $deletedDonation->setCreationTime( new DateTime( '1984-11-11' ) ); - $deletedDonation->setIsPublic( true ); - $deletedDonation->setStatus( Donation::STATUS_CANCELLED ); - $this->entityManager->persist( $deletedDonation ); + private function givenStoredDonationWithPrivateComment( int $donationId, string $date ): void { + $donation = $this->givenDonation( $donationId, $date ); + $donation->setIsPublic( false ); + $this->entityManager->persist( $donation ); } - private function getFirstComment(): CommentWithAmount { - return CommentWithAmount::newInstance() - ->setAuthorName( 'First name' ) - ->setCommentText( 'First comment' ) - ->setDonationAmount( 100 ) - ->setDonationTime( new \DateTime( '1984-01-01' ) ) - ->setDonationId( 1 ) - ->freeze()->assertNoNullFields(); + private function givenDeletedTimeStoredDonationWithComment( int $donationId, string $createdDate, string $deletedDate ): void { + $donation = $this->givenDonation( $donationId, $createdDate ); + $donation->setDeletionTime( new DateTime( $deletedDate ) ); + $this->entityManager->persist( $donation ); } - private function getSecondComment(): CommentWithAmount { - return CommentWithAmount::newInstance() - ->setAuthorName( 'Second name' ) - ->setCommentText( 'Second comment' ) - ->setDonationAmount( 200 ) - ->setDonationTime( new \DateTime( '1984-02-02' ) ) - ->setDonationId( 2 ) - ->freeze()->assertNoNullFields(); + private function givenDeletedStatusStoredDonationWithComment( int $donationId, string $createdDate ): void { + $donation = $this->givenDonation( $donationId, $createdDate ); + $donation->setStatus( Donation::STATUS_CANCELLED ); + $this->entityManager->persist( $donation ); } - private function getThirdComment( int $donationId ): CommentWithAmount { + private function getComment( int $donationId, string $date ): CommentWithAmount { return CommentWithAmount::newInstance() - ->setAuthorName( 'Third name' ) - ->setCommentText( 'Third comment' ) - ->setDonationAmount( 300 ) - ->setDonationTime( new \DateTime( '1984-03-03' ) ) + ->setAuthorName( self::COMMENT_NAME ) + ->setCommentText( self::COMMENT ) + ->setDonationAmount( self::DONATION_AMOUNT_FLOAT ) + ->setDonationTime( new \DateTime( $date ) ) ->setDonationId( $donationId ) ->freeze()->assertNoNullFields(); } - public function testDoctrineThrowsException_getPublicCommentsRethrowsAsDomainException(): void { - $repository = new DoctrineCommentFinder( $this->newThrowingEntityManager() ); - - $this->expectException( CommentListingException::class ); - $repository->getPublicComments( 10 ); - } - private function newThrowingEntityManager(): EntityManager { $entityManager = $this->createMock( EntityManager::class ); @@ -225,32 +201,4 @@ private function newThrowingEntityManager(): EntityManager { return $entityManager; } - - public function testGivenOffsetOfOneCausesOneCommentToBeSkipped(): void { - $this->persistFirstDonationWithComment(); - $this->persistSecondDonationWithComment(); - $this->persistThirdDonationWithComment(); - $this->entityManager->flush(); - - $this->assertEquals( - [ - $this->getSecondComment(), - $this->getFirstComment(), - ], - $this->newDbalCommentRepository()->getPublicComments( 10, 1 ) - ); - } - - public function testGivenOffsetBeyondResultSetCausesEmptyResult(): void { - $this->persistFirstDonationWithComment(); - $this->persistSecondDonationWithComment(); - $this->persistThirdDonationWithComment(); - $this->entityManager->flush(); - - $this->assertEquals( - [], - $this->newDbalCommentRepository()->getPublicComments( 10, 10 ) - ); - } - } diff --git a/tests/Integration/DataAccess/DoctrineDonationAuthorizerTest.php b/tests/Integration/DataAccess/DoctrineDonationAuthorizerTest.php index 76812482..2d9407e3 100644 --- a/tests/Integration/DataAccess/DoctrineDonationAuthorizerTest.php +++ b/tests/Integration/DataAccess/DoctrineDonationAuthorizerTest.php @@ -28,6 +28,7 @@ class DoctrineDonationAuthorizerTest extends TestCase { private const EMPTY_TOKEN = ''; private const MEANINGLESS_DONATION_ID = 1337; private const DUMMY_PAYMENT_ID = 23; + private const DONATION_ID = 42; private EntityManager $entityManager; @@ -49,10 +50,10 @@ public function testGivenNoDonation_authorizationFails(): void { * @dataProvider updateTokenProvider */ public function testAuthorizerChecksUpdateTokenOfDonation( string $updateToken, bool $expectedResult ): void { - $donation = $this->givenDonationWithTokens(); + $this->givenDonationWithTokens(); $authorizer = $this->newAuthorizationService( $updateToken ); - $this->assertSame( $expectedResult, $authorizer->userCanModifyDonation( $donation->getId() ) ); + $this->assertSame( $expectedResult, $authorizer->userCanModifyDonation( self::DONATION_ID ) ); } /** @@ -70,7 +71,7 @@ public function testAuthorizerChecksAccessTokenOfDonation( string $accessToken, $donation = $this->givenDonationWithTokens(); $authorizer = $this->newAuthorizationService( '', $accessToken ); - $this->assertSame( $expectedResult, $authorizer->canAccessDonation( $donation->getId() ) ); + $this->assertSame( $expectedResult, $authorizer->canAccessDonation( self::DONATION_ID ) ); } /** @@ -89,42 +90,42 @@ public function testGivenTokenAndLegacyDonation_updateAuthorizationFails(): void $donation = $this->givenLegacyDonation(); $authorizer = $this->newAuthorizationService( self::MEANINGLESS_TOKEN ); - $this->assertFalse( $authorizer->userCanModifyDonation( $donation->getId() ) ); + $this->assertFalse( $authorizer->userCanModifyDonation( self::DONATION_ID ) ); } public function testGivenTokenAndLegacyDonation_accessAuthorizationFails(): void { $donation = $this->givenLegacyDonation(); $authorizer = $this->newAuthorizationService( self::EMPTY_TOKEN, self::MEANINGLESS_TOKEN ); - $this->assertFalse( $authorizer->canAccessDonation( $donation->getId() ) ); + $this->assertFalse( $authorizer->canAccessDonation( self::DONATION_ID ) ); } public function testGivenEmptyTokenAndLegacyDonation_updateAuthorizationFails(): void { $donation = $this->givenLegacyDonation(); $authorizer = $this->newAuthorizationService( self::EMPTY_TOKEN, self::EMPTY_TOKEN ); - $this->assertFalse( $authorizer->userCanModifyDonation( $donation->getId() ) ); + $this->assertFalse( $authorizer->userCanModifyDonation( self::DONATION_ID ) ); } public function testGivenEmptyTokenAndLegacyDonation_accessAuthorizationFails(): void { $donation = $this->givenLegacyDonation(); $authorizer = $this->newAuthorizationService( self::EMPTY_TOKEN, self::EMPTY_TOKEN ); - $this->assertFalse( $authorizer->canAccessDonation( $donation->getId() ) ); + $this->assertFalse( $authorizer->canAccessDonation( self::DONATION_ID ) ); } public function testWhenUpdateTokenIsExpiredUpdateCheckFailsForUser(): void { $donation = $this->givenDonationWithExpiredUpdateToken(); $authorizer = $this->newAuthorizationService( self::CORRECT_UPDATE_TOKEN ); - $this->assertFalse( $authorizer->userCanModifyDonation( $donation->getId() ) ); + $this->assertFalse( $authorizer->userCanModifyDonation( self::DONATION_ID ) ); } public function testWhenUpdateTokenIsExpiredUpdateCheckSucceedsForSystem(): void { $donation = $this->givenDonationWithExpiredUpdateToken(); $authorizer = $this->newAuthorizationService( self::CORRECT_UPDATE_TOKEN ); - $this->assertTrue( $authorizer->systemCanModifyDonation( $donation->getId() ) ); + $this->assertTrue( $authorizer->systemCanModifyDonation( self::DONATION_ID ) ); } public function testGivenExceptionFromEntityManager_authorizerWrapsExceptionForUserModification(): void { @@ -175,6 +176,7 @@ private function getThrowingEntityManager(): EntityManager { private function givenDonationWithTokens(): Donation { $donation = new Donation(); + $donation->setId( self::DONATION_ID ); $donation->setPaymentId( self::DUMMY_PAYMENT_ID ); $donationData = $donation->getDataObject(); $donationData->setUpdateToken( self::CORRECT_UPDATE_TOKEN ); @@ -187,6 +189,7 @@ private function givenDonationWithTokens(): Donation { private function givenDonationWithExpiredUpdateToken(): Donation { $donation = new Donation(); + $donation->setId( self::DONATION_ID ); $donation->setPaymentId( self::DUMMY_PAYMENT_ID ); $donationData = $donation->getDataObject(); $donationData->setUpdateToken( self::CORRECT_UPDATE_TOKEN ); @@ -198,6 +201,7 @@ private function givenDonationWithExpiredUpdateToken(): Donation { private function givenLegacyDonation(): Donation { $donation = new Donation(); + $donation->setId( self::DONATION_ID ); $donation->setPaymentId( self::DUMMY_PAYMENT_ID ); $this->storeDonation( $donation ); return $donation; diff --git a/tests/Integration/DataAccess/DoctrineDonationEventLoggerTest.php b/tests/Integration/DataAccess/DoctrineDonationEventLoggerTest.php index 39bb9c2e..6098f2ae 100644 --- a/tests/Integration/DataAccess/DoctrineDonationEventLoggerTest.php +++ b/tests/Integration/DataAccess/DoctrineDonationEventLoggerTest.php @@ -19,6 +19,7 @@ class DoctrineDonationEventLoggerTest extends \PHPUnit\Framework\TestCase { private const DEFAULT_MESSAGE = 'Log message'; private const LOG_TIMESTAMP = '2015-10-21 21:00:04'; private const DUMMY_PAYMENT_ID = 42; + private const DONATION_ID = 1; private EntityManager $entityManager; @@ -45,15 +46,16 @@ public function testWhenPersistenceFails_domainExceptionIsThrown(): void { public function testWhenNoLogExists_logGetsAdded(): void { $donation = new Donation(); + $donation->setId( self::DONATION_ID ); $donation->setPaymentId( self::DUMMY_PAYMENT_ID ); $this->entityManager->persist( $donation ); $this->entityManager->flush(); $logger = new DoctrineDonationEventLogger( $this->entityManager, $this->getDefaultTimeFunction() ); - $logger->log( 1, self::DEFAULT_MESSAGE ); + $logger->log( self::DONATION_ID, self::DEFAULT_MESSAGE ); $expectedLog = [ self::LOG_TIMESTAMP => self::DEFAULT_MESSAGE ]; - $donation = $this->getDonationById( $donation->getId() ); + $donation = $this->getDonationById( self::DONATION_ID ); $this->assertNotNull( $donation ); $data = $donation->getDecodedData(); @@ -63,19 +65,20 @@ public function testWhenNoLogExists_logGetsAdded(): void { public function testWhenLogExists_logGetsAppended(): void { $donation = new Donation(); + $donation->setId( self::DONATION_ID ); $donation->setPaymentId( self::DUMMY_PAYMENT_ID ); $donation->encodeAndSetData( [ 'log' => [ '2014-01-01 0:00:00' => 'New year!' ] ] ); $this->entityManager->persist( $donation ); $this->entityManager->flush(); $logger = new DoctrineDonationEventLogger( $this->entityManager, $this->getDefaultTimeFunction() ); - $logger->log( $donation->getId(), self::DEFAULT_MESSAGE ); + $logger->log( self::DONATION_ID, self::DEFAULT_MESSAGE ); $expectedLog = [ '2014-01-01 0:00:00' => 'New year!', self::LOG_TIMESTAMP => self::DEFAULT_MESSAGE ]; - $donation = $this->getDonationById( $donation->getId() ); + $donation = $this->getDonationById( self::DONATION_ID ); $this->assertNotNull( $donation ); $data = $donation->getDecodedData(); diff --git a/tests/Integration/DataAccess/DoctrineDonationExistsCheckerTest.php b/tests/Integration/DataAccess/DoctrineDonationExistsCheckerTest.php new file mode 100644 index 00000000..b0649b1b --- /dev/null +++ b/tests/Integration/DataAccess/DoctrineDonationExistsCheckerTest.php @@ -0,0 +1,46 @@ +getFactory(); + $this->entityManager = $factory->getEntityManager(); + } + + public function testWhenDonationExists_returnsTrue(): void { + $checker = new DoctrineDonationExistsChecker( $this->entityManager ); + $this->givenStoredExistingDonation(); + + $this->assertTrue( $checker->donationExists( self::EXISTING_DONATION_ID ) ); + } + + public function testWhenDonationDoesNotExist_returnsFalse(): void { + $checker = new DoctrineDonationExistsChecker( $this->entityManager ); + $this->givenStoredExistingDonation(); + + $this->assertFalse( $checker->donationExists( self::NON_EXISTING_DONATION_ID ) ); + } + + private function givenStoredExistingDonation(): void { + $this->entityManager->persist( ValidDoctrineDonation::newDirectDebitDoctrineDonation() ); + $this->entityManager->flush(); + } +} diff --git a/tests/Integration/DataAccess/DoctrineDonationIdRepositoryTest.php b/tests/Integration/DataAccess/DoctrineDonationIdRepositoryTest.php new file mode 100644 index 00000000..408b174e --- /dev/null +++ b/tests/Integration/DataAccess/DoctrineDonationIdRepositoryTest.php @@ -0,0 +1,45 @@ +getFactory(); + $this->entityManager = $factory->getEntityManager(); + } + + public function testWhenDonationIdTableIsEmpty_throwsException(): void { + $this->expectException( \RuntimeException::class ); + + $this->makeRepository()->getNewId(); + } + + public function testWhenGetNextId_getsNextId(): void { + $this->whenDonationIdIs( 4 ); + $this->assertEquals( 5, $this->makeRepository()->getNewId() ); + } + + private function makeRepository(): DonationIdRepository { + return new DoctrineDonationIdRepository( $this->entityManager ); + } + + private function whenDonationIdIs( int $donationId ): void { + $this->entityManager->persist( new DonationId( $donationId ) ); + $this->entityManager->flush(); + } +} diff --git a/tests/Integration/DataAccess/DoctrineDonationRepositoryTest.php b/tests/Integration/DataAccess/DoctrineDonationRepositoryTest.php index f0e8e578..79351387 100644 --- a/tests/Integration/DataAccess/DoctrineDonationRepositoryTest.php +++ b/tests/Integration/DataAccess/DoctrineDonationRepositoryTest.php @@ -6,6 +6,7 @@ use Doctrine\ORM\EntityManager; use PHPUnit\Framework\TestCase; +use WMDE\Fundraising\DonationContext\DataAccess\DoctrineDonationExistsChecker; use WMDE\Fundraising\DonationContext\DataAccess\DoctrineDonationRepository; use WMDE\Fundraising\DonationContext\DataAccess\DoctrineEntities\Donation as DoctrineDonation; use WMDE\Fundraising\DonationContext\DataAccess\ModerationReasonRepository; @@ -16,7 +17,9 @@ use WMDE\Fundraising\DonationContext\Tests\Data\ValidDoctrineDonation; use WMDE\Fundraising\DonationContext\Tests\Data\ValidDonation; use WMDE\Fundraising\DonationContext\Tests\Data\ValidPayments; +use WMDE\Fundraising\DonationContext\Tests\Fixtures\FailingDonationExistsChecker; use WMDE\Fundraising\DonationContext\Tests\Fixtures\FixedTokenGenerator; +use WMDE\Fundraising\DonationContext\Tests\Fixtures\SucceedingDonationExistsChecker; use WMDE\Fundraising\DonationContext\Tests\Fixtures\ThrowingEntityManager; use WMDE\Fundraising\DonationContext\Tests\TestEnvironment; use WMDE\Fundraising\PaymentContext\Domain\Model\LegacyPaymentData; @@ -63,6 +66,7 @@ public function testValidDonationGetPersisted(): void { private function newRepository(): DoctrineDonationRepository { return new DoctrineDonationRepository( $this->entityManager, + new DoctrineDonationExistsChecker( $this->entityManager ), $this->makeGetPaymentUseCaseStub(), $this->moderationRepository ); @@ -109,6 +113,7 @@ public function testWhenInsertFails_domainExceptionIsThrown(): void { $repository = new DoctrineDonationRepository( ThrowingEntityManager::newInstance( $this ), + new SucceedingDonationExistsChecker(), $this->makeGetPaymentUseCaseStub(), $this->moderationRepository ); @@ -158,8 +163,7 @@ public function testWhenDonationAlreadyExists_persistingCausesUpdate(): void { $repository->storeDonation( $donation ); // It is important a new instance is created here to test "detached entity" handling - $newDonation = ValidDonation::newDirectDebitDonation(); - $newDonation->assignId( $donation->getId() ); + $newDonation = ValidDonation::newDirectDebitDonation( $donation->getId() ); $newDonation->markForModeration( new ModerationReason( ModerationIdentifier::MANUALLY_FLAGGED_BY_ADMIN ) ); $repository->storeDonation( $newDonation ); @@ -175,6 +179,7 @@ public function testWhenDonationDoesNotExist_getDonationReturnsNull(): void { public function testWhenDoctrineThrowsException_domainExceptionIsThrown(): void { $repository = new DoctrineDonationRepository( ThrowingEntityManager::newInstance( $this ), + new SucceedingDonationExistsChecker(), $this->makeGetPaymentUseCaseStub(), $this->moderationRepository ); @@ -183,16 +188,6 @@ public function testWhenDoctrineThrowsException_domainExceptionIsThrown(): void $repository->getDonationById( self::ID_OF_DONATION_NOT_IN_DB ); } - public function testWhenDonationDoesNotExist_persistingCausesException(): void { - $donation = ValidDonation::newDirectDebitDonation(); - $donation->assignId( self::ID_OF_DONATION_NOT_IN_DB ); - - $repository = $this->newRepository(); - - $this->expectException( StoreDonationException::class ); - $repository->storeDonation( $donation ); - } - public function testGivenDonationUpdateWithoutDonorInformation_DonorNameStaysTheSame(): void { $donation = ValidDonation::newBookedPayPalDonation(); $this->newRepository()->storeDonation( $donation ); @@ -225,8 +220,7 @@ public function testPersistingDonationWithoutCommentCausesCommentToBeCleared(): $repository = $this->newRepository(); $repository->storeDonation( $donation ); - $newDonation = ValidDonation::newDirectDebitDonation(); - $newDonation->assignId( $donation->getId() ); + $newDonation = ValidDonation::newDirectDebitDonation( $donation->getId() ); $repository->storeDonation( $newDonation ); @@ -240,11 +234,11 @@ public function testPersistingDonationWithoutCommentCausesCommentToBeCleared(): } public function testWhenUpdateFails_domainExceptionIsThrown(): void { - $donation = ValidDonation::newDirectDebitDonation(); - $donation->assignId( 42 ); + $donation = ValidDonation::newDirectDebitDonation( 42 ); $repository = new DoctrineDonationRepository( ThrowingEntityManager::newInstance( $this ), + new FailingDonationExistsChecker(), $this->makeGetPaymentUseCaseStub(), $this->moderationRepository ); diff --git a/tests/Integration/DonationAcceptedEventHandlerTest.php b/tests/Integration/DonationAcceptedEventHandlerTest.php index 0ce181a3..1c9c81ad 100644 --- a/tests/Integration/DonationAcceptedEventHandlerTest.php +++ b/tests/Integration/DonationAcceptedEventHandlerTest.php @@ -18,29 +18,15 @@ /** * @covers \WMDE\Fundraising\DonationContext\DonationAcceptedEventHandler - * - * @license GPL-2.0-or-later - * @author Jeroen De Dauw < jeroendedauw@gmail.com > */ class DonationAcceptedEventHandlerTest extends TestCase { private const UNKNOWN_ID = 32202; private const KNOWN_ID = 31337; - /** - * @var DonationAuthorizer - */ - private $authorizer; - - /** - * @var DonationRepository - */ - private $repository; - - /** - * @var DonationNotifier&MockObject - */ - private $mailer; + private DonationAuthorizer $authorizer; + private DonationRepository $repository; + private MockObject&DonationNotifier $mailer; public function setUp(): void { $this->authorizer = new SucceedingDonationAuthorizer(); @@ -49,9 +35,7 @@ public function setUp(): void { } private function newDonation(): Donation { - $donation = ValidDonation::newBankTransferDonation(); - $donation->assignId( self::KNOWN_ID ); - return $donation; + return ValidDonation::newBankTransferDonation( self::KNOWN_ID ); } public function testWhenAuthorizationFails_errorIsReturned(): void { diff --git a/tests/Integration/UseCases/AddComment/AddCommentUseCaseTest.php b/tests/Integration/UseCases/AddComment/AddCommentUseCaseTest.php index cef7cc99..fa5e910a 100644 --- a/tests/Integration/UseCases/AddComment/AddCommentUseCaseTest.php +++ b/tests/Integration/UseCases/AddComment/AddCommentUseCaseTest.php @@ -74,15 +74,13 @@ public function testGivenValidRequest_commentGetsAdded(): void { } private function newFakeRepositoryWithDonation(): FakeDonationRepository { - $donation = ValidDonation::newDirectDebitDonation(); - $donation->assignId( self::DONATION_ID ); + $donation = ValidDonation::newDirectDebitDonation( self::DONATION_ID ); return new FakeDonationRepository( $donation ); } private function newFakeRepositoryWithAnonDonation(): FakeDonationRepository { - $donation = ValidDonation::newBookedAnonymousPayPalDonation(); - $donation->assignId( self::DONATION_ID ); + $donation = ValidDonation::newBookedAnonymousPayPalDonation( self::DONATION_ID ); return new FakeDonationRepository( $donation ); } @@ -176,8 +174,7 @@ public function testWhenTextValidationFails_responseMessageDoesNotContainOK(): v } public function testWhenDonationIsMarkedForModeration_responseMessageDoesNotContainOK(): void { - $donation = ValidDonation::newDirectDebitDonation(); - $donation->assignId( self::DONATION_ID ); + $donation = ValidDonation::newDirectDebitDonation( self::DONATION_ID ); $donation->markForModeration( new ModerationReason( ModerationIdentifier::MANUALLY_FLAGGED_BY_ADMIN ) ); $this->donationRepository = new FakeDonationRepository( $donation ); diff --git a/tests/Integration/UseCases/AddDonation/AddDonationUseCaseTest.php b/tests/Integration/UseCases/AddDonation/AddDonationUseCaseTest.php index 052a9243..0091eece 100644 --- a/tests/Integration/UseCases/AddDonation/AddDonationUseCaseTest.php +++ b/tests/Integration/UseCases/AddDonation/AddDonationUseCaseTest.php @@ -13,6 +13,7 @@ use WMDE\Fundraising\DonationContext\Domain\Model\DonorType; use WMDE\Fundraising\DonationContext\Domain\Model\ModerationIdentifier; use WMDE\Fundraising\DonationContext\Domain\Model\ModerationReason; +use WMDE\Fundraising\DonationContext\Domain\Repositories\DonationIdRepository; use WMDE\Fundraising\DonationContext\Domain\Repositories\DonationRepository; use WMDE\Fundraising\DonationContext\EventEmitter; use WMDE\Fundraising\DonationContext\Tests\Data\ValidDonation; @@ -20,6 +21,7 @@ use WMDE\Fundraising\DonationContext\Tests\Fixtures\EventEmitterSpy; use WMDE\Fundraising\DonationContext\Tests\Fixtures\FakeDonationRepository; use WMDE\Fundraising\DonationContext\Tests\Fixtures\FixedDonationTokenFetcher; +use WMDE\Fundraising\DonationContext\Tests\Fixtures\StaticDonationIdRepository; use WMDE\Fundraising\DonationContext\Tests\Fixtures\SucceedingPaymentServiceStub; use WMDE\Fundraising\DonationContext\Tests\Fixtures\UrlGeneratorSpy; use WMDE\Fundraising\DonationContext\UseCases\AddDonation\AddDonationRequest; @@ -252,14 +254,15 @@ public function testSuccessResponseContainsGeneratedUrl(): void { public function testUrlGeneratorGetsDonationData(): void { $urlGenerator = new UrlGeneratorSpy(); $useCase = $this->makeUseCase( + idGenerator: new StaticDonationIdRepository(), paymentService: $this->makeSuccessfulPaymentServiceWithUrlGenerator( $urlGenerator ) ); $response = $useCase->addDonation( $this->newValidAddDonationRequestWithEmail( 'irrelevant@example.com' ) ); $context = $urlGenerator->getLastContext(); - $this->assertSame( 1, $context->itemId ); - $this->assertSame( 'D' . 1, $context->invoiceId ); + $this->assertSame( StaticDonationIdRepository::DONATION_ID, $context->itemId ); + $this->assertSame( 'D' . StaticDonationIdRepository::DONATION_ID, $context->invoiceId ); $this->assertSame( $response->getAccessToken(), $context->accessToken ); $this->assertSame( $response->getUpdateToken(), $context->updateToken ); $this->assertSame( ValidDonation::DONOR_FIRST_NAME, $context->firstName ); @@ -297,10 +300,14 @@ public function testEventIsEmittedAfterDonationWasStored(): void { public function testWhenEmailAddressIsBlacklisted_donationIsMarkedAsCancelled(): void { $repository = $this->makeDonationRepositoryStub(); - $useCase = $this->makeUseCase( repository: $repository, policyValidator: $this->makeFakeAutodeletingPolicyValidator() ); + $useCase = $this->makeUseCase( + idGenerator: new StaticDonationIdRepository(), + repository: $repository, + policyValidator: $this->makeFakeAutodeletingPolicyValidator() + ); $useCase->addDonation( $this->newValidAddDonationRequestWithEmail( 'foo@bar.baz' ) ); - $donation = $repository->getDonationById( 1 ); + $donation = $repository->getDonationById( StaticDonationIdRepository::DONATION_ID ); $this->assertNotNull( $donation ); $this->assertTrue( $donation->isCancelled() ); @@ -308,13 +315,16 @@ public function testWhenEmailAddressIsBlacklisted_donationIsMarkedAsCancelled(): public function testOptingIntoDonationReceipt_persistedInDonor(): void { $repository = $this->makeDonationRepositoryStub(); - $useCase = $this->makeUseCase( repository: $repository ); + $useCase = $this->makeUseCase( + idGenerator: new StaticDonationIdRepository(), + repository: $repository + ); $request = $this->newValidAddDonationRequestWithEmail( 'foo@bar.baz' ); $request->setOptsIntoDonationReceipt( true ); $useCase->addDonation( $request ); - $donation = $repository->getDonationById( 1 ); + $donation = $repository->getDonationById( StaticDonationIdRepository::DONATION_ID ); $this->assertNotNull( $donation ); $this->assertTrue( $donation->getDonor()->wantsReceipt() ); @@ -322,13 +332,16 @@ public function testOptingIntoDonationReceipt_persistedInDonor(): void { public function testOptingOutOfDonationReceipt_persistedInDonor(): void { $repository = $this->makeDonationRepositoryStub(); - $useCase = $this->makeUseCase( repository: $repository ); + $useCase = $this->makeUseCase( + idGenerator: new StaticDonationIdRepository(), + repository: $repository + ); $request = $this->newValidAddDonationRequestWithEmail( 'foo@bar.baz' ); $request->setOptsIntoDonationReceipt( false ); $useCase->addDonation( $request ); - $donation = $repository->getDonationById( 1 ); + $donation = $repository->getDonationById( StaticDonationIdRepository::DONATION_ID ); $this->assertNotNull( $donation ); $this->assertFalse( $donation->getDonor()->wantsReceipt() ); @@ -336,13 +349,16 @@ public function testOptingOutOfDonationReceipt_persistedInDonor(): void { public function testOptingIntoNewsletter_persistedInDonor(): void { $repository = $this->makeDonationRepositoryStub(); - $useCase = $this->makeUseCase( repository: $repository ); + $useCase = $this->makeUseCase( + idGenerator: new StaticDonationIdRepository(), + repository: $repository + ); $request = $this->newValidAddDonationRequestWithEmail( 'foo@bar.baz' ); $request->setOptsIntoNewsletter( true ); $useCase->addDonation( $request ); - $donation = $repository->getDonationById( 1 ); + $donation = $repository->getDonationById( StaticDonationIdRepository::DONATION_ID ); $this->assertNotNull( $donation ); $this->assertTrue( $donation->getDonor()->wantsNewsletter() ); @@ -350,19 +366,23 @@ public function testOptingIntoNewsletter_persistedInDonor(): void { public function testOptingOutOfNewsletter_persistedInDonor(): void { $repository = $this->makeDonationRepositoryStub(); - $useCase = $this->makeUseCase( repository: $repository ); + $useCase = $this->makeUseCase( + idGenerator: new StaticDonationIdRepository(), + repository: $repository + ); $request = $this->newValidAddDonationRequestWithEmail( 'foo@bar.baz' ); $request->setOptsIntoNewsletter( false ); $useCase->addDonation( $request ); - $donation = $repository->getDonationById( 1 ); + $donation = $repository->getDonationById( StaticDonationIdRepository::DONATION_ID ); $this->assertNotNull( $donation ); $this->assertFalse( $donation->getDonor()->wantsNewsletter() ); } private function makeUseCase( + ?DonationIdRepository $idGenerator = null, ?DonationRepository $repository = null, ?AddDonationValidator $donationValidator = null, ?ModerationService $policyValidator = null, @@ -372,6 +392,7 @@ private function makeUseCase( ?CreatePaymentService $paymentService = null, ): AddDonationUseCase { return new AddDonationUseCase( + $idGenerator ?? new StaticDonationIdRepository(), $repository ?? $this->makeDonationRepositoryStub(), $donationValidator ?? $this->makeFakeSucceedingDonationValidator(), $policyValidator ?? $this->makeFakeSucceedingModerationService(), diff --git a/tests/Integration/UseCases/CancelDonation/CancelDonationUseCaseTest.php b/tests/Integration/UseCases/CancelDonation/CancelDonationUseCaseTest.php index 3f61e3c2..f545cfc6 100644 --- a/tests/Integration/UseCases/CancelDonation/CancelDonationUseCaseTest.php +++ b/tests/Integration/UseCases/CancelDonation/CancelDonationUseCaseTest.php @@ -10,6 +10,7 @@ use WMDE\Fundraising\DonationContext\Authorization\DonationAuthorizer; use WMDE\Fundraising\DonationContext\Domain\Model\Donation; use WMDE\Fundraising\DonationContext\Domain\Repositories\DonationRepository; +use WMDE\Fundraising\DonationContext\Infrastructure\DonationEventLogger; use WMDE\Fundraising\DonationContext\Infrastructure\TemplateMailerInterface; use WMDE\Fundraising\DonationContext\Tests\Data\ValidDonation; use WMDE\Fundraising\DonationContext\Tests\Fixtures\DonationEventLoggerSpy; @@ -18,7 +19,6 @@ use WMDE\Fundraising\DonationContext\Tests\Fixtures\SucceedingDonationAuthorizerSpy; use WMDE\Fundraising\DonationContext\Tests\Fixtures\TemplateBasedMailerSpy; use WMDE\Fundraising\DonationContext\UseCases\CancelDonation\CancelDonationRequest; -use WMDE\Fundraising\DonationContext\UseCases\CancelDonation\CancelDonationResponse; use WMDE\Fundraising\DonationContext\UseCases\CancelDonation\CancelDonationUseCase; use WMDE\Fundraising\PaymentContext\UseCases\CancelPayment\CancelPaymentUseCase; use WMDE\Fundraising\PaymentContext\UseCases\CancelPayment\FailureResponse; @@ -33,38 +33,19 @@ */ class CancelDonationUseCaseTest extends TestCase { - private DonationRepository $repository; - - private TemplateMailerInterface $mailer; - - private DonationAuthorizer $authorizer; - - private DonationEventLoggerSpy $logger; - - public function setUp(): void { - $this->repository = new FakeDonationRepository(); - $this->mailer = new TemplateBasedMailerSpy( $this ); - $this->authorizer = new SucceedingDonationAuthorizerSpy(); - $this->logger = new DonationEventLoggerSpy(); - } - - private function newCancelDonationUseCase(): CancelDonationUseCase { + private function newCancelDonationUseCase( + DonationRepository $repository = null, + TemplateMailerInterface $mailer = null, + DonationAuthorizer $authorizer = null, + DonationEventLogger $logger = null, + CancelPaymentUseCase $cancelPaymentUseCase = null + ): CancelDonationUseCase { return new CancelDonationUseCase( - $this->repository, - $this->mailer, - $this->authorizer, - $this->logger, - $this->getSucceedingCancelPaymentUseCase() - ); - } - - private function newCancelDonationUseCasePaymentCancellationFails(): CancelDonationUseCase { - return new CancelDonationUseCase( - $this->repository, - $this->mailer, - $this->authorizer, - $this->logger, - $this->getFailingCancelPaymentUseCase() + $repository ?? new FakeDonationRepository(), + $mailer ?? new TemplateBasedMailerSpy( $this ), + $authorizer ?? new SucceedingDonationAuthorizerSpy(), + $logger ?? new DonationEventLoggerSpy(), + $cancelPaymentUseCase ?? $this->getSucceedingCancelPaymentUseCase() ); } @@ -94,11 +75,12 @@ public function testResponseContainsDonationId(): void { public function testGivenIdOfCancellableDonation_cancellationIsSuccessful(): void { $donation = $this->newCancelableDonation(); - $this->repository->storeDonation( $donation ); + $repository = new FakeDonationRepository(); + $repository->storeDonation( $donation ); $request = new CancelDonationRequest( $donation->getId() ); - $response = $this->newCancelDonationUseCase()->cancelDonation( $request ); - $donation = $this->repository->getDonationById( $donation->getId() ); + $response = $this->newCancelDonationUseCase( repository: $repository )->cancelDonation( $request ); + $donation = $repository->getDonationById( $donation->getId() ); $this->assertNotNull( $donation ); $this->assertTrue( $response->cancellationSucceeded() ); @@ -108,10 +90,14 @@ public function testGivenIdOfCancellableDonation_cancellationIsSuccessful(): voi public function testGivenIdOfNonCancellableDonation_cancellationIsNotSuccessful(): void { $donation = ValidDonation::newBookedPayPalDonation(); - $this->repository->storeDonation( $donation ); + $repository = new FakeDonationRepository(); + $repository->storeDonation( $donation ); $request = new CancelDonationRequest( $donation->getId() ); - $response = $this->newCancelDonationUseCasePaymentCancellationFails()->cancelDonation( $request ); + $response = $this->newCancelDonationUseCase( + repository: $repository, + cancelPaymentUseCase: $this->getFailingCancelPaymentUseCase() + )->cancelDonation( $request ); $this->assertFalse( $response->cancellationSucceeded() ); } @@ -123,13 +109,18 @@ private function newCancelableDonation(): Donation { public function testWhenDonationGetsCancelled_cancellationConfirmationEmailIsSent(): void { $donation = $this->newCancelableDonation(); - $this->repository->storeDonation( $donation ); + $mailer = new TemplateBasedMailerSpy( $this ); + $repository = new FakeDonationRepository(); + $repository->storeDonation( $donation ); $request = new CancelDonationRequest( $donation->getId() ); - $response = $this->newCancelDonationUseCase()->cancelDonation( $request ); + $response = $this->newCancelDonationUseCase( + repository: $repository, + mailer: $mailer + )->cancelDonation( $request ); $this->assertTrue( $response->cancellationSucceeded() ); - $this->mailer->assertCalledOnceWith( + $mailer->assertCalledOnceWith( new EmailAddress( $donation->getDonor()->getEmailAddress() ), [ 'recipient' => [ @@ -145,38 +136,56 @@ public function testWhenDonationGetsCancelled_cancellationConfirmationEmailIsSen public function testWhenDonationGetsCancelled_logEntryNeededByBackendIsWritten(): void { $donation = $this->newCancelableDonation(); - $this->repository->storeDonation( $donation ); + $logger = new DonationEventLoggerSpy(); + $repository = new FakeDonationRepository(); + $repository->storeDonation( $donation ); - $this->newCancelDonationUseCase()->cancelDonation( new CancelDonationRequest( $donation->getId() ) ); + $this->newCancelDonationUseCase( + repository: $repository, + logger: $logger + )->cancelDonation( new CancelDonationRequest( $donation->getId() ) ); $this->assertSame( [ [ $donation->getId(), 'frontend: storno' ] ], - $this->logger->getLogCalls() + $logger->getLogCalls() ); } public function testWhenDonationGetsCancelledByAdmin_adminUserNameIsWrittenAsLogEntry(): void { $donation = $this->newCancelableDonation(); - $this->repository->storeDonation( $donation ); + $logger = new DonationEventLoggerSpy(); + $repository = new FakeDonationRepository(); + $repository->storeDonation( $donation ); - $this->newCancelDonationUseCase()->cancelDonation( new CancelDonationRequest( $donation->getId(), "coolAdmin" ) ); + $this->newCancelDonationUseCase( + repository: $repository, + logger: $logger + )->cancelDonation( new CancelDonationRequest( $donation->getId(), "coolAdmin" ) ); $this->assertSame( [ [ $donation->getId(), 'cancelled by user: coolAdmin' ] ], - $this->logger->getLogCalls() + $logger->getLogCalls() ); } public function testGivenIdOfNonCancellableDonation_nothingIsWrittenToTheLog(): void { - $this->newCancelDonationUseCase()->cancelDonation( new CancelDonationRequest( 1 ) ); + $logger = new DonationEventLoggerSpy(); + $this->newCancelDonationUseCase( logger: $logger )->cancelDonation( new CancelDonationRequest( 1 ) ); - $this->assertSame( [], $this->logger->getLogCalls() ); + $this->assertSame( [], $logger->getLogCalls() ); } public function testWhenConfirmationMailFails_mailDeliveryFailureResponseIsReturned(): void { - $this->mailer = $this->newThrowingMailer(); + $donation = $this->newCancelableDonation(); + $mailer = $this->newThrowingMailer(); + $repository = new FakeDonationRepository(); + $repository->storeDonation( $donation ); - $response = $this->getResponseForCancellableDonation(); + $request = new CancelDonationRequest( $donation->getId() ); + $response = $this->newCancelDonationUseCase( + repository: $repository, + mailer: $mailer + )->cancelDonation( $request ); $this->assertTrue( $response->cancellationSucceeded() ); $this->assertTrue( $response->mailDeliveryFailed() ); @@ -191,74 +200,84 @@ private function newThrowingMailer(): TemplateMailerInterface { } public function testWhenGetDonationFails_cancellationIsNotSuccessful(): void { - $this->repository->throwOnRead(); - - $response = $this->getResponseForCancellableDonation(); - - $this->assertFalse( $response->cancellationSucceeded() ); - } - - private function getResponseForCancellableDonation(): CancelDonationResponse { $donation = $this->newCancelableDonation(); - $this->repository->storeDonation( $donation ); + $repository = new FakeDonationRepository(); + $repository->throwOnRead(); $request = new CancelDonationRequest( $donation->getId() ); - return $this->newCancelDonationUseCase()->cancelDonation( $request ); + $response = $this->newCancelDonationUseCase( repository: $repository )->cancelDonation( $request ); + + $this->assertFalse( $response->cancellationSucceeded() ); } public function testWhenDonationSavingFails_cancellationIsNotSuccessful(): void { $donation = $this->newCancelableDonation(); - $this->repository->storeDonation( $donation ); - $this->repository->throwOnWrite(); + $repository = new FakeDonationRepository(); + $repository->storeDonation( $donation ); + $repository->throwOnWrite(); $request = new CancelDonationRequest( $donation->getId() ); - $response = $this->newCancelDonationUseCase()->cancelDonation( $request ); + $response = $this->newCancelDonationUseCase( repository: $repository )->cancelDonation( $request ); $this->assertFalse( $response->cancellationSucceeded() ); } public function testWhenAdminUserCancelsDonation_authorizerChecksIfSystemCanModifyDonation(): void { $donation = $this->newCancelableDonation(); - $this->repository->storeDonation( $donation ); + $authorizer = new SucceedingDonationAuthorizerSpy(); + $repository = new FakeDonationRepository(); + $repository->storeDonation( $donation ); $request = new CancelDonationRequest( $donation->getId(), "coolAdmin" ); - $this->newCancelDonationUseCase()->cancelDonation( $request ); + $this->newCancelDonationUseCase( + repository: $repository, + authorizer: $authorizer + )->cancelDonation( $request ); - $this->assertTrue( $this->authorizer->hasAuthorizedAsAdmin() ); - $this->assertFalse( $this->authorizer->hasAuthorizedAsUser() ); + $this->assertTrue( $authorizer->hasAuthorizedAsAdmin() ); + $this->assertFalse( $authorizer->hasAuthorizedAsUser() ); } public function testWhenDonorCancelsDonation_authorizerUsesFullAuthorizationCheck(): void { $donation = $this->newCancelableDonation(); - $this->repository->storeDonation( $donation ); + $authorizer = new SucceedingDonationAuthorizerSpy(); + $repository = new FakeDonationRepository(); + $repository->storeDonation( $donation ); $request = new CancelDonationRequest( $donation->getId() ); - $this->newCancelDonationUseCase()->cancelDonation( $request ); + $this->newCancelDonationUseCase( + repository: $repository, + authorizer: $authorizer + )->cancelDonation( $request ); - $this->assertFalse( $this->authorizer->hasAuthorizedAsAdmin() ); - $this->assertTrue( $this->authorizer->hasAuthorizedAsUser() ); + $this->assertFalse( $authorizer->hasAuthorizedAsAdmin() ); + $this->assertTrue( $authorizer->hasAuthorizedAsUser() ); } public function testWhenAdminUserCancelsDonation_emailIsNotSent(): void { - $this->mailer = $this->createMock( TemplateMailerInterface::class ); - $this->mailer->expects( $this->never() )->method( 'sendMail' ); + $mailer = $this->createMock( TemplateMailerInterface::class ); - $donation = $this->newCancelableDonation(); - $this->repository->storeDonation( $donation ); + $mailer->expects( $this->never() )->method( 'sendMail' ); + $donation = $this->newCancelableDonation(); + $repository = new FakeDonationRepository(); + $repository->storeDonation( $donation ); $request = new CancelDonationRequest( $donation->getId(), "coolAdmin" ); - $this->newCancelDonationUseCase()->cancelDonation( $request ); + $this->newCancelDonationUseCase( + repository: $repository, + mailer: $mailer + )->cancelDonation( $request ); } public function testCanceledDonationIsPersisted(): void { $donation = $this->newCancelableDonation(); - $this->repository = new DonationRepositorySpy( $donation ); + $repository = new DonationRepositorySpy( $donation ); $request = new CancelDonationRequest( $donation->getId(), "coolAdmin" ); - $response = $this->newCancelDonationUseCase()->cancelDonation( $request ); + $response = $this->newCancelDonationUseCase( repository: $repository )->cancelDonation( $request ); $this->assertTrue( $response->cancellationSucceeded() ); - $storeCalls = $this->repository->getStoreDonationCalls(); + $storeCalls = $repository->getStoreDonationCalls(); $this->assertCount( 1, $storeCalls ); $this->assertSame( 1, $storeCalls[0]->getId() ); } diff --git a/tests/Unit/DataAccess/DonorFieldMapperTest.php b/tests/Unit/DataAccess/DonorFieldMapperTest.php index 76dc7670..c3b53288 100644 --- a/tests/Unit/DataAccess/DonorFieldMapperTest.php +++ b/tests/Unit/DataAccess/DonorFieldMapperTest.php @@ -49,7 +49,11 @@ public function toArray(): array { }; $validDonor = ValidDonation::newDonor(); - $extendedDonor = new Donor\PersonDonor( $specialName, $validDonor->getPhysicalAddress(), $validDonor->getEmailAddress() ); + /** + * @var Donor\Address\PostalAddress $address + */ + $address = $validDonor->getPhysicalAddress(); + $extendedDonor = new Donor\PersonDonor( $specialName, $address, $validDonor->getEmailAddress() ); DonorFieldMapper::getPersonalDataFields( $extendedDonor ); } diff --git a/tests/Unit/DataAccess/LegacyConverters/LegacyToDomainConverterTest.php b/tests/Unit/DataAccess/LegacyConverters/LegacyToDomainConverterTest.php index 50e27f0a..3efe9634 100644 --- a/tests/Unit/DataAccess/LegacyConverters/LegacyToDomainConverterTest.php +++ b/tests/Unit/DataAccess/LegacyConverters/LegacyToDomainConverterTest.php @@ -162,4 +162,13 @@ public static function donationProviderForReceipt(): iterable { $doctrineDonation->setDonationReceipt( true ); yield 'converter ignores invalid receipt data from DB for anonymous' => [ $doctrineDonation, false ]; } + + public function testThrowsExceptionWhenGivenDonationWithNullId(): void { + $doctrineDonation = new DoctrineDonation(); + + $this->expectException( \InvalidArgumentException::class ); + $this->expectExceptionMessage( "Doctrine donation ID must not be null" ); + + ( new LegacyToDomainConverter() )->createFromLegacyObject( $doctrineDonation ); + } } diff --git a/tests/Unit/Domain/Model/DonationTest.php b/tests/Unit/Domain/Model/DonationTest.php index 89d9ba1a..2fde321d 100644 --- a/tests/Unit/Domain/Model/DonationTest.php +++ b/tests/Unit/Domain/Model/DonationTest.php @@ -45,28 +45,9 @@ public function testGivenModerationStatus_cancellationSucceeds(): void { $this->assertTrue( $donation->isCancelled() ); } - public function testIdIsNullWhenNotAssigned(): void { - $this->assertNull( ValidDonation::newDirectDebitDonation()->getId() ); - } - - public function testCanAssignIdToNewDonation(): void { - $donation = ValidDonation::newDirectDebitDonation(); - - $donation->assignId( 42 ); - $this->assertSame( 42, $donation->getId() ); - } - - public function testCannotAssignIdToDonationWithIdentity(): void { - $donation = ValidDonation::newDirectDebitDonation(); - $donation->assignId( 42 ); - - $this->expectException( RuntimeException::class ); - $donation->assignId( 43 ); - } - public function testNewDonationsAreNotExported(): void { $donation = new Donation( - null, + 1, ValidDonation::newDonor(), ValidPayments::newDirectDebitPayment()->getId(), ValidDonation::newTrackingInfo(), @@ -83,7 +64,7 @@ private function newInModerationPayPalDonation(): Donation { public function testAddCommentThrowsExceptionWhenCommentAlreadySet(): void { $donation = new Donation( - null, + 1, ValidDonation::newDonor(), ValidPayments::newDirectDebitPayment()->getId(), ValidDonation::newTrackingInfo(), @@ -96,7 +77,7 @@ public function testAddCommentThrowsExceptionWhenCommentAlreadySet(): void { public function testAddCommentSetsWhenCommentNotSetYet(): void { $donation = new Donation( - null, + 1, ValidDonation::newDonor(), ValidPayments::newDirectDebitPayment()->getId(), ValidDonation::newTrackingInfo(), @@ -144,8 +125,8 @@ private function makeGenericModerationReason(): ModerationReason { } public function testCreateFollowupDonationForPayment_duplicatesRelevantFields(): void { - $donation = ValidDonation::newBookedPayPalDonation(); - $followupUpDonation = $donation->createFollowupDonationForPayment( paymentId: 99 ); + $donation = ValidDonation::newBookedPayPalDonation( donationId: 1 ); + $followupUpDonation = $donation->createFollowupDonationForPayment( donationId: 1, paymentId: 99 ); $this->assertSame( 99, $followupUpDonation->getPaymentId() ); $this->assertEquals( $followupUpDonation->getDonor(), $donation->getDonor() ); diff --git a/tests/Unit/Infrastructure/LoggingDonationRepositoryTest.php b/tests/Unit/Infrastructure/LoggingDonationRepositoryTest.php index 36e3a083..7078ea29 100644 --- a/tests/Unit/Infrastructure/LoggingDonationRepositoryTest.php +++ b/tests/Unit/Infrastructure/LoggingDonationRepositoryTest.php @@ -84,8 +84,7 @@ private function assertExceptionLoggedAsCritical( string $expectedExceptionType, public function testWhenGetDonationByIdDoesNotThrow_returnValueIsReturnedWithoutLogging(): void { $logger = new LoggerSpy(); - $donation = ValidDonation::newDirectDebitDonation(); - $donation->assignId( 1337 ); + $donation = ValidDonation::newDirectDebitDonation( 1337 ); $loggingRepo = new LoggingDonationRepository( new FakeDonationRepository( $donation ), diff --git a/tests/Unit/UseCases/CreditCardPaymentNotification/CreditCardNotificationUseCaseTest.php b/tests/Unit/UseCases/CreditCardPaymentNotification/CreditCardNotificationUseCaseTest.php index 48449b84..256ee1c2 100644 --- a/tests/Unit/UseCases/CreditCardPaymentNotification/CreditCardNotificationUseCaseTest.php +++ b/tests/Unit/UseCases/CreditCardPaymentNotification/CreditCardNotificationUseCaseTest.php @@ -6,6 +6,7 @@ use PHPUnit\Framework\TestCase; use WMDE\Fundraising\DonationContext\Authorization\DonationAuthorizer; +use WMDE\Fundraising\DonationContext\Domain\Repositories\DonationIdRepository; use WMDE\Fundraising\DonationContext\Domain\Repositories\DonationRepository; use WMDE\Fundraising\DonationContext\Infrastructure\DonationEventLogger; use WMDE\Fundraising\DonationContext\Services\PaymentBookingService; @@ -15,6 +16,7 @@ use WMDE\Fundraising\DonationContext\Tests\Fixtures\DonationRepositorySpy; use WMDE\Fundraising\DonationContext\Tests\Fixtures\FailingDonationAuthorizer; use WMDE\Fundraising\DonationContext\Tests\Fixtures\FakeDonationRepository; +use WMDE\Fundraising\DonationContext\Tests\Fixtures\StaticDonationIdRepository; use WMDE\Fundraising\DonationContext\Tests\Fixtures\SucceedingDonationAuthorizer; use WMDE\Fundraising\DonationContext\Tests\Integration\DonationEventLoggerAsserter; use WMDE\Fundraising\DonationContext\UseCases\CreditCardPaymentNotification\CreditCardNotificationUseCase; @@ -23,6 +25,7 @@ use WMDE\Fundraising\PaymentContext\UseCases\BookPayment\SuccessResponse; /** + * @covers \WMDE\Fundraising\DonationContext\UseCases\BookDonationUseCase\BookDonationUseCase * @covers \WMDE\Fundraising\DonationContext\UseCases\CreditCardPaymentNotification\CreditCardNotificationUseCase * @covers \WMDE\Fundraising\DonationContext\UseCases\NotificationResponse * @@ -140,14 +143,15 @@ public function testWhenPaymentBookingServiceFails_handlerReturnsFailure(): void } private function newCreditCardNotificationUseCase( + ?DonationIdRepository $idGenerator = null, ?DonationRepository $donationRepository = null, ?DonationAuthorizer $authorizer = null, ?DonationNotifier $notifier = null, ?PaymentBookingService $paymentBookingService = null, ?DonationEventLogger $eventLogger = null - ): CreditCardNotificationUseCase { return new CreditCardNotificationUseCase( + $idGenerator ?? new StaticDonationIdRepository(), $donationRepository ?? new FakeDonationRepository(), $authorizer ?? new SucceedingDonationAuthorizer(), $notifier ?? $this->createNotifierStub(), diff --git a/tests/Unit/UseCases/HandlePayPalPaymentNotification/HandlePayPalPaymentCompletionNotificationUseCaseTest.php b/tests/Unit/UseCases/HandlePayPalPaymentNotification/HandlePayPalPaymentCompletionNotificationUseCaseTest.php index 58ed400b..fe889e60 100644 --- a/tests/Unit/UseCases/HandlePayPalPaymentNotification/HandlePayPalPaymentCompletionNotificationUseCaseTest.php +++ b/tests/Unit/UseCases/HandlePayPalPaymentNotification/HandlePayPalPaymentCompletionNotificationUseCaseTest.php @@ -7,6 +7,7 @@ use PHPUnit\Framework\MockObject\Stub; use PHPUnit\Framework\TestCase; use WMDE\Fundraising\DonationContext\Authorization\DonationAuthorizer; +use WMDE\Fundraising\DonationContext\Domain\Repositories\DonationIdRepository; use WMDE\Fundraising\DonationContext\Domain\Repositories\DonationRepository; use WMDE\Fundraising\DonationContext\Infrastructure\DonationEventLogger; use WMDE\Fundraising\DonationContext\Services\PaymentBookingService; @@ -16,6 +17,7 @@ use WMDE\Fundraising\DonationContext\Tests\Fixtures\DonationRepositorySpy; use WMDE\Fundraising\DonationContext\Tests\Fixtures\FailingDonationAuthorizer; use WMDE\Fundraising\DonationContext\Tests\Fixtures\FakeDonationRepository; +use WMDE\Fundraising\DonationContext\Tests\Fixtures\StaticDonationIdRepository; use WMDE\Fundraising\DonationContext\Tests\Fixtures\SucceedingDonationAuthorizer; use WMDE\Fundraising\DonationContext\Tests\Integration\DonationEventLoggerAsserter; use WMDE\Fundraising\DonationContext\UseCases\DonationNotifier; @@ -243,17 +245,8 @@ public function testWhenPaymentIsAlreadyBooked_returnsPaymentAlreadyBookedRespon $this->assertTrue( $response->paymentWasAlreadyCompleted() ); } - /** - * Create a new use case with stubs that would make it pass - * - * @param DonationRepository|null $repository - * @param DonationAuthorizer|null $authorizer - * @param DonationNotifier|null $notifier - * @param PaymentBookingService|null $paymentBookingService - * @param DonationEventLogger|null $eventLogger - * @return HandlePayPalPaymentCompletionNotificationUseCase - */ private function givenNewUseCase( + ?DonationIdRepository $idGenerator = null, ?DonationRepository $repository = null, ?DonationAuthorizer $authorizer = null, ?DonationNotifier $notifier = null, @@ -261,6 +254,7 @@ private function givenNewUseCase( ?DonationEventLogger $eventLogger = null, ): HandlePayPalPaymentCompletionNotificationUseCase { return new HandlePayPalPaymentCompletionNotificationUseCase( + $idGenerator ?? new StaticDonationIdRepository(), $repository ?? $this->createFakeRepository(), authorizationService: $authorizer ?? new SucceedingDonationAuthorizer(), notifier: $notifier ?? $this->createNotifierStub(), diff --git a/tests/Unit/UseCases/HandlePaypalPaymentWithoutDonation/HandlePaypalPaymentWithoutDonationUseCaseTest.php b/tests/Unit/UseCases/HandlePaypalPaymentWithoutDonation/HandlePaypalPaymentWithoutDonationUseCaseTest.php index 64c8548a..ca8ace55 100644 --- a/tests/Unit/UseCases/HandlePaypalPaymentWithoutDonation/HandlePaypalPaymentWithoutDonationUseCaseTest.php +++ b/tests/Unit/UseCases/HandlePaypalPaymentWithoutDonation/HandlePaypalPaymentWithoutDonationUseCaseTest.php @@ -9,6 +9,7 @@ use WMDE\Fundraising\DonationContext\Tests\Data\ValidPayPalNotificationRequest; use WMDE\Fundraising\DonationContext\Tests\Fixtures\DonationEventLoggerSpy; use WMDE\Fundraising\DonationContext\Tests\Fixtures\DonationRepositorySpy; +use WMDE\Fundraising\DonationContext\Tests\Fixtures\StaticDonationIdRepository; use WMDE\Fundraising\DonationContext\UseCases\DonationNotifier; use WMDE\Fundraising\DonationContext\UseCases\HandlePaypalPaymentWithoutDonation\HandlePaypalPaymentWithoutDonationUseCase; use WMDE\Fundraising\PaymentContext\UseCases\CreateBookedPayPalPayment\FailureResponse; @@ -29,6 +30,7 @@ public function testWhenPaymentIsForNonExistingDonation_newDonationIsCreated(): $useCase = new HandlePaypalPaymentWithoutDonationUseCase( $this->createPaymentService( $bookingData ), $repositorySpy, + new StaticDonationIdRepository(), $this->createNotifierExpectingNotification(), $loggerSpy ); @@ -40,7 +42,7 @@ public function testWhenPaymentIsForNonExistingDonation_newDonationIsCreated(): $logs = $loggerSpy->getLogCalls(); $this->assertCount( 1, $storeDonationCalls, 'Donation is stored' ); - $this->assertNull( $donation->getId(), 'ID is not taken from request' ); + $this->assertEquals( StaticDonationIdRepository::DONATION_ID, $donation->getId() ); $this->assertSame( 1, $donation->getPaymentId() ); $this->assertTrue( $donation->donorIsAnonymous() ); @@ -61,6 +63,7 @@ public function testWhenPayPalBookingServiceFails_ReturnFailureResult(): void { $useCase = new HandlePaypalPaymentWithoutDonationUseCase( $paymentService, $repositorySpy, + new StaticDonationIdRepository(), $notifier, $loggerSpy ); diff --git a/tests/Unit/UseCases/SofortPaymentNotification/SofortPaymentNotificationUseCaseTest.php b/tests/Unit/UseCases/SofortPaymentNotification/SofortPaymentNotificationUseCaseTest.php index e98c07cf..658dddc3 100644 --- a/tests/Unit/UseCases/SofortPaymentNotification/SofortPaymentNotificationUseCaseTest.php +++ b/tests/Unit/UseCases/SofortPaymentNotification/SofortPaymentNotificationUseCaseTest.php @@ -14,6 +14,7 @@ use WMDE\Fundraising\DonationContext\Tests\Fixtures\DonationRepositorySpy; use WMDE\Fundraising\DonationContext\Tests\Fixtures\FailingDonationAuthorizer; use WMDE\Fundraising\DonationContext\Tests\Fixtures\FakeDonationRepository; +use WMDE\Fundraising\DonationContext\Tests\Fixtures\StaticDonationIdRepository; use WMDE\Fundraising\DonationContext\Tests\Fixtures\SucceedingDonationAuthorizer; use WMDE\Fundraising\DonationContext\UseCases\DonationNotifier; use WMDE\Fundraising\DonationContext\UseCases\SofortPaymentNotification\SofortPaymentNotificationUseCase; @@ -21,6 +22,7 @@ use WMDE\Fundraising\PaymentContext\UseCases\BookPayment\SuccessResponse; /** + * @covers \WMDE\Fundraising\DonationContext\UseCases\BookDonationUseCase\BookDonationUseCase * @covers \WMDE\Fundraising\DonationContext\UseCases\SofortPaymentNotification\SofortPaymentNotificationUseCase * @covers \WMDE\Fundraising\DonationContext\UseCases\NotificationRequest * @covers \WMDE\Fundraising\DonationContext\UseCases\NotificationResponse @@ -33,6 +35,7 @@ private function getMailer(): DonationNotifier&MockObject { public function testWhenNotificationIsForNonExistingDonation_failureResponseIsReturned(): void { $useCase = new SofortPaymentNotificationUseCase( + new StaticDonationIdRepository(), new FakeDonationRepository(), new SucceedingDonationAuthorizer(), $this->getMailer(), @@ -52,6 +55,7 @@ public function testWhenAuthorizationFails_unhandledResponseIsReturned(): void { $fakeRepository->storeDonation( ValidDonation::newIncompleteSofortDonation() ); $useCase = new SofortPaymentNotificationUseCase( + new StaticDonationIdRepository(), $fakeRepository, new FailingDonationAuthorizer(), $this->getMailer(), @@ -71,6 +75,7 @@ public function testWhenAuthorizationSucceeds_successResponseIsReturned(): void $paymentBookingServiceStub = $this->getSucceedingPaymentBookingServiceStub(); $useCase = new SofortPaymentNotificationUseCase( + new StaticDonationIdRepository(), $fakeRepository, new SucceedingDonationAuthorizer(), $this->getMailer(), @@ -90,6 +95,7 @@ public function testWhenAuthorizationSucceeds_donationIsStored(): void { $paymentBookingServiceStub = $this->getSucceedingPaymentBookingServiceStub(); $useCase = new SofortPaymentNotificationUseCase( + new StaticDonationIdRepository(), $repositorySpy, new SucceedingDonationAuthorizer(), $this->getMailer(), @@ -116,6 +122,7 @@ public function testWhenAuthorizationSucceeds_paymentIsBooked(): void { ->willReturn( new SuccessResponse() ); $useCase = new SofortPaymentNotificationUseCase( + new StaticDonationIdRepository(), $repository, new SucceedingDonationAuthorizer(), $this->getMailer(), @@ -134,6 +141,7 @@ public function testWhenPaymentServiceReturnsFailure_unhandledResponseIsReturned $paymentBookingServiceStub->method( 'bookPayment' )->willReturn( new FailureResponse( $errorMessage ) ); $useCase = new SofortPaymentNotificationUseCase( + new StaticDonationIdRepository(), $fakeRepository, new SucceedingDonationAuthorizer(), $this->getMailer(), @@ -161,6 +169,7 @@ public function testWhenAuthorizationSucceeds_confirmationMailIsSent(): void { ->with( $donation ); $useCase = new SofortPaymentNotificationUseCase( + new StaticDonationIdRepository(), $fakeRepository, new SucceedingDonationAuthorizer(), $mailer,