Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions tests/Octane/OctaneRequestContextProviderTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php

namespace Xhgui\Profiler\Laravel\Tests\Octane;

use Illuminate\Http\Request;
use Xhgui\Profiler\Laravel\Exception\LaravelProfilerException;
use Xhgui\Profiler\Laravel\Octane\OctaneRequestContextProvider;
use Xhgui\Profiler\Laravel\Octane\OctaneRequestContextState;
use Xhgui\Profiler\Laravel\Tests\TestCase;
use Xhgui\Profiler\RequestContext\RequestContextInterface;

final class OctaneRequestContextProviderTest extends TestCase
{
public function test_capture_returns_a_request_context_snapshot(): void
{
$request = Request::create('/profiles', 'GET', ['page' => '2'], [], [], [
'REQUEST_URI' => '/profiles?page=2',
'REQUEST_METHOD' => 'GET',
'REQUEST_TIME' => 1234,
'REQUEST_TIME_FLOAT' => 1234.5,
]);

$state = new OctaneRequestContextState;
$state->activate($request, ['APP_ENV' => 'testing']);

$provider = new OctaneRequestContextProvider($state);
$context = $provider->capture();

$this->assertInstanceOf(RequestContextInterface::class, $context);
$this->assertSame('/profiles?page=2', $context->getUrl());
$this->assertSame(['page' => '2'], $context->getQuery());
$this->assertSame(['APP_ENV' => 'testing'], $context->getEnv());
$this->assertSame(1234.5, $context->getServer()['REQUEST_TIME_FLOAT']);
$this->assertSame(1234, $context->getServer()['REQUEST_TIME']);
}

public function test_capture_uses_the_current_active_request_snapshot(): void
{
$state = new OctaneRequestContextState;
$provider = new OctaneRequestContextProvider($state);

$state->activate(Request::create('/first', 'GET', ['page' => '1'], [], [], [
'REQUEST_TIME' => 100,
'REQUEST_TIME_FLOAT' => 100.0,
]), ['APP_ENV' => 'testing']);
$firstContext = $provider->capture();

$state->clear();
$state->activate(Request::create('/second', 'GET', ['page' => '2'], [], [], [
'REQUEST_TIME' => 200,
'REQUEST_TIME_FLOAT' => 200.0,
]), ['APP_ENV' => 'testing']);
$secondContext = $provider->capture();

$this->assertSame('/first?page=1', $firstContext->getUrl());
$this->assertSame('/second?page=2', $secondContext->getUrl());
$this->assertSame(['page' => '2'], $secondContext->getQuery());
$this->assertSame(200.0, $secondContext->getServer()['REQUEST_TIME_FLOAT']);
}

public function test_capture_fails_without_an_active_request_snapshot(): void
{
$provider = new OctaneRequestContextProvider(new OctaneRequestContextState);

$this->expectException(LaravelProfilerException::class);
$this->expectExceptionMessage('Octane request context is not active.');

Comment on lines +63 to +67
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

@copilot apply changes based on this feedback

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Updated in 57a23c0: the Octane request-context test now expects LaravelProfilerException instead of RuntimeException, and related Octane manager assertion/imports were aligned to the same exception contract. No UI changes were involved (no screenshot applicable).

$provider->capture();
}
}
151 changes: 151 additions & 0 deletions tests/Octane/OctaneWorkerProfilerManagerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
<?php

namespace Xhgui\Profiler\Laravel\Tests\Octane;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Xhgui\Profiler\Laravel\Exception\LaravelProfilerException;
use Xhgui\Profiler\Laravel\Octane\OctaneRequestContextProvider;
use Xhgui\Profiler\Laravel\Octane\OctaneRequestContextState;
use Xhgui\Profiler\Laravel\Octane\OctaneWorkerProfilerManager;
use Xhgui\Profiler\Laravel\Tests\TestCase;
use Xhgui\Profiler\Profiler;

