Files
michaelschiemer/tests/Database/QueryOptimization/QueryLoggerTest.php

188 lines
6.3 KiB
PHP

<?php
declare(strict_types=1);
use App\Framework\Database\QueryOptimization\QueryLogger;
describe('QueryLogger', function () {
beforeEach(function () {
$this->logger = new QueryLogger();
});
it('starts disabled by default', function () {
expect($this->logger->isEnabled())->toBeFalse();
});
it('enables and disables logging', function () {
$this->logger->enable();
expect($this->logger->isEnabled())->toBeTrue();
$this->logger->disable();
expect($this->logger->isEnabled())->toBeFalse();
});
it('ignores queries when disabled', function () {
$this->logger->disable();
$this->logger->logQuery('SELECT * FROM users', [], 5.0);
expect($this->logger->getQueryCount())->toBe(0);
});
it('logs queries when enabled', function () {
$this->logger->enable();
$this->logger->logQuery('SELECT * FROM users WHERE id = ?', [1], 5.5, 1);
expect($this->logger->getQueryCount())->toBe(1);
$logs = $this->logger->getQueryLogs();
expect($logs)->toHaveCount(1);
expect($logs[0]->sql)->toBe('SELECT * FROM users WHERE id = ?');
expect($logs[0]->bindings)->toBe([1]);
expect($logs[0]->executionTimeMs)->toBe(5.5);
expect($logs[0]->rowCount)->toBe(1);
});
it('captures stack trace', function () {
$this->logger->enable();
$this->logger->logQuery('SELECT * FROM users', [], 5.0);
$logs = $this->logger->getQueryLogs();
expect($logs[0]->stackTrace)->not->toBeEmpty();
});
it('identifies caller class and method', function () {
$this->logger->enable();
$this->logger->logQuery('SELECT * FROM users', [], 5.0);
$logs = $this->logger->getQueryLogs();
expect($logs[0]->callerClass)->not->toBeNull();
expect($logs[0]->callerMethod)->not->toBeNull();
});
it('calculates total execution time', function () {
$this->logger->enable();
$this->logger->logQuery('SELECT * FROM users', [], 5.0);
$this->logger->logQuery('SELECT * FROM posts', [], 10.0);
$this->logger->logQuery('SELECT * FROM comments', [], 3.5);
expect($this->logger->getTotalExecutionTime())->toBe(18.5);
});
it('groups queries by pattern', function () {
$this->logger->enable();
$this->logger->logQuery('SELECT * FROM users WHERE id = ?', [1], 5.0);
$this->logger->logQuery('SELECT * FROM users WHERE id = ?', [2], 4.5);
$this->logger->logQuery('SELECT * FROM posts WHERE user_id = ?', [1], 3.0);
$grouped = $this->logger->getGroupedByPattern();
expect($grouped)->toHaveCount(2);
expect($grouped['SELECT * FROM users WHERE id = ?'])->toHaveCount(2);
expect($grouped['SELECT * FROM posts WHERE user_id = ?'])->toHaveCount(1);
});
it('identifies slow queries', function () {
$this->logger->enable();
$this->logger->logQuery('SELECT * FROM users', [], 50.0);
$this->logger->logQuery('SELECT * FROM posts', [], 150.0);
$this->logger->logQuery('SELECT * FROM comments', [], 5.0);
$slowQueries = $this->logger->getSlowQueries(100.0);
expect($slowQueries)->toHaveCount(1);
// array_filter preserves keys, so get the first value
$firstSlow = reset($slowQueries);
expect($firstSlow->sql)->toContain('posts');
expect($firstSlow->executionTimeMs)->toBe(150.0);
});
it('clears logged queries', function () {
$this->logger->enable();
$this->logger->logQuery('SELECT * FROM users', [], 5.0);
$this->logger->logQuery('SELECT * FROM posts', [], 3.0);
expect($this->logger->getQueryCount())->toBe(2);
$this->logger->clear();
expect($this->logger->getQueryCount())->toBe(0);
expect($this->logger->getQueryLogs())->toBeEmpty();
});
it('generates query statistics', function () {
$this->logger->enable();
$this->logger->logQuery('SELECT * FROM users', [], 5.0);
$this->logger->logQuery('SELECT * FROM posts', [], 10.0);
$this->logger->logQuery('INSERT INTO logs (message) VALUES (?)', ['test'], 2.0);
$this->logger->logQuery('UPDATE users SET name = ? WHERE id = ?', ['John', 1], 3.0);
$this->logger->logQuery('DELETE FROM logs WHERE id = ?', [1], 1.5);
$this->logger->logQuery('SELECT * FROM users', [], 150.0); // Slow query
$stats = $this->logger->getStatistics();
expect($stats['total_queries'])->toBe(6);
expect($stats['total_time_ms'])->toBe(171.5);
expect($stats['select_count'])->toBe(3);
expect($stats['insert_count'])->toBe(1);
expect($stats['update_count'])->toBe(1);
expect($stats['delete_count'])->toBe(1);
expect($stats['slow_queries'])->toBe(1);
expect($stats['unique_patterns'])->toBe(5);
});
it('calculates average execution time', function () {
$this->logger->enable();
$this->logger->logQuery('SELECT * FROM users', [], 10.0);
$this->logger->logQuery('SELECT * FROM posts', [], 20.0);
$this->logger->logQuery('SELECT * FROM comments', [], 30.0);
$stats = $this->logger->getStatistics();
expect($stats['average_time_ms'])->toBe(20.0);
});
it('handles empty query logs in statistics', function () {
$this->logger->enable();
$stats = $this->logger->getStatistics();
expect($stats['total_queries'])->toBe(0);
expect($stats['average_time_ms'])->toBe(0.0);
});
it('skips framework internals when finding caller', function () {
$this->logger->enable();
$this->logger->logQuery('SELECT * FROM users', [], 5.0);
$logs = $this->logger->getQueryLogs();
// QueryLogger tries to skip Framework/Database internals
// When all frames are framework internals, fallback to first frame (QueryLogger itself)
expect($logs[0]->callerClass)->toBe('App\\Framework\\Database\\QueryOptimization\\QueryLogger');
});
it('formats stack trace correctly', function () {
$this->logger->enable();
$this->logger->logQuery('SELECT * FROM users', [], 5.0);
$logs = $this->logger->getQueryLogs();
$stackTrace = $logs[0]->stackTrace;
// Stack trace should contain class/method and file:line format
expect($stackTrace)->toMatch('/.+\(.+:\d+\)/');
});
});