Skip to content

Commit

Permalink
Merge pull request #2 from onlime/pest
Browse files Browse the repository at this point in the history
Test suite converted to Pest + Improved Service Provider
  • Loading branch information
onlime authored Mar 11, 2024
2 parents f02a18f + 17d98b4 commit 09c2c9d
Show file tree
Hide file tree
Showing 20 changed files with 676 additions and 825 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,6 @@ jobs:
composer update --${{ matrix.stability }} --prefer-dist --no-interaction
- name: Execute tests
run: vendor/bin/phpunit
run: |
vendor/bin/pest tests/Feature
vendor/bin/pest tests/Unit
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@

- Feature | Dispatch `QueryLogWritten` event after writing queries to the log, so that the whole query log can be accessed for further processing, e.g. generating reports/notifications.
- Feature | Added auth guard to log headers.
- Laravel 11 support
- Laravel 11 support.
- Migrated to Pest for testing (by @pascalbaljet in #2)
- Added GitHub Actions for all supported Laravel and PHP versions.
- Introduced `orchestra/testbench` dev dependency instead of the whole Laravel framework.
- Improved Service Provider: fixed the publish tag and use the regular base `ServiceProvider`.
- Improved `WriterTest` by not mocking the `Config` class but using the real config values.

## [v1.1.0 (2023-07-16)](https://github.com/onlime/laravel-sql-reporter/compare/v1.0.1...v1.1.0)

Expand Down
9 changes: 7 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"laravel/pint": "^1.14",
"mockery/mockery": "^1.0",
"orchestra/testbench": "^8.0|^9.0",
"phpunit/phpunit": "^10.2"
"pestphp/pest": "^2.34"
},
"autoload": {
"psr-4": {
Expand Down Expand Up @@ -51,5 +51,10 @@
}
},
"minimum-stability": "dev",
"prefer-stable": true
"prefer-stable": true,
"config": {
"allow-plugins": {
"pestphp/pest-plugin": true
}
}
}
2 changes: 1 addition & 1 deletion src/Providers/SqlReporterServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public function boot()
{
$this->publishes([
$this->configFileLocation() => config_path('sql-reporter.php'),
], 'config');
], 'sql-reporter');

if ($this->config->queriesEnabled()) {
DB::enableQueryLog();
Expand Down
8 changes: 0 additions & 8 deletions src/SqlQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,4 @@ public function time(): float
{
return $this->time;
}

/**
* Check if this query should be reported.
*/
public function shouldReport(Config $config): bool
{
return preg_match($config->queriesReportPattern(), $this->rawQuery) === 1;
}
}
10 changes: 9 additions & 1 deletion src/Writer.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public function writeQuery(SqlQuery $query): bool
}
$logLine = $this->formatter->getLine($query);
$this->writeLine($logLine);
if ($query->shouldReport($this->config)) {
if ($this->shouldReportSqlQuery($query)) {
$this->reportQueries[] = $logLine;
}
$this->loggedQueryCount++;
Expand All @@ -57,6 +57,14 @@ public function writeQuery(SqlQuery $query): bool
return false;
}

/**
* Verify whether query should be reported.
*/
private function shouldReportSqlQuery(SqlQuery $query): bool
{
return preg_match($this->config->queriesReportPattern(), $query->rawQuery()) === 1;
}

