feat(Production): Complete production deployment infrastructure

- 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.
This commit is contained in:
2025-10-25 19:18:37 +02:00
parent caa85db796
commit fc3d7e6357
83016 changed files with 378904 additions and 20919 deletions

View File

@@ -0,0 +1,209 @@
<?php
declare(strict_types=1);
use App\Domain\SmartLink\Entities\SmartLink;
use App\Domain\SmartLink\Enums\LinkStatus;
use App\Domain\SmartLink\Enums\LinkType;
use App\Domain\SmartLink\ValueObjects\LinkTitle;
use App\Domain\SmartLink\ValueObjects\ShortCode;
use App\Framework\DateTime\SystemClock;
describe('SmartLink Entity', function () {
beforeEach(function () {
$this->clock = new SystemClock();
$this->shortCode = ShortCode::fromString('abc123');
$this->title = LinkTitle::fromString('My Smart Link');
});
describe('creation', function () {
it('creates smart link with required fields', function () {
$link = SmartLink::create(
clock: $this->clock,
shortCode: $this->shortCode,
type: LinkType::RELEASE,
title: $this->title
);
expect($link->shortCode->toString())->toBe('abc123');
expect($link->type)->toBe(LinkType::RELEASE);
expect($link->title->toString())->toBe('My Smart Link');
expect($link->status)->toBe(LinkStatus::DRAFT);
expect($link->userId)->toBeNull();
expect($link->coverImageUrl)->toBeNull();
});
it('creates smart link with user ID', function () {
$link = SmartLink::create(
clock: $this->clock,
shortCode: $this->shortCode,
type: LinkType::RELEASE,
title: $this->title,
userId: 'user123'
);
expect($link->userId)->toBe('user123');
expect($link->isOwnedBy('user123'))->toBeTrue();
});
it('creates smart link with cover image', function () {
$link = SmartLink::create(
clock: $this->clock,
shortCode: $this->shortCode,
type: LinkType::RELEASE,
title: $this->title,
coverImageUrl: 'https://example.com/cover.jpg'
);
expect($link->coverImageUrl)->toBe('https://example.com/cover.jpg');
});
it('starts in DRAFT status', function () {
$link = SmartLink::create(
clock: $this->clock,
shortCode: $this->shortCode,
type: LinkType::RELEASE,
title: $this->title
);
expect($link->status)->toBe(LinkStatus::DRAFT);
expect($link->isActive())->toBeFalse();
});
});
describe('status transitions', function () {
it('transitions from DRAFT to ACTIVE', function () {
$link = SmartLink::create(
clock: $this->clock,
shortCode: $this->shortCode,
type: LinkType::RELEASE,
title: $this->title
);
$activeLink = $link->withStatus(LinkStatus::ACTIVE);
expect($activeLink->status)->toBe(LinkStatus::ACTIVE);
expect($activeLink->isActive())->toBeTrue();
});
it('transitions from ACTIVE to PAUSED', function () {
$link = SmartLink::create(
clock: $this->clock,
shortCode: $this->shortCode,
type: LinkType::RELEASE,
title: $this->title
)->withStatus(LinkStatus::ACTIVE);
$pausedLink = $link->withStatus(LinkStatus::PAUSED);
expect($pausedLink->status)->toBe(LinkStatus::PAUSED);
});
it('throws exception for invalid transition', function () {
$link = SmartLink::create(
clock: $this->clock,
shortCode: $this->shortCode,
type: LinkType::RELEASE,
title: $this->title
);
// Cannot transition directly from DRAFT to PAUSED
expect(fn() => $link->withStatus(LinkStatus::PAUSED))
->toThrow(\DomainException::class);
});
it('throws exception for invalid transition from EXPIRED', function () {
$link = SmartLink::create(
clock: $this->clock,
shortCode: $this->shortCode,
type: LinkType::RELEASE,
title: $this->title
)->withStatus(LinkStatus::ACTIVE)->withStatus(LinkStatus::EXPIRED);
// Cannot transition from EXPIRED to ACTIVE
expect(fn() => $link->withStatus(LinkStatus::ACTIVE))
->toThrow(\DomainException::class);
});
});
describe('title updates', function () {
it('updates title', function () {
$link = SmartLink::create(
clock: $this->clock,
shortCode: $this->shortCode,
type: LinkType::RELEASE,
title: $this->title
);
$newTitle = LinkTitle::fromString('Updated Title');
$updatedLink = $link->withTitle($newTitle);
expect($updatedLink->title->toString())->toBe('Updated Title');
expect($link->title->toString())->toBe('My Smart Link'); // Original unchanged
});
});
describe('access control', function () {
it('identifies active links as active', function () {
$link = SmartLink::create(
clock: $this->clock,
shortCode: $this->shortCode,
type: LinkType::RELEASE,
title: $this->title
)->withStatus(LinkStatus::ACTIVE);
expect($link->isActive())->toBeTrue();
expect($link->canBeAccessed())->toBeTrue();
});
it('identifies draft links as not accessible', function () {
$link = SmartLink::create(
clock: $this->clock,
shortCode: $this->shortCode,
type: LinkType::RELEASE,
title: $this->title
);
expect($link->canBeAccessed())->toBeFalse();
});
it('identifies paused links as not accessible', function () {
$link = SmartLink::create(
clock: $this->clock,
shortCode: $this->shortCode,
type: LinkType::RELEASE,
title: $this->title
)->withStatus(LinkStatus::ACTIVE)->withStatus(LinkStatus::PAUSED);
expect($link->canBeAccessed())->toBeFalse();
});
});
describe('ownership', function () {
it('identifies owner correctly', function () {
$link = SmartLink::create(
clock: $this->clock,
shortCode: $this->shortCode,
type: LinkType::RELEASE,
title: $this->title,
userId: 'user123'
);
expect($link->isOwnedBy('user123'))->toBeTrue();
expect($link->isOwnedBy('user456'))->toBeFalse();
expect($link->isOwnedBy(null))->toBeFalse();
});
it('handles anonymous links', function () {
$link = SmartLink::create(
clock: $this->clock,
shortCode: $this->shortCode,
type: LinkType::RELEASE,
title: $this->title
);
expect($link->userId)->toBeNull();
expect($link->isOwnedBy('user123'))->toBeFalse();
});
});
});