Skip to content

Commit

Permalink
Comment mail throws error on no recipients (#241)
Browse files Browse the repository at this point in the history
Improve Array methods and add Equatable + Comparable interface
  • Loading branch information
frankdekker committed Apr 19, 2023
1 parent 11e96a0 commit ac256c5
Show file tree
Hide file tree
Showing 11 changed files with 178 additions and 25 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@
"phpstan/phpstan-phpunit": "^1.0",
"phpstan/phpstan-strict-rules": "^1.1",
"phpstan/phpstan-symfony": "^1.1",
"phpunit/phpunit": "^10.0",
"phpunit/phpunit": "10.0.*",
"roave/security-advisories": "dev-latest",
"slevomat/coding-standard": "^8.8",
"squizlabs/php_codesniffer": "^3.6",
Expand Down
2 changes: 1 addition & 1 deletion composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 13 additions & 1 deletion src/Entity/User/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
use DR\Review\Entity\Review\CommentReply;
use DR\Review\Repository\User\UserRepository;
use DR\Review\Security\Role\Roles;
use DR\Review\Utility\ComparableInterface;
use DR\Review\Utility\EquatableInterface;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\UserInterface;
Expand All @@ -36,7 +38,7 @@
#[ORM\Entity(repositoryClass: UserRepository::class)]
#[UniqueEntity(fields: ['email'], message: 'user.email.already.exists')]
#[ORM\UniqueConstraint('EMAIL', ['email'])]
class User implements UserInterface, PasswordAuthenticatedUserInterface
class User implements UserInterface, PasswordAuthenticatedUserInterface, EquatableInterface, ComparableInterface
{
#[ORM\Id]
#[ORM\GeneratedValue]
Expand Down Expand Up @@ -269,4 +271,14 @@ public function setReplies(Collection $replies): self

return $this;
}

public function equalsTo(mixed $other): bool
{
return $other instanceof self && $this->id === $other->id;
}

public function compareTo(mixed $other): int
{
return $other instanceof self === false ? -1 : $this->id <=> $other->id;
}
}
24 changes: 15 additions & 9 deletions src/Service/Mail/CommentMailService.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,6 @@ public function sendNewCommentMail(CodeReview $review, Comment $comment, ?array
$recipients = array_merge($recipients, $this->recipientService->getUserForComment($comment));
}
$recipients = Arrays::remove(Arrays::unique($recipients), Assert::notNull($comment->getUser()));
if (count($recipients) === 0) {
return;
}

$subject = $this->translator->trans(
'mail.new.comment.subject',
Expand All @@ -67,6 +64,11 @@ public function sendNewCommentMail(CodeReview $review, Comment $comment, ?array
$email->addBcc(new Address((string)$recipient->getEmail(), (string)$recipient->getName()));
$this->logger?->info(sprintf('Sending mail to %s for comment %d.', $recipient->getEmail(), $comment->getId()));
}

if (count($email->getBcc()) === 0) {
return;
}

$this->mailer->send($email);
}

Expand All @@ -83,9 +85,6 @@ public function sendNewCommentReplyMail(CodeReview $review, Comment $comment, Co
$recipients = array_merge($recipients, $this->recipientService->getUsersForReply($comment, $reply));
}
$recipients = Arrays::remove(Arrays::unique($recipients), Assert::notNull($reply->getUser()));
if (count($recipients) === 0) {
return;
}

$subject = $this->translator->trans(
'mail.updated.comment.subject',
Expand All @@ -108,6 +107,11 @@ public function sendNewCommentReplyMail(CodeReview $review, Comment $comment, Co
$email->addBcc(new Address((string)$recipient->getEmail(), (string)$recipient->getName()));
$this->logger?->info(sprintf('Sending mail to %s for comment %d.', $recipient->getEmail(), $reply->getId()));
}

if (count($email->getBcc()) === 0) {
return;
}

$this->mailer->send($email);
}

Expand All @@ -120,9 +124,6 @@ public function sendCommentResolvedMail(CodeReview $review, Comment $comment, Us
$recipients = array_merge($recipients, $this->recipientService->getUserForComment($comment));
$recipients = array_merge($recipients, $this->recipientService->getUsersForReply($comment));
$recipients = Arrays::remove(Arrays::unique($recipients), $resolvedBy);
if (count($recipients) === 0) {
return;
}

$subject = $this->translator->trans(
'mail.comment.resolved.subject',
Expand Down Expand Up @@ -152,6 +153,11 @@ public function sendCommentResolvedMail(CodeReview $review, Comment $comment, Us
)
);
}

