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,53 @@
<?php
use App\Framework\MagicLinks\ValueObjects\ActionResultData;
describe('ActionResultData', function () {
it('creates empty result data', function () {
$data = ActionResultData::empty();
expect($data->isEmpty())->toBeTrue();
expect($data->toArray())->toBe([]);
});
it('creates from array', function () {
$data = ActionResultData::fromArray(['user_id' => 123, 'email' => 'test@example.com']);
expect($data->isEmpty())->toBeFalse();
expect($data->toArray())->toBe(['user_id' => 123, 'email' => 'test@example.com']);
});
it('adds values immutably', function () {
$data = ActionResultData::empty();
$updated = $data->with('status', 'success');
expect($data->has('status'))->toBeFalse();
expect($updated->has('status'))->toBeTrue();
expect($updated->get('status'))->toBe('success');
});
it('retrieves values with defaults', function () {
$data = ActionResultData::fromArray(['status' => 'success']);
expect($data->get('status'))->toBe('success');
expect($data->get('missing', 'default'))->toBe('default');
});
it('merges data', function () {
$data1 = ActionResultData::fromArray(['user_id' => 123]);
$data2 = ActionResultData::fromArray(['email' => 'test@example.com']);
$merged = $data1->merge($data2);
expect($merged->toArray())->toBe([
'user_id' => 123,
'email' => 'test@example.com',
]);
});
it('checks key existence', function () {
$data = ActionResultData::fromArray(['user_id' => 123]);
expect($data->has('user_id'))->toBeTrue();
expect($data->has('missing'))->toBeFalse();
});
});

View File

@@ -0,0 +1,64 @@
<?php
use App\Framework\MagicLinks\ValueObjects\ErrorCollection;
describe('ErrorCollection', function () {
it('creates empty collection', function () {
$errors = ErrorCollection::empty();
expect($errors->isEmpty())->toBeTrue();
expect($errors->hasErrors())->toBeFalse();
expect($errors->count())->toBe(0);
});
it('creates collection from array', function () {
$errors = ErrorCollection::fromArray(['Error 1', 'Error 2']);
expect($errors->isEmpty())->toBeFalse();
expect($errors->hasErrors())->toBeTrue();
expect($errors->count())->toBe(2);
});
it('creates single error collection', function () {
$errors = ErrorCollection::single('Test error');
expect($errors->count())->toBe(1);
expect($errors->first())->toBe('Test error');
});
it('adds error immutably', function () {
$errors = ErrorCollection::empty();
$updated = $errors->add('New error');
expect($errors->count())->toBe(0);
expect($updated->count())->toBe(1);
expect($updated->first())->toBe('New error');
});
it('adds multiple errors', function () {
$errors = ErrorCollection::single('Error 1');
$updated = $errors->addMultiple(['Error 2', 'Error 3']);
expect($updated->count())->toBe(3);
expect($updated->toArray())->toBe(['Error 1', 'Error 2', 'Error 3']);
});
it('converts to string', function () {
$errors = ErrorCollection::fromArray(['Error 1', 'Error 2', 'Error 3']);
expect($errors->toString())->toBe('Error 1, Error 2, Error 3');
expect($errors->toString(' | '))->toBe('Error 1 | Error 2 | Error 3');
});
it('returns first error', function () {
$errors = ErrorCollection::fromArray(['First', 'Second']);
expect($errors->first())->toBe('First');
});
it('returns null for empty collection first', function () {
$errors = ErrorCollection::empty();
expect($errors->first())->toBeNull();
});
});

View File

@@ -0,0 +1,51 @@
<?php
use App\Framework\MagicLinks\ValueObjects\ExecutionContext;
describe('ExecutionContext', function () {
it('creates empty context', function () {
$context = ExecutionContext::empty();
expect($context->isEmpty())->toBeTrue();
expect($context->toArray())->toBe([]);
});
it('creates context from array', function () {
$context = ExecutionContext::fromArray(['ip' => '127.0.0.1', 'user_agent' => 'Test']);
expect($context->isEmpty())->toBeFalse();
expect($context->toArray())->toBe(['ip' => '127.0.0.1', 'user_agent' => 'Test']);
});
it('adds values immutably', function () {
$context = ExecutionContext::empty();
$updated = $context->with('ip', '127.0.0.1');
expect($context->has('ip'))->toBeFalse();
expect($updated->has('ip'))->toBeTrue();
expect($updated->get('ip'))->toBe('127.0.0.1');
});
it('retrieves values with defaults', function () {
$context = ExecutionContext::fromArray(['ip' => '127.0.0.1']);
expect($context->get('ip'))->toBe('127.0.0.1');
expect($context->get('missing', 'default'))->toBe('default');
});
it('merges contexts', function () {
$context1 = ExecutionContext::fromArray(['ip' => '127.0.0.1']);
$context2 = ExecutionContext::fromArray(['user_agent' => 'Test']);
$merged = $context1->merge($context2);
expect($merged->toArray())->toBe(['ip' => '127.0.0.1', 'user_agent' => 'Test']);
});
it('merges with override', function () {
$context1 = ExecutionContext::fromArray(['ip' => '127.0.0.1']);
$context2 = ExecutionContext::fromArray(['ip' => '192.168.1.1']);
$merged = $context1->merge($context2);
expect($merged->get('ip'))->toBe('192.168.1.1');
});
});

