Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Illuminate/Mail/Mailable.php
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ public function later($delay, Queue $queue)
*/
protected function newQueuedJob()
{
$messageGroup = $this->messageGroup ?? null;
$messageGroup = $this->messageGroup ?? (method_exists($this, 'messageGroup') ? $this->messageGroup() : null);

/** @phpstan-ignore callable.nonNativeMethod (false positive since method_exists guard is used) */
$deduplicator = $this->deduplicator ?? (method_exists($this, 'deduplicationId') ? $this->deduplicationId(...) : null);
Expand Down
2 changes: 1 addition & 1 deletion src/Illuminate/Notifications/NotificationSender.php
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ protected function queueNotification($notifiables, $notification)
$delay = $notification->withDelay($notifiable, $channel) ?? null;
}

$messageGroup = $notification->messageGroup ?? null;
$messageGroup = $notification->messageGroup ?? (method_exists($notification, 'messageGroup') ? $notification->messageGroup() : null);

if (method_exists($notification, 'withMessageGroups')) {
$messageGroup = $notification->withMessageGroups($notifiable, $channel) ?? null;
Expand Down
2 changes: 1 addition & 1 deletion src/Illuminate/Queue/SqsQueue.php
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ protected function getQueueableOptions($job, $queue, $payload, $delay = null): a
$messageGroupId = null;

if ($isObject) {
$messageGroupId = transform($job->messageGroup ?? null, $transformToString);
$messageGroupId = transform($job->messageGroup ?? (method_exists($job, 'messageGroup') ? $job->messageGroup() : null), $transformToString);
} elseif ($isFifo) {
$messageGroupId = transform($queue, $transformToString);
}
Expand Down
49 changes: 47 additions & 2 deletions tests/Mail/MailableQueuedTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,17 +88,42 @@ public function testQueuedMailableWithAttachmentFromDiskSent(): void
$queueFake->assertPushedOn(null, SendQueuedMailable::class);
}

public function testQueuedMailableForwardsMessageGroupToQueueJob(): void
public function testQueuedMailableForwardsMessageGroupFromMethodToQueueJob(): void
{
$mockedMessageGroupId = 'group-1';

$mailable = $this->getMockBuilder(MailableQueueableStubWithMessageGroup::class)->onlyMethods(['messageGroup'])->getMock();
$mailable->expects($this->once())->method('messageGroup')->willReturn($mockedMessageGroupId);

$queueFake = new QueueFake(new Application);
$mailer = $this->getMockBuilder(Mailer::class)
->setConstructorArgs($this->getMocks())
->onlyMethods(['createMessage', 'to'])
->getMock();
$mailer->setQueue($queueFake);
$queueFake->assertNothingPushed();
$mailer->send($mailable);
$queueFake->assertPushedOn(null, SendQueuedMailable::class);

$pushedJob = $queueFake->pushed(SendQueuedMailable::class)->first();
$this->assertEquals($mockedMessageGroupId, $pushedJob->messageGroup);
}

public function testQueuedMailableForwardsMessageGroupFromPropertyOverridingMethodToQueueJob(): void
{
$mockedMessageGroupId = 'group-1';

// Ensure the messageGroup method is not called when a messageGroup property is provided.
$mailable = $this->getMockBuilder(MailableQueueableStubWithMessageGroup::class)->onlyMethods(['messageGroup'])->getMock();
$mailable->expects($this->never())->method('messageGroup')->willReturn('this-should-not-be-used');
$mailable->onGroup($mockedMessageGroupId);

$queueFake = new QueueFake(new Application);
$mailer = $this->getMockBuilder(Mailer::class)
->setConstructorArgs($this->getMocks())
->onlyMethods(['createMessage', 'to'])
->getMock();
$mailer->setQueue($queueFake);
$mailable = (new MailableQueueableStub)->onGroup($mockedMessageGroupId);
$queueFake->assertNothingPushed();
$mailer->send($mailable);
$queueFake->assertPushedOn(null, SendQueuedMailable::class);
Expand Down Expand Up @@ -166,6 +191,26 @@ public function build(): self
}
}

class MailableQueueableStubWithMessageGroup extends Mailable implements ShouldQueue
{
use Queueable;

public function build(): self
{
$this
->subject('lorem ipsum')
->html('foo bar baz')
->to('foo@example.tld');

return $this;
}

public function messageGroup(): string
{
return 'group-1';
}
}

