- Add comprehensive health check system with multiple endpoints - Add Prometheus metrics endpoint - Add production logging configurations (5 strategies) - Add complete deployment documentation suite: * QUICKSTART.md - 30-minute deployment guide * DEPLOYMENT_CHECKLIST.md - Printable verification checklist * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference * production-logging.md - Logging configuration guide * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation * README.md - Navigation hub * DEPLOYMENT_SUMMARY.md - Executive summary - Add deployment scripts and automation - Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment - Update README with production-ready features All production infrastructure is now complete and ready for deployment.
187 lines
6.2 KiB
PHP
187 lines
6.2 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);
|
|
expect($slowQueries[0]->sql)->toContain('posts');
|
|
expect($slowQueries[0]->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();
|
|
|
|
// Caller should not be from /Framework/Database/ or /vendor/
|
|
if ($logs[0]->callerClass) {
|
|
expect($logs[0]->callerClass)->not->toContain('Framework\\Database');
|
|
expect($logs[0]->callerClass)->not->toContain('vendor');
|
|
}
|
|
});
|
|
|
|
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+\)/');
|
|
});
|
|
});
|