View File

@@ -0,0 +1,61 @@
<?php
use App\Framework\MagicLinks\ValueObjects\MagicLinkPayload;
describe('MagicLinkPayload', function () {
it('creates payload from array', function () {
$payload = MagicLinkPayload::fromArray(['email' => 'test@example.com', 'user_id' => 123]);
expect($payload->toArray())->toBe(['email' => 'test@example.com', 'user_id' => 123]);
});
it('throws exception for empty payload', function () {
expect(fn() => MagicLinkPayload::fromArray([]))
->toThrow(InvalidArgumentException::class, 'Payload cannot be empty');
});
it('retrieves value with get method', function () {
$payload = MagicLinkPayload::fromArray(['email' => 'test@example.com']);
expect($payload->get('email'))->toBe('test@example.com');
expect($payload->get('missing', 'default'))->toBe('default');
});
it('checks if key exists', function () {
$payload = MagicLinkPayload::fromArray(['email' => 'test@example.com']);
expect($payload->has('email'))->toBeTrue();
expect($payload->has('missing'))->toBeFalse();
});
it('creates new instance with added value', function () {
$payload = MagicLinkPayload::fromArray(['email' => 'test@example.com']);
$updated = $payload->with('user_id', 123);
expect($payload->has('user_id'))->toBeFalse();
expect($updated->has('user_id'))->toBeTrue();
expect($updated->get('user_id'))->toBe(123);
});
it('creates new instance without key', function () {
$payload = MagicLinkPayload::fromArray(['email' => 'test@example.com', 'user_id' => 123]);
$updated = $payload->without('user_id');
expect($payload->has('user_id'))->toBeTrue();
expect($updated->has('user_id'))->toBeFalse();
});
it('throws exception when removing last key', function () {
$payload = MagicLinkPayload::fromArray(['email' => 'test@example.com']);
expect(fn() => $payload->without('email'))
->toThrow(InvalidArgumentException::class, 'Payload cannot be empty after removal');
});
it('returns keys and values', function () {
$payload = MagicLinkPayload::fromArray(['email' => 'test@example.com', 'user_id' => 123]);
expect($payload->keys())->toBe(['email', 'user_id']);
expect($payload->values())->toBe(['test@example.com', 123]);
});
});

View File

@@ -0,0 +1,51 @@
<?php
use App\Framework\MagicLinks\ValueObjects\Metadata;
describe('Metadata', function () {
it('creates empty metadata', function () {
$metadata = Metadata::empty();
expect($metadata->isEmpty())->toBeTrue();
expect($metadata->toArray())->toBe([]);
});
it('creates from array', function () {
$metadata = Metadata::fromArray(['source' => 'email', 'campaign' => 'welcome']);
expect($metadata->isEmpty())->toBeFalse();
expect($metadata->toArray())->toBe(['source' => 'email', 'campaign' => 'welcome']);
});
it('adds values immutably', function () {
$metadata = Metadata::empty();
$updated = $metadata->with('source', 'email');
expect($metadata->has('source'))->toBeFalse();
expect($updated->has('source'))->toBeTrue();
expect($updated->get('source'))->toBe('email');
});
it('removes values immutably', function () {
$metadata = Metadata::fromArray(['source' => 'email', 'campaign' => 'welcome']);
$updated = $metadata->without('campaign');
expect($metadata->has('campaign'))->toBeTrue();
expect($updated->has('campaign'))->toBeFalse();
expect($updated->has('source'))->toBeTrue();
});
it('merges metadata', function () {
$meta1 = Metadata::fromArray(['source' => 'email']);
$meta2 = Metadata::fromArray(['campaign' => 'welcome']);
$merged = $meta1->merge($meta2);
expect($merged->toArray())->toBe(['source' => 'email', 'campaign' => 'welcome']);
});
it('returns keys', function () {
$metadata = Metadata::fromArray(['source' => 'email', 'campaign' => 'welcome']);
expect($metadata->keys())->toBe(['source', 'campaign']);
});
});