if (count($email->getBcc()) === 0) {
return;
}

$this->mailer->send($email);
}
}
28 changes: 25 additions & 3 deletions src/Utility/Arrays.php
Original file line number Diff line number Diff line change
Expand Up @@ -118,14 +118,32 @@ public static function mapAssoc(array $items, callable $callback): array
*/
public static function remove(array $items, mixed $item): array
{
$index = array_search($item, $items, true);
$index = self::search($items, $item);
if ($index !== false) {
unset($items[$index]);
}

return $items;
}

/**
* @template T of array-key
*
* @param array<T, mixed> $items
*
* @phpstan-return T|false
*/
public static function search(array $items, mixed $needle): int|string|false
{
foreach ($items as $key => $value) {
if ($value === $needle || ($needle instanceof EquatableInterface && $needle->equalsTo($value))) {
return $key;
}
}

return false;
}

/**
* @template T
*
Expand All @@ -137,7 +155,7 @@ public static function unique(array $items): array
{
$result = [];
foreach ($items as $key => $item) {
if (in_array($item, $result, true) === false) {
if (self::search($result, $item) === false) {
$result[$key] = $item;
}
}
Expand All @@ -159,7 +177,11 @@ public static function diff(array $itemsA, array $itemsB): array
return array_udiff(
$itemsA,
$itemsB,
static function ($itemA, $itemB) {
static function ($itemA, $itemB): int {
if ($itemA instanceof ComparableInterface) {
return $itemA->compareTo($itemB);
}

$keyA = is_object($itemA) ? spl_object_hash($itemA) : (string)$itemA;
$keyB = is_object($itemB) ? spl_object_hash($itemB) : (string)$itemB;

Expand Down
13 changes: 13 additions & 0 deletions src/Utility/ComparableInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

namespace DR\Review\Utility;

interface ComparableInterface
{
/**
* @phpstan-return -1|0|1
*/
public function compareTo(mixed $other): int;
}
9 changes: 9 additions & 0 deletions src/Utility/EquatableInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php
declare(strict_types=1);

namespace DR\Review\Utility;

interface EquatableInterface
{
public function equalsTo(mixed $other): bool;
}
37 changes: 37 additions & 0 deletions tests/Unit/Entity/User/UserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -133,4 +133,41 @@ public function testReplies(): void
$user->setReplies($collection);
static::assertSame($collection, $user->getReplies());
}

/**
* @covers ::equalsTo
*/
public function testEqualsTo(): void
{
$userA = new User();
$userA->setId(123);
$userB = new User();
$userB->setId(456);
$userC = new User();
$userC->setId(123);

static::assertTrue($userA->equalsTo($userA));
static::assertFalse($userA->equalsTo($userB));
static::assertTrue($userA->equalsTo($userC));
static::assertFalse($userA->equalsTo("foobar"));
}

/**
* @covers ::compareTo
*/
public function testCompareTo(): void
{
$userA = new User();
$userA->setId(123);
$userB = new User();
$userB->setId(456);
$userC = new User();
$userC->setId(123);

static::assertSame(0, $userA->compareTo($userA));
static::assertSame(-1, $userA->compareTo($userB));
static::assertSame(1, $userB->compareTo($userA));
static::assertSame(0, $userA->compareTo($userC));
static::assertSame(-1, $userA->compareTo("foobar"));
}
}
25 changes: 19 additions & 6 deletions tests/Unit/Service/Mail/CommentMailServiceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ public function testSendNewCommentMailShouldNotMailWithoutRecipients(): void

$this->recipientService->expects(self::once())->method('getUsersForReview')->with($review)->willReturn([$user]);
$this->recipientService->expects(self::once())->method('getUserForComment')->with($comment)->willReturn([$user]);
$this->translator->expects(self::never())->method('trans');
$this->translator->expects(self::once())->method('trans');
$this->mailer->expects(self::never())->method('send');

