1
0
Fork 0
mirror of https://github.com/archtechx/tenancy.git synced 2025-12-12 08:24:04 +00:00

Improve queue tenancy

This commit is contained in:
Samuel Štancl 2021-12-31 18:10:03 +01:00
parent 435d8528a7
commit 73a4a3018c
8 changed files with 279 additions and 36 deletions

View file

@ -4,18 +4,30 @@ declare(strict_types=1);
namespace Stancl\Tenancy\Tests;
use Exception;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\Events\JobProcessed;
use Illuminate\Queue\Events\JobProcessing;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Schema;
use Spatie\Valuestore\Valuestore;
use Stancl\JobPipeline\JobPipeline;
use Stancl\Tenancy\Bootstrappers\DatabaseTenancyBootstrapper;
use Stancl\Tenancy\Bootstrappers\QueueTenancyBootstrapper;
use Stancl\Tenancy\Events\TenancyEnded;
use Stancl\Tenancy\Events\TenancyInitialized;
use Stancl\Tenancy\Events\TenantCreated;
use Stancl\Tenancy\Jobs\CreateDatabase;
use Stancl\Tenancy\Listeners\BootstrapTenancy;
use Stancl\Tenancy\Listeners\RevertToCentralContext;
use Stancl\Tenancy\Tests\Etc\Tenant;
use Stancl\Tenancy\Tests\Etc\User;
class QueueTest extends TestCase
{
@ -31,15 +43,49 @@ class QueueTest extends TestCase
config([
'tenancy.bootstrappers' => [
QueueTenancyBootstrapper::class,
DatabaseTenancyBootstrapper::class,
],
'queue.default' => 'redis',
]);
Event::listen(TenancyInitialized::class, BootstrapTenancy::class);
Event::listen(TenancyEnded::class, RevertToCentralContext::class);
$this->valuestore = Valuestore::make(__DIR__ . '/Etc/tmp/queuetest.json')->flush();
}
protected function withFailedJobs()
{
Schema::connection('central')->create('failed_jobs', function (Blueprint $table) {
$table->id();
$table->string('uuid')->unique();
$table->text('connection');
$table->text('queue');
$table->longText('payload');
$table->longText('exception');
$table->timestamp('failed_at')->useCurrent();
});
}
protected function withUsers()
{
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('email')->unique();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
}
protected function withTenantDatabases()
{
Event::listen(TenantCreated::class, JobPipeline::make([CreateDatabase::class])->send(function (TenantCreated $event) {
return $event->tenant;
})->toListener());
}
/** @test */
public function tenant_id_is_passed_to_tenant_queues()
{
@ -49,7 +95,7 @@ class QueueTest extends TestCase
tenancy()->initialize($tenant);
Event::fake([JobProcessing::class]);
Event::fake([JobProcessing::class, JobProcessed::class]);
dispatch(new TestJob($this->valuestore));
@ -79,21 +125,91 @@ class QueueTest extends TestCase
});
}
/** @test */
public function tenancy_is_initialized_inside_queues()
/**
* @test
*
* @testWith [true]
* [false]
*/
public function tenancy_is_initialized_inside_queues(bool $shouldEndTenancy)
{
$tenant = Tenant::create([
'id' => 'acme',
]);
$this->withTenantDatabases();
$this->withFailedJobs();
$tenant = Tenant::create();
tenancy()->initialize($tenant);
dispatch(new TestJob($this->valuestore));
$this->withUsers();
$user = User::create(['name' => 'Foo', 'email' => 'foo@bar.com', 'password' => 'secret']);
$this->valuestore->put('userName', 'Bar');
dispatch(new TestJob($this->valuestore, $user));
$this->assertFalse($this->valuestore->has('tenant_id'));
if ($shouldEndTenancy) {
tenancy()->end();
}
$this->artisan('queue:work --once');
$this->assertSame('The current tenant id is: acme', $this->valuestore->get('tenant_id'));
$this->assertSame(0, DB::connection('central')->table('failed_jobs')->count());
$this->assertSame('The current tenant id is: ' . $tenant->id, $this->valuestore->get('tenant_id'));
$tenant->run(function () use ($user) {
$this->assertSame('Bar', $user->fresh()->name);
});
}
/**
* @test
*
* @testWith [true]
* [false]
*/
public function tenancy_is_initialized_when_retrying_jobs(bool $shouldEndTenancy)
{
$this->withFailedJobs();
$this->withTenantDatabases();
$tenant = Tenant::create();
tenancy()->initialize($tenant);
$this->withUsers();
$user = User::create(['name' => 'Foo', 'email' => 'foo@bar.com', 'password' => 'secret']);
$this->valuestore->put('userName', 'Bar');
$this->valuestore->put('shouldFail', true);
dispatch(new TestJob($this->valuestore, $user));
$this->assertFalse($this->valuestore->has('tenant_id'));
if ($shouldEndTenancy) {
tenancy()->end();
}
$this->artisan('queue:work --once');
$this->assertSame(1, DB::connection('central')->table('failed_jobs')->count());
$this->assertNull($this->valuestore->get('tenant_id')); // job failed
$this->artisan('queue:retry all');
$this->artisan('queue:work --once');
$this->assertSame(0, DB::connection('central')->table('failed_jobs')->count());
$this->assertSame('The current tenant id is: ' . $tenant->id, $this->valuestore->get('tenant_id')); // job succeeded
$tenant->run(function () use ($user) {
$this->assertSame('Bar', $user->fresh()->name);
});
}
/** @test */
@ -127,13 +243,31 @@ class TestJob implements ShouldQueue
/** @var Valuestore */
protected $valuestore;
public function __construct(Valuestore $valuestore)
/** @var User|null */
protected $user;
public function __construct(Valuestore $valuestore, User $user = null)
{
$this->valuestore = $valuestore;
$this->user = $user;
}
public function handle()
{
if ($this->valuestore->get('shouldFail')) {
$this->valuestore->put('shouldFail', false);
throw new Exception('failing');
}
if ($this->user) {
assert($this->user->getConnectionName() === 'tenant');
}
$this->valuestore->put('tenant_id', 'The current tenant id is: ' . tenant('id'));
if ($userName = $this->valuestore->get('userName')) {
$this->user->update(['name' => $userName]);
}
}
}