CluPilotCloud/tests/Feature/Admin/BroadcastAuthTest.php

87 lines
3.7 KiB
PHP

<?php
use App\Models\Operator;
use App\Models\User;
use Illuminate\Support\Facades\Auth;
/**
* routes/channels.php's admin.runs channel — the console's live provisioning
* feed. Its own callback checks Auth::guard('operator') correctly, but that
* is not what this file is about: it is about whether the callback ever runs
* at all.
*
* BROADCAST_CONNECTION=null (this suite's forced default, phpunit.xml) backs
* onto NullBroadcaster, whose auth() is a bare no-op — it never calls a
* channel's own callback, so it cannot exercise this bug or its fix either
* way. A real Pusher-protocol broadcaster (Reverb uses PusherBroadcaster
* under the hood, BroadcastManager::createReverbDriver()) is switched in
* instead, via config rather than a container binding: Broadcast::auth()
* resolves through BroadcastManager::__call() -> driver(), which reads
* config('broadcasting.default') and builds its OWN instance — a container
* binding for BroadcasterContract is a separate, unrelated seam that
* driver() never consults. No live Reverb server is needed: auth() is pure
* PHP — channel authorization is an HMAC signature computed locally, never a
* call over the network.
*/
function bindRealBroadcaster(): void
{
config()->set('broadcasting.default', 'reverb');
config()->set('broadcasting.connections.reverb.key', 'test-key');
config()->set('broadcasting.connections.reverb.secret', 'test-secret');
config()->set('broadcasting.connections.reverb.app_id', 'test-app-id');
config()->set('broadcasting.connections.reverb.options', [
'host' => '127.0.0.1', 'port' => 6001, 'scheme' => 'http', 'useTLS' => false,
]);
// routes/channels.php ran once already, at boot, registering admin.runs
// against whichever broadcaster 'null' (this suite's forced default)
// resolved to at the time. Switching the default config above builds a
// brand new driver instance on next use — it does not retroactively
// register anything on it. Re-requiring the routes file does.
require base_path('routes/channels.php');
}
function authorizeAdminRunsChannel()
{
return test()->post('/broadcasting/auth', [
'channel_name' => 'private-admin.runs',
'socket_id' => '1234.1234',
]);
}
it('lets an operator authorize the console live-feed channel on the operator guard', function () {
bindRealBroadcaster();
$operator = Operator::factory()->role('Owner')->create();
$this->actingAs($operator, 'operator');
// actingAs() also flips Auth's DEFAULT guard to 'operator' (Auth::
// shouldUse()) — nothing does that for a real request to this route in
// production (/broadcasting/auth carries only the 'web' middleware
// group, never auth:operator,web), so left alone this would make the
// bare/default guard resolve the operator too and mask exactly the bug
// this test exists to catch. Reset so only the 'operator' GUARD INSTANCE
// (already given its user by actingAs() above, untouched by shouldUse())
// has the operator signed in — matching a real session.
Auth::shouldUse('web');
authorizeAdminRunsChannel()->assertOk();
});
it('refuses a guest on the console live-feed channel', function () {
bindRealBroadcaster();
authorizeAdminRunsChannel()->assertForbidden();
});
it('refuses a signed-in portal customer who is not an operator', function () {
bindRealBroadcaster();
$user = User::factory()->create();
$this->actingAs($user);
// Signed in on 'web', not 'operator' — retrieveUser() now finds THIS
// user (guards => ['operator', 'web']) and reaches the channel's own
// callback, which must still refuse them: they hold no operator role.
authorizeAdminRunsChannel()->assertForbidden();
});