From efa8c0368091d0bc5e86aeeee9282689f6ecbb13 Mon Sep 17 00:00:00 2001 From: lukinovec Date: Fri, 21 Aug 2026 14:06:24 +0200 Subject: [PATCH] Assert that the tenant asset root is read from the resolved disk (regression tests) Test that tenant assets can be served from scoped disks, and that tenant asset roots respect the disk's configured prefix. Currently, the tests fail because TenantAssetController grabs the root from the config instead of resolving it via Storage::disk(...)->path(''). --- tests/TenantAssetTest.php | 78 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 74 insertions(+), 4 deletions(-) diff --git a/tests/TenantAssetTest.php b/tests/TenantAssetTest.php index a29bef97..cb789a40 100644 --- a/tests/TenantAssetTest.php +++ b/tests/TenantAssetTest.php @@ -120,23 +120,93 @@ test('the disk used for serving tenant assets is configurable', function () { expect($response->getFile()->getPathname())->toBe($path); }); -test('tenant asset controller throws when the configured disk has no root', function () { +test('tenant asset controller throws when the configured disk is not local', function () { config([ 'tenancy.identification.default_middleware' => InitializeTenancyByRequestData::class, - 'filesystems.disks.rootless' => ['driver' => 's3'], + // Add a disk that uses the s3 driver (= non-local disk). + // Use dummy credentials so that the s3 disk can be resolved without throwing an AWS exception. + 'filesystems.disks.remote' => [ + 'driver' => 's3', + 'region' => 'us-east-1', + 'key' => 'key', + 'secret' => 'secret', + 'bucket' => 'bucket', + ], ]); - TenantAssetController::$publicDisk = 'rootless'; + TenantAssetController::$publicDisk = 'remote'; $tenant = Tenant::create(); tenancy()->initialize($tenant); $this->withoutExceptionHandling(); - pest()->expectExceptionMessage('Disk [rootless] has no root path configured.'); + pest()->expectExceptionMessage('Disk [remote] is not a local disk.'); pest()->get(tenant_asset('foo.txt'), ['X-Tenant' => $tenant->id]); }); +test('tenant assets are served from the resolved root of a scoped disk', function () { + config([ + 'tenancy.identification.default_middleware' => InitializeTenancyByRequestData::class, + // A scoped disk has no configured root -- it inherits the root of its parent disk + // and appends its prefix to it, both of which happens when the disk is resolved. + 'filesystems.disks.scoped_disk' => [ + 'driver' => 'scoped', + 'disk' => 'public', + 'prefix' => 'scoped_disk_prefix', + ], + // Default tenancy config, set it here for clarity + 'tenancy.filesystem.disks' => ['local', 'public'], + ]); + + TenantAssetController::$publicDisk = 'scoped_disk'; + + $tenant = Tenant::create(); + tenancy()->initialize($tenant); + + $filename = 'testfile' . Str::random(8); + Storage::disk('scoped_disk')->put($filename, 'bar'); + $path = Storage::disk('scoped_disk')->path($filename); + + // The parent disk is tenant-aware, so the scoped disk's root is inside the tenant's storage directory + expect($path)->toBe(storage_path("app/public/scoped_disk_prefix/$filename")); + + $response = pest()->get(tenant_asset($filename), ['X-Tenant' => $tenant->id]); + + $response->assertSuccessful(); + expect($response->getFile()->getPathname())->toBe($path); +}); + +test('tenant assets are served from the resolved root of a disk with a configured prefix', function () { + config([ + 'tenancy.identification.default_middleware' => InitializeTenancyByRequestData::class, + // A prefix is part of the disk's root path, so it has to be included in the asset root + 'filesystems.disks.prefixed' => [ + 'driver' => 'local', + 'root' => storage_path('app/media'), + 'prefix' => 'foo-prefix', + ], + 'tenancy.filesystem.disks' => ['prefixed'], + 'tenancy.filesystem.root_override.prefixed' => '%storage_path%/app/media/', + ]); + + TenantAssetController::$publicDisk = 'prefixed'; + + $tenant = Tenant::create(); + tenancy()->initialize($tenant); + + $filename = 'testfile' . Str::random(8); + Storage::disk('prefixed')->put($filename, 'bar'); + $path = Storage::disk('prefixed')->path($filename); + + expect($path)->toBe(storage_path("app/media/foo-prefix/$filename")); + + $response = pest()->get(tenant_asset($filename), ['X-Tenant' => $tenant->id]); + + $response->assertSuccessful(); + expect($response->getFile()->getPathname())->toBe($path); +}); + test('tenant assets are served from the central storage path in central context', function () { config(['tenancy.identification.default_middleware' => InitializeTenancyByRequestData::class]);