$this->service->sendNewCommentMail($review, $comment);
}
Expand All @@ -69,12 +70,15 @@ public function testSendNewCommentMailShouldNotMailWithoutRecipients(): void
public function testSendNewCommentMailShouldSendMail(): void
{
$userA = new User();
$userA->setId(5);
$userA->setEmail('sherlock@example.com');
$userA->getSetting()->setMailCommentAdded(true);
$userB = new User();
$userB->setId(6);
$userB->setEmail('watson@example.com');
$userB->getSetting()->setMailCommentAdded(true);
$userC = new User();
$userC->setId(7);
$userC->setEmail('enola@example.com');
$userC->getSetting()->setMailCommentAdded(false);
$comment = new Comment();
Expand Down Expand Up @@ -108,7 +112,8 @@ static function (TemplatedEmail $email) {
*/
public function testSendNewCommentReplyMailNoMailForEmptyRecipients(): void
{
$user = new User();
$user = new User();
$user->setId(5);
$comment = new Comment();
$comment->setUser($user);
$reply = new CommentReply();
Expand All @@ -118,7 +123,8 @@ public function testSendNewCommentReplyMailNoMailForEmptyRecipients(): void
$this->recipientService->expects(self::once())->method('getUsersForReview')->with($review)->willReturn([$user]);
$this->recipientService->expects(self::once())->method('getUserForComment')->with($comment)->willReturn([$user]);
$this->recipientService->expects(self::once())->method('getUsersForReply')->with($comment, $reply)->willReturn([$user]);
$this->translator->expects(self::never())->method('trans');
$this->translator->expects(self::once())->method('trans');
$this->mailer->expects(self::never())->method('send');

$this->service->sendNewCommentReplyMail($review, $comment, $reply);
}
Expand All @@ -130,12 +136,15 @@ public function testSendNewCommentReplyMailNoMailForEmptyRecipients(): void
public function testSendNewCommentReplyMail(): void
{
$userA = new User();
$userA->setId(5);
$userA->setEmail('sherlock@example.com');
$userA->getSetting()->setMailCommentReplied(true);
$userB = new User();
$userB->setId(6);
$userB->setEmail('watson@example.com');
$userB->getSetting()->setMailCommentReplied(true);
$userC = new User();
$userC->setId(7);
$userC->setEmail('enola@example.com');
$userC->getSetting()->setMailCommentReplied(false);
$comment = new Comment();
Expand Down Expand Up @@ -172,16 +181,17 @@ static function (TemplatedEmail $email) {
*/
public function testSendCommentResolvedMailNoRecipientsNoMail(): void
{
$user = new User();
$user = (new User())->setId(5);
$comment = new Comment();
$review = new CodeReview();

$this->recipientService->expects(self::once())->method('getUsersForReview')->with($review)->willReturn([$user]);
$this->recipientService->expects(self::once())->method('getUserForComment')->with($comment)->willReturn([$user]);
$this->recipientService->expects(self::once())->method('getUsersForReply')->with($comment)->willReturn([$user]);
$this->translator->expects(self::never())->method('trans');
$this->translator->expects(self::once())->method('trans');
$this->mailer->expects(self::never())->method('send');

$this->service->sendCommentResolvedMail($review, $comment, $user);
$this->service->sendCommentResolvedMail($review, $comment, (new User())->setId(5));
}

/**
Expand All @@ -191,12 +201,15 @@ public function testSendCommentResolvedMailNoRecipientsNoMail(): void
public function testSendCommentResolvedMail(): void
{
$userA = new User();
$userA->setId(5);
$userA->setEmail('sherlock@example.com');
$userA->getSetting()->setMailCommentResolved(true);
$userB = new User();
$userB->setId(6);
$userB->setEmail('watson@example.com');
$userB->getSetting()->setMailCommentResolved(true);
$userC = new User();
$userC->setId(7);
$userC->setEmail('enola@example.com');
$userC->getSetting()->setMailCommentResolved(false);
$comment = new Comment();
Expand Down
3 changes: 3 additions & 0 deletions tests/Unit/Service/Mail/MailRecipientServiceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,11 @@ public function testGetUserForComment(): void
public function testGetUsersForReview(): void
{
$userA = new User();
$userA->setId(123);
$userB = new User();
$userB->setId(456);
$userC = new User();
$userC->setId(789);

$reviewerA = new CodeReviewer();
$reviewerA->setUser($userA);
Expand Down
Loading

0 comments on commit ac256c5

Please sign in to comment.