- 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.
248 lines
8.8 KiB
PHP
248 lines
8.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Framework\Exception\ExceptionMetadata;
|
|
|
|
describe('ExceptionMetadata', function () {
|
|
describe('Factory Methods', function () {
|
|
it('creates default metadata', function () {
|
|
$metadata = ExceptionMetadata::default();
|
|
|
|
expect($metadata->skipAggregation)->toBeFalse();
|
|
expect($metadata->skipReporting)->toBeFalse();
|
|
expect($metadata->reported)->toBeFalse();
|
|
expect($metadata->retryAfter)->toBeNull();
|
|
});
|
|
|
|
it('creates metadata with retry', function () {
|
|
$metadata = ExceptionMetadata::withRetry(60);
|
|
|
|
expect($metadata->retryAfter)->toBe(60);
|
|
expect($metadata->skipAggregation)->toBeFalse();
|
|
});
|
|
});
|
|
|
|
describe('Immutability', function () {
|
|
it('withSkipAggregation creates new instance', function () {
|
|
$original = ExceptionMetadata::default();
|
|
$modified = $original->withSkipAggregation();
|
|
|
|
expect($original)->not->toBe($modified);
|
|
expect($original->skipAggregation)->toBeFalse();
|
|
expect($modified->skipAggregation)->toBeTrue();
|
|
});
|
|
|
|
it('withSkipReporting creates new instance', function () {
|
|
$original = ExceptionMetadata::default();
|
|
$modified = $original->withSkipReporting();
|
|
|
|
expect($original->skipReporting)->toBeFalse();
|
|
expect($modified->skipReporting)->toBeTrue();
|
|
});
|
|
|
|
it('withSkipAll creates new instance', function () {
|
|
$original = ExceptionMetadata::default();
|
|
$modified = $original->withSkipAll();
|
|
|
|
expect($modified->skipAggregation)->toBeTrue();
|
|
expect($modified->skipReporting)->toBeTrue();
|
|
});
|
|
|
|
it('markAsReported creates new instance', function () {
|
|
$original = ExceptionMetadata::default();
|
|
$modified = $original->markAsReported();
|
|
|
|
expect($original->reported)->toBeFalse();
|
|
expect($modified->reported)->toBeTrue();
|
|
});
|
|
|
|
it('withRetryAfter creates new instance', function () {
|
|
$original = ExceptionMetadata::default();
|
|
$modified = $original->withRetryAfter(30);
|
|
|
|
expect($original->retryAfter)->toBeNull();
|
|
expect($modified->retryAfter)->toBe(30);
|
|
});
|
|
});
|
|
|
|
describe('Behavior Control', function () {
|
|
it('shouldAggregate returns correct value', function () {
|
|
$default = ExceptionMetadata::default();
|
|
$skipped = $default->withSkipAggregation();
|
|
|
|
expect($default->shouldAggregate())->toBeTrue();
|
|
expect($skipped->shouldAggregate())->toBeFalse();
|
|
});
|
|
|
|
it('shouldReport considers both flags', function () {
|
|
$default = ExceptionMetadata::default();
|
|
$skipReporting = $default->withSkipReporting();
|
|
$reported = $default->markAsReported();
|
|
|
|
expect($default->shouldReport())->toBeTrue();
|
|
expect($skipReporting->shouldReport())->toBeFalse();
|
|
expect($reported->shouldReport())->toBeFalse();
|
|
});
|
|
|
|
it('wasReported returns correct value', function () {
|
|
$notReported = ExceptionMetadata::default();
|
|
$reported = $notReported->markAsReported();
|
|
|
|
expect($notReported->wasReported())->toBeFalse();
|
|
expect($reported->wasReported())->toBeTrue();
|
|
});
|
|
});
|
|
|
|
describe('Chaining', function () {
|
|
it('chains multiple modifications', function () {
|
|
$metadata = ExceptionMetadata::default()
|
|
->withSkipAggregation()
|
|
->withRetryAfter(60);
|
|
|
|
expect($metadata->skipAggregation)->toBeTrue();
|
|
expect($metadata->retryAfter)->toBe(60);
|
|
expect($metadata->skipReporting)->toBeFalse();
|
|
});
|
|
|
|
it('preserves existing values when chaining', function () {
|
|
$metadata = ExceptionMetadata::withRetry(30)
|
|
->withSkipReporting();
|
|
|
|
expect($metadata->retryAfter)->toBe(30);
|
|
expect($metadata->skipReporting)->toBeTrue();
|
|
expect($metadata->skipAggregation)->toBeFalse();
|
|
});
|
|
});
|
|
|
|
describe('Serialization', function () {
|
|
it('converts to array', function () {
|
|
$metadata = ExceptionMetadata::default()
|
|
->withSkipAggregation()
|
|
->withRetryAfter(60);
|
|
|
|
$array = $metadata->toArray();
|
|
|
|
expect($array)->toBe([
|
|
'skip_aggregation' => true,
|
|
'skip_reporting' => false,
|
|
'reported' => false,
|
|
'retry_after' => 60,
|
|
]);
|
|
});
|
|
|
|
it('creates from array', function () {
|
|
$array = [
|
|
'skip_aggregation' => true,
|
|
'skip_reporting' => true,
|
|
'reported' => false,
|
|
'retry_after' => 30,
|
|
];
|
|
|
|
$metadata = ExceptionMetadata::fromArray($array);
|
|
|
|
expect($metadata->skipAggregation)->toBeTrue();
|
|
expect($metadata->skipReporting)->toBeTrue();
|
|
expect($metadata->reported)->toBeFalse();
|
|
expect($metadata->retryAfter)->toBe(30);
|
|
});
|
|
|
|
it('handles missing array keys with defaults', function () {
|
|
$metadata = ExceptionMetadata::fromArray([]);
|
|
|
|
expect($metadata->skipAggregation)->toBeFalse();
|
|
expect($metadata->skipReporting)->toBeFalse();
|
|
expect($metadata->reported)->toBeFalse();
|
|
expect($metadata->retryAfter)->toBeNull();
|
|
});
|
|
|
|
it('roundtrips through array', function () {
|
|
$original = ExceptionMetadata::default()
|
|
->withSkipAll()
|
|
->withRetryAfter(120)
|
|
->markAsReported();
|
|
|
|
$array = $original->toArray();
|
|
$restored = ExceptionMetadata::fromArray($array);
|
|
|
|
expect($restored->skipAggregation)->toBe($original->skipAggregation);
|
|
expect($restored->skipReporting)->toBe($original->skipReporting);
|
|
expect($restored->reported)->toBe($original->reported);
|
|
expect($restored->retryAfter)->toBe($original->retryAfter);
|
|
});
|
|
});
|
|
|
|
describe('Edge Cases', function () {
|
|
it('handles null retry after', function () {
|
|
$metadata = ExceptionMetadata::default()
|
|
->withRetryAfter(60)
|
|
->withRetryAfter(null);
|
|
|
|
expect($metadata->retryAfter)->toBeNull();
|
|
});
|
|
|
|
it('handles zero retry after', function () {
|
|
$metadata = ExceptionMetadata::withRetry(0);
|
|
|
|
expect($metadata->retryAfter)->toBe(0);
|
|
});
|
|
|
|
it('handles negative retry after', function () {
|
|
$metadata = ExceptionMetadata::withRetry(-1);
|
|
|
|
expect($metadata->retryAfter)->toBe(-1);
|
|
});
|
|
|
|
it('marking as reported multiple times is idempotent', function () {
|
|
$metadata = ExceptionMetadata::default()
|
|
->markAsReported()
|
|
->markAsReported();
|
|
|
|
expect($metadata->reported)->toBeTrue();
|
|
expect($metadata->shouldReport())->toBeFalse();
|
|
});
|
|
});
|
|
|
|
describe('Integration Scenarios', function () {
|
|
it('supports typical exception lifecycle', function () {
|
|
// 1. Create exception with default metadata
|
|
$metadata = ExceptionMetadata::default();
|
|
expect($metadata->shouldReport())->toBeTrue();
|
|
expect($metadata->shouldAggregate())->toBeTrue();
|
|
|
|
// 2. Skip aggregation for specific case
|
|
$metadata = $metadata->withSkipAggregation();
|
|
expect($metadata->shouldAggregate())->toBeFalse();
|
|
expect($metadata->shouldReport())->toBeTrue();
|
|
|
|
// 3. Mark as reported after logging
|
|
$metadata = $metadata->markAsReported();
|
|
expect($metadata->wasReported())->toBeTrue();
|
|
expect($metadata->shouldReport())->toBeFalse();
|
|
});
|
|
|
|
it('supports retry scenario', function () {
|
|
// 1. Create with retry hint
|
|
$metadata = ExceptionMetadata::withRetry(60);
|
|
expect($metadata->retryAfter)->toBe(60);
|
|
|
|
// 2. Update retry time
|
|
$metadata = $metadata->withRetryAfter(30);
|
|
expect($metadata->retryAfter)->toBe(30);
|
|
|
|
// 3. Clear retry
|
|
$metadata = $metadata->withRetryAfter(null);
|
|
expect($metadata->retryAfter)->toBeNull();
|
|
});
|
|
|
|
it('supports skip all for internal exceptions', function () {
|
|
$metadata = ExceptionMetadata::default()
|
|
->withSkipAll();
|
|
|
|
expect($metadata->shouldAggregate())->toBeFalse();
|
|
expect($metadata->shouldReport())->toBeFalse();
|
|
expect($metadata->wasReported())->toBeFalse();
|
|
});
|
|
});
|
|
});
|