Skip to content

Commit 60cc51d

Browse files
committed
Merge branch 'feature/PB-28918_Release-v442' into 'release'
PB-28991: Fix email queue entries not marked as sent See merge request passbolt/passbolt-ce-api!211
2 parents e0b371c + 80c126c commit 60cc51d

8 files changed

+134
-21
lines changed

CHANGELOG.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@
22
All notable changes to this project will be documented in this file.
33
This project adheres to [Semantic Versioning](http://semver.org/).
44

5-
## [4.4.2] - 2023-11-28
5+
## [4.4.2] - 2023-11-29
66
### Improved
77
- PB-27616 As a user I should see improved performances when retrieving resources on the GET resources.json entry point
88

9+
### Fixed
10+
- PB-28991 As a user emails should be resent if the first attempt failed
11+
912
## [4.4.2-test.1] - 2023-11-27
1013
### Improved
1114
- PB-27616 As a user I should see improved performances when retrieving resources on the GET resources.json entry point

RELEASE_NOTES.md

+4
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ The first fix concerns the Time-based One-Time Password (TOTP) feature. In the p
77
The second fix improves the performance of the application, specifically when users are retrieving their resources. This update is part of an ongoing effort to enhance the overall performance of the application, with further improvements planned for future releases.
88

99
We extend our gratitude to the community member who reported this issue.
10+
1011
## [4.4.2] - 2023-11-28
1112
### Improved
1213
- PB-27616 As a user I should see improved performances when retrieving resources on the GET resources.json entry point
14+
15+
### Fixed
16+
- PB-28991 As a user emails should be resent if the first attempt failed

plugins/PassboltCe/EmailDigest/src/Utility/Factory/EmailPreviewFactory.php

+3
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ public function buildSingleEmailDigest(Entity $emailQueueEntity): EmailDigest
9090
return (new EmailDigest())
9191
->addEmailData($emailQueueEntity)
9292
->setSubject($emailQueueEntity->get('subject'))
93+
->setEmailIds([$emailQueueEntity->id])
9394
->setEmailRecipient($emailQueueEntity->get('email'));
9495
}
9596

@@ -111,6 +112,8 @@ public function buildMultipleEmailDigest(Digest $digest): EmailDigest
111112
->setEmailRecipient($emailQueueEntity->get('email'));
112113
}
113114

115+
$emailDigest->setEmailIds($digest->getEmailQueueIds());
116+
114117
return $emailDigest;
115118
}
116119

plugins/PassboltCe/EmailDigest/tests/Factory/EmailQueueFactory.php

+15-3
Original file line numberDiff line numberDiff line change
@@ -146,11 +146,12 @@ public function setFullBaseUrl(string $fullBaseUrl)
146146
}
147147

148148
/**
149-
* @param string $subject locale of the email
150-
* @return EmailQueueFactory
149+
* @param ?string $subject locale of the email
150+
* @return $this
151151
*/
152-
public function setSubject(string $subject)
152+
public function setSubject(?string $subject = null)
153153
{
154+
$subject = $subject ?? $this->getFaker()->sentence(3);
154155
$this->setField('template_vars.body.subject', $subject);
155156

156157
return $this->setField('subject', $subject);
@@ -164,4 +165,15 @@ public function setLocale(string $locale)
164165
{
165166
return $this->setField('template_vars.locale', $locale);
166167
}
168+
169+
/**
170+
* Sets the id of the email. Useful in unit testing, when an ID should be defined, but no persistence needed
171+
*
172+
* @param int|null $id email Id
173+
* @return $this
174+
*/
175+
public function setId(?int $id = null)
176+
{
177+
return $this->setField('id', $id ?? $this->getFaker()->randomNumber(4));
178+
}
167179
}

plugins/PassboltCe/EmailDigest/tests/Factory/GroupUserDeleteEmailQueueFactory.php

