Skip to content

[4.x] BroadcastingConfigBootstrapper: fix central instances persisting in tenant context#1448

Open
lukinovec wants to merge 37 commits intomasterfrom
broadcasting-fixes
Open

[4.x] BroadcastingConfigBootstrapper: fix central instances persisting in tenant context#1448
lukinovec wants to merge 37 commits intomasterfrom
broadcasting-fixes

Conversation

@lukinovec
Copy link
Copy Markdown
Contributor

@lukinovec lukinovec commented Mar 31, 2026

Broadcasting auth route problem (Broadcast facade always uses central BroadcastManager)

Note: Tested with Pusher and Reverb

When using BroadcastingConfigBootstrapper for broadcasting on private channels with multiple broadcasting apps (= each tenant has its own Pusher/Reverb/... app and credentials), the auth requests sent to the broadcasting server use the central broadcasting credentials. This is because the Broadcast facade (used in BroadcastController::authenticate like Broadcast::auth($request) to authorize the channels and then retrieve the auth key that's then sent to the broadcasting server) keeps using the BroadcastManager instance resolved in the central context instead of TenancyBroadcastManager. Calling withBroadcasting() in bootstrap/app.php results in calling Broadcast::routes() in the central context, which resolves and stores the central BroadcastManager (which is never cleared, and is used in tenant context).

Clearing the facade's resolved Illuminate\Contracts\Broadcasting\Factory instance in BroadcastingConfigBootstrapper::bootstrap() forces the facade to re-resolve Factory (= BroadcastManager) on the next use, so the next Broadcast::auth() call uses TenantBroadcastManager in the tenant context (and clearing the resolved instance in revert() makes the Broadcast facade use the central BroadcastManager again), and the credentials from the current config will be used for authentication.

TenancyBroadcastManager doesn't inherit custom driver creators

When registering a custom driver creator (app(BroadcastManager::class)->extend('custom-driver', fn($app, $config) => new CustomBroadcaster(...))) in the central context (e.g. in BroadcastServiceProvider/AppServiceProvider, or in our case, in the tests), the creator won't be available in the tenant context. Calling e.g. app(BroadcastManager::class)->driver('custom-driver') in the tenant context (if the creator was registered only in the central context) would throw InvalidArgumentException ("Driver [custom-driver] is not supported.").

To fix that, register the original (central) BroadcastManager's custom creators in the new TenancyBroadcastManager in BroadcastingConfigBootstrapper::bootstrap().

Central Broadcaster instance bound in tenant context

Resolving Illuminate\Contracts\Broadcasting\Broadcaster::class in the tenant context returns a Broadcaster instance from the central context. Currently, this doesn't cause any issues, but it could be a problem when using DI/resolving Broadcaster.

Fixed by making Illuminate\Contracts\Broadcasting\Broadcaster::class resolve to the driver of the current BroadcastManager instance (which is TenancyBroadcastManager in the tenant context) by using app->extend().

But there's a catch: updating credentials in the config while in tenant context won't be reflected in the bound Broadcaster instance. To make that work (with direct config updates), we'd have to use bind() instead of extend(), but then, we wouldn't be able to resolve the original broadcaster in TenancyBroadcastManager::get() (so that we can pass its channels to the new broadcaster). We'd have to pass the original broadcaster to TenancyBroadcastManager, and for that, we'd have to override the manager's constructor, which doesn't seem worth, since this is a pretty niche use case and simply reinitializing tenancy fixes the problem anyway.

For cases like sending a notification in response to updating tenant's broadcasting credentials in tenant context, it's recommended to reinitialize tenancy after updating the credentials.

Credential map: overriding presets (BroadcastingConfigBootstrapper)

Previously, credential mappings from $mapPresets overrode mappings defined in $credentialsMap. If someone used e.g. Pusher and wanted to override some of that preset's mappings, e.g. use 'pusher_app_key' instead of 'pusher_key' by specifying 'pusher_app_key' in $credentialsMap, the preset's mapping ('pusher_key') would still be used.

Fixed that by reversing the array_merge() order in BroadcastingConfigBootstrapper::__construct().

Tests

Deleted the BroadcastingTest file, moved the tests to appropriate bootstrapper test files.

Added tests for mapping tenant properties to broadcaster credentials. Also added tests for the rest of the changes mentioned above and improved the existing tests.

To-dos

  • Add 'reverb' to TenancyBroadcastManager::$tenantBroadcasters (add it everywhere where the prop is modified to make the tests pass)

Summary by CodeRabbit

  • New Features

    • Added Reverb as a tenant-aware broadcaster.
  • Bug Fixes

    • Tenant broadcaster resolution now switches per-tenant and reliably reverts.
    • Preset credential mappings now take precedence for tenant configs.
    • Central custom broadcaster creators are preserved into the active tenant without leaking.
    • Broadcast resolution/cache is refreshed on tenant bootstrap and revert so connections and channels re-resolve correctly.
    • Channel definitions are transferred to tenant broadcasters when applicable.
  • Tests

    • Expanded and reorganized broadcasting tests, added channel-helper coverage, removed an obsolete suite, and updated the test broadcaster setup.

lukinovec and others added 9 commits March 31, 2026 15:32
Test that BroadcastingConfigBootstrapper correctly maps tenant properties to broadcasting config/credentials, and that the credentials don't leak when switching contexts. Also add the `$config` property to `TestingBroadcaster` so that we can access the credentials used by the broadcaster.
…ators

Test that TenancyBroadcastManager inherits the custom driver creators from the central BroadcastManager.
…ed `Broadcasting\Factory` instance

After initializing tenancy, calls like `Broadcast::auth()` use the central `BroadcastManager`. Clearing the facade's resolved `Broadcasting\Factory` instance fixes that problem.
@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 31, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 86.10%. Comparing base (c32f52c) to head (ddd8c68).

Additional details and impacted files
@@             Coverage Diff              @@
##             master    #1448      +/-   ##
============================================
+ Coverage     86.03%   86.10%   +0.06%     
- Complexity     1156     1157       +1     
============================================
  Files           184      184              
  Lines          3381     3389       +8     
============================================
+ Hits           2909     2918       +9     
+ Misses          472      471       -1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@lukinovec lukinovec marked this pull request as ready for review March 31, 2026 16:03
@lukinovec lukinovec marked this pull request as draft March 31, 2026 16:04
lukinovec and others added 6 commits April 1, 2026 15:50
Moved binding `Broadcaster` to the bootstrapper.
Test that the bound Broadcaster instance inherits the channels too. Also test that the channels aren't lost when switching context to another tenant.
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.
@lukinovec lukinovec marked this pull request as ready for review April 2, 2026 14:54
lukinovec and others added 9 commits April 2, 2026 16:54
Tests now use datasets with all drivers that are in `TenancyBroadcastManager::$tenantBroadcasters` by default plus the custom driver. Also add assertions for updating the tenant properties/config in tenant context.
… order

Previously, credential mappings from `$mapPresets` overrode mappings defined in `$credentialsMap`. If someone used pusher/reverb/ably and wanted to override some of that preset's mappings, e.g. use 'pusher_app_key' instead of 'pusher_key' by specifying 'pusher_app_key' in `$credentialsMap`, the preset's mapping ('pusher_key') would still be used.
Adding 'reverb' to `TenancyBroadcastManager::$tenantBroadcasters` will make these tests pass.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 12, 2026

📝 Walkthrough

Walkthrough

Broadcast credential preset precedence flipped; central BroadcastManager custom creators are copied into the tenancy manager; broadcaster resolution is routed through the tenant manager with channel transfer when applicable; Broadcast facade’s resolved factory is cleared on bootstrap and revert; reverb added as a tenant-scoped driver.

Changes

Cohort / File(s) Summary
Broadcasting bootstrapper
src/Bootstrappers/BroadcastingConfigBootstrapper.php
Flip merge order so mapPresets entries take precedence when building static::$credentialsMap; extend central customCreators into TenancyBroadcastManager; rebind Broadcaster resolution to tenantManager->connection(); clear resolved Illuminate\Contracts\Broadcasting\Factory in bootstrap() and revert(); revert registration callback returns ?BroadcastManager.
Broadcast manager override
src/Overrides/TenancyBroadcastManager.php
Added reverb to static::$tenantBroadcasters; get($name) now always resolves a fresh tenant-scoped broadcaster and, when both old and new implement Broadcaster, transfers channels from the original to the new instance instead of rebinding the singleton.
Tests — added/updated
tests/Bootstrappers/BroadcastChannelPrefixBootstrapperTest.php, tests/Bootstrappers/BroadcastingConfigBootstrapperTest.php
New test for channel helpers and extensive refactor/parameterization of broadcasting bootstrapper tests: credential→config mapping across tenancy lifecycle (including reverb and custom cases), channel presence and propagation, custom-creator isolation, and shared cleanup.
Tests — removal
tests/BroadcastingTest.php
Removed legacy consolidated broadcasting test suite; scenarios redistributed into targeted bootstrapper tests.
Test utilities
tests/Etc/TestingBroadcaster.php
TestingBroadcaster constructor now accepts an additional public array $config parameter (default []).

Sequence Diagram

sequenceDiagram
    participant Central as Central BroadcastManager
    participant Bootstrap as BroadcastingConfigBootstrapper
    participant TenantMgr as TenancyBroadcastManager
    participant Broadcaster as Broadcaster
    participant Facade as Broadcast Facade

    Bootstrap->>Central: read config and customCreators
    Bootstrap->>TenantMgr: instantiate TenancyBroadcastManager
    TenantMgr->>TenantMgr: extend(...) for each central customCreator
    note right of TenantMgr: mapPresets merged first (presets take precedence)
    Bootstrap->>TenantMgr: tenantMgr.connection() -> create Broadcaster
    TenantMgr->>Broadcaster: construct tenant-scoped Broadcaster
    Broadcaster->>Broadcaster: pass channels from original broadcaster (if both are Broadcaster instances)
    Bootstrap->>Facade: clear resolved Factory binding
    Facade->>TenantMgr: subsequent resolves use tenant manager.connection()
    Bootstrap->>Bootstrap: revert()
    Bootstrap->>Facade: clear resolved Factory binding on revert
    Facade->>Central: subsequent resolves use central manager
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~55 minutes

Poem

🐇 I nibbled presets, hopped through code,
Reverb joined the tenant road,
Creators copied, channels kept neat,
Facade forgets then finds the right seat,
A rabbit cheers — broadcasts complete.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately describes the main fix: addressing central broadcaster instances persisting in tenant context.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch broadcasting-fixes

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/Bootstrappers/BroadcastingConfigBootstrapper.php`:
- Around line 96-98: The closure passed to $this->app->extend currently declares
an unused Broadcaster $broadcaster parameter which triggers static analysis;
change the callback signature to remove that parameter so it becomes a
zero-argument closure (e.g., function () { ... } or an arrow fn) and return
$this->app->make(BroadcastManager::class)->connection(); leaving the extend call
and references to Broadcaster::class and BroadcastManager::class unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 36a92dc5-b8bc-4656-be92-2b81ddd77b4c

📥 Commits

Reviewing files that changed from the base of the PR and between 60dd522 and dc344b7.

📒 Files selected for processing (6)
  • src/Bootstrappers/BroadcastingConfigBootstrapper.php
  • src/Overrides/TenancyBroadcastManager.php
  • tests/Bootstrappers/BroadcastChannelPrefixBootstrapperTest.php
  • tests/Bootstrappers/BroadcastingConfigBootstrapperTest.php
  • tests/BroadcastingTest.php
  • tests/Etc/TestingBroadcaster.php
💤 Files with no reviewable changes (1)
  • tests/BroadcastingTest.php

Comment thread src/Bootstrappers/BroadcastingConfigBootstrapper.php Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes tenant broadcasting isolation issues where central broadcasting instances/credentials could leak into tenant context, especially for /broadcasting/auth and custom broadcast drivers.

Changes:

  • Clear the resolved Broadcast facade factory so it re-resolves the (tenant) broadcast manager after tenancy bootstraps.
  • Ensure TenancyBroadcastManager inherits custom driver creators from the central BroadcastManager.
  • Rework/expand tests (including moving prior broadcasting tests into more appropriate bootstrapper test files) and add Reverb coverage.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/Bootstrappers/BroadcastingConfigBootstrapper.php Clears Broadcast facade resolved factory, copies custom creators to tenant manager, and ensures the bound broadcaster resolves via the current manager.
src/Overrides/TenancyBroadcastManager.php Re-resolves tenant broadcasters and passes channels from the “original” broadcaster to the newly resolved one.
tests/Etc/TestingBroadcaster.php Extends test broadcaster to retain resolved config for assertions.
tests/BroadcastingTest.php Removed; tests relocated into more targeted bootstrapper test files.
tests/Bootstrappers/BroadcastingConfigBootstrapperTest.php Adds/updates tests for credential mapping, facade behavior, and custom driver creator inheritance.
tests/Bootstrappers/BroadcastChannelPrefixBootstrapperTest.php Adds tests for tenant_channel / global_channel helpers and channel registration behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/Overrides/TenancyBroadcastManager.php Outdated
Comment thread tests/Bootstrappers/BroadcastingConfigBootstrapperTest.php Outdated
Comment thread tests/Bootstrappers/BroadcastChannelPrefixBootstrapperTest.php Outdated
Comment thread tests/Bootstrappers/BroadcastChannelPrefixBootstrapperTest.php Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

lukinovec and others added 2 commits April 13, 2026 15:28
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/Bootstrappers/BroadcastingConfigBootstrapperTest.php`:
- Around line 21-26: The test currently overwrites
TenancyBroadcastManager::$tenantBroadcasters with a hard-coded array; instead
capture and restore the original value so tests don’t drift if production
defaults change: in the beforeEach store the original list into a local static
or test-scoped variable (e.g., $originalTenantBroadcasters =
TenancyBroadcastManager::$tenantBroadcasters) before modifying it for the test,
and in afterEach set TenancyBroadcastManager::$tenantBroadcasters back to that
saved $originalTenantBroadcasters; apply the same save-and-restore pattern for
the other test blocks that currently hard-code
TenancyBroadcastManager::$tenantBroadcasters.
- Line 73: Update the inline comment in the test to reference the dynamic
connection key used by the assertion: change the mistaken
"broadcasting.connections.testing.key" wording to
"broadcasting.connections.{$driver}.key" (or equivalent
"broadcasting.connections.{driver}.key") so it matches the assertion in
BroadcastingConfigBootstrapperTest and clarifies that the tenant's testing_key
maps to the driver-specific broadcasting.connections entry.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: a47c7533-7284-47f9-b6ee-82f33837df1a

📥 Commits

Reviewing files that changed from the base of the PR and between dc344b7 and 9ab0a72.

📒 Files selected for processing (1)
  • tests/Bootstrappers/BroadcastingConfigBootstrapperTest.php

Comment thread tests/Bootstrappers/BroadcastingConfigBootstrapperTest.php Outdated
Comment thread tests/Bootstrappers/BroadcastingConfigBootstrapperTest.php Outdated
@lukinovec
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 13, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/Bootstrappers/BroadcastingConfigBootstrapperTest.php (1)

29-40: ⚠️ Potential issue | 🟠 Major

Tighten the revert assertion so it can fail.

At Line 40, TenancyBroadcastManager still satisfies instanceof BroadcastManager, so this test passes even if the tenant manager never gets reverted. Check that the resolved manager is not TenancyBroadcastManager before initialization and after tenancy()->end().

♻️ Suggested fix
-    expect(app(BroadcastManager::class))->toBeInstanceOf(BroadcastManager::class);
+    expect(app(BroadcastManager::class))
+        ->toBeInstanceOf(BroadcastManager::class)
+        ->not->toBeInstanceOf(TenancyBroadcastManager::class);
@@
-    expect(app(BroadcastManager::class))->toBeInstanceOf(BroadcastManager::class);
+    expect(app(BroadcastManager::class))
+        ->toBeInstanceOf(BroadcastManager::class)
+        ->not->toBeInstanceOf(TenancyBroadcastManager::class);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/Bootstrappers/BroadcastingConfigBootstrapperTest.php` around lines 29 -
40, The test currently only asserts the resolved BroadcastManager is an instance
of BroadcastManager, which allows TenancyBroadcastManager to satisfy the
assertion; change the assertions to explicitly assert that the resolved manager
is not an instance of TenancyBroadcastManager before tenancy initialization and
again after tenancy()->end(). Locate the test function (test
'BroadcastingConfigBootstrapper binds TenancyBroadcastManager...') and replace
the initial and final expect(...) calls that check app(BroadcastManager::class)
with explicit negative assertions against TenancyBroadcastManager while keeping
the positive assertion during tenancy initialization.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/Bootstrappers/BroadcastingConfigBootstrapperTest.php`:
- Around line 103-110: After updating the broadcasting config for the tenant you
should assert that any previously resolved BroadcasterContract still holds the
stale credentials; add an assertion verifying app(BroadcasterContract::class)
(or the container-resolved Broadcaster instance obtained earlier) still has the
original 'key' value (not 'new_tenant1_key') after calling
config(["broadcasting.connections.{$driver}.key" => 'new_tenant1_key']), in
addition to the existing checks against BroadcastManager::class and
Broadcast::driver().
- Around line 20-26: The tests are failing to reset the cached broadcaster
stored by BroadcastingConfigBootstrapper::__construct() which memoizes
static::$broadcaster (using ??=), so add a reset of that static between dataset
runs: in the before/after hooks that currently clear
BroadcastingConfigBootstrapper::$credentialsMap and
TenancyBroadcastManager::$tenantBroadcasters, also set
BroadcastingConfigBootstrapper::$broadcaster = null (or unset the static cache)
so each dataset will reinitialize its own broadcaster mapping rather than
reusing the memoized instance.

---

Outside diff comments:
In `@tests/Bootstrappers/BroadcastingConfigBootstrapperTest.php`:
- Around line 29-40: The test currently only asserts the resolved
BroadcastManager is an instance of BroadcastManager, which allows
TenancyBroadcastManager to satisfy the assertion; change the assertions to
explicitly assert that the resolved manager is not an instance of
TenancyBroadcastManager before tenancy initialization and again after
tenancy()->end(). Locate the test function (test 'BroadcastingConfigBootstrapper
binds TenancyBroadcastManager...') and replace the initial and final expect(...)
calls that check app(BroadcastManager::class) with explicit negative assertions
against TenancyBroadcastManager while keeping the positive assertion during
tenancy initialization.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 90bccdb7-82ed-4c73-81bf-5c180ba16a09

📥 Commits

Reviewing files that changed from the base of the PR and between b3d5197 and a247bb0.

📒 Files selected for processing (1)
  • tests/Bootstrappers/BroadcastingConfigBootstrapperTest.php

Comment thread tests/Bootstrappers/BroadcastingConfigBootstrapperTest.php Outdated
Comment thread tests/Bootstrappers/BroadcastingConfigBootstrapperTest.php
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/Bootstrappers/BroadcastingConfigBootstrapperTest.php`:
- Around line 25-29: The afterEach teardown does not reset
BroadcastingConfigBootstrapper::$mapPresets which can leak state between tests;
update the afterEach hook to clear or restore
BroadcastingConfigBootstrapper::$mapPresets (alongside
BroadcastingConfigBootstrapper::$broadcaster and::$credentialsMap and
TenancyBroadcastManager::$tenantBroadcasters) so tests are isolated—find the
afterEach closure and add a line that resets
BroadcastingConfigBootstrapper::$mapPresets to its default/empty value.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 9ac5e4b9-8344-4d19-b83c-28fdccb95687

📥 Commits

Reviewing files that changed from the base of the PR and between a3c5568 and f3652a8.

📒 Files selected for processing (1)
  • tests/Bootstrappers/BroadcastingConfigBootstrapperTest.php

Comment thread tests/Bootstrappers/BroadcastingConfigBootstrapperTest.php Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/Bootstrappers/BroadcastingConfigBootstrapperTest.php`:
- Around line 180-204: The test currently asserts exact array order of
app(BroadcastManager::class)->customCreators which is fragile; change the
assertions to be order-insensitive by comparing sets/ sorted keys instead: when
capturing $originalDrivers and subsequent checks after
tenancy()->initialize($tenant), tenancy()->initialize($tenant2), and
tenancy()->end(), sort
array_keys(invade(app(BroadcastManager::class))->customCreators) (or cast to a
set) before comparing to sorted $originalDrivers plus 'testing-tenant1' (or
sorted merge) so the test verifies presence/reversion of creators without
relying on insertion order for BroadcastManager::customCreators and the
TestingBroadcaster registration.
- Around line 22-64: Extract the repeated preset array into a single shared
fixture variable and use that variable in both the setup and teardown hooks
instead of repeating the literal: define a local $presetMap (or similarly named
variable) holding the array, then assign
BroadcastingConfigBootstrapper::$mapPresets = $presetMap and later reset it to
$presetMap in afterEach; keep other resets
(BroadcastingConfigBootstrapper::$broadcaster, ::$credentialsMap and
TenancyBroadcastManager::$tenantBroadcasters) unchanged so setup/teardown cannot
drift.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7f7f6e5d-bb94-45a2-bdef-6b9e568a9933

📥 Commits

Reviewing files that changed from the base of the PR and between f3652a8 and a7acd07.

📒 Files selected for processing (1)
  • tests/Bootstrappers/BroadcastingConfigBootstrapperTest.php

Comment thread tests/Bootstrappers/BroadcastingConfigBootstrapperTest.php Outdated
Comment thread tests/Bootstrappers/BroadcastingConfigBootstrapperTest.php Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
tests/Bootstrappers/BroadcastingConfigBootstrapperTest.php (1)

16-47: 🛠️ Refactor suggestion | 🟠 Major

Restore the original static fixtures instead of duplicating defaults in $cleanup.

Hard-coding BroadcastingConfigBootstrapper::$mapPresets and TenancyBroadcastManager::$tenantBroadcasters here makes the suite drift as production defaults evolve, and these hooks no longer restore the actual pre-test baseline. Capture the current static values once and restore those originals in beforeEach/afterEach.

♻️ Suggested refactor
+$originalBroadcaster = BroadcastingConfigBootstrapper::$broadcaster;
+$originalCredentialsMap = BroadcastingConfigBootstrapper::$credentialsMap;
+$originalMapPresets = BroadcastingConfigBootstrapper::$mapPresets;
+$originalTenantBroadcasters = TenancyBroadcastManager::$tenantBroadcasters;
+
-$cleanup = function () {
-    BroadcastingConfigBootstrapper::$broadcaster = null;
-    BroadcastingConfigBootstrapper::$credentialsMap = [];
-    BroadcastingConfigBootstrapper::$mapPresets = [
-        'pusher' => [
-            'broadcasting.connections.pusher.key' => 'pusher_key',
-            'broadcasting.connections.pusher.secret' => 'pusher_secret',
-            'broadcasting.connections.pusher.app_id' => 'pusher_app_id',
-            'broadcasting.connections.pusher.options.cluster' => 'pusher_cluster',
-        ],
-        'reverb' => [
-            'broadcasting.connections.reverb.key' => 'reverb_key',
-            'broadcasting.connections.reverb.secret' => 'reverb_secret',
-            'broadcasting.connections.reverb.app_id' => 'reverb_app_id',
-            'broadcasting.connections.reverb.options.cluster' => 'reverb_cluster',
-        ],
-        'ably' => [
-            'broadcasting.connections.ably.key' => 'ably_key',
-            'broadcasting.connections.ably.public' => 'ably_public',
-        ],
-    ];
-    TenancyBroadcastManager::$tenantBroadcasters = ['pusher', 'ably', 'reverb'];
+$cleanup = function () use (
+    &$originalBroadcaster,
+    &$originalCredentialsMap,
+    &$originalMapPresets,
+    &$originalTenantBroadcasters,
+) {
+    BroadcastingConfigBootstrapper::$broadcaster = $originalBroadcaster;
+    BroadcastingConfigBootstrapper::$credentialsMap = $originalCredentialsMap;
+    BroadcastingConfigBootstrapper::$mapPresets = $originalMapPresets;
+    TenancyBroadcastManager::$tenantBroadcasters = $originalTenantBroadcasters;
 };

Based on learnings: In the ArchtechX tenancy test suite, do not reset global/static state in beforeEach/afterEach by hard-coding class default values; capture the current/original static values and restore those exact originals in the hooks.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/Bootstrappers/BroadcastingConfigBootstrapperTest.php` around lines 16 -
47, The test teardown currently hard-codes defaults; instead capture the
original static values once and restore them in the hooks: at the top of the
test file save the current BroadcastingConfigBootstrapper::$broadcaster,
BroadcastingConfigBootstrapper::$credentialsMap,
BroadcastingConfigBootstrapper::$mapPresets and
TenancyBroadcastManager::$tenantBroadcasters into local variables, then update
the beforeEach/afterEach cleanup to restore those saved originals (rather than
writing literal arrays), keeping the existing Event::listen calls and using the
same $cleanup closure to reassign the saved originals back to the static
properties.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@tests/Bootstrappers/BroadcastingConfigBootstrapperTest.php`:
- Around line 16-47: The test teardown currently hard-codes defaults; instead
capture the original static values once and restore them in the hooks: at the
top of the test file save the current
BroadcastingConfigBootstrapper::$broadcaster,
BroadcastingConfigBootstrapper::$credentialsMap,
BroadcastingConfigBootstrapper::$mapPresets and
TenancyBroadcastManager::$tenantBroadcasters into local variables, then update
the beforeEach/afterEach cleanup to restore those saved originals (rather than
writing literal arrays), keeping the existing Event::listen calls and using the
same $cleanup closure to reassign the saved originals back to the static
properties.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: ad142858-113d-438a-82ca-82546e00b75f

📥 Commits

Reviewing files that changed from the base of the PR and between a7acd07 and 7db486d.

📒 Files selected for processing (1)
  • tests/Bootstrappers/BroadcastingConfigBootstrapperTest.php

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/Bootstrappers/BroadcastingConfigBootstrapper.php (1)

62-68: ⚠️ Potential issue | 🟠 Major

Constructor leaks preset mappings into later bootstrapper instances.

Line 67 rewrites the global static::$credentialsMap. That fixes precedence for the current instance, but it also makes the class state sticky across later instantiations: if one instance uses pusher and a later one switches to reverb or a custom broadcaster, the old preset keys stay in the static array and setConfig() will keep overriding the previous connection too.

Store the merged map on the instance instead of mutating the public static override.

♻️ Proposed fix
 class BroadcastingConfigBootstrapper implements TenancyBootstrapper
 {
+    protected array $resolvedCredentialsMap = [];
+
     public function __construct(
         protected Repository $config,
         protected Application $app
     ) {
         static::$broadcaster ??= $config->get('broadcasting.default');
-        static::$credentialsMap = array_merge(static::$mapPresets[static::$broadcaster] ?? [], static::$credentialsMap);
+        $this->resolvedCredentialsMap = array_merge(
+            static::$mapPresets[static::$broadcaster] ?? [],
+            static::$credentialsMap,
+        );
     }
@@
     protected function setConfig(Tenant $tenant): void
     {
-        foreach (static::$credentialsMap as $configKey => $storageKey) {
+        foreach ($this->resolvedCredentialsMap as $configKey => $storageKey) {
             $override = $tenant->$storageKey;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Bootstrappers/BroadcastingConfigBootstrapper.php` around lines 62 - 68,
The constructor is mutating the class-wide static::$credentialsMap causing
preset keys to leak across instances; instead compute the merged map from
static::$mapPresets and static::$credentialsMap and assign it to an instance
property (e.g. $this->credentialsMap) inside __construct, leaving
static::$credentialsMap unchanged; then update setConfig (and any other methods
referencing static::$credentialsMap) to read from $this->credentialsMap so each
bootstrapper instance keeps its own merged credentials map.
src/Overrides/TenancyBroadcastManager.php (1)

36-56: ⚠️ Potential issue | 🟠 Major

Custom drivers still bypass channel inheritance.

Line 45 gates the entire branch on a hard-coded driver-name list. After BroadcastingConfigBootstrapper copies central customCreators into this manager, a tenant default custom driver can now resolve here, but it still falls through to parent::get() with no channel transfer. If that broadcaster subclasses Illuminate\Broadcasting\Broadcasters\Broadcaster, Broadcast::auth() will still see an empty channel registry in tenant context.

Please keep the “always resolve fresh” allowlist if needed, but make the channel-copy step capability-based instead of name-based.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Overrides/TenancyBroadcastManager.php` around lines 36 - 56, The current
name-based gate (static::$tenantBroadcasters) prevents channel copying for
custom drivers; instead, always copy channels when both the original and
resolved broadcasters are capability-compatible. In get($name) still keep the
allowlist behavior for "always resolve fresh" (static::$tenantBroadcasters) by
resolving $newBroadcaster = $this->resolve($name) when in_array($name,
static::$tenantBroadcasters) and otherwise obtain the broadcaster via
parent::get($name); but after obtaining $originalBroadcaster (via
$this->app->make(BroadcasterContract::class)) and $newBroadcaster, if both are
instances of Illuminate\Broadcasting\Broadcasters\Broadcaster (the Broadcaster
class used elsewhere) call
$this->passChannelsFromOriginalBroadcaster($originalBroadcaster,
$newBroadcaster) so channel inheritance is capability-based rather than
driver-name-based.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@src/Bootstrappers/BroadcastingConfigBootstrapper.php`:
- Around line 62-68: The constructor is mutating the class-wide
static::$credentialsMap causing preset keys to leak across instances; instead
compute the merged map from static::$mapPresets and static::$credentialsMap and
assign it to an instance property (e.g. $this->credentialsMap) inside
__construct, leaving static::$credentialsMap unchanged; then update setConfig
(and any other methods referencing static::$credentialsMap) to read from
$this->credentialsMap so each bootstrapper instance keeps its own merged
credentials map.

In `@src/Overrides/TenancyBroadcastManager.php`:
- Around line 36-56: The current name-based gate (static::$tenantBroadcasters)
prevents channel copying for custom drivers; instead, always copy channels when
both the original and resolved broadcasters are capability-compatible. In
get($name) still keep the allowlist behavior for "always resolve fresh"
(static::$tenantBroadcasters) by resolving $newBroadcaster =
$this->resolve($name) when in_array($name, static::$tenantBroadcasters) and
otherwise obtain the broadcaster via parent::get($name); but after obtaining
$originalBroadcaster (via $this->app->make(BroadcasterContract::class)) and
$newBroadcaster, if both are instances of
Illuminate\Broadcasting\Broadcasters\Broadcaster (the Broadcaster class used
elsewhere) call $this->passChannelsFromOriginalBroadcaster($originalBroadcaster,
$newBroadcaster) so channel inheritance is capability-based rather than
driver-name-based.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 530b71bf-65da-4141-8bb5-9038284e58dd

📥 Commits

Reviewing files that changed from the base of the PR and between d885659 and ddd8c68.

📒 Files selected for processing (2)
  • src/Bootstrappers/BroadcastingConfigBootstrapper.php
  • src/Overrides/TenancyBroadcastManager.php

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants