Skip to content

Commit b2add06

Browse files
committed
Delete BroadcastingTest
Tests from BroadcastingTest moved to the appropriate bootstrapper test files. The new tenant credentials test has assertions equal to both the original property -> config mapping test and the config -> credentials test.
1 parent bbe2ff0 commit b2add06

3 files changed

Lines changed: 199 additions & 306 deletions

File tree

tests/Bootstrappers/BroadcastChannelPrefixBootstrapperTest.php

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
use Stancl\Tenancy\Bootstrappers\DatabaseTenancyBootstrapper;
1717
use Stancl\Tenancy\Bootstrappers\BroadcastChannelPrefixBootstrapper;
1818
use function Stancl\Tenancy\Tests\pest;
19+
use Illuminate\Broadcasting\Broadcasters\NullBroadcaster;
20+
use Illuminate\Support\Facades\Broadcast;
21+
use Illuminate\Support\Collection;
1922

2023
beforeEach(function () {
2124
Event::listen(TenancyInitialized::class, BootstrapTenancy::class);
@@ -137,3 +140,96 @@ protected function formatChannels(array $channels)
137140
expect(app(BroadcastManager::class)->driver())->toBe($broadcaster);
138141
expect(invade(app(BroadcastManager::class)->driver())->formatChannels($channelNames))->toEqual($channelNames);
139142
});
143+
144+
test('broadcasting channel helpers register channels correctly', function() {
145+
config([
146+
'broadcasting.default' => $driver = 'testing',
147+
'broadcasting.connections.testing.driver' => $driver,
148+
]);
149+
150+
config(['tenancy.bootstrappers' => [DatabaseTenancyBootstrapper::class]]);
151+
152+
Schema::create('users', function (Blueprint $table) {
153+
$table->increments('id');
154+
$table->string('name');
155+
$table->string('email')->unique();
156+
$table->string('password');
157+
$table->rememberToken();
158+
$table->timestamps();
159+
});
160+
161+
$centralUser = User::create(['name' => 'central', 'email' => 'test@central.cz', 'password' => 'test']);
162+
$tenant = Tenant::create();
163+
164+
migrateTenants();
165+
166+
tenancy()->initialize($tenant);
167+
168+
// Same ID as $centralUser
169+
$tenantUser = User::create(['name' => 'tenant', 'email' => 'test@tenant.cz', 'password' => 'test']);
170+
171+
tenancy()->end();
172+
173+
/** @var BroadcastManager $broadcastManager */
174+
$broadcastManager = app(BroadcastManager::class);
175+
176+
// Use a driver with no channels
177+
$broadcastManager->extend($driver, fn () => new NullBroadcaster);
178+
179+
$getChannels = fn (): Collection => $broadcastManager->driver($driver)->getChannels();
180+
181+
expect($getChannels())->toBeEmpty();
182+
183+
// Basic channel registration
184+
Broadcast::channel($channelName = 'user.{userName}', $channelClosure = function ($user, $userName) {
185+
return User::firstWhere('name', $userName)?->is($user) ?? false;
186+
});
187+
188+
// Check if the channel is registered
189+
$centralChannelClosure = $getChannels()->first(fn ($closure, $name) => $name === $channelName);
190+
expect($centralChannelClosure)->not()->toBeNull();
191+
192+
// Channel closures work as expected (running in central context)
193+
expect($centralChannelClosure($centralUser, $centralUser->name))->toBeTrue();
194+
expect($centralChannelClosure($centralUser, $tenantUser->name))->toBeFalse();
195+
196+
// Register a tenant broadcasting channel (almost identical to the original channel, just able to accept the tenant key)
197+
tenant_channel($channelName, $channelClosure);
198+
199+
// Tenant channel registered – its name is correctly prefixed ("{tenant}.user.{userId}")
200+
$tenantChannelClosure = $getChannels()->first(fn ($closure, $name) => $name === "{tenant}.$channelName");
201+
expect($tenantChannelClosure)->toBe($centralChannelClosure);
202+
203+
// The tenant channels are prefixed with '{tenant}.'
204+
// They accept the tenant key, but their closures only run in tenant context when tenancy is initialized
205+
// The regular channels don't accept the tenant key, but they also respect the current context
206+
// The tenant key is used solely for the name prefixing – the closures can still run in the central context
207+
tenant_channel($channelName, $tenantChannelClosure = function ($user, $tenant, $userName) {
208+
return User::firstWhere('name', $userName)?->is($user) ?? false;
209+
});
210+
211+
expect($tenantChannelClosure)->not()->toBe($centralChannelClosure);
212+
213+
expect($tenantChannelClosure($centralUser, $tenant->getTenantKey(), $centralUser->name))->toBeTrue();
214+
expect($tenantChannelClosure($centralUser, $tenant->getTenantKey(), $tenantUser->name))->toBeFalse();
215+
216+
tenancy()->initialize($tenant);
217+
218+
// The channel closure runs in the central context
219+
// Only the central user is available
220+
expect($tenantChannelClosure($centralUser, $tenant->getTenantKey(), $tenantUser->name))->toBeFalse();
221+
expect($tenantChannelClosure($tenantUser, $tenant->getTenantKey(), $tenantUser->name))->toBeTrue();
222+
223+
// Use a new channel instance to delete the previously registered channels before testing the universal_channel helper
224+
$broadcastManager->purge($driver);
225+
$broadcastManager->extend($driver, fn () => new NullBroadcaster);
226+
227+
expect($getChannels())->toBeEmpty();
228+
229+
// Global channel helper prefixes the channel name with 'global__'
230+
global_channel($channelName, $channelClosure);
231+
232+
// Channel prefixed with 'global__' found
233+
$foundChannelClosure = $getChannels()->first(fn ($closure, $name) => $name === 'global__' . $channelName);
234+
expect($foundChannelClosure)->not()->toBeNull();
235+
});

