Files
michaelschiemer/tests/Unit/Framework/Logging/ValueObjects/LogSearchResultTest.php
Michael Schiemer fc3d7e6357 feat(Production): Complete production deployment infrastructure
- 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.
2025-10-25 19:18:37 +02:00

257 lines
8.3 KiB
PHP

<?php
declare(strict_types=1);
use App\Framework\Filesystem\ValueObjects\FilePath;
use App\Framework\Logging\LogLevel;
use App\Framework\Logging\ValueObjects\LogEntry;
use App\Framework\Logging\ValueObjects\LogName;
use App\Framework\Logging\ValueObjects\LogReadResult;
use App\Framework\Logging\ValueObjects\LogSearchResult;
describe('LogSearchResult', function () {
beforeEach(function () {
$this->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');
});