Skip to content

Commit

Permalink
Add tests for not tenant aware jobs in a default tenant aware applica…
Browse files Browse the repository at this point in the history
…tion + fix them
  • Loading branch information
riasvdv committed Aug 19, 2024
1 parent 23190e4 commit cc19794
Show file tree
Hide file tree
Showing 14 changed files with 315 additions and 3 deletions.
19 changes: 19 additions & 0 deletions config/multitenancy.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
<?php

use Illuminate\Broadcasting\BroadcastEvent;
use Illuminate\Events\CallQueuedListener;
use Illuminate\Mail\SendQueuedMailable;
use Illuminate\Notifications\SendQueuedNotifications;
use Illuminate\Queue\CallQueuedClosure;
use Spatie\Multitenancy\Actions\ForgetCurrentTenantAction;
use Spatie\Multitenancy\Actions\MakeQueueTenantAwareAction;
use Spatie\Multitenancy\Actions\MakeTenantCurrentAction;
Expand Down Expand Up @@ -88,6 +93,20 @@
'migrate_tenant' => MigrateTenantAction::class,
],

/*
* You can customize the way in which the package resolves the queueable to a job.
*
* For example, using the package laravel-actions (by Loris Leiva), you can
* resolve JobDecorator to getAction() like so: JobDecorator::class => 'getAction'
*/
'queueable_to_job' => [
SendQueuedMailable::class => 'mailable',
SendQueuedNotifications::class => 'notification',
CallQueuedClosure::class => 'closure',
CallQueuedListener::class => 'class',
BroadcastEvent::class => 'event',
],

/*
* Jobs tenant aware even if these don't implement the TenantAware interface.
*/
Expand Down
2 changes: 1 addition & 1 deletion phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<env name="CACHE_DRIVER" value="file"/>
<env name="APP_KEY" value="base64:m+pDa0MKS1KpMlxzzdVEaqFHysv3IPhrx/3TFSWBqJA="/>
<env name="DB_USERNAME" value="root"/>
<env name="DB_PASSWORD" value=""/>
<env name="DB_PASSWORD" value="password"/>
<env name="DB_HOST" value="127.0.0.1"/>
<env name="DB_PORT" value="3306"/>
</php>
Expand Down
27 changes: 25 additions & 2 deletions src/Actions/MakeQueueTenantAwareAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@

namespace Spatie\Multitenancy\Actions;

use Illuminate\Mail\SendQueuedMailable;
use Illuminate\Notifications\SendQueuedNotifications;
use Illuminate\Queue\CallQueuedClosure;
use Illuminate\Queue\Events\JobProcessing;
use Illuminate\Queue\Events\JobRetryRequested;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Context;
use Spatie\Multitenancy\Concerns\BindAsCurrentTenant;
use Spatie\Multitenancy\Concerns\UsesMultitenancyConfig;
Expand Down Expand Up @@ -44,9 +48,13 @@ protected function listenForJobsRetryRequested(): static

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

$reflection = new \ReflectionClass($jobName);
$command = unserialize($payload['data']['command']);

$job = $this->getJobFromQueueable($command);

$reflection = new \ReflectionClass($job);

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

protected function getJobFromQueueable(object $queueable)
{
$job = Arr::get(config('multitenancy.queueable_to_job'), $queueable::class);

if (! $job) {
return $queueable;
}

if (method_exists($queueable, $job)) {
return $queueable->{$job}();
}

return $queueable->$job;
}