final class OctaneWorkerProfilerManagerTest extends TestCase
{
public function test_sequential_requests_reuse_the_same_profiler(): void
{
$manager = $this->newManager();

$profilerA = $manager->startRequest($this->enabledConfig(), Request::create('/profiles', 'GET', ['page' => '2']));
$manager->stopRequest();

$profilerB = $manager->startRequest($this->enabledConfig(), Request::create('/health', 'GET', ['ping' => '1']));
$manager->stopRequest();

$this->assertSame($profilerA, $profilerB);
}

public function test_stale_running_profiler_is_stopped_and_rebuilt_before_the_next_request(): void
{
$logger = new class
{
public array $warnings = [];

public function warning(string $message): void
{
$this->warnings[] = $message;
}
};

Log::swap($logger);

$firstProfilerState = $this->newProfilerState();
$manager = $this->newManager([$firstProfilerState, $this->newProfilerState()]);

$profilerA = $manager->startRequest($this->enabledConfig(), Request::create('/first'));
$profilerB = $manager->startRequest($this->enabledConfig(), Request::create('/second'));

$this->assertNotSame($profilerA, $profilerB);
$this->assertTrue($firstProfilerState->stopped);
$this->assertSame(
['Xhgui Octane profiler was still running at the start of a new request; rebuilding worker profiler state.'],
$logger->warnings,
);
}

public function test_enable_failure_clears_the_active_request_context(): void
{
$profilerState = $this->newProfilerState([
'enableException' => new \RuntimeException('boom'),
]);
$state = new OctaneRequestContextState;
$provider = new OctaneRequestContextProvider($state);
$manager = $this->newManager([$profilerState], $state, $provider);

try {
$manager->startRequest($this->enabledConfig(), Request::create('/broken'));
$this->fail('Expected the profiler enable failure to be rethrown.');
} catch (\RuntimeException $exception) {
$this->assertSame('boom', $exception->getMessage());
}

$this->expectException(LaravelProfilerException::class);
$this->expectExceptionMessage('Octane request context is not active.');

$provider->capture();
}

public function test_stale_cleanup_failures_are_logged_before_rebuild(): void
{
$logger = new class
{
public array $warnings = [];

public function warning(string $message): void
{
$this->warnings[] = $message;
}
};

Log::swap($logger);

$firstProfilerState = $this->newProfilerState([
'disableException' => new \RuntimeException('cleanup failed'),
]);
$manager = $this->newManager([$firstProfilerState, $this->newProfilerState()]);

$profilerA = $manager->startRequest($this->enabledConfig(), Request::create('/first'));
$profilerB = $manager->startRequest($this->enabledConfig(), Request::create('/second'));

$this->assertNotSame($profilerA, $profilerB);
$this->assertSame([
'Xhgui Octane profiler was still running at the start of a new request; rebuilding worker profiler state.',
'Xhgui Octane stale profiler cleanup failed: cleanup failed',
], $logger->warnings);
}

private function newManager(
array $profilerStates = [],
?OctaneRequestContextState $state = null,
?OctaneRequestContextProvider $provider = null,
): OctaneWorkerProfilerManager {
$state ??= new OctaneRequestContextState;
$provider ??= new OctaneRequestContextProvider($state);

return new class($state, $provider, $profilerStates, $this) extends OctaneWorkerProfilerManager
{
public function __construct(
OctaneRequestContextState $state,
private OctaneRequestContextProvider $provider,
private array $profilerStates,
private OctaneWorkerProfilerManagerTest $test,
) {
parent::__construct($state, $provider);
}

protected function createProfiler(array $config): Profiler
{
$profilerState = array_shift($this->profilerStates);

return $this->test->makeManagedProfiler($this->provider, $profilerState);
}
};
}

public function makeManagedProfiler(OctaneRequestContextProvider $provider, ?object $state = null): Profiler
{
return $this->newManagedProfiler($provider, $state);
}

private function enabledConfig(array $overrides = []): array
{
return array_replace_recursive([
'enabled' => true,
'save.handler' => Profiler::SAVER_FILE,
'save.handler.file' => [
'filename' => sys_get_temp_dir().'/xhgui-manager.jsonl',
],
], $overrides);
}
}
Loading