getExecutionCount())->toBe(3); expect($pattern->getTotalExecutionTimeMs())->toBe(15.5); expect($pattern->getAverageExecutionTimeMs())->toBe(5.166666666666667); }); it('detects potential N+1 pattern', function () { $queries = []; for ($i = 1; $i <= 10; $i++) { $queries[] = new QueryLog( sql: 'SELECT * FROM posts WHERE user_id = ?', bindings: [$i], executionTimeMs: 5.0, stackTrace: 'UserController::show' ); } $pattern = new QueryPattern('SELECT * FROM posts WHERE user_id = ?', $queries); expect($pattern->isPotentialNPlusOne())->toBeTrue(); }); it('rejects pattern with too few executions', function () { $queries = [ new QueryLog('SELECT * FROM users WHERE id = ?', [1], 5.0, ''), new QueryLog('SELECT * FROM users WHERE id = ?', [2], 5.0, ''), ]; $pattern = new QueryPattern('SELECT * FROM users WHERE id = ?', $queries); expect($pattern->isPotentialNPlusOne())->toBeFalse(); }); it('rejects pattern without WHERE clause', function () { $queries = []; for ($i = 1; $i <= 10; $i++) { $queries[] = new QueryLog( sql: 'SELECT * FROM users', bindings: [], executionTimeMs: 5.0, stackTrace: '' ); } $pattern = new QueryPattern('SELECT * FROM users', $queries); expect($pattern->isPotentialNPlusOne())->toBeFalse(); }); it('rejects pattern with JOIN', function () { $queries = []; for ($i = 1; $i <= 10; $i++) { $queries[] = new QueryLog( sql: 'SELECT * FROM users LEFT JOIN posts ON users.id = posts.user_id WHERE users.id = ?', bindings: [$i], executionTimeMs: 5.0, stackTrace: '' ); } $pattern = new QueryPattern( 'SELECT * FROM users LEFT JOIN posts ON users.id = posts.user_id WHERE users.id = ?', $queries ); expect($pattern->isPotentialNPlusOne())->toBeFalse(); }); it('calculates N+1 severity correctly', function () { $queries = []; // High execution count (20 queries) for ($i = 1; $i <= 20; $i++) { $queries[] = new QueryLog( sql: 'SELECT * FROM posts WHERE user_id = ?', bindings: [$i], executionTimeMs: 10.0, stackTrace: 'UserController::show', callerClass: 'UserController', callerMethod: 'show' ); } $pattern = new QueryPattern('SELECT * FROM posts WHERE user_id = ?', $queries); $severity = $pattern->getNPlusOneSeverity(); // Should be high severity (execution count > 10, consistent caller, high total time) expect($severity)->toBeGreaterThan(6); }); it('classifies severity levels correctly', function () { $highSeverityQueries = []; for ($i = 1; $i <= 50; $i++) { $highSeverityQueries[] = new QueryLog( sql: 'SELECT * FROM posts WHERE user_id = ?', bindings: [$i], executionTimeMs: 15.0, stackTrace: 'UserController::show', callerClass: 'UserController', callerMethod: 'show' ); } $pattern = new QueryPattern('SELECT * FROM posts WHERE user_id = ?', $highSeverityQueries); expect($pattern->getSeverityLevel())->toBeIn(['CRITICAL', 'HIGH']); }); it('identifies table name', function () { $queries = [ new QueryLog('SELECT * FROM users WHERE id = ?', [1], 5.0, ''), ]; $pattern = new QueryPattern('SELECT * FROM users WHERE id = ?', $queries); expect($pattern->getTableName())->toBe('users'); }); it('checks for consistent caller', function () { $queries = []; for ($i = 1; $i <= 10; $i++) { $queries[] = new QueryLog( sql: 'SELECT * FROM posts WHERE user_id = ?', bindings: [$i], executionTimeMs: 5.0, stackTrace: '', callerClass: 'UserController', callerMethod: 'show' ); } $pattern = new QueryPattern('SELECT * FROM posts WHERE user_id = ?', $queries); expect($pattern->hasConsistentCaller())->toBeTrue(); }); it('detects inconsistent caller', function () { $queries = [ new QueryLog('SELECT * FROM posts WHERE user_id = ?', [1], 5.0, '', callerClass: 'UserController', callerMethod: 'show'), new QueryLog('SELECT * FROM posts WHERE user_id = ?', [2], 5.0, '', callerClass: 'PostController', callerMethod: 'index'), new QueryLog('SELECT * FROM posts WHERE user_id = ?', [3], 5.0, '', callerClass: 'UserController', callerMethod: 'show'), ]; $pattern = new QueryPattern('SELECT * FROM posts WHERE user_id = ?', $queries); expect($pattern->hasConsistentCaller())->toBeFalse(); }); it('gets caller locations', function () { $queries = [ new QueryLog('SELECT * FROM posts WHERE user_id = ?', [1], 5.0, '', callerClass: 'UserController', callerMethod: 'show', callerLine: 42), new QueryLog('SELECT * FROM posts WHERE user_id = ?', [2], 5.0, '', callerClass: 'UserController', callerMethod: 'show', callerLine: 42), ]; $pattern = new QueryPattern('SELECT * FROM posts WHERE user_id = ?', $queries); $locations = $pattern->getCallerLocations(); expect($locations)->toContain('UserController::show:42'); }); });