Skip to content

Commit cc19794

Browse files
committed
Add tests for not tenant aware jobs in a default tenant aware application + fix them
1 parent 23190e4 commit cc19794

14 files changed

+315
-3
lines changed

config/multitenancy.php

+19
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
<?php
22

3+
use Illuminate\Broadcasting\BroadcastEvent;
4+
use Illuminate\Events\CallQueuedListener;
5+
use Illuminate\Mail\SendQueuedMailable;
6+
use Illuminate\Notifications\SendQueuedNotifications;
7+
use Illuminate\Queue\CallQueuedClosure;
38
use Spatie\Multitenancy\Actions\ForgetCurrentTenantAction;
49
use Spatie\Multitenancy\Actions\MakeQueueTenantAwareAction;
510
use Spatie\Multitenancy\Actions\MakeTenantCurrentAction;
@@ -88,6 +93,20 @@
8893
'migrate_tenant' => MigrateTenantAction::class,
8994
],
9095

96+
/*
97+
* You can customize the way in which the package resolves the queueable to a job.
98+
*
99+
* For example, using the package laravel-actions (by Loris Leiva), you can
100+
* resolve JobDecorator to getAction() like so: JobDecorator::class => 'getAction'
101+
*/
102+
'queueable_to_job' => [
103+
SendQueuedMailable::class => 'mailable',
104+
SendQueuedNotifications::class => 'notification',
105+
CallQueuedClosure::class => 'closure',
106+
CallQueuedListener::class => 'class',
107+
BroadcastEvent::class => 'event',
108+
],
109+
91110
/*
92111
* Jobs tenant aware even if these don't implement the TenantAware interface.
93112
*/

phpunit.xml.dist

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
<env name="CACHE_DRIVER" value="file"/>
1010
<env name="APP_KEY" value="base64:m+pDa0MKS1KpMlxzzdVEaqFHysv3IPhrx/3TFSWBqJA="/>
1111
<env name="DB_USERNAME" value="root"/>
12-
<env name="DB_PASSWORD" value=""/>
12+
<env name="DB_PASSWORD" value="password"/>
1313
<env name="DB_HOST" value="127.0.0.1"/>
1414
<env name="DB_PORT" value="3306"/>
1515
</php>

src/Actions/MakeQueueTenantAwareAction.php

+25-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@
22

33
namespace Spatie\Multitenancy\Actions;
44

5+
use Illuminate\Mail\SendQueuedMailable;
6+
use Illuminate\Notifications\SendQueuedNotifications;
7+
use Illuminate\Queue\CallQueuedClosure;
58
use Illuminate\Queue\Events\JobProcessing;
69
use Illuminate\Queue\Events\JobRetryRequested;
10+
use Illuminate\Support\Arr;
711
use Illuminate\Support\Facades\Context;
812
use Spatie\Multitenancy\Concerns\BindAsCurrentTenant;
913
use Spatie\Multitenancy\Concerns\UsesMultitenancyConfig;
@@ -44,9 +48,13 @@ protected function listenForJobsRetryRequested(): static
4448