+5-3
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,11 @@ class GroupUserDeleteEmailQueueFactory extends EmailQueueFactory
2323
protected function setDefaultTemplate(): void
2424
{
2525
parent::setDefaultTemplate();
26-
$this->setTemplate(GroupUserDeleteEmailRedactor::TEMPLATE);
27-
$this->setGroup();
28-
$this->setOperator();
26+
$this
27+
->setSubject()
28+
->setTemplate(GroupUserDeleteEmailRedactor::TEMPLATE)
29+
->setGroup()
30+
->setOperator();
2931
}
3032

3133
/**

plugins/PassboltCe/EmailDigest/tests/Factory/ResourceDeleteEmailQueueFactory.php

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ protected function setDefaultTemplate(): void
2828
{
2929
parent::setDefaultTemplate();
3030
$this
31+
->setSubject()
3132
->setTemplate(ResourceDeleteEmailRedactor::TEMPLATE)
3233
->setResource()
3334
->setOperator()

plugins/PassboltCe/EmailDigest/tests/TestCase/Command/SenderCommandTest.php

+80-3
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,12 @@
2424
use App\Test\Factory\UserFactory;
2525
use App\Test\Lib\AppIntegrationTestCase;
2626
use App\Test\Lib\Utility\EmailTestTrait;
27+
use App\Utility\UuidFactory;
2728
use Cake\Chronos\Chronos;
2829
use Cake\Console\TestSuite\ConsoleIntegrationTestTrait;
2930
use Cake\I18n\I18n;
3031
use Cake\Mailer\Mailer;
32+
use Cake\Network\Exception\SocketException;
3133
use Passbolt\EmailDigest\Test\Factory\EmailQueueFactory;
3234
use Passbolt\EmailDigest\Test\Lib\EmailDigestMockTestTrait;
3335
use Passbolt\EmailDigest\Utility\Digest\DigestTemplateRegistry;
@@ -124,17 +126,86 @@ public function testSenderCommandLocale()
124126
$this->assertMailBodyStringCount(1, '</head>');
125127
}
126128

127-
public function testSenderCommand_NoRowsAreLockedWhenThresholdIsExceeded()
129+
public function noRowsLockedProvider(): array
128130
{
129-
$nResourcesAdded = 15;
130-
$operator = UserFactory::make()->withAvatar()->persist();
131+
return [
132+
['single_email' => 1],
133+
['multiple_emails_in_digest' => 2],
134+
['threshold_exceeded' => 11],
135+
];
136+
}
137+
138+
/**
139+
* @dataProvider noRowsLockedProvider
140+
*/
141+
public function testSenderCommand_EmailQueueRowsAreUnlockedAfterSuccessfulSend(int $nResourcesAdded)
142+
{
143+
$operator = UserFactory::make()->withAvatar()->getEntity();
144+
$recipient = '[email protected]';
145+
EmailQueueFactory::make($nResourcesAdded)
146+
->setRecipient($recipient)
147+
->setTemplate(ResourceCreateEmailRedactor::TEMPLATE)
148+
->setField('template_vars.body.user', $operator)
149+
->setField('template_vars.body.resource', [
150+
// Dummy data to render email without any warnings
151+
'id' => UuidFactory::uuid(),
152+
'created' => 'now',
153+
'name' => 'My pass',
154+
'secrets' => [['data' => 'foo bar baz']],
155+
])
156+
->setField('template_vars.body.showUsername', false)
157+
->setField('template_vars.body.showUri', false)
158+
->setField('template_vars.body.showDescription', false)
159+
->setField('template_vars.body.showSecret', false)
160+
->persist();
161+
162+
$this->exec('passbolt email_digest send');
163+
164+
$this->assertExitSuccess();
165+
$this->assertMailCount(1);
166+
$this->assertMailSentToAt(0, [$recipient => $recipient]);
167+
// Make sure email queue entries are not locked
168+
$count = EmailQueueFactory::find()->where(['locked' => true])->count();
169+
$this->assertEquals(0, $count);
170+
// Make sure email queue entries are sent
171+
$count = EmailQueueFactory::find()->where(['sent' => true, 'locked' => false])->count();
172+
$this->assertEquals($nResourcesAdded, $count);
173+
}
174+
175+
/**
176+
* @dataProvider noRowsLockedProvider
177+
*/
178+
public function testSenderCommand_EmailQueueRowsAreUnlockedAfterFailedSend(int $nResourcesAdded)
179+
{
180+
$operator = UserFactory::make()->withAvatar()->getEntity();
131181
$recipient = '[email protected]';
132182
EmailQueueFactory::make($nResourcesAdded)
133183
->setRecipient($recipient)
134184
->setTemplate(ResourceCreateEmailRedactor::TEMPLATE)
135185
->setField('template_vars.body.user', $operator)
186+
->setField('template_vars.body.resource', [
187+
// Dummy data to render email without any warnings
188+
'id' => UuidFactory::uuid(),
189+
'created' => 'now',
190+
'name' => 'My pass',
191+
'secrets' => [['data' => 'foo bar baz']],
192+
])
193+
->setField('template_vars.body.showUsername', false)
194+
->setField('template_vars.body.showUri', false)
195+
->setField('template_vars.body.showDescription', false)
196+
->setField('template_vars.body.showSecret', false)
136197
->persist();
137198

199+
$mockedEmailQueue = $this->getMockForModel(
200+
'EmailQueue.EmailQueue',
201+
['success',],
202+
['table' => 'email_queue',]
203+
);
204+
$mockedEmailQueue
205+
->expects($this->once())
206+
->method('success')
207+
->willThrowException(new SocketException());
208+
138209
$this->exec('passbolt email_digest send');
139210

140211
$this->assertExitSuccess();
@@ -143,6 +214,9 @@ public function testSenderCommand_NoRowsAreLockedWhenThresholdIsExceeded()
143214
// Make sure email queue entries are not locked
144215
$count = EmailQueueFactory::find()->where(['locked' => true])->count();
145216
$this->assertEquals(0, $count);
217+
// Make sure email queue entries are not sent
218+
$count = EmailQueueFactory::find()->where(['sent' => false, 'locked' => false])->count();
219+
$this->assertEquals($nResourcesAdded, $count);
146220
}
147221