protected function getEventPayload($event): ?array
{
return match (true) {
Expand Down
55 changes: 55 additions & 0 deletions tests/Feature/TenantAwareJobs/QueuedBroadcastEventTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

use Illuminate\Auth\Events\Authenticated;
use Illuminate\Foundation\Auth\User;
use Illuminate\Support\Facades\Broadcast;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Mail;
use Spatie\Multitenancy\Exceptions\CurrentTenantCouldNotBeDeterminedInTenantAwareJob;
use Spatie\Multitenancy\Models\Tenant;
use Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses\BroadcastNotTenantAware;
use Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses\BroadcastTenantAware;
use Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses\ListenerNotTenantAware;
use Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses\ListenerTenantAware;
use Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses\MailableNotTenantAware;
use Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses\MailableTenantAware;
use Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses\TestEvent;

beforeEach(function () {
config()->set('multitenancy.queues_are_tenant_aware_by_default', true);
config()->set('queue.default', 'sync');
config()->set('mail.default', 'log');

$this->tenant = Tenant::factory()->create();
});

it('will fail when no tenant is present and listeners are tenant aware by default', function () {
config()->set('multitenancy.queues_are_tenant_aware_by_default', true);

Event::listen(TestEvent::class, ListenerTenantAware::class);

Broadcast::event(new BroadcastTenantAware("Hello world!"));
})->throws(CurrentTenantCouldNotBeDeterminedInTenantAwareJob::class);

it('will not fail when no tenant is present and listeners are tenant aware by default', function () {
config()->set('multitenancy.queues_are_tenant_aware_by_default', true);

Event::listen(TestEvent::class, ListenerNotTenantAware::class);
Broadcast::event(new BroadcastNotTenantAware("Hello world!"));

$this->expectExceptionMessage("Method Illuminate\Events\Dispatcher::assertDispatchedTimes does not exist.");

Event::assertDispatchedTimes(TestEvent::class);
});

it('will inject the current tenant id', function () {
config()->set('multitenancy.queues_are_tenant_aware_by_default', true);

$this->tenant->makeCurrent();

Event::listen(TestEvent::class, ListenerNotTenantAware::class);

expect(
Broadcast::event(new BroadcastTenantAware("Hello world!"))
)->toBeInstanceOf(\Illuminate\Broadcasting\PendingBroadcast::class);
});
52 changes: 52 additions & 0 deletions tests/Feature/TenantAwareJobs/QueuedListenerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

use Illuminate\Auth\Events\Authenticated;
use Illuminate\Foundation\Auth\User;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Mail;
use Spatie\Multitenancy\Exceptions\CurrentTenantCouldNotBeDeterminedInTenantAwareJob;
use Spatie\Multitenancy\Models\Tenant;
use Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses\ListenerNotTenantAware;
use Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses\ListenerTenantAware;
use Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses\MailableNotTenantAware;
use Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses\MailableTenantAware;
use Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses\TestEvent;

beforeEach(function () {
config()->set('multitenancy.queues_are_tenant_aware_by_default', true);
config()->set('queue.default', 'sync');
config()->set('mail.default', 'log');

$this->tenant = Tenant::factory()->create();
});

it('will fail when no tenant is present and listeners are tenant aware by default', function () {
config()->set('multitenancy.queues_are_tenant_aware_by_default', true);

Event::listen(TestEvent::class, ListenerTenantAware::class);

Event::dispatch(new TestEvent("Hello world!"));
})->throws(CurrentTenantCouldNotBeDeterminedInTenantAwareJob::class);

it('will not fail when no tenant is present and listeners are tenant aware by default', function () {
config()->set('multitenancy.queues_are_tenant_aware_by_default', true);

Event::listen(TestEvent::class, ListenerNotTenantAware::class);
Event::dispatch(new TestEvent("Hello world!"));

$this->expectExceptionMessage("Method Illuminate\Events\Dispatcher::assertDispatchedTimes does not exist.");

Event::assertDispatchedTimes(TestEvent::class);
});

it('will inject the current tenant id', function () {
config()->set('multitenancy.queues_are_tenant_aware_by_default', true);

$this->tenant->makeCurrent();

Event::listen(TestEvent::class, ListenerNotTenantAware::class);

expect(
Event::dispatch(new TestEvent("Hello world!"))
)->toEqual([0 => null]);
});
11 changes: 11 additions & 0 deletions tests/Feature/TenantAwareJobs/QueuedMailableTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use Illuminate\Support\Facades\Mail;
use Spatie\Multitenancy\Exceptions\CurrentTenantCouldNotBeDeterminedInTenantAwareJob;
use Spatie\Multitenancy\Models\Tenant;
use Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses\MailableNotTenantAware;
use Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses\MailableTenantAware;

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

it('will not fail when no tenant is present and mailables are tenant aware by default', function () {
config()->set('multitenancy.queues_are_tenant_aware_by_default', true);

Mail::to('[email protected]')->queue(new MailableNotTenantAware());

$this->expectExceptionMessage("Method Illuminate\Mail\Mailer::assertSentCount does not exist.");

Mail::assertSentCount(1);
});

it('will inject the current tenant id', function () {
config()->set('multitenancy.queues_are_tenant_aware_by_default', true);

Expand Down
11 changes: 11 additions & 0 deletions tests/Feature/TenantAwareJobs/QueuedNotificationsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use Illuminate\Support\Facades\Notification;
use Spatie\Multitenancy\Exceptions\CurrentTenantCouldNotBeDeterminedInTenantAwareJob;
use Spatie\Multitenancy\Tests\Feature\Models\TenantNotifiable;
use Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses\NotificationNotTenantAware;
use Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses\NotificationTenantAware;

beforeEach(function () {
Expand All @@ -21,6 +22,16 @@
Notification::assertNothingSent();
})->throws(CurrentTenantCouldNotBeDeterminedInTenantAwareJob::class);

it('will not fail when no tenant is present and mailables are tenant aware by default', function () {
config()->set('multitenancy.queues_are_tenant_aware_by_default', true);

$this->tenant->notify((new NotificationNotTenantAware()));

$this->expectExceptionMessage("Call to undefined method Illuminate\Notifications\Channels\MailChannel::assertCount()");

Notification::assertCount(1);
});

it('will inject the current tenant id', function () {
config()->set('multitenancy.queues_are_tenant_aware_by_default', true);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses;

use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
use Spatie\Multitenancy\Jobs\NotTenantAware;

class BroadcastNotTenantAware implements ShouldBroadcast, NotTenantAware
{
public function __construct(
public string $message,
) {}

public function broadcastOn()
{
return [
new Channel('test-channel'),
];
}
}
25 changes: 25 additions & 0 deletions tests/Feature/TenantAwareJobs/TestClasses/BroadcastTenantAware.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

namespace Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses;

use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
use Spatie\Multitenancy\Jobs\NotTenantAware;
use Spatie\Multitenancy\Jobs\TenantAware;

class BroadcastTenantAware implements ShouldBroadcast, TenantAware
{
public function __construct(
public string $message,
) {}

public function broadcastOn()
{
return [
new Channel('test-channel'),
];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

namespace Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses;

use Illuminate\Contracts\Queue\ShouldQueue;
use Spatie\Multitenancy\Jobs\NotTenantAware;

class ListenerNotTenantAware implements ShouldQueue, NotTenantAware
{
public function handle(TestEvent $event): void
{

}
}
14 changes: 14 additions & 0 deletions tests/Feature/TenantAwareJobs/TestClasses/ListenerTenantAware.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

namespace Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses;

use Illuminate\Contracts\Queue\ShouldQueue;
use Spatie\Multitenancy\Jobs\TenantAware;

class ListenerTenantAware implements ShouldQueue, TenantAware
{
public function handle(TestEvent $event): void
{

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

namespace Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses;

use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Spatie\Multitenancy\Jobs\NotTenantAware;

class MailableNotTenantAware extends Mailable implements ShouldQueue, NotTenantAware
{
public function build(): Mailable
{
return $this->view('mailable');
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

namespace Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
use Spatie\Multitenancy\Jobs\NotTenantAware;
use Spatie\Multitenancy\Jobs\TenantAware;

class NotificationNotTenantAware extends Notification implements ShouldQueue, NotTenantAware
{
use Queueable;

public function via($notifiable)
{
return ['mail'];
}

public function toMail($notifiable)
{
return (new MailMessage())
->subject('Message')
->greeting('Hello!')
->line('Say goodbye!');
}

public function toArray($notifiable)
{
return [ ];
}
}
16 changes: 16 additions & 0 deletions tests/Feature/TenantAwareJobs/TestClasses/TestEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace Spatie\Multitenancy\Tests\Feature\TenantAwareJobs\TestClasses;

use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class TestEvent
{
use Dispatchable, InteractsWithSockets, SerializesModels;

public function __construct(
public string $message,
) {}
}

0 comments on commit cc19794

Please sign in to comment.