/**
* Create directory if it does not exist yet.
*
Expand Down
32 changes: 32 additions & 0 deletions tests/Feature/SqlReporterServiceProviderTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

use Illuminate\Console\Events\CommandFinished;
use Illuminate\Foundation\Http\Events\RequestHandled;
use Illuminate\Support\Facades\Event;
use Onlime\LaravelSqlReporter\Listeners\LogSqlQuery;

it('registers event listeners', function (string $eventName) {
$listeners = Event::getRawListeners()[$eventName] ?? [];

expect($listeners)->not->toBeEmpty()
->and($listeners)->toContain(LogSqlQuery::class);
})->with([
CommandFinished::class,
RequestHandled::class,
]);

it('merges the default config', function () {
$config = config('sql-reporter');

expect($config)->toBeArray()
->and($config)->toHaveKey('queries')
->and($config)->toHaveKey('general');
});

it('can publish the config file', function () {
@unlink(config_path('sql-reporter.php'));

$this->artisan('vendor:publish', ['--tag' => 'sql-reporter']);

$this->assertFileExists(config_path('sql-reporter.php'));
});
242 changes: 242 additions & 0 deletions tests/Feature/WriterTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
<?php

use Carbon\Carbon;
use Illuminate\Filesystem\Filesystem;
use Onlime\LaravelSqlReporter\Config;
use Onlime\LaravelSqlReporter\FileName;
use Onlime\LaravelSqlReporter\Formatter;
use Onlime\LaravelSqlReporter\SqlQuery;
use Onlime\LaravelSqlReporter\Writer;

beforeEach(function () {
$this->now = Carbon::parse('2015-02-03 06:41:31');
Carbon::setTestNow($this->now);
$this->formatter = Mockery::mock(Formatter::class);
$this->config = app(Config::class);
$this->filename = Mockery::mock(FileName::class);
$this->writer = new Writer($this->formatter, $this->config, $this->filename);
$this->directory = __DIR__.'/test-dir/directory';
setConfig('general.directory', $this->directory);
$this->filesystem = new Filesystem();
});

afterEach(function () {
$this->filesystem->deleteDirectory($this->directory);
});

function setConfig(string|array $key, mixed $value =null)
{
config()->set(
// prepend all keys with 'sql-reporter.' prefix
is_array($key)
? collect($key)->mapWithKeys(fn (string $value, mixed $key) => ["sql-reporter.$key" => $value])->all()
: ['sql-reporter.'.$key => $value]
);
}

it('creates directory if it does not exist for 1st query', function () {
$query = new SqlQuery(1, 'test', 5.41);
setConfig('queries.enabled', false);
expect($this->directory)->not()->toBeDirectory();
$this->writer->writeQuery($query);
expect($this->directory)->toBeFile()
->and($this->filesystem->allFiles($this->directory))->toBeEmpty();
});

it('does not create directory if it does not exist for 2nd query', function () {
$query = new SqlQuery(2, 'test', 5.41);
setConfig('queries.enabled', false);
expect($this->directory)->not()->toBeDirectory();
$this->writer->writeQuery($query);
expect($this->directory)->not()->toBeDirectory();
});

it('creates log file', function () {
$lineContent = 'Sample log line';
$expectedContent = "-- header\nSample log line\n";
$expectedFileName = $this->now->format('Y-m').'-log.sql';

$query = new SqlQuery(1, 'test', 5.41);
$this->formatter->shouldReceive('getLine')->once()->with($query)->andReturn($lineContent);
$this->formatter->shouldReceive('getHeader')->once()->withNoArgs()->andReturn('-- header');

setConfig('queries.include_pattern', '#.*#i');

$this->filename->shouldReceive('getLogfile')->times(2)->withNoArgs()->andReturn($expectedFileName);
$this->writer->writeQuery($query);
expect($this->directory)->toBeFile()
->and($this->filesystem->allFiles($this->directory))->toHaveCount(1)
->and($this->directory.'/'.$expectedFileName)->toBeFile()
->and(file_get_contents($this->directory.'/'.$expectedFileName))->toBe($expectedContent);
});

it('appends to existing log file', function () {
$expectedFileName = $this->now->format('Y-m').'-log.sql';
mkdir($this->directory, 0777, true);
$initialContent = "Initial file content\n";
file_put_contents($this->directory.'/'.$expectedFileName, $initialContent);

$lineContent = 'Sample log line';
$expectedContent = $initialContent."-- header\nSample log line\n";

$query = new SqlQuery(1, 'test', 5.41);
$this->formatter->shouldReceive('getLine')->once()->with($query)->andReturn($lineContent);
$this->formatter->shouldReceive('getHeader')->once()->withNoArgs()->andReturn('-- header');
setConfig('queries.include_pattern', '#.*#i');
$this->filename->shouldReceive('getLogfile')->times(2)->withNoArgs()->andReturn($expectedFileName);
expect($this->directory.'/'.$expectedFileName)->toBeFile();
$this->writer->writeQuery($query);
expect($this->filesystem->allFiles($this->directory))->toHaveCount(1)
->and($this->directory.'/'.$expectedFileName)->toBeFile()
->and(file_get_contents($this->directory.'/'.$expectedFileName))->toBe($expectedContent);
});

it('replaces current file content for 1st query when overriding is turned on', function () {
$expectedFileName = $this->now->format('Y-m').'-log.sql';
mkdir($this->directory, 0777, true);
$initialContent = "Initial file content\n";
file_put_contents($this->directory.'/'.$expectedFileName, $initialContent);

$lineContent = 'Sample log line';
$expectedContent = "-- header\nSample log line\n";

$query = new SqlQuery(1, 'test', 5.41);
$this->formatter->shouldReceive('getLine')->once()->with($query)->andReturn($lineContent);
$this->formatter->shouldReceive('getHeader')->once()->withNoArgs()->andReturn('-- header');
setConfig([
'queries.include_pattern' => '#.*#i',
'queries.override_log' => true,
]);
$this->filename->shouldReceive('getLogfile')->times(2)->withNoArgs()->andReturn($expectedFileName);
expect($this->directory.'/'.$expectedFileName)->toBeFile();
$this->writer->writeQuery($query);
expect($this->filesystem->allFiles($this->directory))->toHaveCount(1)
->and($this->directory.'/'.$expectedFileName)->toBeFile()
->and(file_get_contents($this->directory.'/'.$expectedFileName))->toBe($expectedContent);
});

it('appends to current file content for 2nd query when overriding is turned on', function () {
$expectedFileName = $this->now->format('Y-m').'-log.sql';
mkdir($this->directory, 0777, true);
$initialContent = "Initial file content\n";
file_put_contents($this->directory.'/'.$expectedFileName, $initialContent);

$lineContent = 'Sample log line';
$expectedContent = "-- header\n$lineContent\n$lineContent\n";

$query1 = new SqlQuery(1, 'test', 5.41);
$query2 = new SqlQuery(2, 'test', 5.41);
$this->formatter->shouldReceive('getLine')->twice()->andReturn($lineContent);
$this->formatter->shouldReceive('getHeader')->once()->withNoArgs()->andReturn('-- header');
setConfig([
'queries.include_pattern' => '#.*#i',
'queries.override_log' => true,
]);
$this->filename->shouldReceive('getLogfile')->times(3)->withNoArgs()->andReturn($expectedFileName);
expect($this->directory.'/'.$expectedFileName)->toBeFile();
$this->writer->writeQuery($query1);
$this->writer->writeQuery($query2);
expect($this->filesystem->allFiles($this->directory))->toHaveCount(1)
->and($this->directory.'/'.$expectedFileName)->toBeFile()
->and(file_get_contents($this->directory.'/'.$expectedFileName))->toBe($expectedContent);
});

it('saves select query to file when pattern set to select queries', function () {
$expectedFileName = $this->now->format('Y-m').'-log.sql';
$lineContent = 'Sample log line';
$expectedContent = "\n$lineContent\n";

$query = new SqlQuery(1, 'select * FROM test', 5.41);
$this->formatter->shouldReceive('getLine')->once()->with($query)->andReturn($lineContent);
$this->formatter->shouldReceive('getHeader')->once()->withNoArgs()->andReturn('');
setConfig('queries.include_pattern', '#^SELECT .*$#i');
$this->filename->shouldReceive('getLogfile')->twice()->withNoArgs()->andReturn($expectedFileName);
$this->writer->writeQuery($query);
expect($this->directory)->toBeFile()
->and($this->filesystem->allFiles($this->directory))->toHaveCount(1)
->and($this->directory.'/'.$expectedFileName)->toBeFile()
->and(file_get_contents($this->directory.'/'.$expectedFileName))->toBe($expectedContent);
});

it('doesnt save select query to file when pattern set to insert or update queries', function () {
$query = new SqlQuery(1, 'select * FROM test', 5.41);
setConfig('queries.include_pattern', '#^(?:UPDATE|INSERT) .*$#i');
$this->writer->writeQuery($query);
expect($this->directory)->toBeFile()
->and($this->filesystem->allFiles($this->directory))->toHaveCount(0);
});

it('saves insert query to file when pattern set to insert or update queries', function () {
$expectedFileName = $this->now->format('Y-m').'-log.sql';
$lineContent = 'Sample log line';
$expectedContent = "\n$lineContent\n";

$query = new SqlQuery(1, 'INSERT INTO test(one, two, three) values(?, ?, ?)', 5.41);
$this->formatter->shouldReceive('getLine')->once()->with($query)->andReturn($lineContent);
$this->formatter->shouldReceive('getHeader')->once()->withNoArgs()->andReturn('');
setConfig('queries.include_pattern', '#^(?:UPDATE|INSERT) .*$#i');
$this->filename->shouldReceive('getLogfile')->twice()->withNoArgs()->andReturn($expectedFileName);
$this->writer->writeQuery($query);
expect($this->directory)->toBeFile()
->and($this->filesystem->allFiles($this->directory))->toHaveCount(1)
->and($this->directory.'/'.$expectedFileName)->toBeFile()
->and(file_get_contents($this->directory.'/'.$expectedFileName))->toBe($expectedContent);
});

it('uses raw query without bindings when using query pattern', function () {
$expectedFileName = $this->now->format('Y-m').'-log.sql';
$lineContent = 'Sample log line';
$expectedContent = "\n$lineContent\n";

$query = new SqlQuery(1, 'UPDATE test SET x = 2 WHERE id = 3', 5.41);
$this->formatter->shouldReceive('getLine')->once()->with($query)->andReturn($lineContent);
$this->formatter->shouldReceive('getHeader')->once()->withNoArgs()->andReturn('');
setConfig('queries.include_pattern', '#^(?:UPDATE test SET x = \d |INSERT ).*$#i');
$this->filename->shouldReceive('getLogfile')->twice()->withNoArgs()->andReturn($expectedFileName);
$this->writer->writeQuery($query);
expect($this->directory)->toBeFile()
->and($this->filesystem->allFiles($this->directory))->toHaveCount(1)
->and($this->directory.'/'.$expectedFileName)->toBeFile()
->and(file_get_contents($this->directory.'/'.$expectedFileName))->toBe($expectedContent);
});

it('only logs slow queries', function () {
$query1 = new SqlQuery(1, 'test1', 5.41);
$query2 = new SqlQuery(2, 'test2', 500.5);

setConfig('queries.min_exec_time', 500);

$this->formatter->shouldReceive('getHeader')->once()->withNoArgs()->andReturn('');
$this->formatter->shouldReceive('getLine')->once()->with($query2)->andReturn('');

$writer = Mockery::mock(Writer::class, [$this->formatter, $this->config, $this->filename])
->makePartial()->shouldAllowMockingProtectedMethods();
$writer->shouldAllowMockingProtectedMethods();
$writer->shouldReceive('writeLine')->twice()->andReturn(false);

expect($writer->writeQuery($query1))->toBeFalse()
->and($writer->writeQuery($query2))->toBeTrue();
});

it('respects query patterns', function () {
$query1 = new SqlQuery(1, 'select foo from bar', 5.41);
$query2 = new SqlQuery(2, 'update bar set foo = 1', 3.55);
$query3 = new SqlQuery(3, "update bar set last_visit = '2021-06-03 10:26:00'", 3.22);

setConfig([
'queries.include_pattern' => '/^(?!SELECT).*$/i',
'queries.exclude_pattern' => '/^UPDATE.*last_visit/i',
]);

$this->formatter->shouldReceive('getHeader')->once()->withNoArgs()->andReturn('');
$this->formatter->shouldReceive('getLine')->once()->with($query2)->andReturn('');

$writer = Mockery::mock(Writer::class, [$this->formatter, $this->config, $this->filename])
->makePartial()->shouldAllowMockingProtectedMethods();
$writer->shouldAllowMockingProtectedMethods();
$writer->shouldReceive('writeLine')->twice()->andReturn(false);

expect($writer->writeQuery($query1))->toBeFalse()
->and($writer->writeQuery($query2))->toBeTrue()
->and($writer->writeQuery($query3))->toBeFalse();
});
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace Tests\Unit;
namespace Tests;

use Carbon\Carbon;
use Mockery;
Expand Down
Loading

0 comments on commit 09c2c9d

Please sign in to comment.