tests/Bootstrappers/BroadcastingConfigBootstrapperTest.php

Lines changed: 103 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
use Stancl\Tenancy\Listeners\RevertToCentralContext;
1111
use Stancl\Tenancy\Overrides\TenancyBroadcastManager;
1212
use Stancl\Tenancy\Bootstrappers\BroadcastingConfigBootstrapper;
13+
use Illuminate\Support\Facades\Broadcast;
14+
use Illuminate\Contracts\Broadcasting\Broadcaster as BroadcasterContract;
1315

1416
beforeEach(function () {
1517
Event::listen(TenancyInitialized::class, BootstrapTenancy::class);
@@ -38,68 +40,135 @@
3840
expect(app(BroadcastManager::class))->toBeInstanceOf(BroadcastManager::class);
3941
});
4042

41-
test('BroadcastingConfigBootstrapper maps tenant broadcaster credentials to config as specified in the $credentialsMap property and reverts the config after ending tenancy', function() {
43+
test('BroadcastingConfigBootstrapper maps tenant properties to broadcaster credentials correctly', function() {
4244
config([
43-
'broadcasting.connections.testing.driver' => 'testing',
44-
'broadcasting.connections.testing.message' => $defaultMessage = 'default',
45-
'tenancy.bootstrappers' => [BroadcastingConfigBootstrapper::class],
45+
'broadcasting.default' => $driver = 'testing',
46+
'broadcasting.connections.testing.driver' => $driver,
47+
'broadcasting.connections.testing.key' => 'central_key',
48+
'tenancy.bootstrappers' => [
49+
BroadcastingConfigBootstrapper::class,
50+
],
4651
]);
4752

48-
BroadcastingConfigBootstrapper::$credentialsMap = [
49-
'broadcasting.connections.testing.message' => 'testing_broadcaster_message',
50-
];
53+
BroadcastingConfigBootstrapper::$credentialsMap['broadcasting.connections.testing.key'] = 'testing_key';
5154

52-
$tenant = Tenant::create(['testing_broadcaster_message' => $tenantMessage = 'first testing']);
53-
$tenant2 = Tenant::create(['testing_broadcaster_message' => $secondTenantMessage = 'second testing']);
55+
// Register the testing broadcaster
56+
app(BroadcastManager::class)->extend('testing', fn($app, $config) => new TestingBroadcaster('testing', $config));
57+
58+
$tenant1 = Tenant::create(['testing_key' => 'tenant1_key']);
59+
$tenant2 = Tenant::create(['testing_key' => 'tenant2_key']);
60+
61+
expect(config('broadcasting.connections.testing.key'))->toBe('central_key');
62+
expect(app(BroadcastManager::class)->driver()->config['key'])->toBe('central_key');
63+
expect(app(BroadcasterContract::class)->config['key'])->toBe('central_key');
64+
expect(Broadcast::driver()->config['key'])->toBe('central_key');
65+
66+
tenancy()->initialize($tenant1);
67+
68+
expect(array_key_exists('testing_key', tenant()->getAttributes()))->toBeTrue();
69+
// Tenant's testing_key property is mapped to broadcasting.connections.testing.key config value
70+
expect(config('broadcasting.connections.testing.key'))->toBe('tenant1_key');
71+
expect(app(BroadcastManager::class)->driver()->config['key'])->toBe('tenant1_key');
72+
// Switching to tenant context makes the currently bound Broadcaster instance use the tenant's config
73+
expect(app(BroadcasterContract::class)->config['key'])->toBe('tenant1_key');
74+
// The Broadcast facade (used in BroadcastController::authenticate) uses the broadcaster with tenant config
75+
// instead of the stale broadcaster instance resolved before tenancy was initialized
76+
expect(Broadcast::driver()->config['key'])->toBe('tenant1_key');
77+
78+
tenancy()->initialize($tenant2);
79+
80+
expect(array_key_exists('testing_key', tenant()->getAttributes()))->toBeTrue();
81+
expect(config('broadcasting.connections.testing.key'))->toBe('tenant2_key');
82+
// Switching to another tenant context makes the current broadcaster use the new tenant's config
83+
expect(app(BroadcastManager::class)->driver()->config['key'])->toBe('tenant2_key');
84+
expect(app(BroadcasterContract::class)->config['key'])->toBe('tenant2_key');
85+
expect(Broadcast::driver()->config['key'])->toBe('tenant2_key');
86+
87+
tenancy()->end();
88+
89+
expect(config('broadcasting.connections.testing.key'))->toBe('central_key');
90+
// Ending tenancy reverts the broadcaster changes
91+
expect(app(BroadcastManager::class)->driver()->config['key'])->toBe('central_key');
92+
expect(app(BroadcasterContract::class)->config['key'])->toBe('central_key');
93+
expect(Broadcast::driver()->config['key'])->toBe('central_key');
94+
});
95+
96+
test('tenant broadcast manager receives the custom driver creators of the central broadcast manager', function() {
97+
config([
98+
'tenancy.bootstrappers' => [
99+
BroadcastingConfigBootstrapper::class,
100+
],
101+
]);
102+
103+
$tenant = Tenant::create();
104+
$tenant2 = Tenant::create();
105+
106+
app(BroadcastManager::class)->extend('testing', fn($app, $config) => new TestingBroadcaster('testing', $config));
107+
108+
$originalDrivers = array_keys(invade(app(BroadcastManager::class))->customCreators);
109+
110+
expect($originalDrivers)->toContain('testing');
54111

55112
tenancy()->initialize($tenant);
56113

57-
expect(array_key_exists('testing_broadcaster_message', tenant()->getAttributes()))->toBeTrue();
58-
expect(config('broadcasting.connections.testing.message'))->toBe($tenantMessage);
114+
app(BroadcastManager::class)->extend(
115+
'testing-tenant1',
116+
fn($app, $config) => new TestingBroadcaster('testing-tenant1', $config)
117+
);
118+
119+
// Current BroadcastManager instance has the original custom creators plus the newly registered testing-tenant1 creator
120+
expect(array_keys(invade(app(BroadcastManager::class))->customCreators))->toBe([...$originalDrivers, 'testing-tenant1']);
59121

60122
tenancy()->initialize($tenant2);
61123

62-
expect(config('broadcasting.connections.testing.message'))->toBe($secondTenantMessage);
124+
// Current BroadcastManager only has the original custom creators,
125+
// the creator added in the previous tenant's context doesn't persist.
126+
expect(array_keys(invade(app(BroadcastManager::class))->customCreators))->toBe($originalDrivers);
63127

64128
tenancy()->end();
65129

66-
expect(config('broadcasting.connections.testing.message'))->toBe($defaultMessage);
130+
// Ending tenancy reverts the BroadcastManager binding back to the original state,
131+
// the creator registered in the tenant context doesn't persist.
132+
expect(array_keys(invade(app(BroadcastManager::class))->customCreators))->toBe($originalDrivers);
67133
});
68134

69-
test('BroadcastingConfigBootstrapper makes the app use broadcasters with the correct credentials', function() {
135+
test('tenant broadcasters receive the channels from the broadcaster bound in central context', function() {
136+
config(['tenancy.bootstrappers' => [BroadcastingConfigBootstrapper::class]]);
70137
config([
71-
'broadcasting.default' => 'testing',
72-
'broadcasting.connections.testing.driver' => 'testing',
73-
'broadcasting.connections.testing.message' => $defaultMessage = 'default',
74-
'tenancy.bootstrappers' => [BroadcastingConfigBootstrapper::class],
138+
'broadcasting.default' => $driver = 'testing',
139+
'broadcasting.connections.testing.driver' => $driver,
75140
]);
76141

77-
TenancyBroadcastManager::$tenantBroadcasters[] = 'testing';
78-
BroadcastingConfigBootstrapper::$credentialsMap = [
79-
'broadcasting.connections.testing.message' => 'testing_broadcaster_message',
80-
];
142+
TenancyBroadcastManager::$tenantBroadcasters[] = $driver;
81143

82-
$registerTestingBroadcaster = fn() => app(BroadcastManager::class)->extend('testing', fn ($app, $config) => new TestingBroadcaster($config['message']));
144+
$tenant1 = Tenant::create();
145+
$tenant2 = Tenant::create();
83146

84-
$registerTestingBroadcaster();
147+
app(BroadcastManager::class)->extend('testing', fn($app, $config) => new TestingBroadcaster('testing'));
148+
$getCurrentChannelsFromBoundBroadcaster = fn() => array_keys(invade(app(BroadcasterContract::class))->channels);
149+
$getCurrentChannelsThroughManager = fn() => array_keys(invade(app(BroadcastManager::class)->driver())->channels);
85150

86-
expect(invade(app(BroadcastManager::class)->driver())->message)->toBe($defaultMessage);
151+
Broadcast::channel($channel = 'testing-channel', fn() => true);
87152

88-
$tenant = Tenant::create(['testing_broadcaster_message' => $tenantMessage = 'first testing']);
89-
$tenant2 = Tenant::create(['testing_broadcaster_message' => $secondTenantMessage = 'second testing']);
153+
expect($channel)
154+
->toBeIn($getCurrentChannelsThroughManager())
155+
->toBeIn($getCurrentChannelsFromBoundBroadcaster());
90156

91-
tenancy()->initialize($tenant);
92-
$registerTestingBroadcaster();
157+
tenancy()->initialize($tenant1);
93158

94-
expect(invade(app(BroadcastManager::class)->driver())->message)->toBe($tenantMessage);
159+
expect($channel)
160+
->toBeIn($getCurrentChannelsThroughManager())
161+
->toBeIn($getCurrentChannelsFromBoundBroadcaster());
95162

96163
tenancy()->initialize($tenant2);
97-
$registerTestingBroadcaster();
98164

99-
expect(invade(app(BroadcastManager::class)->driver())->message)->toBe($secondTenantMessage);
165+
expect($channel)
166+
->toBeIn($getCurrentChannelsThroughManager())
167+
->toBeIn($getCurrentChannelsFromBoundBroadcaster());
100168

101169
tenancy()->end();
102-
$registerTestingBroadcaster();
103170

104-
expect(invade(app(BroadcastManager::class)->driver())->message)->toBe($defaultMessage);
171+
expect($channel)
172+
->toBeIn($getCurrentChannelsThroughManager())
173+
->toBeIn($getCurrentChannelsFromBoundBroadcaster());
105174
});

0 commit comments

Comments
 (0)