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,180 @@
<?php
declare(strict_types=1);
namespace Tests\Framework\Health\ValueObjects;
use App\Framework\Health\ValueObjects\HealthDetails;
use InvalidArgumentException;
describe('HealthDetails Value Object', function () {
it('can be created empty', function () {
$details = HealthDetails::empty();
expect($details->isEmpty())->toBeTrue();
expect($details->count())->toBe(0);
expect($details->toArray())->toBe([]);
});
it('can be created from array', function () {
$data = ['host' => 'localhost', 'port' => 3306];
$details = HealthDetails::fromArray($data);
expect($details->isEmpty())->toBeFalse();
expect($details->count())->toBe(2);
expect($details->get('host'))->toBe('localhost');
expect($details->get('port'))->toBe(3306);
});
it('can add and remove details immutably', function () {
$details = HealthDetails::empty();
$newDetails = $details->with('key', 'value');
expect($details->has('key'))->toBeFalse(); // Original unchanged
expect($newDetails->has('key'))->toBeTrue();
expect($newDetails->get('key'))->toBe('value');
$withoutKey = $newDetails->without('key');
expect($withoutKey->has('key'))->toBeFalse();
expect($newDetails->has('key'))->toBeTrue(); // Original unchanged
});
it('gets values with defaults', function () {
$details = HealthDetails::fromArray(['existing' => 'value']);
expect($details->get('existing'))->toBe('value');
expect($details->get('missing'))->toBeNull();
expect($details->get('missing', 'default'))->toBe('default');
});
it('creates database details correctly', function () {
$details = HealthDetails::forDatabase('localhost', 3306, 'testdb', 25.5, 10);
expect($details->get('host'))->toBe('localhost');
expect($details->get('port'))->toBe(3306);
expect($details->get('database'))->toBe('testdb');
expect($details->get('connection_time_ms'))->toBe(25.5);
expect($details->get('active_connections'))->toBe(10);
});
it('creates cache details correctly', function () {
$details = HealthDetails::forCache('redis', 95, 1000, '128MB');
expect($details->get('driver'))->toBe('redis');
expect($details->get('hit_rate_percent'))->toBe(95);
expect($details->get('total_keys'))->toBe(1000);
expect($details->get('memory_usage'))->toBe('128MB');
});
it('creates disk details correctly', function () {
$details = HealthDetails::forDisk('/var/log', '100GB', '25GB', 75.0);
expect($details->get('path'))->toBe('/var/log');
expect($details->get('total_space'))->toBe('100GB');
expect($details->get('free_space'))->toBe('25GB');
expect($details->get('usage_percent'))->toBe(75.0);
});
it('creates system details correctly', function () {
$details = HealthDetails::forSystem(45.2, '8GB/16GB', 1.5, '5 days');
expect($details->get('cpu_usage_percent'))->toBe(45.2);
expect($details->get('memory_usage'))->toBe('8GB/16GB');
expect($details->get('load_average'))->toBe(1.5);
expect($details->get('uptime'))->toBe('5 days');
});
it('creates API details correctly', function () {
$details = HealthDetails::forApi('/health', 200, 150.5, 'v1.2.3');
expect($details->get('endpoint'))->toBe('/health');
expect($details->get('status_code'))->toBe(200);
expect($details->get('response_time_ms'))->toBe(150.5);
expect($details->get('version'))->toBe('v1.2.3');
});
it('creates error details correctly', function () {
$exception = new \RuntimeException('Test error', 500);
$details = HealthDetails::forError($exception);
expect($details->get('error_type'))->toBe('RuntimeException');
expect($details->get('error_message'))->toBe('Test error');
expect($details->get('error_code'))->toBe(500);
expect($details->get('error_file'))->toBeString();
expect($details->get('error_line'))->toBeInt();
});
it('can merge details', function () {
$details1 = HealthDetails::fromArray(['key1' => 'value1']);
$details2 = HealthDetails::fromArray(['key2' => 'value2']);
$merged = $details1->merge($details2);
expect($merged->get('key1'))->toBe('value1');
expect($merged->get('key2'))->toBe('value2');
expect($merged->count())->toBe(2);
});
it('identifies sensitive keys', function () {
$details = HealthDetails::fromArray([
'username' => 'john',
'password' => 'secret123',
'api_key' => 'abc123',
'database_token' => 'xyz789',
'normal_field' => 'value',
]);
$sensitiveKeys = $details->getSensitiveKeys();
expect($sensitiveKeys)->toContain('password');
expect($sensitiveKeys)->toContain('api_key');
expect($sensitiveKeys)->toContain('database_token');
expect($sensitiveKeys)->not->toContain('username');
expect($sensitiveKeys)->not->toContain('normal_field');
});
it('sanitizes sensitive data', function () {
$details = HealthDetails::fromArray([
'username' => 'john',
'password' => 'secret123',
'normal_field' => 'value',
]);
$sanitized = $details->toSanitizedArray();
expect($sanitized['username'])->toBe('john');
expect($sanitized['password'])->toBe('[REDACTED]');
expect($sanitized['normal_field'])->toBe('value');
});
it('throws exception for non-string keys', function () {
expect(fn () => HealthDetails::fromArray([123 => 'value']))->toThrow(InvalidArgumentException::class);
});
it('throws exception for empty keys', function () {
expect(fn () => HealthDetails::fromArray(['' => 'value']))->toThrow(InvalidArgumentException::class);
expect(fn () => HealthDetails::fromArray([' ' => 'value']))->toThrow(InvalidArgumentException::class);
});
it('throws exception for resource values', function () {
$resource = fopen('php://memory', 'r');
expect(fn () => HealthDetails::fromArray(['resource' => $resource]))->toThrow(InvalidArgumentException::class);
fclose($resource);
});
it('allows scalar and null values', function () {
$details = HealthDetails::fromArray([
'string' => 'text',
'int' => 123,
'float' => 12.34,
'bool' => true,
'null' => null,
]);
expect($details->get('string'))->toBe('text');
expect($details->get('int'))->toBe(123);
expect($details->get('float'))->toBe(12.34);
expect($details->get('bool'))->toBeTrue();
expect($details->get('null'))->toBeNull();
});
});

