logPath1 = FilePath::create('/tmp/app.log'); $this->logPath2 = FilePath::create('/tmp/debug.log'); $this->result1 = LogReadResult::fromEntries( logName: LogName::fromString('app_error'), logPath: $this->logPath1, entries: [ LogEntry::fromParsedLine( timestamp: '2024-01-15 10:30:45', level: 'ERROR', messageWithContext: 'Database connection failed', raw: '[2024-01-15 10:30:45] local.ERROR: Database connection failed', sourcePath: $this->logPath1 ), LogEntry::fromParsedLine( timestamp: '2024-01-15 10:31:00', level: 'WARNING', messageWithContext: 'Database slow query', raw: '[2024-01-15 10:31:00] local.WARNING: Database slow query', sourcePath: $this->logPath1 ), ], limit: 100 ); $this->result2 = LogReadResult::fromEntries( logName: LogName::fromString('debug_log'), logPath: $this->logPath2, entries: [ LogEntry::fromParsedLine( timestamp: '2024-01-15 10:32:00', level: 'INFO', messageWithContext: 'Database query executed', raw: '[2024-01-15 10:32:00] local.INFO: Database query executed', sourcePath: $this->logPath2 ), ], limit: 100 ); }); it('creates from results', function () { $searchResult = LogSearchResult::fromResults( 'database', [$this->result1, $this->result2] ); expect($searchResult->searchTerm)->toBe('database'); expect($searchResult->results)->toHaveCount(2); expect($searchResult->totalMatches)->toBe(3); expect($searchResult->filesSearched)->toBe(2); }); it('creates empty result', function () { $searchResult = LogSearchResult::empty('test'); expect($searchResult->searchTerm)->toBe('test'); expect($searchResult->results)->toBe([]); expect($searchResult->totalMatches)->toBe(0); expect($searchResult->filesSearched)->toBe(0); expect($searchResult->isEmpty())->toBeTrue(); }); it('checks for matches', function () { $searchResult = LogSearchResult::fromResults( 'database', [$this->result1, $this->result2] ); expect($searchResult->hasMatches())->toBeTrue(); expect($searchResult->isEmpty())->toBeFalse(); }); it('gets all entries', function () { $searchResult = LogSearchResult::fromResults( 'database', [$this->result1, $this->result2] ); $allEntries = $searchResult->getAllEntries(); expect($allEntries)->toHaveCount(3); expect($allEntries[0])->toBeInstanceOf(LogEntry::class); }); it('filters by log name', function () { $searchResult = LogSearchResult::fromResults( 'database', [$this->result1, $this->result2] ); $filtered = $searchResult->filterByLogName(LogName::fromString('app_error')); expect($filtered->results)->toHaveCount(1); expect($filtered->totalMatches)->toBe(2); }); it('filters by minimum level', function () { $searchResult = LogSearchResult::fromResults( 'database', [$this->result1, $this->result2] ); $filtered = $searchResult->filterByMinimumLevel(LogLevel::WARNING); expect($filtered->totalMatches)->toBe(2); // ERROR and WARNING only }); it('sorts by most recent', function () { $searchResult = LogSearchResult::fromResults( 'database', [$this->result1, $this->result2] ); $sorted = $searchResult->sortByMostRecent(); expect($sorted->results)->toHaveCount(2); // Most recent should be first expect($sorted->results[0]->last()->message)->toContain('executed'); }); it('takes limited results', function () { $searchResult = LogSearchResult::fromResults( 'database', [$this->result1, $this->result2] ); $limited = $searchResult->take(1); expect($limited->results)->toHaveCount(1); expect($limited->totalMatches)->toBe(2); expect($limited->filesSearched)->toBe(1); }); it('groups by level', function () { $searchResult = LogSearchResult::fromResults( 'database', [$this->result1, $this->result2] ); $grouped = $searchResult->groupByLevel(); expect($grouped)->toBeArray(); expect($grouped)->toHaveKey('ERROR'); expect($grouped)->toHaveKey('WARNING'); expect($grouped)->toHaveKey('INFO'); expect($grouped['ERROR'])->toHaveCount(1); expect($grouped['WARNING'])->toHaveCount(1); expect($grouped['INFO'])->toHaveCount(1); }); it('gets statistics', function () { $searchResult = LogSearchResult::fromResults( 'database', [$this->result1, $this->result2] ); $stats = $searchResult->getStatistics(); expect($stats)->toBeArray(); expect($stats)->toHaveKey('search_term'); expect($stats)->toHaveKey('total_matches'); expect($stats)->toHaveKey('files_searched'); expect($stats)->toHaveKey('level_distribution'); expect($stats)->toHaveKey('files_with_matches'); expect($stats['search_term'])->toBe('database'); expect($stats['total_matches'])->toBe(3); expect($stats['files_searched'])->toBe(2); expect($stats['level_distribution'])->toBeArray(); }); it('converts to array', function () { $searchResult = LogSearchResult::fromResults( 'database', [$this->result1, $this->result2] ); $array = $searchResult->toArray(); expect($array)->toBeArray(); expect($array)->toHaveKey('search_term'); expect($array)->toHaveKey('results'); expect($array)->toHaveKey('total_matches'); expect($array)->toHaveKey('files_searched'); expect($array)->toHaveKey('statistics'); expect($array['results'])->toBeArray(); expect($array['results'])->toHaveCount(2); }); it('gets summary', function () { $searchResult = LogSearchResult::fromResults( 'database', [$this->result1, $this->result2] ); $summary = $searchResult->getSummary(); expect($summary)->toBeString(); expect($summary)->toContain('database'); expect($summary)->toContain('3'); expect($summary)->toContain('2'); }); it('gets empty summary', function () { $searchResult = LogSearchResult::empty('test'); $summary = $searchResult->getSummary(); expect($summary)->toBeString(); expect($summary)->toContain('No matches'); expect($summary)->toContain('test'); }); it('creates iterator', function () { $searchResult = LogSearchResult::fromResults( 'database', [$this->result1, $this->result2] ); $iterator = $searchResult->getIterator(); expect($iterator)->toBeInstanceOf(\ArrayIterator::class); $count = 0; foreach ($iterator as $result) { expect($result)->toBeInstanceOf(LogReadResult::class); $count++; } expect($count)->toBe(2); }); it('validates results array contains only LogReadResult', function () { new LogSearchResult( searchTerm: 'test', results: ['invalid'], totalMatches: 0, filesSearched: 0 ); })->throws(\InvalidArgumentException::class, 'All results must be LogReadResult instances'); });