From b02a05030b9950f148bea5ca8753fea1f5442579 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Mon, 21 Aug 2023 12:33:17 +1000 Subject: [PATCH] wip --- src/Pulse.php | 6 +- tests/Feature/IngestTest.php | 83 ---------------- tests/Feature/QueryIngestTest.php | 155 ++++++++++++++++++++++++++++++ tests/Pest.php | 25 ++++- 4 files changed, 182 insertions(+), 87 deletions(-) delete mode 100644 tests/Feature/IngestTest.php create mode 100644 tests/Feature/QueryIngestTest.php diff --git a/src/Pulse.php b/src/Pulse.php index daf803c1..a72e74de 100644 --- a/src/Pulse.php +++ b/src/Pulse.php @@ -84,12 +84,14 @@ public function __construct( */ public function ignore($callback): mixed { - $this->stopRecording(); + $recording = $this->shouldRecord; + + $this->shouldRecord = false; try { return $callback(); } finally { - $this->startRecording(); + $this->shouldRecord = $recording; } } diff --git a/tests/Feature/IngestTest.php b/tests/Feature/IngestTest.php deleted file mode 100644 index 9929828b..00000000 --- a/tests/Feature/IngestTest.php +++ /dev/null @@ -1,83 +0,0 @@ - throw $e); - - Pulse::ignore(fn () => Schema::create('users', function (Blueprint $table) { - $table->id(); - $table->string('name'); - })); -}); - -it('ingests queries', function () { - Config::set('pulse.slow_query_threshold', 0); - - expect(Pulse::queue())->toHaveCount(0); - - DB::table('users')->count(); - expect(Pulse::queue())->toHaveCount(1); - Pulse::ignore(fn () => expect(DB::table('pulse_queries')->count())->toBe(0)); - - Pulse::store(); - expect(Pulse::queue())->toHaveCount(0); - Pulse::ignore(fn () => expect(DB::table('pulse_queries')->count())->toBe(1)); -}); - -it('does not ingest queries under the slow query threshold', function () { - Config::set('pulse.slow_query_threshold', 1000); - $listeners = Event::getRawListeners()[QueryExecuted::class]; - Event::forget(QueryExecuted::class); - collect([ - function (QueryExecuted $event) { - $event->time = 999; - }, - ...$listeners, - ])->each(fn ($listener) => Event::listen(QueryExecuted::class, $listener)); - - DB::table('users')->count(); - Pulse::store(); - - Pulse::ignore(fn () => expect(DB::table('pulse_queries')->count())->toBe(0)); -}); - -it('ingests queries equal to the slow query threshold', function () { - Config::set('pulse.slow_query_threshold', 1000); - $listeners = Event::getRawListeners()[QueryExecuted::class]; - Event::forget(QueryExecuted::class); - collect([ - function (QueryExecuted $event) { - $event->time = 1000; - }, - ...$listeners, - ])->each(fn ($listener) => Event::listen(QueryExecuted::class, $listener)); - - DB::table('users')->count(); - Pulse::store(); - - Pulse::ignore(fn () => expect(DB::table('pulse_queries')->count())->toBe(1)); -}); - -it('ingests queries over the slow query threshold', function () { - Config::set('pulse.slow_query_threshold', 1000); - $listeners = Event::getRawListeners()[QueryExecuted::class]; - Event::forget(QueryExecuted::class); - collect([ - function (QueryExecuted $event) { - $event->time = 1001; - }, - ...$listeners, - ])->each(fn ($listener) => Event::listen(QueryExecuted::class, $listener)); - - DB::table('users')->count(); - Pulse::store(); - - Pulse::ignore(fn () => expect(DB::table('pulse_queries')->count())->toBe(1)); -}); diff --git a/tests/Feature/QueryIngestTest.php b/tests/Feature/QueryIngestTest.php new file mode 100644 index 00000000..1f6ec89b --- /dev/null +++ b/tests/Feature/QueryIngestTest.php @@ -0,0 +1,155 @@ + Schema::create('users', function (Blueprint $table) { + $table->id(); + $table->string('name'); + $table->timestamps(); + })); +}); + +it('ingests queries', function () { + Config::set('pulse.slow_query_threshold', 0); + Carbon::setTestNow('2020-01-02 03:04:05'); + prependListener(QueryExecuted::class, function (QueryExecuted $event) { + $event->time = 5000; + }); + + DB::table('users')->count(); + expect(Pulse::queue())->toHaveCount(1); + Pulse::ignore(fn () => expect(DB::table('pulse_queries')->count())->toBe(0)); + + Pulse::store(); + $queries = Pulse::ignore(fn () => DB::table('pulse_queries')->get()); + + expect(Pulse::queue())->toHaveCount(0); + expect($queries)->toHaveCount(1); + expect((array) $queries->first())->toEqual([ + 'date' => '2020-01-02 03:04:00', + 'user_id' => null, + 'sql' => 'select count(*) as aggregate from "users"', + 'duration' => 5000, + ]); +}); + +it('does not ingest queries under the slow query threshold', function () { + Config::set('pulse.slow_query_threshold', 5000); + prependListener(QueryExecuted::class, function (QueryExecuted $event) { + $event->time = 4999; + }); + + DB::table('users')->count(); + Pulse::store(); + + Pulse::ignore(fn () => expect(DB::table('pulse_queries')->count())->toBe(0)); +}); + +it('ingests queries equal to the slow query threshold', function () { + Config::set('pulse.slow_query_threshold', 5000); + prependListener(QueryExecuted::class, function (QueryExecuted $event) { + $event->time = 5000; + }); + + DB::table('users')->count(); + Pulse::store(); + + Pulse::ignore(fn () => expect(DB::table('pulse_queries')->count())->toBe(1)); +}); + +it('ingests queries over the slow query threshold', function () { + Config::set('pulse.slow_query_threshold', 5000); + prependListener(QueryExecuted::class, function (QueryExecuted $event) { + $event->time = 5001; + }); + + DB::table('users')->count(); + Pulse::store(); + + Pulse::ignore(fn () => expect(DB::table('pulse_queries')->count())->toBe(1)); +}); + +it('captures the authenticated user', function () { + Config::set('pulse.slow_query_threshold', 0); + + Auth::setUser(User::make(['id' => '567'])); + DB::table('users')->count(); + Pulse::store(); + + $queries = Pulse::ignore(fn () => DB::table('pulse_queries')->get()); + expect($queries)->toHaveCount(1); + expect($queries[0]->user_id)->toBe('567'); +}); + +it('captures the authenticated user if they login after the query is made', function () { + Config::set('pulse.slow_query_threshold', 0); + Config::set('auth.guards.db', ['driver' => 'db']); + + Auth::forgetUser(); + DB::table('users')->count(); + Auth::setUser(User::make(['id' => '567'])); + Pulse::store(); + + $queries = Pulse::ignore(fn () => DB::table('pulse_queries')->get()); + expect($queries)->toHaveCount(1); + expect($queries[0]->user_id)->toBe('567'); +}); + +it('captures the authenticated user if they logout after the query is made', function () { + Config::set('pulse.slow_query_threshold', 0); + Config::set('auth.guards.db', ['driver' => 'db']); + + Auth::setUser(User::make(['id' => '567'])); + DB::table('users')->count(); + Auth::forgetUser(); + Pulse::store(); + + $queries = Pulse::ignore(fn () => DB::table('pulse_queries')->get()); + expect($queries)->toHaveCount(1); + expect($queries[0]->user_id)->toBe('567'); +}); + +it('does not trigger an inifite loop when retriving the authenticated user from the database', function () { + Config::set('pulse.slow_query_threshold', 0); + Config::set('auth.guards.db', ['driver' => 'db']); + Auth::extend('db', fn () => new class implements Guard { + use GuardHelpers; + + public function validate(array $credentials = []) + { + return true; + } + + public function user() + { + static $count = 0; + + if (++$count > 5) { + throw new RuntimeException('Infinite loop detected.'); + } + + return User::first(); + } + })->shouldUse('db'); + + DB::table('users')->count(); + Pulse::store(); + + $queries = Pulse::ignore(fn () => DB::table('pulse_queries')->get()); + expect($queries)->toHaveCount(1); + expect($queries[0]->user_id)->toBe(null); +}); diff --git a/tests/Pest.php b/tests/Pest.php index 8c3480ec..46658c42 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -1,5 +1,10 @@ in('Unit', 'Feature'); +uses(TestCase::class) + ->beforeEach(function () { + Model::unguard(); + Pulse::handleExceptionsUsing(fn (Throwable $e) => throw $e); + }) + ->afterEach(function () { + if (Pulse::queue()->isNotEmpty()) { + throw new RuntimeException('The queue is not empty'); + } + }) + ->in('Unit', 'Feature'); /* |-------------------------------------------------------------------------- @@ -37,4 +52,10 @@ | */ -// ... +function prependListener(string $event, callable $listener): void { + $listeners = Event::getRawListeners()[$event]; + + Event::forget($event); + + collect([$listener, ...$listeners])->each(fn ($listener) => Event::listen($event, $listener)); +}