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,191 @@
<?php
declare(strict_types=1);
namespace Tests\Framework\Exception;
use App\Framework\Exception\ErrorCode;
use App\Framework\Exception\ExceptionContext;
use App\Framework\Exception\FrameworkException;
describe('FrameworkException', function () {
it('can be created with basic message and context', function () {
$context = ExceptionContext::empty();
$exception = new FrameworkException(
message: 'Test error',
context: $context
);
expect($exception->getMessage())->toBe('Test error');
expect($exception->getContext())->toBe($context);
expect($exception->getErrorCode())->toBeNull();
expect($exception->getRetryAfter())->toBeNull();
expect($exception->isRecoverable())->toBeFalse();
});
it('can be created with ErrorCode', function () {
$context = ExceptionContext::empty();
$exception = new FrameworkException(
message: 'Database error',
context: $context,
errorCode: ErrorCode::DB_CONNECTION_FAILED
);
expect($exception->getMessage())->toBe('Database error');
expect($exception->getErrorCode())->toBe(ErrorCode::DB_CONNECTION_FAILED);
expect($exception->isErrorCode(ErrorCode::DB_CONNECTION_FAILED))->toBeTrue();
expect($exception->isErrorCode(ErrorCode::AUTH_TOKEN_EXPIRED))->toBeFalse();
expect($exception->isCategory('DB'))->toBeTrue();
expect($exception->isCategory('AUTH'))->toBeFalse();
});
it('supports simple factory method', function () {
$exception = FrameworkException::simple('Simple error message');
expect($exception->getMessage())->toBe('Simple error message');
expect($exception->getErrorCode())->toBeNull();
expect($exception->getContext()->toArray())->toHaveKey('operation');
});
it('supports create factory method with ErrorCode', function () {
$exception = FrameworkException::create(
ErrorCode::VAL_BUSINESS_RULE_VIOLATION,
'Custom validation error'
);
expect($exception->getMessage())->toBe('Custom validation error');
expect($exception->getErrorCode())->toBe(ErrorCode::VAL_BUSINESS_RULE_VIOLATION);
expect($exception->isCategory('VAL'))->toBeTrue();
});
it('supports forOperation factory method', function () {
$exception = FrameworkException::forOperation(
operation: 'user.create',
component: 'UserService',
message: 'Failed to create user',
errorCode: ErrorCode::VAL_BUSINESS_RULE_VIOLATION
);
expect($exception->getMessage())->toBe('Failed to create user');
expect($exception->getErrorCode())->toBe(ErrorCode::VAL_BUSINESS_RULE_VIOLATION);
expect($exception->getContext()->operation)->toBe('user.create');
expect($exception->getContext()->component)->toBe('UserService');
});
it('supports fromContext factory method', function () {
$context = ExceptionContext::forOperation('payment.process', 'PaymentService')
->withData(['amount' => 100]);
$exception = FrameworkException::fromContext(
message: 'Payment failed',
context: $context,
errorCode: ErrorCode::PAYMENT_GATEWAY_ERROR
);
expect($exception->getMessage())->toBe('Payment failed');
expect($exception->getContext())->toBe($context);
expect($exception->getData())->toBe(['amount' => 100]);
});
it('supports immutable transformations', function () {
$original = FrameworkException::simple('Original message');
$withData = $original->withData(['key' => 'value']);
$withOperation = $withData->withOperation('test.operation', 'TestComponent');
$withDebug = $withOperation->withDebug(['debug' => 'info']);
$withMetadata = $withDebug->withMetadata(['meta' => 'data']);
// Original should be unchanged
expect($original->getData())->toBe([]);
expect($original->getContext()->operation)->toBeNull();
// New instance should have all data
expect($withMetadata->getData())->toBe(['key' => 'value']);
expect($withMetadata->getContext()->operation)->toBe('test.operation');
expect($withMetadata->getContext()->component)->toBe('TestComponent');
expect($withMetadata->getContext()->debug)->toBe(['debug' => 'info']);
expect($withMetadata->getContext()->metadata)->toBe(['meta' => 'data']);
});
it('supports ErrorCode modifications', function () {
$exception = FrameworkException::simple('Test error');
expect($exception->getErrorCode())->toBeNull();
$withErrorCode = $exception->withErrorCode(ErrorCode::DB_QUERY_FAILED);
expect($withErrorCode->getErrorCode())->toBe(ErrorCode::DB_QUERY_FAILED);
expect($withErrorCode->isCategory('DB'))->toBeTrue();
// Original should be unchanged
expect($exception->getErrorCode())->toBeNull();
});
it('supports retry after modifications', function () {
$exception = FrameworkException::create(ErrorCode::HTTP_RATE_LIMIT_EXCEEDED);
$withRetry = $exception->withRetryAfter(300);
expect($withRetry->getRetryAfter())->toBe(300);
expect($withRetry->isRecoverable())->toBeTrue();
});
it('converts to array with all relevant data', function () {
$context = ExceptionContext::forOperation('test.operation', 'TestComponent')
->withData(['test' => 'data'])
->withDebug(['debug' => 'info']);
$exception = FrameworkException::fromContext(
message: 'Test exception',
context: $context,
errorCode: ErrorCode::VAL_BUSINESS_RULE_VIOLATION
);
$array = $exception->toArray();
expect($array)->toHaveKey('class');
expect($array)->toHaveKey('message');
expect($array)->toHaveKey('context');
expect($array)->toHaveKey('error_code');
expect($array)->toHaveKey('error_category');
expect($array)->toHaveKey('description');
expect($array)->toHaveKey('recovery_hint');
expect($array)->toHaveKey('is_recoverable');
expect($array['message'])->toBe('Test exception');
expect($array['error_code'])->toBe(ErrorCode::VAL_BUSINESS_RULE_VIOLATION->value);
expect($array['error_category'])->toBe('VAL');
});
it('handles string representation correctly', function () {
$exception = FrameworkException::create(
ErrorCode::DB_CONNECTION_FAILED,
'Connection failed'
);
$string = (string) $exception;
expect($string)->toContain('FrameworkException');
expect($string)->toContain('[DB001]');
expect($string)->toContain('Connection failed');
expect($string)->toMatch('/\.php:\d+$/');
});
it('supports chaining operations fluently', function () {
$exception = FrameworkException::simple('Base error')
->withErrorCode(ErrorCode::VAL_BUSINESS_RULE_VIOLATION)
->withOperation('user.validate', 'UserValidator')
->withData(['field' => 'email', 'value' => 'invalid'])
->withDebug(['rule' => 'email_format'])
->withMetadata(['attempt' => 1])
->withRetryAfter(60);
expect($exception->getMessage())->toBe('Base error');
expect($exception->getErrorCode())->toBe(ErrorCode::VAL_BUSINESS_RULE_VIOLATION);
expect($exception->getContext()->operation)->toBe('user.validate');
expect($exception->getContext()->component)->toBe('UserValidator');
expect($exception->getData())->toBe(['field' => 'email', 'value' => 'invalid']);
expect($exception->getContext()->debug)->toBe(['rule' => 'email_format']);
expect($exception->getContext()->metadata)->toBe(['attempt' => 1]);
expect($exception->getRetryAfter())->toBe(60);
expect($exception->isRecoverable())->toBeTrue();
expect($exception->isCategory('VAL'))->toBeTrue();
});
});