View File

@@ -0,0 +1,128 @@
<?php
declare(strict_types=1);
namespace Tests\Framework\Health\ValueObjects;
use App\Framework\Health\ValueObjects\HealthMessage;
use App\Framework\Health\ValueObjects\HealthMessageType;
use InvalidArgumentException;
describe('HealthMessage Value Object', function () {
it('can be created with message and type', function () {
$message = new HealthMessage('Database connection successful', HealthMessageType::SUCCESS);
expect($message->message)->toBe('Database connection successful');
expect($message->type)->toBe(HealthMessageType::SUCCESS);
});
it('can be created with plain message', function () {
$message = HealthMessage::plain('Simple message');
expect($message->message)->toBe('Simple message');
expect($message->type)->toBe(HealthMessageType::PLAIN);
});
it('can create success messages', function () {
$message = HealthMessage::success('Database');
expect($message->message)->toBe('Database is working correctly');
expect($message->type)->toBe(HealthMessageType::SUCCESS);
expect($message->isSuccess())->toBeTrue();
});
it('can create warning messages', function () {
$message = HealthMessage::warning('Cache', 'High memory usage');
expect($message->message)->toBe('Cache has issues: High memory usage');
expect($message->type)->toBe(HealthMessageType::WARNING);
expect($message->isWarning())->toBeTrue();
});
it('can create failure messages', function () {
$message = HealthMessage::failure('API', 'Connection refused');
expect($message->message)->toBe('API failed: Connection refused');
expect($message->type)->toBe(HealthMessageType::FAILURE);
expect($message->isError())->toBeTrue();
});
it('can create timeout messages', function () {
$message = HealthMessage::timeout('Service', 5000.0);
expect($message->message)->toBe('Service timed out after 5000ms');
expect($message->type)->toBe(HealthMessageType::TIMEOUT);
expect($message->isError())->toBeTrue();
});
it('can create degraded messages', function () {
$message = HealthMessage::degraded('Queue', 'Processing slowly');
expect($message->message)->toBe('Queue is degraded: Processing slowly');
expect($message->type)->toBe(HealthMessageType::DEGRADED);
expect($message->isWarning())->toBeTrue();
});
it('formats messages with emoji correctly', function () {
$success = HealthMessage::success('Test');
$warning = HealthMessage::warning('Test', 'Issue');
$failure = HealthMessage::failure('Test', 'Error');
expect($success->toFormattedString())->toBe('✓ Test is working correctly');
expect($warning->toFormattedString())->toBe('⚠ Test has issues: Issue');
expect($failure->toFormattedString())->toBe('✗ Test failed: Error');
});
it('can add prefix and suffix', function () {
$message = HealthMessage::success('Database');
$withPrefix = $message->withPrefix('MySQL');
expect($withPrefix->message)->toBe('MySQL: Database is working correctly');
expect($withPrefix->type)->toBe(HealthMessageType::SUCCESS);
$withSuffix = $message->withSuffix('port 3306');
expect($withSuffix->message)->toBe('Database is working correctly - port 3306');
});
it('can change type', function () {
$message = HealthMessage::plain('Database status');
$warningMessage = $message->withType(HealthMessageType::WARNING);
expect($warningMessage->message)->toBe('Database status');
expect($warningMessage->type)->toBe(HealthMessageType::WARNING);
expect($warningMessage->isWarning())->toBeTrue();
});
it('converts to array correctly', function () {
$message = HealthMessage::success('Test service');
$array = $message->toArray();
expect($array)->toHaveKey('message');
expect($array)->toHaveKey('type');
expect($array)->toHaveKey('formatted');
expect($array)->toHaveKey('severity');
expect($array)->toHaveKey('emoji');
expect($array['message'])->toBe('Test service is working correctly');
expect($array['type'])->toBe('success');
expect($array['severity'])->toBe(0);
expect($array['emoji'])->toBe('✓');
});
it('throws exception for empty message', function () {
expect(fn () => new HealthMessage(''))->toThrow(InvalidArgumentException::class);
expect(fn () => new HealthMessage(' '))->toThrow(InvalidArgumentException::class);
});
it('throws exception for too long message', function () {
$longMessage = str_repeat('a', 501);
expect(fn () => new HealthMessage($longMessage))->toThrow(InvalidArgumentException::class);
});
it('converts to string correctly', function () {
$message = HealthMessage::success('Test');
expect($message->toString())->toBe('Test is working correctly');
expect((string) $message)->toBe('Test is working correctly');
});
});