148222
public function testSenderCommandMultipleDigests()
@@ -174,6 +248,9 @@ public function testSenderCommandMultipleDigests()
174248
$this->exec('passbolt email_digest send');
175249
$this->assertExitSuccess();
176250

251+
$sentCount = EmailQueueFactory::find()->where(['sent' => true])->count();
252+
$this->assertSame($nEmailsSent * 2, $sentCount);
253+
177254
$this->assertMailCount(2);
178255
$subject = $user->profile->full_name . ' has made changes on several resources';
179256
$this->assertMailSubjectContainsAt(0, $subject);

plugins/PassboltCe/EmailDigest/tests/TestCase/Service/SendEmailBatchServiceUnitTest.php

+22-11
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@
3434
use Passbolt\EmailDigest\Utility\Digest\DigestTemplateRegistry;
3535
use Passbolt\Locale\LocalePlugin;
3636

37+
/**
38+
* @covers \Passbolt\EmailDigest\Service\SendEmailBatchService
39+
*/
3740
class SendEmailBatchServiceUnitTest extends TestCase
3841
{
3942
use EmailTestTrait;
@@ -64,13 +67,13 @@ public function tearDown(): void
6467
parent::tearDown();
6568
}
6669

67-
public function testSendEmailBatchService_On_No_Email()
70+
public function testSendEmailBatchServiceUnitTest_On_No_Email()
6871
{
6972
$this->service->sendNextEmailsBatch([]);
7073
$this->assertMailCount(0);
7174
}
7275