4549
protected function isTenantAware(JobProcessing|JobRetryRequested $event): bool
4650
{
47-
$jobName = $this->getEventPayload($event)['data']['commandName'];
51+
$payload = $this->getEventPayload($event);
4852

49-
$reflection = new \ReflectionClass($jobName);
53+
$command = unserialize($payload['data']['command']);
54+
55+
$job = $this->getJobFromQueueable($command);
56+
57+
$reflection = new \ReflectionClass($job);
5058

5159
if ($reflection->implementsInterface(TenantAware::class)) {
5260
return true;
@@ -67,6 +75,21 @@ protected function isTenantAware(JobProcessing|JobRetryRequested $event): bool
6775
return config('multitenancy.queues_are_tenant_aware_by_default') === true;
6876
}
6977

78+
protected function getJobFromQueueable(object $queueable)
79+
{
80+
$job = Arr::get(config('multitenancy.queueable_to_job'), $queueable::class);
81+
82+
if (! $job) {
83+
return $queueable;
84+
}
85+
86+
if (method_exists($queueable, $job)) {
87+
return $queueable->{$job}();
88+
}
89+
90+
return $queueable->$job;
91+
}
92+
7093
protected function getEventPayload($event): ?array
7194
{
7295
return match (true) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
3+
use Illuminate\Auth\Events\Authenticated;
4+
use Illuminate\Foundation\Auth\User;
5+
use Illuminate\Support\Facades\Broadcast;
6+
use Illuminate\Support\Facades\Event;
7+
use Illuminate\Support\Facades\Mail;
8+
use Spatie\Multitenancy\Exceptions\CurrentTenantCouldNotBeDeterminedInTenantAwareJob;
9+
use Spatie\Multitenancy\Models\Tenant;
10+
use Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses\BroadcastNotTenantAware;
11+
use Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses\BroadcastTenantAware;
12+
use Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses\ListenerNotTenantAware;
13+
use Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses\ListenerTenantAware;
14+
use Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses\MailableNotTenantAware;
15+
use Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses\MailableTenantAware;
16+
use Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses\TestEvent;
17+
18+
beforeEach(function () {
19+
config()->set('multitenancy.queues_are_tenant_aware_by_default', true);
20+
config()->set('queue.default', 'sync');
21+
config()->set('mail.default', 'log');
22+
23+
$this->tenant = Tenant::factory()->create();
24+
});
25+
26+
it('will fail when no tenant is present and listeners are tenant aware by default', function () {
27+
config()->set('multitenancy.queues_are_tenant_aware_by_default', true);
28+
29+
Event::listen(TestEvent::class, ListenerTenantAware::class);
30+
31+
Broadcast::event(new BroadcastTenantAware("Hello world!"));
32+
})->throws(CurrentTenantCouldNotBeDeterminedInTenantAwareJob::class);
33+
34+
it('will not fail when no tenant is present and listeners are tenant aware by default', function () {
35+
config()->set('multitenancy.queues_are_tenant_aware_by_default', true);
36+
37+
Event::listen(TestEvent::class, ListenerNotTenantAware::class);
38+
Broadcast::event(new BroadcastNotTenantAware("Hello world!"));
39+
40+
$this->expectExceptionMessage("Method Illuminate\Events\Dispatcher::assertDispatchedTimes does not exist.");
41+
42+
Event::assertDispatchedTimes(TestEvent::class);
43+
});
44+
45+
it('will inject the current tenant id', function () {
46+
config()->set('multitenancy.queues_are_tenant_aware_by_default', true);
47+
48+
$this->tenant->makeCurrent();
49+
50+
Event::listen(TestEvent::class, ListenerNotTenantAware::class);
51+
52+
expect(
53+
Broadcast::event(new BroadcastTenantAware("Hello world!"))
54+
)->toBeInstanceOf(\Illuminate\Broadcasting\PendingBroadcast::class);
55+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
use Illuminate\Auth\Events\Authenticated;
4+
use Illuminate\Foundation\Auth\User;
5+
use Illuminate\Support\Facades\Event;
6+
use Illuminate\Support\Facades\Mail;
7+
use Spatie\Multitenancy\Exceptions\CurrentTenantCouldNotBeDeterminedInTenantAwareJob;
8+
use Spatie\Multitenancy\Models\Tenant;
9+
use Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses\ListenerNotTenantAware;
10+
use Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses\ListenerTenantAware;
11+
use Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses\MailableNotTenantAware;
12+
use Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses\MailableTenantAware;
13+
use Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses\TestEvent;
14+
15+
beforeEach(function () {
16+
config()->set('multitenancy.queues_are_tenant_aware_by_default', true);
17+
config()->set('queue.default', 'sync');
18+
config()->set('mail.default', 'log');
19+
20+
$this->tenant = Tenant::factory()->create();
21+
});
22+
23+
it('will fail when no tenant is present and listeners are tenant aware by default', function () {
24+
config()->set('multitenancy.queues_are_tenant_aware_by_default', true);
25+
26+
Event::listen(TestEvent::class, ListenerTenantAware::class);
27+
28+
Event::dispatch(new TestEvent("Hello world!"));
29+
})->throws(CurrentTenantCouldNotBeDeterminedInTenantAwareJob::class);
30+
31+
it('will not fail when no tenant is present and listeners are tenant aware by default', function () {
32+
config()->set('multitenancy.queues_are_tenant_aware_by_default', true);
33+
34+
Event::listen(TestEvent::class, ListenerNotTenantAware::class);
35+
Event::dispatch(new TestEvent("Hello world!"));
36+
37+
$this->expectExceptionMessage("Method Illuminate\Events\Dispatcher::assertDispatchedTimes does not exist.");
38+
39+
Event::assertDispatchedTimes(TestEvent::class);
40+
});
41+
42+
it('will inject the current tenant id', function () {
43+
config()->set('multitenancy.queues_are_tenant_aware_by_default', true);
44+
45+
$this->tenant->makeCurrent();
46+
47+
Event::listen(TestEvent::class, ListenerNotTenantAware::class);
48+
49+
expect(
50+
Event::dispatch(new TestEvent("Hello world!"))
51+
)->toEqual([0 => null]);
52+
});

tests/Feature/TenantAwareJobs/QueuedMailableTest.php

+11
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use Illuminate\Support\Facades\Mail;
44
use Spatie\Multitenancy\Exceptions\CurrentTenantCouldNotBeDeterminedInTenantAwareJob;
55
use Spatie\Multitenancy\Models\Tenant;
6+
use Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses\MailableNotTenantAware;
67
use Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses\MailableTenantAware;
78

89
beforeEach(function () {
@@ -19,6 +20,16 @@
1920
Mail::to('[email protected]')->queue(new MailableTenantAware());
2021
})->throws(CurrentTenantCouldNotBeDeterminedInTenantAwareJob::class);
2122

23+
it('will not fail when no tenant is present and mailables are tenant aware by default', function () {
24+
config()->set('multitenancy.queues_are_tenant_aware_by_default', true);
25+
26+
Mail::to('[email protected]')->queue(new MailableNotTenantAware());
27+
28+
$this->expectExceptionMessage("Method Illuminate\Mail\Mailer::assertSentCount does not exist.");
29+
30+
Mail::assertSentCount(1);
31+
});
32+
2233
it('will inject the current tenant id', function () {
2334
config()->set('multitenancy.queues_are_tenant_aware_by_default', true);
2435

tests/Feature/TenantAwareJobs/QueuedNotificationsTest.php

+11
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use Illuminate\Support\Facades\Notification;
44
use Spatie\Multitenancy\Exceptions\CurrentTenantCouldNotBeDeterminedInTenantAwareJob;
55
use Spatie\Multitenancy\Tests\Feature\Models\TenantNotifiable;
6+
use Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses\NotificationNotTenantAware;
67
use Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses\NotificationTenantAware;
78

89
beforeEach(function () {
@@ -21,6 +22,16 @@
2122
Notification::assertNothingSent();
2223
})->throws(CurrentTenantCouldNotBeDeterminedInTenantAwareJob::class);
2324

25+
it('will not fail when no tenant is present and mailables are tenant aware by default', function () {
26+
config()->set('multitenancy.queues_are_tenant_aware_by_default', true);
27+
28+
$this->tenant->notify((new NotificationNotTenantAware()));
29+
30+
$this->expectExceptionMessage("Call to undefined method Illuminate\Notifications\Channels\MailChannel::assertCount()");
31+
32+
Notification::assertCount(1);
33+
});
34+
2435
it('will inject the current tenant id', function () {
2536
config()->set('multitenancy.queues_are_tenant_aware_by_default', true);
2637

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
namespace Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses;
4+
5+
use Illuminate\Broadcasting\Channel;
6+
use Illuminate\Broadcasting\InteractsWithSockets;
7+
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
8+
use Illuminate\Foundation\Events\Dispatchable;
9+
use Illuminate\Queue\SerializesModels;
10+
use Spatie\Multitenancy\Jobs\NotTenantAware;
11+
12+
class BroadcastNotTenantAware implements ShouldBroadcast, NotTenantAware
13+
{
14+
public function __construct(
15+
public string $message,
16+
) {}
17+
18+
public function broadcastOn()
19+
{
20+
return [
21+
new Channel('test-channel'),
22+
];
23+
}
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
namespace Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses;
4+
5+
use Illuminate\Broadcasting\Channel;
6+
use Illuminate\Broadcasting\InteractsWithSockets;
7+
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
8+
use Illuminate\Foundation\Events\Dispatchable;
9+
use Illuminate\Queue\SerializesModels;
10+
use Spatie\Multitenancy\Jobs\NotTenantAware;
11+
use Spatie\Multitenancy\Jobs\TenantAware;
12+
13+
class BroadcastTenantAware implements ShouldBroadcast, TenantAware
14+
{
15+
public function __construct(
16+
public string $message,
17+
) {}
18+
19+
public function broadcastOn()
20+
{
21+
return [
22+
new Channel('test-channel'),
23+
];
24+
}
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
namespace Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses;
4+
5+
use Illuminate\Contracts\Queue\ShouldQueue;
6+
use Spatie\Multitenancy\Jobs\NotTenantAware;
7+
8+
class ListenerNotTenantAware implements ShouldQueue, NotTenantAware
9+
{
10+
public function handle(TestEvent $event): void
11+
{
12+
13+
}
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
namespace Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses;
4+
5+
use Illuminate\Contracts\Queue\ShouldQueue;
6+
use Spatie\Multitenancy\Jobs\TenantAware;
7+
8+
class ListenerTenantAware implements ShouldQueue, TenantAware
9+
{
10+
public function handle(TestEvent $event): void
11+
{
12+
13+
}
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
namespace Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses;
4+
5+
use Illuminate\Contracts\Queue\ShouldQueue;
6+
use Illuminate\Mail\Mailable;
7+
use Spatie\Multitenancy\Jobs\NotTenantAware;
8+
9+
class MailableNotTenantAware extends Mailable implements ShouldQueue, NotTenantAware
10+
{
11+
public function build(): Mailable
12+
{
13+
return $this->view('mailable');
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
namespace Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses;
4+
5+
use Illuminate\Bus\Queueable;
6+
use Illuminate\Contracts\Queue\ShouldQueue;
7+
use Illuminate\Notifications\Messages\MailMessage;
8+
use Illuminate\Notifications\Notification;
9+
use Spatie\Multitenancy\Jobs\NotTenantAware;
10+
use Spatie\Multitenancy\Jobs\TenantAware;
11+
12+
class NotificationNotTenantAware extends Notification implements ShouldQueue, NotTenantAware
13+
{
14+
use Queueable;
15+
16+
public function via($notifiable)
17+
{
18+
return ['mail'];
19+
}
20+
21+
public function toMail($notifiable)
22+
{
23+
return (new MailMessage())
24+
->subject('Message')
25+
->greeting('Hello!')
26+
->line('Say goodbye!');
27+
}
28+
29+
public function toArray($notifiable)
30+
{
31+
return [ ];
32+
}
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
namespace Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses;
4+
5+
use Illuminate\Broadcasting\InteractsWithSockets;
6+
use Illuminate\Foundation\Events\Dispatchable;
7+
use Illuminate\Queue\SerializesModels;
8+
9+
class TestEvent
10+
{
11+
use Dispatchable, InteractsWithSockets, SerializesModels;
12+
13+
public function __construct(
14+
public string $message,
15+
) {}
16+
}

0 commit comments

Comments
 (0)