Skip to content

Commit a3dcc14

Browse files
committed
Add wiring-level test coverage for the Octane reset listener
`PermissionServiceProvider::registerOctaneListener()` wires two listeners for `Laravel\Octane\Contracts\OperationTerminated` — resetting the permissions team ID on every request, and (opt-in via `permission.register_octane_reset_listener`) clearing the in-memory permissions collection — but neither had any test coverage. The registration code bails out early via `if ($this->app->runningInConsole() || ...)`, and since Pest/Testbench always runs under the CLI SAPI, `runningInConsole()` is always `true` in the test suite, so this code path was silently never exercised. This PR adds real coverage without requiring a running Swoole/RoadRunner server: - Adds `laravel/octane` as a **dev-only** dependency: it's pure PHP with no extension requirement — swoole/roadrunner are only needed to actually *serve* traffic via `octane:start`, not to use Octane's `Contracts`/`Events` classes. - `tests/Integration/OctaneListenerTest.php` forces `Application::$isRunningInConsole` to `false` via reflection (the real, memoized gate Laravel checks (see `Illuminate\Foundation\Application::runningInConsole()`), invokes the real (protected) `registerOctaneListener()` method, and dispatches an actual `Laravel\Octane\Events\RequestTerminated` event to exercise the genuine listener wiring end-to-end. **Coverage added:** - Listener is not registered when `octane.listeners` is disabled. - `PermissionRegistrar::getPermissionsTeamId()` resets to `null` on operation termination. - The permissions collection is cleared on termination only when `permission.register_octane_reset_listener` is enabled, and left untouched when it isn't. **Maintenance Trade-off:** the tests reach into a protected framework property (`Application::$isRunningInConsole`) and a protected provider method via reflection. If Laravel renames that property, or Octane changes `RequestTerminated`'s constructor signature, these tests could break for reasons unrelated to an actual regression here ... but we keep in sync with Laravel releases already. **No behavior change** — this is test-only coverage plus a dev dependency.
1 parent 9d4eb1e commit a3dcc14

2 files changed

Lines changed: 97 additions & 0 deletions

File tree

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
},
3232
"require-dev": {
3333
"larastan/larastan": "^3.9",
34+
"laravel/octane": "^2.0",
3435
"laravel/passport": "^13.0",
3536
"laravel/pint": "^1.0",
3637
"orchestra/testbench": "^10.0|^11.0",
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
<?php
2+
3+
use Illuminate\Http\Request;
4+
use Illuminate\Http\Response;
5+
use Laravel\Octane\Events\RequestTerminated;
6+
use Spatie\Permission\PermissionRegistrar;
7+
use Spatie\Permission\PermissionServiceProvider;
8+
9+
/**
10+
* PermissionServiceProvider::registerOctaneListener() bails out early when
11+
* running in console. Pest/Testbench always runs under the CLI SAPI,
12+
* so runningInConsole() would otherwise always be true. Here we force it false
13+
* (it's memoized on Application, checked before falling back to PHP_SAPI)
14+
* so the real registration code path actually runs, then invoke it directly
15+
* since it's protected and normally only called from packageBooted().
16+
*/
17+
function registerOctaneListenerForTest(): void
18+
{
19+
$app = app();
20+
21+
$isRunningInConsole = new ReflectionProperty($app, 'isRunningInConsole');
22+
$isRunningInConsole->setAccessible(true);
23+
$isRunningInConsole->setValue($app, false);
24+
25+
$registerOctaneListener = new ReflectionMethod(PermissionServiceProvider::class, 'registerOctaneListener');
26+
$registerOctaneListener->setAccessible(true);
27+
$registerOctaneListener->invoke(new PermissionServiceProvider($app));
28+
}
29+
30+
function dispatchOctaneRequestTerminated(): void
31+
{
32+
event(new RequestTerminated(app(), app(), Request::create('/'), new Response()));
33+
}
34+
35+
it('does not register octane listeners when octane.listeners is not enabled', function () {
36+
config(['octane.listeners' => false]);
37+
38+
registerOctaneListenerForTest();
39+
40+
app(PermissionRegistrar::class)->setPermissionsTeamId(1);
41+
42+
dispatchOctaneRequestTerminated();
43+
44+
expect(app(PermissionRegistrar::class)->getPermissionsTeamId())->toBe(1);
45+
});
46+
47+
it('resets the permissions team id when an octane operation terminates', function () {
48+
config(['octane.listeners' => true]);
49+
50+
registerOctaneListenerForTest();
51+
52+
app(PermissionRegistrar::class)->setPermissionsTeamId(1);
53+
expect(app(PermissionRegistrar::class)->getPermissionsTeamId())->toBe(1);
54+
55+
dispatchOctaneRequestTerminated();
56+
57+
expect(app(PermissionRegistrar::class)->getPermissionsTeamId())->toBeNull();
58+
});
59+
60+
it('clears the loaded permissions collection on octane termination when register_octane_reset_listener is enabled', function () {
61+
config([
62+
'octane.listeners' => true,
63+
'permission.register_octane_reset_listener' => true,
64+
]);
65+
66+
registerOctaneListenerForTest();
67+
68+
$permissions = new ReflectionProperty(PermissionRegistrar::class, 'permissions');
69+
$permissions->setAccessible(true);
70+
71+
app(PermissionRegistrar::class)->getPermissions();
72+
expect($permissions->getValue(app(PermissionRegistrar::class)))->not->toBeNull();
73+
74+
dispatchOctaneRequestTerminated();
75+
76+
expect($permissions->getValue(app(PermissionRegistrar::class)))->toBeNull();
77+
});
78+
79+
it('does not clear the loaded permissions collection on octane termination unless register_octane_reset_listener is enabled', function () {
80+
config([
81+
'octane.listeners' => true,
82+
'permission.register_octane_reset_listener' => false,
83+
]);
84+
85+
registerOctaneListenerForTest();
86+
87+
$permissions = new ReflectionProperty(PermissionRegistrar::class, 'permissions');
88+
$permissions->setAccessible(true);
89+
90+
app(PermissionRegistrar::class)->getPermissions();
91+
expect($permissions->getValue(app(PermissionRegistrar::class)))->not->toBeNull();
92+
93+
dispatchOctaneRequestTerminated();
94+
95+
expect($permissions->getValue(app(PermissionRegistrar::class)))->not->toBeNull();
96+
});

0 commit comments

Comments
 (0)