docs: consolidate documentation into organized structure

- Move 12 markdown files from root to docs/ subdirectories
- Organize documentation by category:
  • docs/troubleshooting/ (1 file)  - Technical troubleshooting guides
  • docs/deployment/      (4 files) - Deployment and security documentation
  • docs/guides/          (3 files) - Feature-specific guides
  • docs/planning/        (4 files) - Planning and improvement proposals

Root directory cleanup:
- Reduced from 16 to 4 markdown files in root
- Only essential project files remain:
  • CLAUDE.md (AI instructions)
  • README.md (Main project readme)
  • CLEANUP_PLAN.md (Current cleanup plan)
  • SRC_STRUCTURE_IMPROVEMENTS.md (Structure improvements)

This improves:
 Documentation discoverability
 Logical organization by purpose
 Clean root directory
 Better maintainability
This commit is contained in:
2025-10-05 11:05:04 +02:00
parent 887847dde6
commit 5050c7d73a
36686 changed files with 196456 additions and 12398919 deletions

View File

@@ -0,0 +1,691 @@
<?php
declare(strict_types=1);
use App\Framework\Queue\ValueObjects\JobMetrics;
use App\Framework\Queue\ValueObjects\JobId;
use App\Framework\Queue\ValueObjects\QueueName;
use App\Framework\Core\ValueObjects\Percentage;
describe('JobMetrics Value Object', function () {
describe('Creation and Basic Properties', function () {
it('can create job metrics with minimal parameters', function () {
$jobId = JobId::generate()->toString();
$queueName = 'email-queue';
$metrics = JobMetrics::create($jobId, $queueName);
expect($metrics->jobId)->toBe($jobId);
expect($metrics->queueName)->toBe($queueName);
expect($metrics->status)->toBe('pending');
expect($metrics->attempts)->toBe(0);
expect($metrics->maxAttempts)->toBe(3);
expect($metrics->executionTimeMs)->toBe(0.0);
expect($metrics->memoryUsageBytes)->toBe(0);
expect($metrics->errorMessage)->toBeNull();
expect($metrics->createdAt)->toBeString();
expect($metrics->startedAt)->toBeNull();
expect($metrics->completedAt)->toBeNull();
expect($metrics->failedAt)->toBeNull();
expect($metrics->metadata)->toBe([]);
});
it('can create job metrics with custom parameters', function () {
$jobId = JobId::generate()->toString();
$queueName = 'report-queue';
$status = 'running';
$attempts = 2;
$maxAttempts = 5;
$metrics = JobMetrics::create($jobId, $queueName, $status, $attempts, $maxAttempts);
expect($metrics->status)->toBe($status);
expect($metrics->attempts)->toBe($attempts);
expect($metrics->maxAttempts)->toBe($maxAttempts);
});
it('is readonly and immutable', function () {
$metrics = JobMetrics::create('test-job', 'test-queue');
$reflection = new ReflectionClass($metrics);
expect($reflection->isReadOnly())->toBeTrue();
// All properties should be readonly
$properties = ['jobId', 'queueName', 'status', 'attempts', 'maxAttempts',
'executionTimeMs', 'memoryUsageBytes', 'errorMessage',
'createdAt', 'startedAt', 'completedAt', 'failedAt', 'metadata'];
foreach ($properties as $prop) {
$property = $reflection->getProperty($prop);
expect($property->isReadOnly())->toBeTrue("Property {$prop} should be readonly");
}
});
});
describe('Job State Transitions', function () {
beforeEach(function () {
$this->baseMetrics = JobMetrics::create('test-job-123', 'email-queue');
});
it('can transition from pending to running', function () {
$startTime = microtime(true) * 1000; // milliseconds
$memoryUsage = 1024 * 1024; // 1MB
$runningMetrics = $this->baseMetrics->withStarted($startTime, $memoryUsage);
expect($runningMetrics->status)->toBe('running');
expect($runningMetrics->attempts)->toBe(1); // Incremented from 0
expect($runningMetrics->executionTimeMs)->toBe($startTime);
expect($runningMetrics->memoryUsageBytes)->toBe($memoryUsage);
expect($runningMetrics->startedAt)->toBeString();
expect($runningMetrics->startedAt)->not->toBeNull();
// Original should be unchanged
expect($this->baseMetrics->status)->toBe('pending');
expect($this->baseMetrics->attempts)->toBe(0);
});
it('can transition from running to completed', function () {
$startTime = microtime(true) * 1000;
$runningMetrics = $this->baseMetrics->withStarted($startTime, 1024 * 1024);
$totalExecutionTime = 5500.0; // 5.5 seconds in milliseconds
$peakMemoryUsage = 2 * 1024 * 1024; // 2MB
$completedMetrics = $runningMetrics->withCompleted($totalExecutionTime, $peakMemoryUsage);
expect($completedMetrics->status)->toBe('completed');
expect($completedMetrics->executionTimeMs)->toBe($totalExecutionTime);
expect($completedMetrics->memoryUsageBytes)->toBe($peakMemoryUsage);
expect($completedMetrics->completedAt)->toBeString();
expect($completedMetrics->completedAt)->not->toBeNull();
expect($completedMetrics->attempts)->toBe(1); // Same as running state
});
it('can transition from running to failed', function () {
$startTime = microtime(true) * 1000;
$runningMetrics = $this->baseMetrics->withStarted($startTime, 1024 * 1024);
$errorMessage = 'Database connection timeout';
$executionTime = 2500.0; // 2.5 seconds
$memoryUsage = 1.5 * 1024 * 1024; // 1.5MB
$failedMetrics = $runningMetrics->withFailed($errorMessage, $executionTime, $memoryUsage);
expect($failedMetrics->status)->toBe('failed');
expect($failedMetrics->errorMessage)->toBe($errorMessage);
expect($failedMetrics->executionTimeMs)->toBe($executionTime);
expect($failedMetrics->memoryUsageBytes)->toBe($memoryUsage);
expect($failedMetrics->failedAt)->toBeString();
expect($failedMetrics->failedAt)->not->toBeNull();
});
it('preserves metadata across state transitions', function () {
$originalMetadata = ['batch_id' => 123, 'priority' => 'high'];
$metricsWithMetadata = $this->baseMetrics->withMetadata($originalMetadata);
$runningMetrics = $metricsWithMetadata->withStarted(1000.0, 1024 * 1024);
expect($runningMetrics->metadata)->toBe($originalMetadata);
$completedMetrics = $runningMetrics->withCompleted(5000.0, 2 * 1024 * 1024);
expect($completedMetrics->metadata)->toBe($originalMetadata);
});
});
describe('Metadata Management', function () {
beforeEach(function () {
$this->baseMetrics = JobMetrics::create('test-job', 'test-queue');
});
it('can add metadata to metrics', function () {
$metadata = [
'user_id' => 12345,
'email_template' => 'newsletter',
'batch_size' => 1000
];
$metricsWithMetadata = $this->baseMetrics->withMetadata($metadata);
expect($metricsWithMetadata->metadata)->toBe($metadata);
expect($this->baseMetrics->metadata)->toBe([]); // Original unchanged
});
it('merges metadata when called multiple times', function () {
$firstMetadata = ['user_id' => 123, 'type' => 'email'];
$secondMetadata = ['priority' => 'high', 'retry_count' => 2];
$metrics = $this->baseMetrics
->withMetadata($firstMetadata)
->withMetadata($secondMetadata);
expect($metrics->metadata)->toBe([
'user_id' => 123,
'type' => 'email',
'priority' => 'high',
'retry_count' => 2
]);
});
it('overwrites existing metadata keys', function () {
$firstMetadata = ['priority' => 'low', 'attempts' => 1];
$secondMetadata = ['priority' => 'high']; // Overwrites priority
$metrics = $this->baseMetrics
->withMetadata($firstMetadata)
->withMetadata($secondMetadata);
expect($metrics->metadata['priority'])->toBe('high');
expect($metrics->metadata['attempts'])->toBe(1); // Preserved
});
});
describe('Status Check Methods', function () {
beforeEach(function () {
$this->jobId = 'status-test-job';
$this->queueName = 'status-queue';
});
it('correctly identifies pending status', function () {
$metrics = JobMetrics::create($this->jobId, $this->queueName, 'pending');
expect($metrics->isPending())->toBeTrue();
expect($metrics->isRunning())->toBeFalse();
expect($metrics->isCompleted())->toBeFalse();
expect($metrics->isFailed())->toBeFalse();
});
it('correctly identifies running status', function () {
$metrics = JobMetrics::create($this->jobId, $this->queueName, 'running');
expect($metrics->isRunning())->toBeTrue();
expect($metrics->isPending())->toBeFalse();
expect($metrics->isCompleted())->toBeFalse();
expect($metrics->isFailed())->toBeFalse();
});
it('correctly identifies completed status', function () {
$metrics = JobMetrics::create($this->jobId, $this->queueName, 'completed');
expect($metrics->isCompleted())->toBeTrue();
expect($metrics->isPending())->toBeFalse();
expect($metrics->isRunning())->toBeFalse();
expect($metrics->isFailed())->toBeFalse();
});
it('correctly identifies failed status', function () {
$metrics = JobMetrics::create($this->jobId, $this->queueName, 'failed');
expect($metrics->isFailed())->toBeTrue();
expect($metrics->isPending())->toBeFalse();
expect($metrics->isRunning())->toBeFalse();
expect($metrics->isCompleted())->toBeFalse();
});
it('correctly checks max attempts', function () {
$metrics = JobMetrics::create($this->jobId, $this->queueName, 'running', 2, 3);
expect($metrics->hasMaxAttempts())->toBeFalse();
$maxedMetrics = JobMetrics::create($this->jobId, $this->queueName, 'failed', 3, 3);
expect($maxedMetrics->hasMaxAttempts())->toBeTrue();
$exceededMetrics = JobMetrics::create($this->jobId, $this->queueName, 'failed', 5, 3);
expect($exceededMetrics->hasMaxAttempts())->toBeTrue();
});
});
describe('Calculation Methods', function () {
it('calculates success rate correctly', function () {
// Job not started yet
$pendingMetrics = JobMetrics::create('job', 'queue', 'pending', 0);
expect($pendingMetrics->getSuccessRate()->getValue())->toBe(100.0);
// Failed job with attempts
$failedMetrics = JobMetrics::create('job', 'queue', 'failed', 2);
expect($failedMetrics->getSuccessRate()->getValue())->toBe(0.0);
// Completed job
$completedMetrics = JobMetrics::create('job', 'queue', 'completed', 1);
expect($completedMetrics->getSuccessRate()->getValue())->toBe(100.0);
});
it('converts execution time from milliseconds to seconds', function () {
$metrics = JobMetrics::create('job', 'queue')
->withCompleted(5500.0, 1024 * 1024); // 5.5 seconds
expect($metrics->getExecutionTimeSeconds())->toBe(5.5);
});
it('converts memory usage from bytes to MB', function () {
$metrics = JobMetrics::create('job', 'queue')
->withCompleted(1000.0, 2.5 * 1024 * 1024); // 2.5 MB
expect($metrics->getMemoryUsageMB())->toBe(2.5);
});
it('calculates duration correctly', function () {
$metrics = JobMetrics::create('job', 'queue');
// No duration for pending job
expect($metrics->getDuration())->toBeNull();
// Mock started/completed times
$startedMetrics = new JobMetrics(
jobId: 'job',
queueName: 'queue',
status: 'completed',
attempts: 1,
maxAttempts: 3,
executionTimeMs: 1000.0,
memoryUsageBytes: 1024,
errorMessage: null,
createdAt: '2024-01-01 10:00:00',
startedAt: '2024-01-01 10:00:05',
completedAt: '2024-01-01 10:00:15',
failedAt: null
);
$duration = $startedMetrics->getDuration();
expect($duration)->toBe(10); // 10 seconds difference
});
it('calculates duration for failed jobs', function () {
$failedMetrics = new JobMetrics(
jobId: 'job',
queueName: 'queue',
status: 'failed',
attempts: 2,
maxAttempts: 3,
executionTimeMs: 500.0,
memoryUsageBytes: 1024,
errorMessage: 'Error occurred',
createdAt: '2024-01-01 10:00:00',
startedAt: '2024-01-01 10:00:05',
completedAt: null,
failedAt: '2024-01-01 10:00:08'
);
$duration = $failedMetrics->getDuration();
expect($duration)->toBe(3); // 3 seconds from start to failure
});
});
describe('Array Conversion', function () {
it('provides comprehensive metrics information', function () {
$metrics = JobMetrics::create('test-job-456', 'email-queue', 'completed', 1, 3)
->withCompleted(3750.0, 1.5 * 1024 * 1024)
->withMetadata(['batch_id' => 789, 'template' => 'welcome']);
$array = $metrics->toArray();
// Verify all expected keys exist
$expectedKeys = [
'job_id', 'queue_name', 'status', 'attempts', 'max_attempts',
'execution_time_ms', 'execution_time_seconds', 'memory_usage_bytes',
'memory_usage_mb', 'success_rate', 'duration_seconds', 'error_message',
'created_at', 'started_at', 'completed_at', 'failed_at', 'metadata'
];
foreach ($expectedKeys as $key) {
expect($array)->toHaveKey($key);
}
// Verify calculated values
expect($array['job_id'])->toBe('test-job-456');
expect($array['queue_name'])->toBe('email-queue');
expect($array['status'])->toBe('completed');
expect($array['execution_time_seconds'])->toBe(3.75);
expect($array['memory_usage_mb'])->toBe(1.5);
expect($array['success_rate'])->toBe(100.0);
expect($array['metadata'])->toBe(['batch_id' => 789, 'template' => 'welcome']);
});
it('handles null values correctly in array conversion', function () {
$pendingMetrics = JobMetrics::create('pending-job', 'test-queue');
$array = $pendingMetrics->toArray();
expect($array['error_message'])->toBeNull();
expect($array['started_at'])->toBeNull();
expect($array['completed_at'])->toBeNull();
expect($array['failed_at'])->toBeNull();
expect($array['duration_seconds'])->toBeNull();
});
});
describe('Edge Cases and Error Handling', function () {
it('handles zero execution time', function () {
$metrics = JobMetrics::create('instant-job', 'fast-queue')
->withCompleted(0.0, 1024);
expect($metrics->getExecutionTimeSeconds())->toBe(0.0);
});
it('handles zero memory usage', function () {
$metrics = JobMetrics::create('no-memory-job', 'efficient-queue')
->withCompleted(1000.0, 0);
expect($metrics->getMemoryUsageMB())->toBe(0.0);
});
it('handles very large execution times', function () {
$largeTime = 3600000.0; // 1 hour in milliseconds
$metrics = JobMetrics::create('long-job', 'slow-queue')
->withCompleted($largeTime, 1024);
expect($metrics->getExecutionTimeSeconds())->toBe(3600.0); // 1 hour
});
it('handles very large memory usage', function () {
$largeMemory = 10 * 1024 * 1024 * 1024; // 10GB
$metrics = JobMetrics::create('memory-intensive-job', 'heavy-queue')
->withCompleted(1000.0, $largeMemory);
expect($metrics->getMemoryUsageMB())->toBe(10240.0); // 10GB in MB
});
it('handles empty metadata gracefully', function () {
$metrics = JobMetrics::create('no-metadata-job', 'simple-queue')
->withMetadata([]);
expect($metrics->metadata)->toBe([]);
});
it('handles complex metadata structures', function () {
$complexMetadata = [
'nested' => ['level1' => ['level2' => 'value']],
'array' => [1, 2, 3, 4, 5],
'mixed' => ['string', 42, true, null]
];
$metrics = JobMetrics::create('complex-job', 'data-queue')
->withMetadata($complexMetadata);
expect($metrics->metadata)->toBe($complexMetadata);
});
});
});
describe('Job Metrics Collection Mock System', function () {
beforeEach(function () {
// Create a mock metrics manager for testing
$this->metricsManager = new class {
private array $jobMetrics = [];
private array $queueStats = [];
public function recordJobExecution(string $jobId, float $executionTimeMs, int $memoryUsage): void {
if (!isset($this->jobMetrics[$jobId])) {
$this->jobMetrics[$jobId] = JobMetrics::create($jobId, 'default-queue');
}
$this->jobMetrics[$jobId] = $this->jobMetrics[$jobId]
->withCompleted($executionTimeMs, $memoryUsage);
}
public function recordJobFailure(string $jobId, string $errorMessage, float $executionTimeMs, int $memoryUsage): void {
if (!isset($this->jobMetrics[$jobId])) {
$this->jobMetrics[$jobId] = JobMetrics::create($jobId, 'default-queue');
}
$this->jobMetrics[$jobId] = $this->jobMetrics[$jobId]
->withFailed($errorMessage, $executionTimeMs, $memoryUsage);
}
public function getJobMetrics(string $jobId): ?JobMetrics {
return $this->jobMetrics[$jobId] ?? null;
}
public function getQueueMetrics(string $queueName): array {
$jobs = array_filter($this->jobMetrics, fn($metrics) => $metrics->queueName === $queueName);
if (empty($jobs)) {
return [
'queue_name' => $queueName,
'total_jobs' => 0,
'completed_jobs' => 0,
'failed_jobs' => 0,
'average_execution_time_ms' => 0.0,
'average_memory_usage_mb' => 0.0,
'success_rate' => 100.0
];
}
$totalJobs = count($jobs);
$completedJobs = count(array_filter($jobs, fn($m) => $m->isCompleted()));
$failedJobs = count(array_filter($jobs, fn($m) => $m->isFailed()));
$avgExecutionTime = array_sum(array_map(fn($m) => $m->executionTimeMs, $jobs)) / $totalJobs;
$avgMemoryUsage = array_sum(array_map(fn($m) => $m->getMemoryUsageMB(), $jobs)) / $totalJobs;
$successRate = ($completedJobs / $totalJobs) * 100;
return [
'queue_name' => $queueName,
'total_jobs' => $totalJobs,
'completed_jobs' => $completedJobs,
'failed_jobs' => $failedJobs,
'average_execution_time_ms' => round($avgExecutionTime, 2),
'average_memory_usage_mb' => round($avgMemoryUsage, 2),
'success_rate' => round($successRate, 2)
];
}
public function getSystemMetrics(): array {
if (empty($this->jobMetrics)) {
return [
'total_jobs' => 0,
'completed_jobs' => 0,
'failed_jobs' => 0,
'running_jobs' => 0,
'overall_success_rate' => 100.0,
'average_execution_time_ms' => 0.0,
'peak_memory_usage_mb' => 0.0
];
}
$totalJobs = count($this->jobMetrics);
$completedJobs = count(array_filter($this->jobMetrics, fn($m) => $m->isCompleted()));
$failedJobs = count(array_filter($this->jobMetrics, fn($m) => $m->isFailed()));
$runningJobs = count(array_filter($this->jobMetrics, fn($m) => $m->isRunning()));
$overallSuccessRate = ($completedJobs / $totalJobs) * 100;
$avgExecutionTime = array_sum(array_map(fn($m) => $m->executionTimeMs, $this->jobMetrics)) / $totalJobs;
$peakMemoryUsage = max(array_map(fn($m) => $m->getMemoryUsageMB(), $this->jobMetrics));
return [
'total_jobs' => $totalJobs,
'completed_jobs' => $completedJobs,
'failed_jobs' => $failedJobs,
'running_jobs' => $runningJobs,
'overall_success_rate' => round($overallSuccessRate, 2),
'average_execution_time_ms' => round($avgExecutionTime, 2),
'peak_memory_usage_mb' => round($peakMemoryUsage, 2)
];
}
public function getTopSlowJobs(int $limit = 10): array {
$jobs = $this->jobMetrics;
usort($jobs, fn($a, $b) => $b->executionTimeMs <=> $a->executionTimeMs);
return array_slice($jobs, 0, $limit);
}
public function getTopMemoryJobs(int $limit = 10): array {
$jobs = $this->jobMetrics;
usort($jobs, fn($a, $b) => $b->memoryUsageBytes <=> $a->memoryUsageBytes);
return array_slice($jobs, 0, $limit);
}
public function getJobsByQueue(string $queueName): array {
return array_filter($this->jobMetrics, fn($metrics) => $metrics->queueName === $queueName);
}
};
});
describe('Job Metrics Recording', function () {
it('can record successful job execution', function () {
$jobId = 'success-job-123';
$executionTime = 2500.0; // 2.5 seconds
$memoryUsage = 3 * 1024 * 1024; // 3MB
$this->metricsManager->recordJobExecution($jobId, $executionTime, $memoryUsage);
$metrics = $this->metricsManager->getJobMetrics($jobId);
expect($metrics)->not->toBeNull();
expect($metrics->isCompleted())->toBeTrue();
expect($metrics->executionTimeMs)->toBe($executionTime);
expect($metrics->memoryUsageBytes)->toBe($memoryUsage);
});
it('can record failed job execution', function () {
$jobId = 'failed-job-456';
$errorMessage = 'Database connection timeout';
$executionTime = 1200.0; // 1.2 seconds
$memoryUsage = 1.5 * 1024 * 1024; // 1.5MB
$this->metricsManager->recordJobFailure($jobId, $errorMessage, $executionTime, $memoryUsage);
$metrics = $this->metricsManager->getJobMetrics($jobId);
expect($metrics)->not->toBeNull();
expect($metrics->isFailed())->toBeTrue();
expect($metrics->errorMessage)->toBe($errorMessage);
expect($metrics->executionTimeMs)->toBe($executionTime);
expect($metrics->memoryUsageBytes)->toBe($memoryUsage);
});
it('handles non-existent job metrics', function () {
$metrics = $this->metricsManager->getJobMetrics('non-existent-job');
expect($metrics)->toBeNull();
});
});
describe('Queue-Level Metrics', function () {
beforeEach(function () {
// Set up test data with different queue metrics
// Email queue jobs
$this->metricsManager->recordJobExecution('email-1', 1500.0, 2 * 1024 * 1024);
$this->metricsManager->recordJobExecution('email-2', 2000.0, 2.5 * 1024 * 1024);
$this->metricsManager->recordJobFailure('email-3', 'SMTP error', 800.0, 1 * 1024 * 1024);
// Report queue jobs
$this->metricsManager->recordJobExecution('report-1', 5000.0, 10 * 1024 * 1024);
$this->metricsManager->recordJobExecution('report-2', 7500.0, 15 * 1024 * 1024);
});
it('calculates queue metrics correctly', function () {
$emailMetrics = $this->metricsManager->getQueueMetrics('email-queue');
expect($emailMetrics['queue_name'])->toBe('email-queue');
expect($emailMetrics['total_jobs'])->toBe(3);
expect($emailMetrics['completed_jobs'])->toBe(2);
expect($emailMetrics['failed_jobs'])->toBe(1);
expect($emailMetrics['success_rate'])->toBe(66.67); // 2/3 * 100
});
it('handles empty queue metrics', function () {
$emptyMetrics = $this->metricsManager->getQueueMetrics('empty-queue');
expect($emptyMetrics['total_jobs'])->toBe(0);
expect($emptyMetrics['completed_jobs'])->toBe(0);
expect($emptyMetrics['failed_jobs'])->toBe(0);
expect($emptyMetrics['success_rate'])->toBe(100.0);
expect($emptyMetrics['average_execution_time_ms'])->toBe(0.0);
});
});
describe('System-Level Metrics', function () {
beforeEach(function () {
// Add various jobs across different queues
$this->metricsManager->recordJobExecution('job-1', 1000.0, 1 * 1024 * 1024);
$this->metricsManager->recordJobExecution('job-2', 2000.0, 2 * 1024 * 1024);
$this->metricsManager->recordJobExecution('job-3', 3000.0, 4 * 1024 * 1024);
$this->metricsManager->recordJobFailure('job-4', 'Error', 500.0, 0.5 * 1024 * 1024);
$this->metricsManager->recordJobFailure('job-5', 'Error', 750.0, 1 * 1024 * 1024);
});
it('calculates system-wide metrics correctly', function () {
$systemMetrics = $this->metricsManager->getSystemMetrics();
expect($systemMetrics['total_jobs'])->toBe(5);
expect($systemMetrics['completed_jobs'])->toBe(3);
expect($systemMetrics['failed_jobs'])->toBe(2);
expect($systemMetrics['overall_success_rate'])->toBe(60.0); // 3/5 * 100
expect($systemMetrics['average_execution_time_ms'])->toBe(1450.0); // (1000+2000+3000+500+750)/5
expect($systemMetrics['peak_memory_usage_mb'])->toBe(4.0); // Highest from job-3
});
it('handles empty system gracefully', function () {
$emptyManager = new class {
private array $jobMetrics = [];
public function getSystemMetrics(): array {
return [
'total_jobs' => 0,
'completed_jobs' => 0,
'failed_jobs' => 0,
'running_jobs' => 0,
'overall_success_rate' => 100.0,
'average_execution_time_ms' => 0.0,
'peak_memory_usage_mb' => 0.0
];
}
};
$systemMetrics = $emptyManager->getSystemMetrics();
expect($systemMetrics['total_jobs'])->toBe(0);
expect($systemMetrics['overall_success_rate'])->toBe(100.0);
});
});
describe('Performance Analysis', function () {
beforeEach(function () {
// Add jobs with varying performance characteristics
$this->metricsManager->recordJobExecution('fast-job', 100.0, 0.5 * 1024 * 1024);
$this->metricsManager->recordJobExecution('medium-job', 2500.0, 2 * 1024 * 1024);
$this->metricsManager->recordJobExecution('slow-job', 10000.0, 1 * 1024 * 1024);
$this->metricsManager->recordJobExecution('memory-heavy', 1500.0, 50 * 1024 * 1024);
$this->metricsManager->recordJobExecution('balanced-job', 3000.0, 3 * 1024 * 1024);
});
it('identifies slowest jobs correctly', function () {
$slowJobs = $this->metricsManager->getTopSlowJobs(3);
expect(count($slowJobs))->toBe(3);
expect($slowJobs[0]->jobId)->toBe('slow-job');
expect($slowJobs[1]->jobId)->toBe('balanced-job');
expect($slowJobs[2]->jobId)->toBe('medium-job');
});
it('identifies memory-intensive jobs correctly', function () {
$memoryJobs = $this->metricsManager->getTopMemoryJobs(3);
expect(count($memoryJobs))->toBe(3);
expect($memoryJobs[0]->jobId)->toBe('memory-heavy');
expect($memoryJobs[1]->jobId)->toBe('balanced-job');
expect($memoryJobs[2]->jobId)->toBe('medium-job');
});
it('respects limit parameter for top jobs', function () {
$limitedSlowJobs = $this->metricsManager->getTopSlowJobs(2);
expect(count($limitedSlowJobs))->toBe(2);
$limitedMemoryJobs = $this->metricsManager->getTopMemoryJobs(1);
expect(count($limitedMemoryJobs))->toBe(1);
});
});
describe('Queue Filtering and Analysis', function () {
beforeEach(function () {
// This would require modifying the mock to support different queues
// For now, we'll test the interface
});
it('can retrieve jobs by queue', function () {
$this->metricsManager->recordJobExecution('email-1', 1000.0, 1024 * 1024);
$this->metricsManager->recordJobExecution('email-2', 2000.0, 2048 * 1024);
$queueJobs = $this->metricsManager->getJobsByQueue('default-queue');
expect(count($queueJobs))->toBe(2);
});
it('handles non-existent queue filtering', function () {
$emptyQueue = $this->metricsManager->getJobsByQueue('non-existent-queue');
expect($emptyQueue)->toBe([]);
});
});
});