class MailableQueueableStubWithDeduplication extends Mailable implements ShouldQueue
{
use Queueable;
Expand Down
54 changes: 51 additions & 3 deletions tests/Notifications/NotificationChannelManagerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -178,15 +178,44 @@ public function testSendQueuedNotificationsCanBeOverrideViaContainer()
$manager->send([new NotificationChannelManagerTestNotifiable], new NotificationChannelManagerTestQueuedNotification);
}

public function testQueuedNotificationForwardsMessageGroupToQueueJob()
public function testQueuedNotificationForwardsMessageGroupFromMethodToQueueJob()
{
$mockedMessageGroupId = 'group-1';

$notification = $this->getMockBuilder(NotificationChannelManagerTestQueuedNotificationWithMessageGroupMethod::class)->onlyMethods(['messageGroup'])->getMock();
$notification->expects($this->exactly(2))->method('messageGroup')->willReturn($mockedMessageGroupId);

$container = new Container;
$container->instance('config', ['app.name' => 'Name', 'app.logo' => 'Logo']);
$container->instance(Dispatcher::class, $events = m::mock());
$container->instance(Bus::class, $bus = m::mock());
$bus->shouldReceive('dispatch')->twice()->withArgs(function ($job) use ($mockedMessageGroupId) {
$this->assertInstanceOf(SendQueuedNotifications::class, $job);
$this->assertEquals($mockedMessageGroupId, $job->messageGroup);

return true;
});
Container::setInstance($container);
$manager = m::mock(ChannelManager::class.'[driver]', [$container]);
$events->shouldReceive('listen')->once();

$manager->send([new NotificationChannelManagerTestNotifiable], $notification);
}

