Skip to content

Commit

Permalink
Updated tests for new Lumen support
Browse files Browse the repository at this point in the history
  • Loading branch information
asgrim committed Apr 2, 2021
1 parent aefef10 commit 446641b
Show file tree
Hide file tree
Showing 9 changed files with 306 additions and 182 deletions.
4 changes: 2 additions & 2 deletions .phpstorm.meta.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
'log' => \Psr\Log\LoggerInterface::class,
'view' => \Illuminate\View\Factory::class,
'events' => \Illuminate\Contracts\Events\Dispatcher::class,
'connection' => \Illuminate\Database\Connection::class,
'db' => \Illuminate\Database\DatabaseManager::class,
]));
override(\Illuminate\Foundation\Application::make(0), map([
'' => '@',
Expand All @@ -16,6 +16,6 @@
'log' => \Psr\Log\LoggerInterface::class,
'view' => \Illuminate\View\Factory::class,
'events' => \Illuminate\Contracts\Events\Dispatcher::class,
'connection' => \Illuminate\Database\Connection::class,
'db' => \Illuminate\Database\DatabaseManager::class,
]));
}
4 changes: 4 additions & 0 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory>./src/</directory>
<exclude>
<file>./src/ScoutApmBundle/Twig/TwigMethods-Twig2.php</file>
<file>./src/ScoutApmBundle/Twig/TwigMethods-Twig3.php</file>
</exclude>
</whitelist>
</filter>
<listeners>
Expand Down
4 changes: 1 addition & 3 deletions src/Laravel/Providers/ScoutApmServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -179,9 +179,7 @@ public function boot(
'Could not set up DB instrumentation: %s',
$bindingResolutionException->getMessage()
),
[
'exception' => $bindingResolutionException,
]
['exception' => $bindingResolutionException]
);
}

Expand Down
1 change: 0 additions & 1 deletion src/Laravel/Router/DetermineLaravelControllerName.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ public function __construct(FilteredLogLevelDecorator $logger, Router $router)
$this->router = $router;
}

