Files
michaelschiemer/tests/Unit/Framework/MagicLinks/MagicLinkDataTest.php
Michael Schiemer fc3d7e6357 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.
2025-10-25 19:18:37 +02:00

155 lines
5.3 KiB
PHP

<?php
declare(strict_types=1);
use App\Framework\MagicLinks\MagicLinkData;
use App\Framework\MagicLinks\TokenAction;
use App\Framework\MagicLinks\ValueObjects\MagicLinkPayload;
describe('MagicLinkData', function () {
beforeEach(function () {
$this->now = new DateTimeImmutable();
$this->futureExpiry = $this->now->modify('+1 hour');
$this->pastExpiry = $this->now->modify('-1 hour');
});
it('creates valid magiclink data', function () {
$action = new TokenAction('email_verification');
$payload = MagicLinkPayload::fromArray(['user_id' => 123, 'email' => 'test@example.com']);
$data = new MagicLinkData(
id: 'test-id-123',
action: $action,
payload: $payload,
expiresAt: $this->futureExpiry,
createdAt: $this->now
);
expect($data->id)->toBe('test-id-123');
expect($data->action)->toBe($action);
expect($data->payload)->toBe($payload);
expect($data->expiresAt)->toBe($this->futureExpiry);
expect($data->createdAt)->toBe($this->now);
expect($data->oneTimeUse)->toBeFalse();
expect($data->isUsed)->toBeFalse();
expect($data->usedAt)->toBeNull();
});
it('detects expired tokens', function () {
$data = new MagicLinkData(
id: 'expired-token',
action: new TokenAction('test_action'),
payload: MagicLinkPayload::fromArray(['test' => 'data']),
expiresAt: $this->pastExpiry,
createdAt: $this->now
);
expect($data->isExpired())->toBeTrue();
expect($data->isValid())->toBeFalse();
});
it('detects valid non-expired tokens', function () {
$data = new MagicLinkData(
id: 'valid-token',
action: new TokenAction('test_action'),
payload: MagicLinkPayload::fromArray(['test' => 'data']),
expiresAt: $this->futureExpiry,
createdAt: $this->now
);
expect($data->isExpired())->toBeFalse();
expect($data->isValid())->toBeTrue();
});
it('validates one-time use tokens', function () {
$data = new MagicLinkData(
id: 'one-time-token',
action: new TokenAction('test_action'),
payload: MagicLinkPayload::fromArray(['test' => 'data']),
expiresAt: $this->futureExpiry,
createdAt: $this->now,
oneTimeUse: true,
isUsed: false
);
expect($data->isValid())->toBeTrue();
// After marking as used
$usedData = $data->withUsed(new DateTimeImmutable());
expect($usedData->isValid())->toBeFalse();
expect($usedData->isUsed)->toBeTrue();
});
it('calculates remaining time correctly', function () {
$oneHourFuture = $this->now->modify('+1 hour');
$data = new MagicLinkData(
id: 'test-token',
action: new TokenAction('test_action'),
payload: MagicLinkPayload::fromArray(['test' => 'data']),
expiresAt: $oneHourFuture,
createdAt: $this->now
);
$remaining = $data->getRemainingTime();
// Should be approximately 1 hour (allowing for small execution time variance)
expect($remaining->h)->toBeGreaterThanOrEqual(0);
expect($remaining->i)->toBeGreaterThan(50); // At least 50 minutes
});
it('returns zero interval for expired tokens', function () {
$data = new MagicLinkData(
id: 'expired-token',
action: new TokenAction('test_action'),
payload: MagicLinkPayload::fromArray(['test' => 'data']),
expiresAt: $this->pastExpiry,
createdAt: $this->now
);
$remaining = $data->getRemainingTime();
expect($remaining->d)->toBe(0); // d = day component
expect($remaining->h)->toBe(0); // h = hour component
expect($remaining->i)->toBe(0); // i = minute component
});
it('marks token as used immutably', function () {
$originalData = new MagicLinkData(
id: 'test-token',
action: new TokenAction('test_action'),
payload: MagicLinkPayload::fromArray(['test' => 'data']),
expiresAt: $this->futureExpiry,
createdAt: $this->now,
oneTimeUse: true
);
$usedAt = new DateTimeImmutable('+10 seconds');
$usedData = $originalData->withUsed($usedAt);
// Original unchanged
expect($originalData->isUsed)->toBeFalse();
expect($originalData->usedAt)->toBeNull();
// New instance has updated values
expect($usedData->isUsed)->toBeTrue();
expect($usedData->usedAt)->toBe($usedAt);
expect($usedData->id)->toBe($originalData->id);
expect($usedData->action)->toBe($originalData->action);
});
it('stores IP address and user agent', function () {
$data = new MagicLinkData(
id: 'test-token',
action: new TokenAction('test_action'),
payload: MagicLinkPayload::fromArray(['test' => 'data']),
expiresAt: $this->futureExpiry,
createdAt: $this->now,
createdByIp: '192.168.1.1',
userAgent: 'Mozilla/5.0 Test Browser'
);
expect($data->createdByIp)->toBe('192.168.1.1');
expect($data->userAgent)->toBe('Mozilla/5.0 Test Browser');
});
});