View File

@@ -0,0 +1,143 @@
<?php
declare(strict_types=1);
namespace Tests\Framework\Health\ValueObjects;
use App\Framework\Core\ValueObjects\Duration;
use App\Framework\Health\ValueObjects\ResponseTime;
use App\Framework\Health\ValueObjects\ResponseTimeLevel;
use InvalidArgumentException;
describe('ResponseTime Value Object', function () {
it('can be created from Duration', function () {
$duration = Duration::fromMilliseconds(150);
$responseTime = ResponseTime::fromDuration($duration);
expect($responseTime->duration->toMilliseconds())->toBe(150.0);
});
it('can be created from seconds', function () {
$responseTime = ResponseTime::fromSeconds(0.5);
expect($responseTime->duration->toSeconds())->toBe(0.5);
expect($responseTime->duration->toMilliseconds())->toBe(500.0);
});
it('can be created from milliseconds', function () {
$responseTime = ResponseTime::fromMilliseconds(250);
expect($responseTime->duration->toMilliseconds())->toBe(250.0);
});
it('can measure execution time', function () {
$responseTime = ResponseTime::measure(function () {
usleep(10000); // 10ms
});
expect($responseTime->duration->toMilliseconds())->toBeGreaterThan(5);
expect($responseTime->duration->toMilliseconds())->toBeLessThan(50); // Allow some variance
});
it('can be created from start time', function () {
$startTime = microtime(true);
usleep(5000); // 5ms
$responseTime = ResponseTime::fromStartTime($startTime);
expect($responseTime->duration->toMilliseconds())->toBeGreaterThan(3);
expect($responseTime->duration->toMilliseconds())->toBeLessThan(20);
});
it('creates zero response time', function () {
$responseTime = ResponseTime::zero();
expect($responseTime->duration->isZero())->toBeTrue();
expect($responseTime->duration->toMilliseconds())->toBe(0.0);
});
it('classifies performance levels correctly', function () {
$fast = ResponseTime::fromMilliseconds(50);
$acceptable = ResponseTime::fromMilliseconds(300);
$slow = ResponseTime::fromMilliseconds(1000);
$verySlow = ResponseTime::fromMilliseconds(3000);
expect($fast->isFast())->toBeTrue();
expect($fast->getPerformanceLevel())->toBe(ResponseTimeLevel::FAST);
expect($acceptable->isAcceptable())->toBeTrue();
expect($acceptable->getPerformanceLevel())->toBe(ResponseTimeLevel::ACCEPTABLE);
expect($slow->isSlow())->toBeTrue();
expect($slow->getPerformanceLevel())->toBe(ResponseTimeLevel::SLOW);
expect($verySlow->isVerySlow())->toBeTrue();
expect($verySlow->getPerformanceLevel())->toBe(ResponseTimeLevel::VERY_SLOW);
});
it('checks threshold compliance', function () {
$responseTime = ResponseTime::fromMilliseconds(300);
$threshold = Duration::fromMilliseconds(500);
$strictThreshold = Duration::fromMilliseconds(200);
expect($responseTime->isWithinThreshold($threshold))->toBeTrue();
expect($responseTime->exceedsThreshold($threshold))->toBeFalse();
expect($responseTime->isWithinThreshold($strictThreshold))->toBeFalse();
expect($responseTime->exceedsThreshold($strictThreshold))->toBeTrue();
});
it('compares response times correctly', function () {
$fast = ResponseTime::fromMilliseconds(100);
$slow = ResponseTime::fromMilliseconds(500);
$same = ResponseTime::fromMilliseconds(100);
expect($slow->isSlowerThan($fast))->toBeTrue();
expect($fast->isFasterThan($slow))->toBeTrue();
expect($fast->equals($same))->toBeTrue();
expect($fast->equals($slow))->toBeFalse();
});
it('formats to string correctly', function () {
$fast = ResponseTime::fromMilliseconds(50);
$slow = ResponseTime::fromSeconds(2.5);
expect($fast->toString())->toBe('50 ms');
expect($slow->toString())->toBe('2.5 s');
expect((string) $fast)->toBe('50 ms');
});
it('converts to array correctly', function () {
$responseTime = ResponseTime::fromMilliseconds(750);
$array = $responseTime->toArray();
expect($array)->toHaveKey('milliseconds');
expect($array)->toHaveKey('seconds');
expect($array)->toHaveKey('formatted');
expect($array)->toHaveKey('level');
expect($array)->toHaveKey('is_fast');
expect($array)->toHaveKey('is_acceptable');
expect($array)->toHaveKey('is_slow');
expect($array)->toHaveKey('is_very_slow');
expect($array['milliseconds'])->toBe(750.0);
expect($array['seconds'])->toBe(0.75);
expect($array['level'])->toBe('slow');
expect($array['is_slow'])->toBeTrue();
expect($array['is_fast'])->toBeFalse();
});
it('throws exception for excessive response time', function () {
$excessiveDuration = Duration::fromSeconds(400); // > 5 minutes
expect(fn () => ResponseTime::fromDuration($excessiveDuration))
->toThrow(InvalidArgumentException::class);
});
it('allows response times up to 5 minutes', function () {
$maxDuration = Duration::fromSeconds(300); // exactly 5 minutes
$responseTime = ResponseTime::fromDuration($maxDuration);
expect($responseTime->duration->toSeconds())->toBe(300.0);
expect($responseTime->duration->toMilliseconds())->toBe(300000.0);
});
});