Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve test coverage for Agent #152

Merged
merged 2 commits into from
Dec 31, 2019
Merged
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ composer.phar
.DS_STORE
.idea
.phpcs-cache
build
18 changes: 18 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
.PHONY: *

default: unit cs static-analysis ## all the things

help:
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'

unit: ## run unit tests
vendor/bin/phpunit

cs: ## verify code style rules
vendor/bin/phpcs

static-analysis: ## verify that no new static analysis issues were introduced
vendor/bin/psalm

coverage: ## generate code coverage reports
vendor/bin/phpunit --testsuite unit --coverage-html build/coverage-html --coverage-text
4 changes: 3 additions & 1 deletion src/Agent.php
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ public function send() : bool
{
// Don't send if the agent is not enabled.
if (! $this->enabled()) {
$this->logger->debug('Not sending payload, logging is not enabled');
$this->logger->debug('Not sending payload, monitoring is not enabled');

return false;
}
Expand All @@ -308,6 +308,8 @@ public function send() : bool
return false;
}

// Logic dictates that this can't happen, but static analysis would disagree since the annotation is nullable
// $this->request is only null when the request has been ignored (for now).
if ($this->request === null) {
$this->logger->debug('Not sending payload, request was not set');

Expand Down
227 changes: 227 additions & 0 deletions tests/Unit/AgentTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
use Scoutapm\Config;
use Scoutapm\Config\ConfigKey;
use Scoutapm\Connector\Connector;
use Scoutapm\Connector\Exception\FailedToConnect;
use Scoutapm\Connector\Exception\FailedToSendCommand;
use Scoutapm\Connector\Exception\NotConnected;
use Scoutapm\Events\Metadata;
use Scoutapm\Events\RegisterMessage;
use Scoutapm\Events\Request\Request;
Expand All @@ -23,6 +26,7 @@
use Scoutapm\ScoutApmAgent;
use function array_map;
use function end;
use function json_encode;
use function sprintf;
use function uniqid;

Expand Down Expand Up @@ -332,6 +336,8 @@ public function testIgnoredAgentSequence() : void
// Start a Child Span
$span = $agent->startSpan('SQL/Query');

$agent->changeRequestUri('new request URI');

// Tag the span
$span->tag('sql.query', 'select * from foo');

Expand Down Expand Up @@ -412,4 +418,225 @@ public function testRequestIsResetAfterStartingANewRequest() : void

self::assertNotSame($requestBeforeReset, $agent->getRequest());
}

public function testAgentLogsWarningWhenFailingToConnectToSocket() : void
{
$agent = Agent::fromConfig(
Config::fromArray([
ConfigKey::APPLICATION_NAME => 'My test app',
ConfigKey::APPLICATION_KEY => uniqid('applicationKey', true),
ConfigKey::MONITORING_ENABLED => true,
ConfigKey::CORE_AGENT_SOCKET_PATH => '/socket/path/should/not/exist',
ConfigKey::CORE_AGENT_DOWNLOAD_ENABLED => false,
]),
$this->logger,
new DevNullCache()
);
$agent->connect();

self::assertTrue($this->logger->hasWarningThatContains(
'Failed to connect to socket on path "/socket/path/should/not/exist"'
));
}

public function testAgentLogsDebugWhenConnectedToSocket() : void
{
$this->connector
->expects(self::once())
->method('connected')
->willReturn(false);

$this->connector
->expects(self::once())
->method('connect');

$agent = $this->agentFromConfigArray([
ConfigKey::APPLICATION_NAME => 'My test app',
ConfigKey::APPLICATION_KEY => uniqid('applicationKey', true),
ConfigKey::MONITORING_ENABLED => true,
ConfigKey::CORE_AGENT_DOWNLOAD_ENABLED => false,
]);

$agent->connect();

self::assertTrue($this->logger->hasDebugThatContains('Connected to connector.'));
}

public function testAgentLogsDebugWhenAlreadyConnectedToSocket() : void
{
$this->connector
->expects(self::once())
->method('connected')
->willReturn(true);

$this->connector
->expects(self::never())
->method('connect');

$agent = $this->agentFromConfigArray([
ConfigKey::APPLICATION_NAME => 'My test app',
ConfigKey::APPLICATION_KEY => uniqid('applicationKey', true),
ConfigKey::MONITORING_ENABLED => true,
ConfigKey::CORE_AGENT_DOWNLOAD_ENABLED => false,
]);

$agent->connect();

self::assertTrue($this->logger->hasDebugThatContains('Scout Core Agent Connected'));
}