public function testQueuedNotificationForwardsMessageGroupFromPropertyOverridingMethodToQueueJob()
{
$mockedMessageGroupId = 'group-1';

// Ensure the messageGroup method is not called when a messageGroup property is provided.
$notification = $this->getMockBuilder(NotificationChannelManagerTestQueuedNotificationWithMessageGroupMethod::class)->onlyMethods(['messageGroup'])->getMock();
$notification->expects($this->never())->method('messageGroup')->willReturn('this-should-not-be-used');
$notification->onGroup($mockedMessageGroupId);

$container = new Container;
$container->instance('config', ['app.name' => 'Name', 'app.logo' => 'Logo']);
$container->instance(Dispatcher::class, $events = m::mock());
$container->instance(Bus::class, $bus = m::mock());
$bus->shouldReceive('dispatch')->once()->withArgs(function ($job) use ($mockedMessageGroupId) {
$bus->shouldReceive('dispatch')->twice()->withArgs(function ($job) use ($mockedMessageGroupId) {
$this->assertInstanceOf(SendQueuedNotifications::class, $job);
$this->assertEquals($mockedMessageGroupId, $job->messageGroup);

Expand All @@ -196,7 +225,6 @@ public function testQueuedNotificationForwardsMessageGroupToQueueJob()
$manager = m::mock(ChannelManager::class.'[driver]', [$container]);
$events->shouldReceive('listen')->once();

$notification = (new NotificationChannelManagerTestQueuedNotification)->onGroup($mockedMessageGroupId);
$manager->send([new NotificationChannelManagerTestNotifiable], $notification);
}

Expand Down Expand Up @@ -443,6 +471,26 @@ public function message()
}
}

class NotificationChannelManagerTestQueuedNotificationWithMessageGroupMethod extends Notification implements ShouldQueue
{
use Queueable;

public function via()
{
return ['test', 'test2'];
}

public function message()
{
return $this->line('test')->action('Text', 'url');
}

public function messageGroup()
{
return 'group-1';
}
}

class NotificationChannelManagerTestQueuedNotificationWithMessageGroups extends Notification implements ShouldQueue
{
use Queueable;
Expand Down
26 changes: 26 additions & 0 deletions tests/Queue/Fixtures/FakeSqsJobWithMessageGroup.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

namespace Illuminate\Tests\Queue\Fixtures;

use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;

class FakeSqsJobWithMessageGroup implements ShouldQueue
{
use Queueable;

public function handle(): void
{
//
}

/**
* Message group method called by SqsQueue.
*
* @return string
*/
public function messageGroup(): string
{
return 'group-1';
}
}
52 changes: 52 additions & 0 deletions tests/Queue/QueueSqsQueueTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use Illuminate\Support\Str;
use Illuminate\Tests\Queue\Fixtures\FakeSqsJob;
use Illuminate\Tests\Queue\Fixtures\FakeSqsJobWithDeduplication;
use Illuminate\Tests\Queue\Fixtures\FakeSqsJobWithMessageGroup;
use Laravel\SerializableClosure\SerializableClosure;
use Mockery as m;
use PHPUnit\Framework\TestCase;
Expand Down Expand Up @@ -363,6 +364,57 @@ public function testPushProperlyPushesJobObjectOntoSqsFifoQueue()
Str::createUuidsNormally();
}

public function testPushProperlyPushesJobObjectOntoSqsFifoQueueWithMessageGroupMethod()
{
Str::createUuidsUsing(fn () => $this->mockedDeduplicationId);

$job = $this->getMockBuilder(FakeSqsJobWithMessageGroup::class)->onlyMethods(['messageGroup'])->getMock();
$job->expects($this->once())->method('messageGroup')->willReturn($this->mockedMessageGroupId);

$queue = $this->getMockBuilder(SqsQueue::class)->onlyMethods(['createPayload', 'getQueue'])->setConstructorArgs([$this->sqs, $this->fifoQueueName, $this->account])->getMock();
$queue->setContainer($container = m::spy(Container::class));
$queue->expects($this->once())->method('createPayload')->with($job, $this->fifoQueueName, $this->mockedData)->willReturn($this->mockedPayload);
$queue->expects($this->once())->method('getQueue')->with($this->fifoQueueName)->willReturn($this->fifoQueueUrl);
$this->sqs->shouldReceive('sendMessage')->once()->with([
'QueueUrl' => $this->fifoQueueUrl,
'MessageBody' => $this->mockedPayload,
'MessageGroupId' => $this->mockedMessageGroupId,
'MessageDeduplicationId' => $this->mockedDeduplicationId,
])->andReturn($this->mockedSendMessageResponseModel);
$id = $queue->push($job, $this->mockedData, $this->fifoQueueName);
$this->assertEquals($this->mockedMessageId, $id);
$container->shouldHaveReceived('bound')->with('events')->twice();

Str::createUuidsNormally();
}

public function testPushProperlyPushesJobObjectOntoSqsFifoQueueWithMessageGroupPropertyOverridingMethod()
{
Str::createUuidsUsing(fn () => $this->mockedDeduplicationId);

$job = $this->getMockBuilder(FakeSqsJobWithMessageGroup::class)->onlyMethods(['messageGroup'])->getMock();

// Ensure the messageGroup method is not called when a messageGroup property is provided.
$job->expects($this->never())->method('messageGroup')->willReturn('this-should-not-be-used');
$job->onGroup($this->mockedMessageGroupId);

$queue = $this->getMockBuilder(SqsQueue::class)->onlyMethods(['createPayload', 'getQueue'])->setConstructorArgs([$this->sqs, $this->fifoQueueName, $this->account])->getMock();
$queue->setContainer($container = m::spy(Container::class));
$queue->expects($this->once())->method('createPayload')->with($job, $this->fifoQueueName, $this->mockedData)->willReturn($this->mockedPayload);
$queue->expects($this->once())->method('getQueue')->with($this->fifoQueueName)->willReturn($this->fifoQueueUrl);
$this->sqs->shouldReceive('sendMessage')->once()->with([
'QueueUrl' => $this->fifoQueueUrl,
'MessageBody' => $this->mockedPayload,
'MessageGroupId' => $this->mockedMessageGroupId,
'MessageDeduplicationId' => $this->mockedDeduplicationId,
])->andReturn($this->mockedSendMessageResponseModel);
$id = $queue->push($job, $this->mockedData, $this->fifoQueueName);
$this->assertEquals($this->mockedMessageId, $id);
$container->shouldHaveReceived('bound')->with('events')->twice();

Str::createUuidsNormally();
}

public function testPushProperlyPushesJobObjectOntoSqsFifoQueueWithDeduplicationId()
{
$job = $this->getMockBuilder(FakeSqsJobWithDeduplication::class)->onlyMethods(['deduplicationId'])->getMock();
Expand Down