From 66ee08776a4d1c6aaae0f21ec602287643f95ab9 Mon Sep 17 00:00:00 2001 From: Ollie Read Date: Mon, 18 Nov 2024 11:14:58 +0000 Subject: [PATCH 01/21] chore: Temporarily migrate tests --- .../Database/Eloquent/BelongsToManyTenantsTest.php | 3 +-- .../Database/Eloquent/BelongsToTenantTest.php | 2 +- tests/{ => _Original}/Database/Eloquent/TenantChildTest.php | 3 +-- tests/{ => _Original}/Http/Resolvers/CookieResolverTest.php | 2 +- tests/{ => _Original}/Http/Resolvers/HeaderResolverTest.php | 2 +- tests/{ => _Original}/Http/Resolvers/PathResolverTest.php | 2 +- tests/{ => _Original}/Http/Resolvers/SessionResolverTest.php | 3 +-- .../{ => _Original}/Http/Resolvers/SubdomainResolverTest.php | 2 +- .../{ => _Original}/Listeners/SetCurrentTenantForJobTest.php | 4 +--- tests/{ => _Original}/Overrides/CookieOverrideTest.php | 5 +---- tests/{ => _Original}/Overrides/StorageOverrideTest.php | 2 +- .../{ => _Original}/Providers/DatabaseTenantProviderTest.php | 2 +- .../{ => _Original}/Providers/EloquentTenantProviderTest.php | 4 +--- tests/{ => _Original}/ServiceProviderTest.php | 4 ++-- tests/{ => _Original}/SproutTest.php | 3 +-- tests/{ => _Original}/TenancyOptionsTest.php | 2 +- 16 files changed, 17 insertions(+), 28 deletions(-) rename tests/{ => _Original}/Database/Eloquent/BelongsToManyTenantsTest.php (99%) rename tests/{ => _Original}/Database/Eloquent/BelongsToTenantTest.php (99%) rename tests/{ => _Original}/Database/Eloquent/TenantChildTest.php (98%) rename tests/{ => _Original}/Http/Resolvers/CookieResolverTest.php (97%) rename tests/{ => _Original}/Http/Resolvers/HeaderResolverTest.php (97%) rename tests/{ => _Original}/Http/Resolvers/PathResolverTest.php (98%) rename tests/{ => _Original}/Http/Resolvers/SessionResolverTest.php (98%) rename tests/{ => _Original}/Http/Resolvers/SubdomainResolverTest.php (99%) rename tests/{ => _Original}/Listeners/SetCurrentTenantForJobTest.php (96%) rename tests/{ => _Original}/Overrides/CookieOverrideTest.php (97%) rename tests/{ => _Original}/Overrides/StorageOverrideTest.php (99%) rename tests/{ => _Original}/Providers/DatabaseTenantProviderTest.php (98%) rename tests/{ => _Original}/Providers/EloquentTenantProviderTest.php (95%) rename tests/{ => _Original}/ServiceProviderTest.php (98%) rename tests/{ => _Original}/SproutTest.php (97%) rename tests/{ => _Original}/TenancyOptionsTest.php (98%) diff --git a/tests/Database/Eloquent/BelongsToManyTenantsTest.php b/tests/_Original/Database/Eloquent/BelongsToManyTenantsTest.php similarity index 99% rename from tests/Database/Eloquent/BelongsToManyTenantsTest.php rename to tests/_Original/Database/Eloquent/BelongsToManyTenantsTest.php index 2c4891a..1dfda19 100644 --- a/tests/Database/Eloquent/BelongsToManyTenantsTest.php +++ b/tests/_Original/Database/Eloquent/BelongsToManyTenantsTest.php @@ -1,7 +1,7 @@ assertArrayHasKey($key . '/../resources/config/multitenancy.php', $paths); $this->assertContains(config_path('multitenancy.php'), $paths); diff --git a/tests/SproutTest.php b/tests/_Original/SproutTest.php similarity index 97% rename from tests/SproutTest.php rename to tests/_Original/SproutTest.php index 43760f2..26d126b 100644 --- a/tests/SproutTest.php +++ b/tests/_Original/SproutTest.php @@ -1,7 +1,7 @@ Date: Mon, 18 Nov 2024 20:57:35 +0000 Subject: [PATCH 02/21] test: Separate out test suites --- phpunit.xml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/phpunit.xml b/phpunit.xml index 36e7c26..6bae6fd 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -15,8 +15,14 @@ testdox="true" > - - tests + + tests/_Original + + + tests/Feature + + + tests/Unit From fb6376cf60aed144204eeaa3b4dc695553ad114e Mon Sep 17 00:00:00 2001 From: Ollie Read Date: Mon, 18 Nov 2024 20:57:47 +0000 Subject: [PATCH 03/21] refactor: Remove unused method --- src/Sprout.php | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/Sprout.php b/src/Sprout.php index f8beb82..a2ecdff 100644 --- a/src/Sprout.php +++ b/src/Sprout.php @@ -118,18 +118,6 @@ public function getAllCurrentTenancies(): array return $this->tenancies; } - /** - * Should Sprout listen for the routing event - * - * @return bool - * - * @throws \Illuminate\Contracts\Container\BindingResolutionException - */ - public function shouldListenForRouting(): bool - { - return (bool)$this->config('listen_for_routing', true); - } - /** * Get the identity resolver manager * From e44e72369433f437ba27da8b86d34c04a39d7d1c Mon Sep 17 00:00:00 2001 From: Ollie Read Date: Mon, 18 Nov 2024 20:57:58 +0000 Subject: [PATCH 04/21] fix: Correct naming of markOutsideContext method --- src/Sprout.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Sprout.php b/src/Sprout.php index a2ecdff..c853f13 100644 --- a/src/Sprout.php +++ b/src/Sprout.php @@ -188,7 +188,7 @@ public function markAsInContext(): self * * @return static */ - public function maskAsOutsideContext(): self + public function markAsOutsideContext(): self { $this->withinContext = false; From 9554346055330f4a56dd86aea209f4ac49fac245 Mon Sep 17 00:00:00 2001 From: Ollie Read Date: Mon, 18 Nov 2024 20:58:09 +0000 Subject: [PATCH 05/21] test: Start separate unit tests --- tests/Unit/Attributes/CurrentTenantTest.php | 83 +++++++++ tests/Unit/SproutServiceProviderTest.php | 181 ++++++++++++++++++++ tests/Unit/SproutTest.php | 120 +++++++++++++ tests/Unit/Support/DefaultTenancyTest.php | 169 ++++++++++++++++++ tests/Unit/Support/ResolutionHelperTest.php | 48 ++++++ tests/Unit/TenancyOptionsTest.php | 73 ++++++++ tests/Unit/UnitTestCase.php | 13 ++ 7 files changed, 687 insertions(+) create mode 100644 tests/Unit/Attributes/CurrentTenantTest.php create mode 100644 tests/Unit/SproutServiceProviderTest.php create mode 100644 tests/Unit/SproutTest.php create mode 100644 tests/Unit/Support/DefaultTenancyTest.php create mode 100644 tests/Unit/Support/ResolutionHelperTest.php create mode 100644 tests/Unit/TenancyOptionsTest.php create mode 100644 tests/Unit/UnitTestCase.php diff --git a/tests/Unit/Attributes/CurrentTenantTest.php b/tests/Unit/Attributes/CurrentTenantTest.php new file mode 100644 index 0000000..2015310 --- /dev/null +++ b/tests/Unit/Attributes/CurrentTenantTest.php @@ -0,0 +1,83 @@ +set('multitenancy.providers.tenants.model', TenantModel::class); + }); + } + + protected function setupSecondTenancy($app): void + { + tap($app['config'], static function (Repository $config) { + $config->set('multitenancy.providers.backup', [ + 'driver' => 'database', + 'table' => 'tenants', + ]); + + $config->set('multitenancy.tenancies.backup', [ + 'provider' => 'backup', + ]); + }); + } + + #[Test] + public function resolvesCurrentTenant(): void + { + /** @var \Sprout\Contracts\Tenancy $tenancy */ + $tenancy = app(TenancyManager::class)->get('tenants'); + + sprout()->setCurrentTenancy($tenancy); + + $tenant = TenantModel::factory()->createOne(); + + $tenancy->setTenant($tenant); + + $callback = static function (#[CurrentTenant] TenantModel $tenant) { + return $tenant; + }; + + $currentTenant = $this->app->call($callback); + + $this->assertSame($tenant, $currentTenant); + $this->assertSame($tenancy->tenant(), $currentTenant); + } + + #[Test, DefineEnvironment('setupSecondTenancy')] + public function resolvesCurrentTenantForSpecificTenancy(): void + { + /** @var \Sprout\Contracts\Tenancy $tenancy */ + $tenancy = app(TenancyManager::class)->get('backup'); + + sprout()->setCurrentTenancy($tenancy); + + $tenant = new GenericTenant(TenantModel::factory()->createOne()->toArray()); + + $tenancy->setTenant($tenant); + + $callback = static function (#[CurrentTenant('backup')] GenericTenant $tenant) { + return $tenant; + }; + + $currentTenant = $this->app->call($callback); + + $this->assertSame($tenant, $currentTenant); + $this->assertSame($tenancy->tenant(), $currentTenant); + } +} diff --git a/tests/Unit/SproutServiceProviderTest.php b/tests/Unit/SproutServiceProviderTest.php new file mode 100644 index 0000000..7bf8914 --- /dev/null +++ b/tests/Unit/SproutServiceProviderTest.php @@ -0,0 +1,181 @@ +assertTrue(app()->providerIsLoaded(SproutServiceProvider::class)); + } + + #[Test] + public function serviceProviderIsDiscovered(): void + { + $manifest = app(PackageManifest::class); + + $this->assertContains(SproutServiceProvider::class, $manifest->providers()); + } + + #[Test] + public function sproutIsRegistered(): void + { + $this->assertTrue(app()->has(Sprout::class)); + $this->assertTrue(app()->has('sprout')); + $this->assertTrue(app()->isShared(Sprout::class)); + $this->assertFalse(app()->isShared('sprout')); + + $this->assertSame(app()->make(Sprout::class), app()->make(Sprout::class)); + $this->assertSame(app()->make('sprout'), app()->make('sprout')); + $this->assertSame(app()->make(Sprout::class), app()->make('sprout')); + $this->assertSame(app()->make('sprout'), app()->make(Sprout::class)); + $this->assertSame(sprout(), sprout()); + $this->assertSame(app()->make(Sprout::class), sprout()); + } + + #[Test] + public function providerManagerIsRegistered(): void + { + $this->assertTrue(app()->has(ProviderManager::class)); + $this->assertTrue(app()->has('sprout.providers')); + $this->assertTrue(app()->isShared(ProviderManager::class)); + $this->assertFalse(app()->isShared('sprout.providers')); + + $this->assertSame(app()->make(ProviderManager::class), app()->make(ProviderManager::class)); + $this->assertSame(app()->make('sprout.providers'), app()->make('sprout.providers')); + $this->assertSame(app()->make(ProviderManager::class), app()->make('sprout.providers')); + $this->assertSame(app()->make('sprout.providers'), app()->make(ProviderManager::class)); + $this->assertSame(app()->make(Sprout::class)->providers(), app()->make('sprout.providers')); + $this->assertSame(app()->make(Sprout::class)->providers(), app()->make(ProviderManager::class)); + $this->assertSame(sprout()->providers(), sprout()->providers()); + $this->assertSame(app()->make(Sprout::class)->providers(), sprout()->providers()); + } + + #[Test] + public function identityResolverManagerIsRegistered(): void + { + $this->assertTrue(app()->has(IdentityResolverManager::class)); + $this->assertTrue(app()->has('sprout.resolvers')); + $this->assertTrue(app()->isShared(IdentityResolverManager::class)); + $this->assertFalse(app()->isShared('sprout.resolvers')); + + $this->assertSame(app()->make(IdentityResolverManager::class), app()->make(IdentityResolverManager::class)); + $this->assertSame(app()->make('sprout.resolvers'), app()->make('sprout.resolvers')); + $this->assertSame(app()->make(IdentityResolverManager::class), app()->make('sprout.resolvers')); + $this->assertSame(app()->make('sprout.resolvers'), app()->make(IdentityResolverManager::class)); + $this->assertSame(app()->make(Sprout::class)->resolvers(), app()->make('sprout.resolvers')); + $this->assertSame(app()->make(Sprout::class)->resolvers(), app()->make(IdentityResolverManager::class)); + $this->assertSame(sprout()->resolvers(), sprout()->resolvers()); + $this->assertSame(app()->make(Sprout::class)->resolvers(), sprout()->resolvers()); + } + + #[Test] + public function tenancyManagerIsRegistered(): void + { + $this->assertTrue(app()->has(TenancyManager::class)); + $this->assertTrue(app()->has('sprout.tenancies')); + $this->assertTrue(app()->isShared(TenancyManager::class)); + $this->assertFalse(app()->isShared('sprout.tenancies')); + + $this->assertSame(app()->make(TenancyManager::class), app()->make(TenancyManager::class)); + $this->assertSame(app()->make('sprout.tenancies'), app()->make('sprout.tenancies')); + $this->assertSame(app()->make(TenancyManager::class), app()->make('sprout.tenancies')); + $this->assertSame(app()->make('sprout.tenancies'), app()->make(TenancyManager::class)); + $this->assertSame(app()->make(Sprout::class)->tenancies(), app()->make('sprout.tenancies')); + $this->assertSame(app()->make(Sprout::class)->tenancies(), app()->make(TenancyManager::class)); + $this->assertSame(sprout()->tenancies(), sprout()->tenancies()); + $this->assertSame(app()->make(Sprout::class)->tenancies(), sprout()->tenancies()); + } + + #[Test] + public function registersTenantRoutesMiddleware(): void + { + $router = $this->app->make(Router::class); + $middleware = $router->getMiddleware(); + + $this->assertTrue(isset($middleware[TenantRoutes::ALIAS])); + $this->assertSame(TenantRoutes::class, $middleware[TenantRoutes::ALIAS]); + $this->assertContains(TenantRoutes::class, $middleware); + } + + #[Test] + public function registersRouterMixinMethods(): void + { + $this->assertTrue(Router::hasMacro('tenanted')); + } + + #[Test] + public function publishesConfig(): void + { + $paths = ServiceProvider::pathsToPublish(SproutServiceProvider::class, 'config'); + + $key = realpath(__DIR__ . '/../../src'); + + $this->assertArrayHasKey($key . '/../resources/config/multitenancy.php', $paths); + $this->assertContains(config_path('multitenancy.php'), $paths); + } + + #[Test] + public function coreSproutConfigExists(): void + { + $this->assertTrue(app()['config']->has('sprout')); + $this->assertIsArray(app()['config']->get('sprout')); + $this->assertTrue(app()['config']->has('sprout.hooks')); + } + + #[Test] + public function registersServiceOverrides(): void + { + $overrides = config('sprout.services'); + + foreach ($overrides as $override) { + $this->assertTrue(sprout()->hasRegisteredOverride($override)); + } + } + + #[Test] + public function registersEventHandlers(): void + { + $dispatcher = app()->make(Dispatcher::class); + + $this->assertTrue($dispatcher->hasListeners(RouteMatched::class)); + + $listeners = $dispatcher->getRawListeners(); + + $this->assertContains(IdentifyTenantOnRouting::class, $listeners[RouteMatched::class]); + } + + #[Test] + public function registersTenancyBootstrappers(): void + { + $bootstrappers = config('sprout.bootstrappers'); + + $dispatcher = app()->make(Dispatcher::class); + + $this->assertTrue($dispatcher->hasListeners(RouteMatched::class)); + + $listeners = $dispatcher->getRawListeners(); + + foreach ($bootstrappers as $bootstrapper) { + $this->assertContains($bootstrapper, $listeners[CurrentTenantChanged::class]); + } + } +} diff --git a/tests/Unit/SproutTest.php b/tests/Unit/SproutTest.php new file mode 100644 index 0000000..a24499a --- /dev/null +++ b/tests/Unit/SproutTest.php @@ -0,0 +1,120 @@ +set('multitenancy.providers.backup', [ + 'driver' => 'database', + 'table' => 'tenants', + ]); + + $config->set('multitenancy.tenancies.backup', [ + 'provider' => 'backup', + ]); + }); + } + + #[Test] + public function allowsAccessToCoreConfig(): void + { + $this->assertSame(sprout()->config('hooks'), config('sprout.hooks')); + + config()->set('sprout.hooks', []); + + $this->assertSame(sprout()->config('hooks'), config('sprout.hooks')); + } + + #[Test] + public function hasNoCurrentTenancyByDefault(): void + { + $this->assertFalse(sprout()->hasCurrentTenancy()); + } + + #[Test] + public function isNotWithinMultitenantedContextByDefault(): void + { + $this->assertFalse(sprout()->withinContext()); + } + + #[Test] + public function setsCurrentTenancy(): void + { + $tenancy = sprout()->tenancies()->get(); + + $this->assertFalse(sprout()->hasCurrentTenancy()); + $this->assertNull(sprout()->getCurrentTenancy()); + $this->assertFalse(sprout()->withinContext()); + + sprout()->setCurrentTenancy($tenancy); + + $this->assertTrue(sprout()->hasCurrentTenancy()); + $this->assertSame($tenancy, sprout()->getCurrentTenancy()); + $this->assertTrue(sprout()->withinContext()); + } + + #[Test, DefineEnvironment('setupSecondTenancy')] + public function canStackCurrentTenancies(): void + { + $tenancy1 = sprout()->tenancies()->get(); + $tenancy2 = sprout()->tenancies()->get('backup'); + + $this->assertFalse(sprout()->hasCurrentTenancy()); + $this->assertNull(sprout()->getCurrentTenancy()); + $this->assertFalse(sprout()->withinContext()); + + sprout()->setCurrentTenancy($tenancy1); + + $this->assertTrue(sprout()->hasCurrentTenancy()); + $this->assertSame($tenancy1, sprout()->getCurrentTenancy()); + $this->assertTrue(sprout()->withinContext()); + + sprout()->setCurrentTenancy($tenancy2); + + $this->assertTrue(sprout()->hasCurrentTenancy()); + $this->assertSame($tenancy2, sprout()->getCurrentTenancy()); + $this->assertTrue(sprout()->withinContext()); + + $this->assertContains($tenancy1, sprout()->getAllCurrentTenancies()); + $this->assertContains($tenancy2, sprout()->getAllCurrentTenancies()); + } + + #[Test] + public function isAwareOfHooksToSupport(): void + { + $hooks = config('sprout.hooks'); + + foreach ($hooks as $hook) { + $this->assertTrue(sprout()->supportsHook($hook)); + } + + config()->set('sprout.hooks', []); + + foreach ($hooks as $hook) { + $this->assertFalse(sprout()->supportsHook($hook)); + } + } + + #[Test] + public function canManuallyMarkAsInOrOutOfContext(): void + { + $this->assertFalse(sprout()->withinContext()); + + sprout()->markAsInContext(); + + $this->assertTrue(sprout()->withinContext()); + + sprout()->markAsOutsideContext(); + + $this->assertFalse(sprout()->withinContext()); + } +} diff --git a/tests/Unit/Support/DefaultTenancyTest.php b/tests/Unit/Support/DefaultTenancyTest.php new file mode 100644 index 0000000..a0cfc51 --- /dev/null +++ b/tests/Unit/Support/DefaultTenancyTest.php @@ -0,0 +1,169 @@ +set('multitenancy.defaults.resolver', 'path'); + $config->set('multitenancy.providers.tenants.model', TenantModel::class); + }); + } + + #[Test] + public function hasName(): void + { + /** @var \Sprout\Contracts\Tenancy $tenancy */ + $tenancy = sprout()->tenancies()->get(); + + $this->assertInstanceOf(DefaultTenancy::class, $tenancy); + $this->assertSame('tenants', $tenancy->getName()); + } + + #[Test] + public function hasNoCurrentTenantByDefault(): void + { + /** @var \Sprout\Contracts\Tenancy $tenancy */ + $tenancy = sprout()->tenancies()->get(); + + $this->assertFalse($tenancy->check()); + } + + #[Test] + public function storesCurrentTenantForAccess(): void + { + /** @var \Sprout\Contracts\Tenancy $tenancy */ + $tenancy = sprout()->tenancies()->get(); + + $this->assertFalse($tenancy->check()); + + $tenant = TenantModel::factory()->createOne(); + + $tenancy->setTenant($tenant); + + $this->assertTrue($tenancy->check()); + $this->assertSame($tenant, $tenancy->tenant()); + $this->assertSame($tenant->getTenantKey(), $tenancy->key()); + $this->assertSame($tenant->getTenantIdentifier(), $tenancy->identifier()); + } + + #[Test] + public function identifiesTenant(): void + { + /** @var \Sprout\Contracts\Tenancy $tenancy */ + $tenancy = sprout()->tenancies()->get(); + + $this->assertFalse($tenancy->check()); + + $tenant = TenantModel::factory()->createOne(); + + $this->assertFalse($tenancy->identify('non-existent')); + + Event::fake([TenantIdentified::class]); + + $this->assertTrue($tenancy->identify($tenant->getTenantIdentifier())); + + Event::assertDispatched(TenantIdentified::class); + } + + #[Test] + public function loadsTenant(): void + { + /** @var \Sprout\Contracts\Tenancy $tenancy */ + $tenancy = sprout()->tenancies()->get(); + + $this->assertFalse($tenancy->check()); + + $tenant = TenantModel::factory()->createOne(); + + $this->assertFalse($tenancy->load(-99999)); + + Event::fake([TenantLoaded::class]); + + $this->assertTrue($tenancy->load($tenant->getTenantKey())); + + Event::assertDispatched(TenantLoaded::class); + } + + #[Test] + public function hasATenantProvider(): void + { + /** @var \Sprout\Contracts\Tenancy $tenancy */ + $tenancy = sprout()->tenancies()->get(); + + $provider = $tenancy->provider(); + + $this->assertNotNull($provider); + $this->assertInstanceOf(EloquentTenantProvider::class, $provider); + } + + #[Test] + public function storesHowAndWhenTheTenantWasResolved(): void + { + /** @var \Sprout\Contracts\Tenancy $tenancy */ + $tenancy = sprout()->tenancies()->get(); + + $this->assertFalse($tenancy->wasResolved()); + $this->assertNull($tenancy->resolver()); + $this->assertNull($tenancy->hook()); + + $tenant = TenantModel::factory()->createOne(); + + $tenancy->setTenant($tenant); + + $this->assertFalse($tenancy->wasResolved()); + $this->assertNull($tenancy->resolver()); + $this->assertNull($tenancy->hook()); + + $tenancy->resolvedVia(sprout()->resolvers()->get()); + $tenancy->resolvedAt(ResolutionHook::Booting); + + $this->assertTrue($tenancy->wasResolved()); + $this->assertNotNull($tenancy->resolver()); + $this->assertSame(sprout()->resolvers()->get(), $tenancy->resolver()); + $this->assertNotNull($tenancy->hook()); + $this->assertSame(ResolutionHook::Booting, $tenancy->hook()); + } + + #[Test] + public function hasOptions(): void + { + /** @var \Sprout\Contracts\Tenancy $tenancy */ + $tenancy = sprout()->tenancies()->get(); + + $this->assertSame(config('multitenancy.tenancies.tenants.options'), $tenancy->options()); + + $this->assertTrue($tenancy->hasOption(TenancyOptions::hydrateTenantRelation())); + $this->assertTrue($tenancy->hasOption(TenancyOptions::throwIfNotRelated())); + + $tenancy->removeOption(TenancyOptions::hydrateTenantRelation()); + + $this->assertFalse($tenancy->hasOption(TenancyOptions::hydrateTenantRelation())); + $this->assertTrue($tenancy->hasOption(TenancyOptions::throwIfNotRelated())); + + $tenancy->removeOption(TenancyOptions::throwIfNotRelated()); + + $this->assertFalse($tenancy->hasOption(TenancyOptions::hydrateTenantRelation())); + $this->assertFalse($tenancy->hasOption(TenancyOptions::throwIfNotRelated())); + + $tenancy->addOption(TenancyOptions::hydrateTenantRelation()); + + $this->assertTrue($tenancy->hasOption(TenancyOptions::hydrateTenantRelation())); + $this->assertFalse($tenancy->hasOption(TenancyOptions::throwIfNotRelated())); + } +} diff --git a/tests/Unit/Support/ResolutionHelperTest.php b/tests/Unit/Support/ResolutionHelperTest.php new file mode 100644 index 0000000..81efd9c --- /dev/null +++ b/tests/Unit/Support/ResolutionHelperTest.php @@ -0,0 +1,48 @@ +assertNull($resolverName); + $this->assertNull($tenancyName); + + [$resolverName, $tenancyName] = ResolutionHelper::parseOptions(['test']); + + $this->assertNotNull($resolverName); + $this->assertSame('test', $resolverName); + $this->assertNull($tenancyName); + + [$resolverName, $tenancyName] = ResolutionHelper::parseOptions(['test', 'more']); + + $this->assertNotNull($resolverName); + $this->assertSame('test', $resolverName); + $this->assertNotNull($tenancyName); + $this->assertSame('more', $tenancyName); + } + + #[Test] + public function throwsExceptionWhenHandlingResolutionForUnsupportedHook(): void + { + $this->expectException(MisconfigurationException::class); + $this->expectExceptionMessage('The resolution hook [Booting] is not supported'); + + /** @var \Illuminate\Http\Request $fakeRequest */ + $fakeRequest = $this->mock(Request::class); + + ResolutionHelper::handleResolution($fakeRequest, ResolutionHook::Booting); + } +} diff --git a/tests/Unit/TenancyOptionsTest.php b/tests/Unit/TenancyOptionsTest.php new file mode 100644 index 0000000..3f83d2f --- /dev/null +++ b/tests/Unit/TenancyOptionsTest.php @@ -0,0 +1,73 @@ +set('multitenancy.providers.backup', [ + 'driver' => 'database', + 'table' => 'tenants', + ]); + + $config->set('multitenancy.tenancies.backup', [ + 'provider' => 'backup', + ]); + }); + } + + #[Test] + public function hydrateTenantRelationOption(): void + { + $this->assertSame('tenant-relation.hydrate', TenancyOptions::hydrateTenantRelation()); + } + + #[Test] + public function throwIfNotRelatedOption(): void + { + $this->assertSame('tenant-relation.strict', TenancyOptions::throwIfNotRelated()); + } + + #[Test, DefineEnvironment('setupSecondTenancy')] + public function correctlyReportsHydrateTenantRelationOptionPresence(): void + { + $tenancy = app(TenancyManager::class)->get('tenants'); + $tenancy->removeOption(TenancyOptions::hydrateTenantRelation()); + + $this->assertFalse(TenancyOptions::shouldHydrateTenantRelation($tenancy)); + + $tenancy->addOption(TenancyOptions::hydrateTenantRelation()); + + $this->assertTrue(TenancyOptions::shouldHydrateTenantRelation($tenancy)); + + $tenancy = app(TenancyManager::class)->get('backup'); + + $this->assertFalse(TenancyOptions::shouldHydrateTenantRelation($tenancy)); + } + + #[Test, DefineEnvironment('setupSecondTenancy')] + public function correctlyReportsThrowIfNotRelatedOptionPresence(): void + { + $tenancy = app(TenancyManager::class)->get('tenants'); + $tenancy->removeOption(TenancyOptions::throwIfNotRelated()); + + $this->assertFalse(TenancyOptions::shouldThrowIfNotRelated($tenancy)); + + $tenancy->addOption(TenancyOptions::throwIfNotRelated()); + + $this->assertTrue(TenancyOptions::shouldThrowIfNotRelated($tenancy)); + + $tenancy = app(TenancyManager::class)->get('backup'); + + $this->assertFalse(TenancyOptions::shouldThrowIfNotRelated($tenancy)); + } +} diff --git a/tests/Unit/UnitTestCase.php b/tests/Unit/UnitTestCase.php new file mode 100644 index 0000000..02e2b6a --- /dev/null +++ b/tests/Unit/UnitTestCase.php @@ -0,0 +1,13 @@ + Date: Mon, 18 Nov 2024 21:00:16 +0000 Subject: [PATCH 06/21] chore: Don't pass null to ltrim --- src/Concerns/OverridesCookieSettings.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Concerns/OverridesCookieSettings.php b/src/Concerns/OverridesCookieSettings.php index 3ff4e86..b992b35 100644 --- a/src/Concerns/OverridesCookieSettings.php +++ b/src/Concerns/OverridesCookieSettings.php @@ -39,7 +39,7 @@ public static function setDomain(?string $domain): void */ public static function setPath(?string $path): void { - self::$settings['path'] = '/' . ltrim($path, '/'); + self::$settings['path'] = $path ? '/' . ltrim($path, '/') : null; } // @codeCoverageIgnoreStart From baea8681bcbdc1bc0df40a254394e9f4d579f0f8 Mon Sep 17 00:00:00 2001 From: Ollie Read Date: Mon, 18 Nov 2024 21:01:09 +0000 Subject: [PATCH 07/21] build(github): Separate out testsuites for workflow --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 2932450..36f2581 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -39,7 +39,7 @@ jobs: run: vendor/bin/testbench vendor:publish --provider="Sprout\\SproutServiceProvider" - name: Execute tests - run: composer test + run: vendor/bin/phpunit --testsuite=Unit,Feature - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v4 From 294d12cc1fd1fab86916a76c8aa4ca162dd4a090 Mon Sep 17 00:00:00 2001 From: Ollie Read Date: Mon, 18 Nov 2024 21:03:59 +0000 Subject: [PATCH 08/21] build(github): Remove unnecessary vendor:publish --- .github/workflows/tests.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 36f2581..41b0136 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -35,9 +35,6 @@ jobs: - name: Prepare testbench run: composer clear && composer prepare && composer build - - name: Publish required assets - run: vendor/bin/testbench vendor:publish --provider="Sprout\\SproutServiceProvider" - - name: Execute tests run: vendor/bin/phpunit --testsuite=Unit,Feature From 297e7d02ab891d96f9323cac6bcbdd7fb2805e3c Mon Sep 17 00:00:00 2001 From: Ollie Read Date: Mon, 18 Nov 2024 21:06:04 +0000 Subject: [PATCH 09/21] build(github): Revert unit test command for workflow --- .github/workflows/tests.yml | 2 +- composer.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 41b0136..9ece740 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -36,7 +36,7 @@ jobs: run: composer clear && composer prepare && composer build - name: Execute tests - run: vendor/bin/phpunit --testsuite=Unit,Feature + run: composer test - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v4 diff --git a/composer.json b/composer.json index 013db51..0bd998d 100644 --- a/composer.json +++ b/composer.json @@ -63,7 +63,7 @@ "@clear", "@prepare", "@build", - "@php vendor/bin/phpunit" + "@php vendor/bin/phpunit --testsuite=Unit,Feature" ] }, "extra" : { From c4a3a1b1a28d4a8c9993d22535d58cf9ce2bb4c9 Mon Sep 17 00:00:00 2001 From: Ollie Read Date: Mon, 18 Nov 2024 21:08:46 +0000 Subject: [PATCH 10/21] chore: Fix paths for feature and unit testing --- phpunit.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/phpunit.xml b/phpunit.xml index 6bae6fd..7e3a02d 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -19,10 +19,10 @@ tests/_Original - tests/Feature + ./tests/Feature - tests/Unit + ./tests/Unit From 55954507787f05f91c4b5e21dcb342345c3f0ba8 Mon Sep 17 00:00:00 2001 From: Ollie Read Date: Mon, 18 Nov 2024 21:11:12 +0000 Subject: [PATCH 11/21] chore: Ensure tests/Feature directory exists --- tests/Feature/.gitkeep | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/Feature/.gitkeep diff --git a/tests/Feature/.gitkeep b/tests/Feature/.gitkeep new file mode 100644 index 0000000..e69de29 From a00be22d107aa7241cd75fb3f0e6724b2c38d110 Mon Sep 17 00:00:00 2001 From: Ollie Read Date: Tue, 19 Nov 2024 12:00:17 +0000 Subject: [PATCH 12/21] chore: Reset resolver and hook when current tenant is nullified --- src/Support/DefaultTenancy.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Support/DefaultTenancy.php b/src/Support/DefaultTenancy.php index f88b381..c30dd8b 100644 --- a/src/Support/DefaultTenancy.php +++ b/src/Support/DefaultTenancy.php @@ -257,6 +257,11 @@ public function setTenant(?Tenant $tenant): static event(new CurrentTenantChanged($this, $previousTenant, $tenant)); } + if ($tenant === null) { + $this->resolver = null; + $this->hook = null; + } + return $this; } From 994871e43d5a32955166bf33ab7e2cab02de3ac0 Mon Sep 17 00:00:00 2001 From: Ollie Read Date: Tue, 19 Nov 2024 12:00:33 +0000 Subject: [PATCH 13/21] chore: Do not generate code coverage for the generic tenant class --- src/Support/GenericTenant.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Support/GenericTenant.php b/src/Support/GenericTenant.php index 305f510..9b627c1 100644 --- a/src/Support/GenericTenant.php +++ b/src/Support/GenericTenant.php @@ -13,6 +13,8 @@ * as the tenant entity. * * @pacakge Core + * + * @codeCoverageIgnore */ class GenericTenant implements Tenant { From 7d73645ed7a8ed0fe4ff8f145ce0ec64ce94b5f7 Mon Sep 17 00:00:00 2001 From: Ollie Read Date: Tue, 19 Nov 2024 12:00:45 +0000 Subject: [PATCH 14/21] test: Add unit tests for the resolution helper --- tests/Unit/Support/ResolutionHelperTest.php | 297 ++++++++++++++++++++ 1 file changed, 297 insertions(+) diff --git a/tests/Unit/Support/ResolutionHelperTest.php b/tests/Unit/Support/ResolutionHelperTest.php index 81efd9c..f7d55f1 100644 --- a/tests/Unit/Support/ResolutionHelperTest.php +++ b/tests/Unit/Support/ResolutionHelperTest.php @@ -4,14 +4,31 @@ namespace Sprout\Tests\Unit\Support; use Illuminate\Http\Request; +use Illuminate\Routing\Route; +use Mockery\MockInterface; use PHPUnit\Framework\Attributes\Test; use Sprout\Exceptions\MisconfigurationException; +use Sprout\Exceptions\NoTenantFound; +use Sprout\Managers\IdentityResolverManager; +use Sprout\Managers\TenancyManager; use Sprout\Support\ResolutionHelper; use Sprout\Support\ResolutionHook; use Sprout\Tests\Unit\UnitTestCase; +use Workbench\App\Models\TenantModel; +use function Sprout\resolver; +use function Sprout\sprout; class ResolutionHelperTest extends UnitTestCase { + protected function defineEnvironment($app): void + { + tap($app['config'], static function ($config) { + $config->set('multitenancy.defaults.resolver', 'path'); + $config->set('multitenancy.providers.tenants.model', TenantModel::class); + $config->set('multitenancy.resolvers.subdomain.domain', 'localhost'); + }); + } + #[Test] public function parsesMiddlewareOptions(): void { @@ -45,4 +62,284 @@ public function throwsExceptionWhenHandlingResolutionForUnsupportedHook(): void ResolutionHelper::handleResolution($fakeRequest, ResolutionHook::Booting); } + + #[Test] + public function returnsFalseIfThereIsAlreadyATenant(): void + { + /** @var \Sprout\Contracts\Tenancy $tenancy */ + $tenancy = app(TenancyManager::class)->get(); + + $tenancy->setTenant(TenantModel::factory()->createOne()); + + /** @var \Sprout\Contracts\IdentityResolver $resolver */ + $resolver = app(IdentityResolverManager::class)->get('path'); + + /** @var \Illuminate\Http\Request $fakeRequest */ + $fakeRequest = $this->mock(Request::class); + + $this->assertTrue($tenancy->check()); + $this->assertTrue($resolver->canResolve($fakeRequest, $tenancy, ResolutionHook::Routing)); + $this->assertFalse(ResolutionHelper::handleResolution($fakeRequest, ResolutionHook::Routing, $resolver->getName(), $tenancy->getName())); + } + + #[Test] + public function returnsFalseIfTheResolverCannotResolve(): void + { + /** @var \Sprout\Contracts\Tenancy $tenancy */ + $tenancy = app(TenancyManager::class)->get(); + + /** @var \Sprout\Contracts\IdentityResolver $resolver */ + $resolver = app(IdentityResolverManager::class)->get('path'); + + $tenancy->setTenant(TenantModel::factory()->createOne()) + ->resolvedVia($resolver) + ->resolvedAt(ResolutionHook::Routing); + + /** @var \Illuminate\Http\Request $fakeRequest */ + $fakeRequest = $this->mock(Request::class); + + $this->assertTrue($tenancy->check()); + $this->assertFalse($resolver->canResolve($fakeRequest, $tenancy, ResolutionHook::Routing)); + $this->assertFalse(ResolutionHelper::handleResolution($fakeRequest, ResolutionHook::Routing, $resolver->getName(), $tenancy->getName())); + } + + #[Test] + public function resolvesTenantUsingRouteParameters(): void + { + $tenant = TenantModel::factory()->createOne(); + + /** @var \Sprout\Contracts\Tenancy $tenancy */ + $tenancy = app(TenancyManager::class)->get(); + + /** @var \Sprout\Contracts\IdentityResolver&\Sprout\Contracts\IdentityResolverUsesParameters $resolver */ + $resolver = app(IdentityResolverManager::class)->get('path'); + + /** @var \Illuminate\Routing\Route $fakeRoute */ + $fakeRoute = $this->mock(Route::class, function (MockInterface $mock) use ($tenant, $tenancy, $resolver) { + $parameterName = $resolver->getRouteParameterName($tenancy); + + $mock->shouldReceive('hasParameter') + ->with($parameterName) + ->andReturn(true); + + $mock->shouldReceive('parameter') + ->with($parameterName) + ->andReturn($tenant->getTenantIdentifier()); + + $mock->shouldReceive('forgetParameter') + ->with($parameterName); + }); + + /** @var \Illuminate\Http\Request $fakeRequest */ + $fakeRequest = $this->mock(Request::class, function (MockInterface $mock) use ($fakeRoute) { + $mock->shouldReceive('route')->andReturn($fakeRoute); + }); + + $this->assertTrue(ResolutionHelper::handleResolution($fakeRequest, ResolutionHook::Routing, $resolver->getName(), $tenancy->getName())); + $this->assertSame($tenancy, sprout()->getCurrentTenancy()); + $this->assertTrue($tenancy->check()); + $this->assertTrue($tenant->is($tenancy->tenant())); + $this->assertTrue($tenancy->wasResolved()); + $this->assertSame($resolver, $tenancy->resolver()); + $this->assertSame(ResolutionHook::Routing, $tenancy->hook()); + + $tenancy->setTenant(null); + + $this->assertTrue(ResolutionHelper::handleResolution($fakeRequest, ResolutionHook::Routing)); + $this->assertSame($tenancy, sprout()->getCurrentTenancy()); + $this->assertTrue($tenancy->check()); + $this->assertTrue($tenant->is($tenancy->tenant())); + $this->assertTrue($tenancy->wasResolved()); + $this->assertSame($resolver, $tenancy->resolver()); + $this->assertSame(ResolutionHook::Routing, $tenancy->hook()); + } + + #[Test] + public function throwsAnExceptionWhenUnableToIdentifyATenantFromTheRoute(): void + { + /** @var \Sprout\Contracts\Tenancy $tenancy */ + $tenancy = app(TenancyManager::class)->get(); + + /** @var \Sprout\Contracts\IdentityResolver&\Sprout\Contracts\IdentityResolverUsesParameters $resolver */ + $resolver = app(IdentityResolverManager::class)->get('path'); + + /** @var \Illuminate\Routing\Route $fakeRoute */ + $fakeRoute = $this->mock(Route::class, function (MockInterface $mock) use ($tenancy, $resolver) { + $parameterName = $resolver->getRouteParameterName($tenancy); + + $mock->shouldReceive('hasParameter') + ->with($parameterName) + ->andReturn(true); + + $mock->shouldReceive('parameter') + ->with($parameterName) + ->andReturn('fake-identifier'); + + $mock->shouldReceive('forgetParameter') + ->with($parameterName); + }); + + /** @var \Illuminate\Http\Request $fakeRequest */ + $fakeRequest = $this->mock(Request::class, function (MockInterface $mock) use ($fakeRoute) { + $mock->shouldReceive('route')->andReturn($fakeRoute); + }); + + $this->expectException(NoTenantFound::class); + $this->expectExceptionMessage('No valid tenant [' . $tenancy->getName() . '] found [' . $resolver->getName() . ']'); + + ResolutionHelper::handleResolution($fakeRequest, ResolutionHook::Routing, $resolver->getName(), $tenancy->getName()); + + $this->expectException(NoTenantFound::class); + $this->expectExceptionMessage('No valid tenant [' . $tenancy->getName() . '] found [subdomain]'); + + ResolutionHelper::handleResolution($fakeRequest, ResolutionHook::Routing); + } + + #[Test] + public function returnsFalseWhenUnableToIdentifyATenantFromTheRouteAndToldNotToThrow(): void + { + /** @var \Sprout\Contracts\Tenancy $tenancy */ + $tenancy = app(TenancyManager::class)->get(); + + /** @var \Sprout\Contracts\IdentityResolver&\Sprout\Contracts\IdentityResolverUsesParameters $resolver */ + $resolver = app(IdentityResolverManager::class)->get('path'); + + /** @var \Illuminate\Routing\Route $fakeRoute */ + $fakeRoute = $this->mock(Route::class, function (MockInterface $mock) use ($tenancy, $resolver) { + $parameterName = $resolver->getRouteParameterName($tenancy); + + $mock->shouldReceive('hasParameter') + ->with($parameterName) + ->andReturn(true); + + $mock->shouldReceive('parameter') + ->with($parameterName) + ->andReturn('fake-identifier'); + + $mock->shouldReceive('forgetParameter') + ->with($parameterName); + }); + + /** @var \Illuminate\Http\Request $fakeRequest */ + $fakeRequest = $this->mock(Request::class, function (MockInterface $mock) use ($fakeRoute) { + $mock->shouldReceive('route')->andReturn($fakeRoute); + }); + + $this->assertFalse(ResolutionHelper::handleResolution($fakeRequest, ResolutionHook::Routing, $resolver->getName(), $tenancy->getName(), false)); + $this->assertSame($tenancy, sprout()->getCurrentTenancy()); + $this->assertFalse($tenancy->check()); + $this->assertFalse($tenancy->wasResolved()); + $this->assertNull($tenancy->resolver()); + $this->assertNull($tenancy->hook()); + + $this->assertFalse(ResolutionHelper::handleResolution($fakeRequest, ResolutionHook::Routing, throw: false)); + $this->assertSame($tenancy, sprout()->getCurrentTenancy()); + $this->assertFalse($tenancy->check()); + $this->assertFalse($tenancy->wasResolved()); + $this->assertNull($tenancy->resolver()); + $this->assertNull($tenancy->hook()); + } + + #[Test] + public function resolvesTenantWithoutRouteParameters(): void + { + $tenant = TenantModel::factory()->createOne(); + + /** @var \Sprout\Contracts\Tenancy $tenancy */ + $tenancy = app(TenancyManager::class)->get(); + + /** @var \Sprout\Contracts\IdentityResolver&\Sprout\Contracts\IdentityResolverUsesParameters $resolver */ + $resolver = app(IdentityResolverManager::class)->get('path'); + + /** @var \Illuminate\Http\Request $fakeRequest */ + $fakeRequest = $this->mock(Request::class, function (MockInterface $mock) use ($tenant) { + $mock->shouldReceive('route')->andReturnNull(); + + $mock->shouldReceive('segment') + ->with(1) + ->andReturn($tenant->getTenantIdentifier()); + }); + + $this->assertTrue(ResolutionHelper::handleResolution($fakeRequest, ResolutionHook::Routing, $resolver->getName(), $tenancy->getName())); + $this->assertSame($tenancy, sprout()->getCurrentTenancy()); + $this->assertTrue($tenancy->check()); + $this->assertTrue($tenant->is($tenancy->tenant())); + $this->assertTrue($tenancy->wasResolved()); + $this->assertSame($resolver, $tenancy->resolver()); + $this->assertSame(ResolutionHook::Routing, $tenancy->hook()); + + $tenancy->setTenant(null); + + $this->assertTrue(ResolutionHelper::handleResolution($fakeRequest, ResolutionHook::Routing)); + $this->assertSame($tenancy, sprout()->getCurrentTenancy()); + $this->assertTrue($tenancy->check()); + $this->assertTrue($tenant->is($tenancy->tenant())); + $this->assertTrue($tenancy->wasResolved()); + $this->assertSame($resolver, $tenancy->resolver()); + $this->assertSame(ResolutionHook::Routing, $tenancy->hook()); + } + + #[Test] + public function throwsAnExceptionWhenUnableToIdentifyATenantFromTheRequest(): void + { + /** @var \Sprout\Contracts\Tenancy $tenancy */ + $tenancy = app(TenancyManager::class)->get(); + + /** @var \Sprout\Contracts\IdentityResolver&\Sprout\Contracts\IdentityResolverUsesParameters $resolver */ + $resolver = app(IdentityResolverManager::class)->get('path'); + + /** @var \Illuminate\Http\Request $fakeRequest */ + $fakeRequest = $this->mock(Request::class, function (MockInterface $mock) { + $mock->shouldReceive('route')->andReturnNull(); + + $mock->shouldReceive('segment') + ->with(1) + ->andReturn('fake-identifier'); + }); + + $this->expectException(NoTenantFound::class); + $this->expectExceptionMessage('No valid tenant [' . $tenancy->getName() . '] found [' . $resolver->getName() . ']'); + + ResolutionHelper::handleResolution($fakeRequest, ResolutionHook::Routing, $resolver->getName(), $tenancy->getName()); + + $this->expectException(NoTenantFound::class); + $this->expectExceptionMessage('No valid tenant [' . $tenancy->getName() . '] found [subdomain]'); + + ResolutionHelper::handleResolution($fakeRequest, ResolutionHook::Routing); + } + + #[Test] + public function returnsFalseWhenUnableToIdentifyATenantFromTheRequestAndToldNotToThrow(): void + { + /** @var \Sprout\Contracts\Tenancy $tenancy */ + $tenancy = app(TenancyManager::class)->get(); + + /** @var \Sprout\Contracts\IdentityResolver&\Sprout\Contracts\IdentityResolverUsesParameters $resolver */ + $resolver = app(IdentityResolverManager::class)->get('path'); + + /** @var \Illuminate\Http\Request $fakeRequest */ + $fakeRequest = $this->mock(Request::class, function (MockInterface $mock) { + $mock->shouldReceive('route')->andReturnNull(); + + $mock->shouldReceive('segment') + ->with(1) + ->andReturn('fake-identifier'); + }); + + $this->assertFalse(ResolutionHelper::handleResolution($fakeRequest, ResolutionHook::Routing, $resolver->getName(), $tenancy->getName(), false)); + $this->assertSame($tenancy, sprout()->getCurrentTenancy()); + $this->assertFalse($tenancy->check()); + $this->assertFalse($tenancy->wasResolved()); + $this->assertNull($tenancy->resolver()); + $this->assertNull($tenancy->hook()); + + $tenancy->setTenant(null); + + $this->assertFalse(ResolutionHelper::handleResolution($fakeRequest, ResolutionHook::Routing, throw: false)); + $this->assertSame($tenancy, sprout()->getCurrentTenancy()); + $this->assertFalse($tenancy->check()); + $this->assertFalse($tenancy->wasResolved()); + $this->assertNull($tenancy->resolver()); + $this->assertNull($tenancy->hook()); + } } From f6eaf0037626dce9d3cebc98a20d1f0d893276c4 Mon Sep 17 00:00:00 2001 From: Ollie Read Date: Tue, 19 Nov 2024 12:04:47 +0000 Subject: [PATCH 15/21] chore: Tidy up code to use helper functions --- tests/Unit/Attributes/CurrentTenantTest.php | 7 ++--- tests/Unit/Support/ResolutionHelperTest.php | 35 ++++++++++----------- tests/Unit/TenancyOptionsTest.php | 10 +++--- tests/Unit/UnitTestCase.php | 3 +- 4 files changed, 27 insertions(+), 28 deletions(-) diff --git a/tests/Unit/Attributes/CurrentTenantTest.php b/tests/Unit/Attributes/CurrentTenantTest.php index 2015310..631ddd8 100644 --- a/tests/Unit/Attributes/CurrentTenantTest.php +++ b/tests/Unit/Attributes/CurrentTenantTest.php @@ -7,12 +7,11 @@ use Orchestra\Testbench\Attributes\DefineEnvironment; use PHPUnit\Framework\Attributes\Test; use Sprout\Attributes\CurrentTenant; -use Sprout\Contracts\Tenant; -use Sprout\Managers\TenancyManager; use Sprout\Support\GenericTenant; use Sprout\Tests\Unit\UnitTestCase; use Workbench\App\Models\TenantModel; use function Sprout\sprout; +use function Sprout\tenancy; class CurrentTenantTest extends UnitTestCase { @@ -41,7 +40,7 @@ protected function setupSecondTenancy($app): void public function resolvesCurrentTenant(): void { /** @var \Sprout\Contracts\Tenancy $tenancy */ - $tenancy = app(TenancyManager::class)->get('tenants'); + $tenancy = tenancy('tenants'); sprout()->setCurrentTenancy($tenancy); @@ -63,7 +62,7 @@ public function resolvesCurrentTenant(): void public function resolvesCurrentTenantForSpecificTenancy(): void { /** @var \Sprout\Contracts\Tenancy $tenancy */ - $tenancy = app(TenancyManager::class)->get('backup'); + $tenancy = tenancy('backup'); sprout()->setCurrentTenancy($tenancy); diff --git a/tests/Unit/Support/ResolutionHelperTest.php b/tests/Unit/Support/ResolutionHelperTest.php index f7d55f1..5385696 100644 --- a/tests/Unit/Support/ResolutionHelperTest.php +++ b/tests/Unit/Support/ResolutionHelperTest.php @@ -9,14 +9,13 @@ use PHPUnit\Framework\Attributes\Test; use Sprout\Exceptions\MisconfigurationException; use Sprout\Exceptions\NoTenantFound; -use Sprout\Managers\IdentityResolverManager; -use Sprout\Managers\TenancyManager; use Sprout\Support\ResolutionHelper; use Sprout\Support\ResolutionHook; use Sprout\Tests\Unit\UnitTestCase; use Workbench\App\Models\TenantModel; use function Sprout\resolver; use function Sprout\sprout; +use function Sprout\tenancy; class ResolutionHelperTest extends UnitTestCase { @@ -67,12 +66,12 @@ public function throwsExceptionWhenHandlingResolutionForUnsupportedHook(): void public function returnsFalseIfThereIsAlreadyATenant(): void { /** @var \Sprout\Contracts\Tenancy $tenancy */ - $tenancy = app(TenancyManager::class)->get(); + $tenancy = tenancy(); $tenancy->setTenant(TenantModel::factory()->createOne()); /** @var \Sprout\Contracts\IdentityResolver $resolver */ - $resolver = app(IdentityResolverManager::class)->get('path'); + $resolver = resolver('path'); /** @var \Illuminate\Http\Request $fakeRequest */ $fakeRequest = $this->mock(Request::class); @@ -86,10 +85,10 @@ public function returnsFalseIfThereIsAlreadyATenant(): void public function returnsFalseIfTheResolverCannotResolve(): void { /** @var \Sprout\Contracts\Tenancy $tenancy */ - $tenancy = app(TenancyManager::class)->get(); + $tenancy = tenancy(); /** @var \Sprout\Contracts\IdentityResolver $resolver */ - $resolver = app(IdentityResolverManager::class)->get('path'); + $resolver = resolver('path'); $tenancy->setTenant(TenantModel::factory()->createOne()) ->resolvedVia($resolver) @@ -109,10 +108,10 @@ public function resolvesTenantUsingRouteParameters(): void $tenant = TenantModel::factory()->createOne(); /** @var \Sprout\Contracts\Tenancy $tenancy */ - $tenancy = app(TenancyManager::class)->get(); + $tenancy = tenancy(); /** @var \Sprout\Contracts\IdentityResolver&\Sprout\Contracts\IdentityResolverUsesParameters $resolver */ - $resolver = app(IdentityResolverManager::class)->get('path'); + $resolver = resolver('path'); /** @var \Illuminate\Routing\Route $fakeRoute */ $fakeRoute = $this->mock(Route::class, function (MockInterface $mock) use ($tenant, $tenancy, $resolver) { @@ -158,10 +157,10 @@ public function resolvesTenantUsingRouteParameters(): void public function throwsAnExceptionWhenUnableToIdentifyATenantFromTheRoute(): void { /** @var \Sprout\Contracts\Tenancy $tenancy */ - $tenancy = app(TenancyManager::class)->get(); + $tenancy = tenancy(); /** @var \Sprout\Contracts\IdentityResolver&\Sprout\Contracts\IdentityResolverUsesParameters $resolver */ - $resolver = app(IdentityResolverManager::class)->get('path'); + $resolver = resolver('path'); /** @var \Illuminate\Routing\Route $fakeRoute */ $fakeRoute = $this->mock(Route::class, function (MockInterface $mock) use ($tenancy, $resolver) { @@ -199,10 +198,10 @@ public function throwsAnExceptionWhenUnableToIdentifyATenantFromTheRoute(): void public function returnsFalseWhenUnableToIdentifyATenantFromTheRouteAndToldNotToThrow(): void { /** @var \Sprout\Contracts\Tenancy $tenancy */ - $tenancy = app(TenancyManager::class)->get(); + $tenancy = tenancy(); /** @var \Sprout\Contracts\IdentityResolver&\Sprout\Contracts\IdentityResolverUsesParameters $resolver */ - $resolver = app(IdentityResolverManager::class)->get('path'); + $resolver = resolver('path'); /** @var \Illuminate\Routing\Route $fakeRoute */ $fakeRoute = $this->mock(Route::class, function (MockInterface $mock) use ($tenancy, $resolver) { @@ -246,10 +245,10 @@ public function resolvesTenantWithoutRouteParameters(): void $tenant = TenantModel::factory()->createOne(); /** @var \Sprout\Contracts\Tenancy $tenancy */ - $tenancy = app(TenancyManager::class)->get(); + $tenancy = tenancy(); /** @var \Sprout\Contracts\IdentityResolver&\Sprout\Contracts\IdentityResolverUsesParameters $resolver */ - $resolver = app(IdentityResolverManager::class)->get('path'); + $resolver = resolver('path'); /** @var \Illuminate\Http\Request $fakeRequest */ $fakeRequest = $this->mock(Request::class, function (MockInterface $mock) use ($tenant) { @@ -283,10 +282,10 @@ public function resolvesTenantWithoutRouteParameters(): void public function throwsAnExceptionWhenUnableToIdentifyATenantFromTheRequest(): void { /** @var \Sprout\Contracts\Tenancy $tenancy */ - $tenancy = app(TenancyManager::class)->get(); + $tenancy = tenancy(); /** @var \Sprout\Contracts\IdentityResolver&\Sprout\Contracts\IdentityResolverUsesParameters $resolver */ - $resolver = app(IdentityResolverManager::class)->get('path'); + $resolver = resolver('path'); /** @var \Illuminate\Http\Request $fakeRequest */ $fakeRequest = $this->mock(Request::class, function (MockInterface $mock) { @@ -312,10 +311,10 @@ public function throwsAnExceptionWhenUnableToIdentifyATenantFromTheRequest(): vo public function returnsFalseWhenUnableToIdentifyATenantFromTheRequestAndToldNotToThrow(): void { /** @var \Sprout\Contracts\Tenancy $tenancy */ - $tenancy = app(TenancyManager::class)->get(); + $tenancy = tenancy(); /** @var \Sprout\Contracts\IdentityResolver&\Sprout\Contracts\IdentityResolverUsesParameters $resolver */ - $resolver = app(IdentityResolverManager::class)->get('path'); + $resolver = resolver('path'); /** @var \Illuminate\Http\Request $fakeRequest */ $fakeRequest = $this->mock(Request::class, function (MockInterface $mock) { diff --git a/tests/Unit/TenancyOptionsTest.php b/tests/Unit/TenancyOptionsTest.php index 3f83d2f..9ee73df 100644 --- a/tests/Unit/TenancyOptionsTest.php +++ b/tests/Unit/TenancyOptionsTest.php @@ -6,8 +6,8 @@ use Illuminate\Config\Repository; use Orchestra\Testbench\Attributes\DefineEnvironment; use PHPUnit\Framework\Attributes\Test; -use Sprout\Managers\TenancyManager; use Sprout\TenancyOptions; +use function Sprout\tenancy; class TenancyOptionsTest extends UnitTestCase { @@ -40,7 +40,7 @@ public function throwIfNotRelatedOption(): void #[Test, DefineEnvironment('setupSecondTenancy')] public function correctlyReportsHydrateTenantRelationOptionPresence(): void { - $tenancy = app(TenancyManager::class)->get('tenants'); + $tenancy = tenancy('tenants'); $tenancy->removeOption(TenancyOptions::hydrateTenantRelation()); $this->assertFalse(TenancyOptions::shouldHydrateTenantRelation($tenancy)); @@ -49,7 +49,7 @@ public function correctlyReportsHydrateTenantRelationOptionPresence(): void $this->assertTrue(TenancyOptions::shouldHydrateTenantRelation($tenancy)); - $tenancy = app(TenancyManager::class)->get('backup'); + $tenancy = tenancy('backup'); $this->assertFalse(TenancyOptions::shouldHydrateTenantRelation($tenancy)); } @@ -57,7 +57,7 @@ public function correctlyReportsHydrateTenantRelationOptionPresence(): void #[Test, DefineEnvironment('setupSecondTenancy')] public function correctlyReportsThrowIfNotRelatedOptionPresence(): void { - $tenancy = app(TenancyManager::class)->get('tenants'); + $tenancy = tenancy('tenants'); $tenancy->removeOption(TenancyOptions::throwIfNotRelated()); $this->assertFalse(TenancyOptions::shouldThrowIfNotRelated($tenancy)); @@ -66,7 +66,7 @@ public function correctlyReportsThrowIfNotRelatedOptionPresence(): void $this->assertTrue(TenancyOptions::shouldThrowIfNotRelated($tenancy)); - $tenancy = app(TenancyManager::class)->get('backup'); + $tenancy = tenancy('backup'); $this->assertFalse(TenancyOptions::shouldThrowIfNotRelated($tenancy)); } diff --git a/tests/Unit/UnitTestCase.php b/tests/Unit/UnitTestCase.php index 02e2b6a..049d89c 100644 --- a/tests/Unit/UnitTestCase.php +++ b/tests/Unit/UnitTestCase.php @@ -4,8 +4,9 @@ namespace Sprout\Tests\Unit; use Orchestra\Testbench\Concerns\WithWorkbench; +use Orchestra\Testbench\TestCase; -abstract class UnitTestCase extends \Orchestra\Testbench\TestCase +abstract class UnitTestCase extends TestCase { use WithWorkbench; From 45d6c800a8dea389d9f3d0a334670aa64cd8da4d Mon Sep 17 00:00:00 2001 From: Ollie Read Date: Tue, 19 Nov 2024 12:56:04 +0000 Subject: [PATCH 16/21] chore: Add RefreshDatabase trait to relevant tests --- tests/Unit/Support/DefaultTenancyTest.php | 3 +++ tests/Unit/Support/ResolutionHelperTest.php | 3 +++ 2 files changed, 6 insertions(+) diff --git a/tests/Unit/Support/DefaultTenancyTest.php b/tests/Unit/Support/DefaultTenancyTest.php index a0cfc51..f152494 100644 --- a/tests/Unit/Support/DefaultTenancyTest.php +++ b/tests/Unit/Support/DefaultTenancyTest.php @@ -3,6 +3,7 @@ namespace Sprout\Tests\Unit\Support; +use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Support\Facades\Event; use PHPUnit\Framework\Attributes\Test; use Sprout\Events\TenantIdentified; @@ -17,6 +18,8 @@ class DefaultTenancyTest extends UnitTestCase { + use RefreshDatabase; + protected function defineEnvironment($app): void { tap($app['config'], static function ($config) { diff --git a/tests/Unit/Support/ResolutionHelperTest.php b/tests/Unit/Support/ResolutionHelperTest.php index 5385696..91875b5 100644 --- a/tests/Unit/Support/ResolutionHelperTest.php +++ b/tests/Unit/Support/ResolutionHelperTest.php @@ -3,6 +3,7 @@ namespace Sprout\Tests\Unit\Support; +use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Http\Request; use Illuminate\Routing\Route; use Mockery\MockInterface; @@ -19,6 +20,8 @@ class ResolutionHelperTest extends UnitTestCase { + use RefreshDatabase; + protected function defineEnvironment($app): void { tap($app['config'], static function ($config) { From dc25c65e8417288114325f5412d5a594458c637b Mon Sep 17 00:00:00 2001 From: Ollie Read Date: Tue, 19 Nov 2024 12:56:19 +0000 Subject: [PATCH 17/21] test: Add unit test for eloquent tenant provider --- tests/Unit/Providers/EloquentProviderTest.php | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 tests/Unit/Providers/EloquentProviderTest.php diff --git a/tests/Unit/Providers/EloquentProviderTest.php b/tests/Unit/Providers/EloquentProviderTest.php new file mode 100644 index 0000000..0689aa8 --- /dev/null +++ b/tests/Unit/Providers/EloquentProviderTest.php @@ -0,0 +1,71 @@ +set('multitenancy.providers.tenants.model', TenantModel::class); + }); + } + + #[Test] + public function hasARegisteredName(): void + { + $provider = provider('tenants'); + + $this->assertInstanceOf(EloquentTenantProvider::class, $provider); + $this->assertSame('tenants', $provider->getName()); + } + + #[Test] + public function hasAModelClass(): void + { + $provider = provider('tenants'); + + $this->assertInstanceOf(EloquentTenantProvider::class, $provider); + $this->assertSame(TenantModel::class, $provider->getModelClass()); + } + + #[Test] + public function retrievesTenantsByTheirIdentifier(): void + { + $provider = provider('tenants'); + + $tenant = TenantModel::factory()->createOne(); + + $found = $provider->retrieveByIdentifier($tenant->getTenantIdentifier()); + + $this->assertNotNull($found); + $this->assertTrue($tenant->is($found)); + + $this->assertNull($provider->retrieveByIdentifier('fake-identifier')); + } + + #[Test] + public function retrievesTenantsByTheirKey(): void + { + $provider = provider('tenants'); + + $tenant = TenantModel::factory()->createOne(); + + $found = $provider->retrieveByKey($tenant->getTenantKey()); + + $this->assertNotNull($found); + $this->assertTrue($tenant->is($found)); + + $this->assertNull($provider->retrieveByKey(-999)); + } +} From a21189ad8077bba250625d4ca5ef87893f948e7c Mon Sep 17 00:00:00 2001 From: Ollie Read Date: Tue, 19 Nov 2024 12:56:49 +0000 Subject: [PATCH 18/21] test: Add unit test for the database tenant provider --- tests/Unit/Providers/DatabaseProviderTest.php | 120 ++++++++++++++++ workbench/app/CustomTenantEntity.php | 131 ++++++++++++++++++ 2 files changed, 251 insertions(+) create mode 100644 tests/Unit/Providers/DatabaseProviderTest.php create mode 100644 workbench/app/CustomTenantEntity.php diff --git a/tests/Unit/Providers/DatabaseProviderTest.php b/tests/Unit/Providers/DatabaseProviderTest.php new file mode 100644 index 0000000..3ac615e --- /dev/null +++ b/tests/Unit/Providers/DatabaseProviderTest.php @@ -0,0 +1,120 @@ +set('multitenancy.providers.tenants.driver', 'database'); + $config->set('multitenancy.providers.tenants.table', 'tenants'); + }); + } + + protected function withCustomTenantEntity($app): void + { + tap($app['config'], static function ($config) { + $config->set('multitenancy.providers.tenants.entity', CustomTenantEntity::class); + }); + } + + #[Test] + public function hasARegisteredName(): void + { + $provider = provider('tenants'); + + $this->assertInstanceOf(DatabaseTenantProvider::class, $provider); + $this->assertSame('tenants', $provider->getName()); + } + + #[Test] + public function hasATable(): void + { + $provider = provider('tenants'); + + $this->assertInstanceOf(DatabaseTenantProvider::class, $provider); + $this->assertSame('tenants', $provider->getTable()); + } + + #[Test] + public function hasATenantEntity(): void + { + $provider = provider('tenants'); + + $this->assertInstanceOf(DatabaseTenantProvider::class, $provider); + $this->assertSame(GenericTenant::class, $provider->getEntityClass()); + } + + #[Test] + public function retrievesTenantsByTheirIdentifier(): void + { + $provider = provider('tenants'); + + $tenantData = [ + 'name' => 'Test Tenant', + 'identifier' => 'tenant-test', + 'active' => true, + ]; + + $tenantData['id'] = DB::table('tenants')->insertGetId($tenantData); + + $found = $provider->retrieveByIdentifier($tenantData['identifier']); + + $this->assertNotNull($found); + $this->assertInstanceOf(GenericTenant::class, $found); + $this->assertSame($tenantData['identifier'], $found->getTenantIdentifier()); + $this->assertSame($tenantData['id'], $found->getTenantKey()); + + $this->assertNull($provider->retrieveByIdentifier('fake-identifier')); + } + + #[Test] + public function retrievesTenantsByTheirKey(): void + { + $provider = provider('tenants'); + + $tenantData = [ + 'name' => 'Test Tenant', + 'identifier' => 'tenant-test', + 'active' => true, + ]; + + $tenantData['id'] = DB::table('tenants')->insertGetId($tenantData); + + $found = $provider->retrieveByKey($tenantData['id']); + + $this->assertNotNull($found); + $this->assertInstanceOf(GenericTenant::class, $found); + $this->assertSame($tenantData['identifier'], $found->getTenantIdentifier()); + $this->assertSame($tenantData['id'], $found->getTenantKey()); + + $this->assertNull($provider->retrieveByKey(-999)); + } + + #[Test, DefineEnvironment('withCustomTenantEntity')] + public function canHaveCustomTenantEntity(): void + { + // This is necessary as the provider has already been resolved + sprout()->providers()->flushResolved(); + + $provider = provider('tenants'); + + $this->assertInstanceOf(DatabaseTenantProvider::class, $provider); + $this->assertSame(CustomTenantEntity::class, $provider->getEntityClass()); + } +} diff --git a/workbench/app/CustomTenantEntity.php b/workbench/app/CustomTenantEntity.php new file mode 100644 index 0000000..a566c7a --- /dev/null +++ b/workbench/app/CustomTenantEntity.php @@ -0,0 +1,131 @@ + + */ + protected array $attributes; + + /** + * Create a new generic User object. + * + * @param array $attributes + * + * @return void + */ + public function __construct(array $attributes = []) + { + $this->attributes = $attributes; + } + + /** + * Get the tenant identifier + * + * Retrieve the identifier used to publicly identify the tenant. + * + * @return string + */ + public function getTenantIdentifier(): string + { + /** @phpstan-ignore-next-line */ + return $this->attributes[$this->getTenantIdentifierName()]; + } + + /** + * Get the name of the tenant identifier + * + * Retrieve the storage name for the tenant identifier, whether that's an + * attribute, column name, array key or something else. + * Used primarily by {@see \Sprout\Contracts\TenantProvider}. + * + * @return string + */ + public function getTenantIdentifierName(): string + { + return 'identifier'; + } + + /** + * Get the tenant key + * + * Retrieve the key used to identify a tenant internally. + * + * @return int|string + */ + public function getTenantKey(): int|string + { + /** @phpstan-ignore-next-line */ + return $this->attributes[$this->getTenantKeyName()]; + } + + /** + * Get the name of the tenant key + * + * Retrieve the storage name for the tenant key, whether that's an + * attribute, column name, array key or something else. + * Used primarily by {@see \Sprout\Contracts\TenantProvider}. + * + * @return string + */ + public function getTenantKeyName(): string + { + return 'id'; + } + + /** + * Dynamically access the tenant's attributes. + * + * @param string $key + * + * @return mixed + */ + public function __get(string $key): mixed + { + return $this->attributes[$key]; + } + + /** + * Dynamically set an attribute on the tenant. + * + * @param string $key + * @param mixed $value + * + * @return void + */ + public function __set(string $key, mixed $value): void + { + $this->attributes[$key] = $value; + } + + /** + * Dynamically check if a value is set on the tenant. + * + * @param string $key + * + * @return bool + */ + public function __isset(string $key): bool + { + return isset($this->attributes[$key]); + } + + /** + * Dynamically unset a value on the tenant. + * + * @param string $key + * + * @return void + */ + public function __unset(string $key): void + { + unset($this->attributes[$key]); + } +} From 6c118f2d0108c7a8888f0ccaa1668289e362d46b Mon Sep 17 00:00:00 2001 From: Ollie Read Date: Sat, 23 Nov 2024 11:45:42 +0000 Subject: [PATCH 19/21] chore: Remove unnecessary service override from AuthOverride --- src/Overrides/AuthOverride.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Overrides/AuthOverride.php b/src/Overrides/AuthOverride.php index c7d41da..fcfdeee 100644 --- a/src/Overrides/AuthOverride.php +++ b/src/Overrides/AuthOverride.php @@ -21,7 +21,7 @@ * * @package Overrides */ -final class AuthOverride implements ServiceOverride, BootableServiceOverride, DeferrableServiceOverride +final class AuthOverride implements BootableServiceOverride, DeferrableServiceOverride { /** * @var \Illuminate\Auth\AuthManager From 98854efe31ec89249a1ba15dddc5d21c8241bddb Mon Sep 17 00:00:00 2001 From: Ollie Read Date: Sat, 23 Nov 2024 11:47:43 +0000 Subject: [PATCH 20/21] refactor: Boot service override _AFTER_ the application has booted Remove the manual booting of service overrides from the SproutServiceProvider::boot() method and add to the Application::booted() lifecycle event --- src/SproutServiceProvider.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/SproutServiceProvider.php b/src/SproutServiceProvider.php index f6c1604..a3e3368 100644 --- a/src/SproutServiceProvider.php +++ b/src/SproutServiceProvider.php @@ -31,6 +31,7 @@ public function register(): void $this->registerManagers(); $this->registerMiddleware(); $this->registerRouteMixin(); + $this->registerServiceOverrideBooting(); } private function registerSprout(): void @@ -78,13 +79,17 @@ protected function registerRouteMixin(): void Router::mixin(new RouterMethods()); } + protected function registerServiceOverrideBooting(): void + { + $this->app->booted($this->sprout->bootOverrides(...)); + } + public function boot(): void { $this->publishConfig(); $this->registerServiceOverrides(); $this->registerEventListeners(); $this->registerTenancyBootstrappers(); - $this->bootServiceOverrides(); } private function publishConfig(): void @@ -128,9 +133,4 @@ private function registerTenancyBootstrappers(): void $events->listen(CurrentTenantChanged::class, $bootstrapper); } } - - private function bootServiceOverrides(): void - { - $this->sprout->bootOverrides(); - } } From 346a6ee9f20412d17145db1fef01c1d2dc1a9bb1 Mon Sep 17 00:00:00 2001 From: Ollie Read Date: Mon, 25 Nov 2024 09:44:57 +0000 Subject: [PATCH 21/21] fix: Correct return type for the AddTenantHeaderToResponse middleware --- src/Http/Middleware/AddTenantHeaderToResponse.php | 10 +++++----- src/Http/Middleware/TenantRoutes.php | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Http/Middleware/AddTenantHeaderToResponse.php b/src/Http/Middleware/AddTenantHeaderToResponse.php index 057d56f..bbda4c2 100644 --- a/src/Http/Middleware/AddTenantHeaderToResponse.php +++ b/src/Http/Middleware/AddTenantHeaderToResponse.php @@ -5,11 +5,10 @@ use Closure; use Illuminate\Http\Request; -use Illuminate\Http\Response; use Sprout\Http\Resolvers\HeaderIdentityResolver; use Sprout\Sprout; use Sprout\Support\ResolutionHelper; -use Sprout\Support\ResolutionHook; +use Symfony\Component\HttpFoundation\Response; /** * Add Tenant Header to Response @@ -43,9 +42,10 @@ public function __construct(Sprout $sprout) * @param \Closure $next * @param string ...$options * - * @return \Illuminate\Http\Response + * @return \Symfony\Component\HttpFoundation\Response * - * @throws \Sprout\Exceptions\NoTenantFound + * @throws \Illuminate\Contracts\Container\BindingResolutionException + * @throws \Sprout\Exceptions\MisconfigurationException */ public function handle(Request $request, Closure $next, string ...$options): Response { @@ -68,7 +68,7 @@ public function handle(Request $request, Closure $next, string ...$options): Res } return $response->withHeaders([ - $resolver->getRequestHeaderName($tenancy) => $tenancy->identifier() + $resolver->getRequestHeaderName($tenancy) => $tenancy->identifier(), ]); } } diff --git a/src/Http/Middleware/TenantRoutes.php b/src/Http/Middleware/TenantRoutes.php index 941d501..1bc981f 100644 --- a/src/Http/Middleware/TenantRoutes.php +++ b/src/Http/Middleware/TenantRoutes.php @@ -48,7 +48,7 @@ public function __construct(Sprout $sprout) * @param \Closure $next * @param string ...$options * - * @return \Illuminate\Http\Response + * @return \Symfony\Component\HttpFoundation\Response * * @throws \Sprout\Exceptions\NoTenantFound * @throws \Illuminate\Contracts\Container\BindingResolutionException