public function testRequestUriCanBeChanged() : void
{
$agent = $this->agentFromConfigArray([
ConfigKey::APPLICATION_NAME => 'My test app',
ConfigKey::APPLICATION_KEY => uniqid('applicationKey', true),
ConfigKey::MONITORING_ENABLED => true,
ConfigKey::LOG_LEVEL => LogLevel::NOTICE,
]);

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

$this->connector->method('connected')->willReturn(true);

$this->connector->expects(self::at(1))
->method('sendCommand')
->with(self::isInstanceOf(RegisterMessage::class))
->willReturn('{"Register":"Success"}');
$this->connector->expects(self::at(2))
->method('sendCommand')
->with(self::isInstanceOf(Metadata::class))
->willReturn('{"Metadata":"Success"}');
$this->connector->expects(self::at(3))
->method('sendCommand')
->with(self::callback(static function (Request $request) use ($requestUri) {
$serialisedRequest = json_encode($request);

self::assertContains(sprintf('"tag":"path","value":"%s"', $requestUri), $serialisedRequest);

return true;
}))
->willReturn('{"Request":"Success"}');

$agent->changeRequestUri($requestUri);

self::assertTrue($agent->send());
}

public function testDisablingMonitoringDoesNotSendPayload() : void
{
$agent = $this->agentFromConfigArray([
ConfigKey::APPLICATION_NAME => 'My test app',
ConfigKey::APPLICATION_KEY => uniqid('applicationKey', true),
ConfigKey::MONITORING_ENABLED => false,
]);

$this->connector->expects(self::never())->method('connected')->willReturn(true);

$this->connector->expects(self::never())
->method('sendCommand');

self::assertFalse($agent->send());

self::assertTrue($this->logger->hasDebugThatContains('Not sending payload, monitoring is not enabled'));
}

public function testSendingRequestAttemptsToConnectIfNotAlreadyConnected() : void
{
$agent = $this->agentFromConfigArray([
ConfigKey::APPLICATION_NAME => 'My test app',
ConfigKey::APPLICATION_KEY => uniqid('applicationKey', true),
ConfigKey::MONITORING_ENABLED => true,
]);

$this->connector->expects(self::once())
->method('connected')
->willReturn(false);

$this->connector->expects(self::once())
->method('connect');

$this->connector->expects(self::at(2))
->method('sendCommand')
->with(self::isInstanceOf(RegisterMessage::class))
->willReturn('{"Register":"Success"}');
$this->connector->expects(self::at(3))
->method('sendCommand')
->with(self::isInstanceOf(Metadata::class))
->willReturn('{"Metadata":"Success"}');
$this->connector->expects(self::at(4))
->method('sendCommand')
->with(self::isInstanceOf(Request::class))
->willReturn('{"Request":"Success"}');

self::assertTrue($agent->send());

self::assertTrue($this->logger->hasDebugThatContains('Connected to connector whilst sending'));
}

public function testFailureToConnectWhilstSendingIsLoggedAsAnError() : void
{
$agent = $this->agentFromConfigArray([
ConfigKey::APPLICATION_NAME => 'My test app',
ConfigKey::APPLICATION_KEY => uniqid('applicationKey', true),
ConfigKey::MONITORING_ENABLED => true,
]);

$this->connector->expects(self::once())
->method('connected')
->willReturn(false);

$this->connector->expects(self::once())
->method('connect')
->willThrowException(new FailedToConnect('Uh oh, failed to reticulate the splines'));

$this->connector->expects(self::never())
->method('sendCommand');

self::assertFalse($agent->send());

self::assertTrue($this->logger->hasErrorThatContains('Uh oh, failed to reticulate the splines'));
}

public function testNotConnectedExceptionIsCaughtWhilstSending() : void
{
$agent = $this->agentFromConfigArray([
ConfigKey::APPLICATION_NAME => 'My test app',
ConfigKey::APPLICATION_KEY => uniqid('applicationKey', true),
ConfigKey::MONITORING_ENABLED => true,
]);

$this->connector->expects(self::once())
->method('connected')
->willReturn(true);

// This scenario is very unlikely, but ensure it's caught anyway...
$this->connector->expects(self::once())
->method('sendCommand')
->willThrowException(new NotConnected('Lost connectivity whilst reticulating splines'));

self::assertFalse($agent->send());

self::assertTrue($this->logger->hasErrorThatContains('Lost connectivity whilst reticulating splines'));
}

public function testFailureToSendCommandExceptionIsCaughtWhilstSending() : void
{
$agent = $this->agentFromConfigArray([
ConfigKey::APPLICATION_NAME => 'My test app',
ConfigKey::APPLICATION_KEY => uniqid('applicationKey', true),
ConfigKey::MONITORING_ENABLED => true,
]);

$this->connector->expects(self::once())
->method('connected')
->willReturn(true);

$this->connector->expects(self::once())
->method('sendCommand')
->willThrowException(new FailedToSendCommand('Splines did not reticulate to send the message'));

self::assertFalse($agent->send());

self::assertTrue($this->logger->hasErrorThatContains('Splines did not reticulate to send the message'));
}
}