73-
public function testSendEmailBatchService_On_Email_With_Unknown_Template()
76+
public function testSendEmailBatchServiceUnitTest_On_Email_With_Unknown_Template()
7477
{
7578
$email = EmailQueueFactory::make()->setTemplate('foo')->getEntity();
7679
$this->expectException(MissingTemplateException::class);
@@ -90,7 +93,7 @@ public function withAndWithoutDigestTemplate(): array
9093
/**
9194
* @dataProvider withAndWithoutDigestTemplate
9295
*/
93-
public function testSendEmailBatchService_On_One_Email_Translated(bool $withDigestTemplate)
96+
public function testSendEmailBatchServiceUnitTest_On_One_Email_Translated(bool $withDigestTemplate)
9497
{
9598
if (!$withDigestTemplate) {
9699
DigestTemplateRegistry::clearInstance();
@@ -99,6 +102,7 @@ public function testSendEmailBatchService_On_One_Email_Translated(bool $withDige
99102
$resourceDeleted = ResourceFactory::make()->getEntity();
100103
$subjectTranslated = 'Le sujet';
101104
$email = ResourceDeleteEmailQueueFactory::make()
105+
->setId()
102106
->setOperator($operator)
103107
->setResource($resourceDeleted)
104108
->setSubject($subjectTranslated)
@@ -117,7 +121,7 @@ public function testSendEmailBatchService_On_One_Email_Translated(bool $withDige
117121
/**
118122
* @dataProvider withAndWithoutDigestTemplate
119123
*/
120-
public function testSendEmailBatchService_On_Multiple_Emails_Same_Recipient_Below_Threshold(
124+
public function testSendEmailBatchServiceUnitTest_On_Multiple_Emails_Same_Recipient_Below_Threshold(
121125
bool $withDigestTemplate
122126
) {
123127
if (!$withDigestTemplate) {
@@ -130,6 +134,7 @@ public function testSendEmailBatchService_On_Multiple_Emails_Same_Recipient_Belo
130134
$recipient = '[email protected]';
131135
$nEmails = rand(2, 10);
132136
$emails = ResourceDeleteEmailQueueFactory::make($nEmails)
137+
->setId()
133138
->setRecipient($recipient)
134139
->setOperator($operator)
135140
->setResource($resourceDeleted)
@@ -159,7 +164,7 @@ public function testSendEmailBatchService_On_Multiple_Emails_Same_Recipient_Belo
159164
/**
160165
* @dataProvider withAndWithoutDigestTemplate
161166
*/
162-
public function testSendEmailBatchService_On_Same_Digest_Template_Various_Recipients(
167+
public function testSendEmailBatchServiceUnitTest_On_Same_Digest_Template_Various_Recipients(
163168
bool $withDigestTemplate
164169
) {
165170
if (!$withDigestTemplate) {
@@ -173,12 +178,14 @@ public function testSendEmailBatchService_On_Same_Digest_Template_Various_Recipi
173178
$nEmails1 = rand(2, 10);
174179
$nEmails2 = rand(2, 10);
175180
$emails1 = ResourceDeleteEmailQueueFactory::make($nEmails1)
181+
->setId()
176182
->setRecipient($recipient1)
177183
->setOperator($operator)
178184
->setResource($resourceDeleted1)
179185
->setSubject($subject)
180186
->getEntities();
181187
$emails2 = ResourceDeleteEmailQueueFactory::make($nEmails2)
188+
->setId()
182189
->setRecipient($recipient2)
183190
->setOperator($operator)
184191
->setResource($resourceDeleted2)
@@ -222,7 +229,7 @@ public function testSendEmailBatchService_On_Same_Digest_Template_Various_Recipi
222229
/**
223230
* @dataProvider withAndWithoutDigestTemplate
224231
*/
225-
public function testSendEmailBatchService_On_Multiple_Emails_Multiple_Operators_Below_Threshold(
232+
public function testSendEmailBatchServiceUnitTest_On_Multiple_Emails_Multiple_Operators_Below_Threshold(
226233
bool $withDigestTemplate
227234
) {
228235
if (!$withDigestTemplate) {
@@ -236,13 +243,15 @@ public function testSendEmailBatchService_On_Multiple_Emails_Multiple_Operators_
236243
$nEmails1 = rand(2, 10);
237244
$nEmails2 = rand(2, 10);
238245
$emails1 = ResourceDeleteEmailQueueFactory::make($nEmails1)
246+
->setId()
239247
->setRecipient($recipient)
240248
->setOperator($operator1)
241249
->setResource($resourceDeleted1)
242250
->setSubject($subjectTranslated)
243251
->setLocale('fr-FR')
244252
->getEntities();
245253
$emails2 = ResourceDeleteEmailQueueFactory::make($nEmails2)
254+
->setId()
246255
->setRecipient($recipient)
247256
->setOperator($operator2)
248257
->setResource($resourceDeleted2)
@@ -287,7 +296,7 @@ public function testSendEmailBatchService_On_Multiple_Emails_Multiple_Operators_
287296
/**
288297
* @dataProvider withAndWithoutDigestTemplate
289298
*/
290-
public function testSendEmailBatchService_On_Multiple_Emails_Multiple_Operators_Below_And_Above_Threshold(
299+
public function testSendEmailBatchServiceUnitTest_On_Multiple_Emails_Multiple_Operators_Below_And_Above_Threshold(
291300
bool $withDigestTemplate
292301
) {
293302
if (!$withDigestTemplate) {
@@ -297,17 +306,19 @@ public function testSendEmailBatchService_On_Multiple_Emails_Multiple_Operators_
297306
[$resourceDeleted1, $resourceDeleted2] = ResourceFactory::make(2)->getEntities();
298307
$subjectTranslated = 'Le sujet';
299308
$recipient = '[email protected]';
300-
$nEmails1 = 10 + rand(2, 10);
301-
$nEmails2 = rand(2, 10);
309+
$nEmails1 = 12;
310+
$nEmails2 = 2;
302311
// These emails are above the threshold
303312
$emails1 = ResourceDeleteEmailQueueFactory::make($nEmails1)
313+
->setId()
304314
->setRecipient($recipient)
305315
->setOperator($operator1)
306316
->setResource($resourceDeleted1)
307317
->setSubject($subjectTranslated)
308318
->setLocale('fr-FR')
309319
->getEntities();
310320
$emails2 = ResourceDeleteEmailQueueFactory::make($nEmails2)
321+
->setId()
311322
->setRecipient($recipient)
312323
->setOperator($operator2)
313324
->setResource($resourceDeleted2)
@@ -350,7 +361,7 @@ public function testSendEmailBatchService_On_Multiple_Emails_Multiple_Operators_
350361
}
351362
}
352363

353-
public function testSendEmailBatchService_On_Multiple_Emails_Multiple_Operators_Multiple_Digest_Templates_Below_And_Above_Threshold()
364+
public function testSendEmailBatchServiceUnitTest_On_Multiple_Emails_Multiple_Operators_Multiple_Digest_Templates_Below_And_Above_Threshold()
354365
{
355366
DigestTemplateRegistry::clearInstance();
356367
// Emails of the Group User delete template should be sent first
@@ -416,7 +427,7 @@ public function testSendEmailBatchService_On_Multiple_Emails_Multiple_Operators_
416427
$this->assertMailSubjectContainsAt(3, $operator2->profile->full_name . ' has made changes on several resources');
417428
}
418429

419-
public function testSendEmailBatchService_On_Multiple_Full_Base_Url()
430+
public function testSendEmailBatchServiceUnitTest_On_Multiple_Full_Base_Url()
420431
{
421432
$recipient = '[email protected]';
422433
$subject = 'Some subject';

0 commit comments

Comments
 (0)