// @todo needs tests
public function __invoke(Request $request): string
{
$name = 'unknown';
Expand Down
54 changes: 25 additions & 29 deletions src/Laravel/Router/DetermineLumenControllerName.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,20 @@

use function array_key_exists;
use function basename;
use function count;
use function get_class;
use function is_array;
use function is_callable;
use function is_object;
use function is_string;
use function sprintf;
use function trim;

/**
* @psalm-type LumenRouterActionShape = array{
* "as"?: string,
* uses?: string,
* 0?: ?\Closure,
* }
*/
final class DetermineLumenControllerName implements AutomaticallyDetermineControllerName
{
private const CONTROLLER_PREFIX = 'Controller/';

/** @var FilteredLogLevelDecorator */
private $logger;
/** @var Router */
Expand All @@ -35,14 +38,23 @@ public function __construct(FilteredLogLevelDecorator $logger, Router $router)
$this->router = $router;
}

// @todo needs tests
public function __invoke(Request $request): string
{
$name = 'unknown';

try {
$route = $request->getMethod() . '/' . trim($request->getPathInfo(), '/');

/**
* @psalm-var array<
* string,
* array{
* method: string,
* uri: string,
* action: LumenRouterActionShape
* }
* > $lumenRouteConfiguration
*/
$lumenRouteConfiguration = $this->router->getRoutes();

if (array_key_exists($route, $lumenRouteConfiguration)) {
Expand All @@ -52,33 +64,17 @@ public function __invoke(Request $request): string
$matchedRouteAction = $matchedRoute['action'];

if (array_key_exists('as', $matchedRouteAction)) {
$name = $matchedRouteAction['as'];
return self::CONTROLLER_PREFIX . $matchedRouteAction['as'];
}

if (array_key_exists('uses', $matchedRouteAction)) {
$name = $matchedRouteAction['uses'];
}

if (
is_callable($matchedRouteAction)
|| (is_array($matchedRouteAction) && count($matchedRouteAction) === 2)
) {
if (is_string($matchedRouteAction)) {
$name = $matchedRouteAction;
}

if (is_array($matchedRoute['action'])) {
$name = sprintf(
'%s@%s',
is_object($matchedRouteAction[0]) ? get_class($matchedRouteAction[0]) : trim($matchedRouteAction[0]),
trim($matchedRouteAction[1])
);
}
return self::CONTROLLER_PREFIX . $matchedRouteAction['uses'];
}

if (is_array($matchedRouteAction) && array_key_exists(0, $matchedRouteAction) && $matchedRouteAction[0] instanceof Closure) {
if (array_key_exists(0, $matchedRouteAction) && $matchedRouteAction[0] instanceof Closure) {
$function = new ReflectionFunction($matchedRouteAction[0]);
$name = sprintf(

return self::CONTROLLER_PREFIX . sprintf(
'closure_%s@%d',
basename($function->getFileName()),
$function->getStartLine()
Expand All @@ -93,6 +89,6 @@ public function __invoke(Request $request): string
);
}

return 'Controller/' . $name;
return self::CONTROLLER_PREFIX . $name;
}
}
158 changes: 13 additions & 145 deletions tests/Unit/Laravel/Middleware/ActionInstrumentTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
use Exception;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Routing\Route;
use Illuminate\Routing\Router;
use PHPUnit\Framework\Constraint\IsType;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
Expand All @@ -17,6 +15,7 @@
use Scoutapm\Events\Span\Span;
use Scoutapm\Events\Span\SpanReference;
use Scoutapm\Laravel\Middleware\ActionInstrument;
use Scoutapm\Laravel\Router\AutomaticallyDetermineControllerName;
use Scoutapm\Logger\FilteredLogLevelDecorator;
use Scoutapm\ScoutApmAgent;
use Throwable;
Expand All @@ -28,49 +27,45 @@ final class ActionInstrumentTest extends TestCase
{
/** @var ScoutApmAgent&MockObject */
private $agent;

/** @var LoggerInterface&MockObject */
private $logger;

/** @var Router&MockObject */
private $router;

/** @var Span&MockObject */
private $span;

/** @var AutomaticallyDetermineControllerName&MockObject */
private $determineControllerName;
/** @var ActionInstrument */
private $middleware;

public function setUp(): void
{
parent::setUp();

$this->agent = $this->createMock(ScoutApmAgent::class);
$this->logger = $this->createMock(LoggerInterface::class);
$this->router = $this->createMock(Router::class);
$this->span = $this->createMock(Span::class);
$this->agent = $this->createMock(ScoutApmAgent::class);
$this->logger = $this->createMock(LoggerInterface::class);
$this->span = $this->createMock(Span::class);
$this->determineControllerName = $this->createMock(AutomaticallyDetermineControllerName::class);

$this->middleware = new ActionInstrument(
$this->agent,
new FilteredLogLevelDecorator($this->logger, LogLevel::DEBUG),
$this->router
$this->determineControllerName
);
}

/** @throws Throwable */
public function testHandleRecordsControllerNameWhenRouteHasControllerKey(): void
public function testHandleRecordsControllerName(): void
{
$expectedResponse = new Response();

$controllerName = uniqid('controllerName', true);

$this->router->expects(self::once())
->method('current')
->willReturn(new Route('GET', '/default-url', ['controller' => $controllerName]));
$this->determineControllerName
->method('__invoke')
->willReturn($controllerName);

$this->span->expects(self::once())
->method('updateName')
->with('Controller/' . $controllerName);
->with($controllerName);

$this->logger->expects(self::once())
->method('log')
Expand Down Expand Up @@ -103,8 +98,6 @@ public function testHandleRecordsDoesNotUpdateNameWhenSpanIsNull(): void
{
$expectedResponse = new Response();

$controllerName = uniqid('controllerName', true);

$this->span->expects(self::never())
->method('updateName');

Expand All @@ -130,131 +123,6 @@ static function () use ($expectedResponse) {
);
}

/** @throws Throwable */
public function testHandleRecordsControllerNameWhenRouteDoesNotHaveAControllerKey(): void
{
$expectedResponse = new Response();

$url = uniqid('url', true);

$this->router->expects(self::once())
->method('current')
->willReturn(new Route('GET', $url, []));

$this->span->expects(self::once())
->method('updateName')
->with('Controller/' . $url);

$this->logger->expects(self::once())
->method('log')
->with(LogLevel::DEBUG, '[Scout] Handle ActionInstrument');

$this->agent
->expects(self::once())
->method('webTransaction')
->with('unknown', self::isType(IsType::TYPE_CALLABLE))
->willReturnCallback(
/** @return mixed */
function (string $originalName, callable $transaction) {
return $transaction(SpanReference::fromSpan($this->span));
}
);

self::assertSame(
$expectedResponse,
$this->middleware->handle(
new Request(),
static function () use ($expectedResponse) {
return $expectedResponse;
}
)
);
}

/** @throws Throwable */
public function testHandleRecordsUnknownControllerNameWhenNoRouteFound(): void
{
$expectedResponse = new Response();

$this->router->expects(self::once())
->method('current')
->willReturn(null);

$this->span->expects(self::once())
->method('updateName')
->with('Controller/unknown');

$this->logger->expects(self::once())
->method('log')
->with(LogLevel::DEBUG, '[Scout] Handle ActionInstrument');

$this->agent
->expects(self::once())
->method('webTransaction')
->with('unknown', self::isType(IsType::TYPE_CALLABLE))
->willReturnCallback(
/** @return mixed */
function (string $originalName, callable $transaction) {
return $transaction(SpanReference::fromSpan($this->span));
}
);

self::assertSame(
$expectedResponse,
$this->middleware->handle(
new Request(),
static function () use ($expectedResponse) {
return $expectedResponse;
}
)
);
}

/** @throws Throwable */
public function testHandleRecordsUnknownControllerNameWhenRouterCurrentThrowsException(): void
{
$expectedResponse = new Response();

$this->router->expects(self::once())
->method('current')
->willThrowException(new Exception('oh no'));

$this->span->expects(self::once())
->method('updateName')
->with('Controller/unknown');

$this->logger->expects(self::exactly(2))
->method('log')
->with(
LogLevel::DEBUG,
self::logicalOr(
'[Scout] Handle ActionInstrument',
'[Scout] Exception obtaining name of endpoint: oh no'
)
);

$this->agent
->expects(self::once())
->method('webTransaction')
->with('unknown', self::isType(IsType::TYPE_CALLABLE))
->willReturnCallback(
/** @return mixed */
function (string $originalName, callable $transaction) {
return $transaction(SpanReference::fromSpan($this->span));
}
);

self::assertSame(
$expectedResponse,
$this->middleware->handle(
new Request(),
static function () use ($expectedResponse) {
return $expectedResponse;
}
)
);
}

/** @throws Throwable */
public function testTagAsErrorIfControllerRaises(): void
{
Expand Down
10 changes: 8 additions & 2 deletions tests/Unit/Laravel/Providers/ScoutApmServiceProviderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use Illuminate\Contracts\View\Engine;
use Illuminate\Contracts\View\View;
use Illuminate\Database\Connection;
use Illuminate\Database\DatabaseManager;
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Http\Kernel as HttpKernelImplementation;
use Illuminate\Queue\Events\JobProcessed;
Expand Down Expand Up @@ -289,8 +290,13 @@ public function testDatabaseQueryListenerIsRegistered(): void
->method('listen')
->with(self::isInstanceOf(Closure::class));

$this->application->singleton('connection', function () {
return $this->connection;
$dbManager = $this->createMock(DatabaseManager::class);
$dbManager->expects(self::once())
->method('connection')
->willReturn($this->connection);

$this->application->singleton('db', static function () use ($dbManager) {
return $dbManager;
});

$this->bootServiceProvider();
Expand Down
Loading

0 comments on commit 446641b

Please sign in to comment.