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,197 @@
<?php
declare(strict_types=1);
use App\Framework\StateManagement\Transformers\EncryptionTransformer;
use App\Framework\StateManagement\SerializableState;
use App\Framework\LiveComponents\Serialization\EncryptedStateSerializer;
use App\Framework\LiveComponents\Serialization\StateEncryptor;
use App\Framework\Cryptography\CryptographicUtilities;
use App\Framework\Random\SecureRandomGenerator;
// Test State for transformer tests
final readonly class TransformerTestState implements SerializableState
{
public function __construct(
public string $name,
public int $value
) {
}
public function toArray(): array
{
return [
'name' => $this->name,
'value' => $this->value,
];
}
public static function fromArray(array $data): self
{
return new self(
name: $data['name'] ?? '',
value: $data['value'] ?? 0
);
}
}
describe('EncryptionTransformer', function () {
beforeEach(function () {
$random = new SecureRandomGenerator();
$crypto = new CryptographicUtilities($random);
$encryptionKey = $random->bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES);
$encryptor = new StateEncryptor($encryptionKey, $crypto, $random);
$serializer = new EncryptedStateSerializer($encryptor);
$this->transformer = new EncryptionTransformer(
$serializer,
TransformerTestState::class
);
$this->testState = new TransformerTestState(
name: 'Test User',
value: 42
);
});
it('has correct name', function () {
expect($this->transformer->getName())->toBe('encryption');
});
it('has medium priority', function () {
expect($this->transformer->getPriority())->toBe(50);
});
it('transforms state to encrypted string', function () {
$transformed = $this->transformer->transformIn($this->testState);
expect($transformed)->toBeString();
expect($transformed)->not->toBeEmpty();
// Should be base64-encoded encrypted data
$decoded = base64_decode($transformed, strict: true);
expect($decoded)->not->toBeFalse();
});
it('transforms encrypted string back to state', function () {
$encrypted = $this->transformer->transformIn($this->testState);
$state = $this->transformer->transformOut($encrypted);
expect($state)->toBeInstanceOf(TransformerTestState::class);
expect($state->name)->toBe('Test User');
expect($state->value)->toBe(42);
});
it('round-trip transformation preserves state', function () {
$original = new TransformerTestState(
name: 'Round Trip Test',
value: 999
);
$encrypted = $this->transformer->transformIn($original);
$restored = $this->transformer->transformOut($encrypted);
expect($restored)->toBeInstanceOf(TransformerTestState::class);
expect($restored->name)->toBe($original->name);
expect($restored->value)->toBe($original->value);
});
it('throws exception for non-string data in transformOut', function () {
$this->transformer->transformOut(['not' => 'string']);
})->throws(\InvalidArgumentException::class, 'expects string data');
it('throws exception for non-SerializableState in transformOut', function () {
// Create invalid encrypted data that decrypts to non-SerializableState
// This would happen if stateClass is wrong
$transformer = new EncryptionTransformer(
$this->transformer->serializer,
\stdClass::class // Wrong class
);
$encrypted = $this->transformer->transformIn($this->testState);
$transformer->transformOut($encrypted);
})->throws(\RuntimeException::class, 'must implement SerializableState');
it('produces different encrypted output for same state', function () {
$encrypted1 = $this->transformer->transformIn($this->testState);
$encrypted2 = $this->transformer->transformIn($this->testState);
// Should be different due to unique nonces
expect($encrypted1)->not->toBe($encrypted2);
// But both should decrypt to same state
$state1 = $this->transformer->transformOut($encrypted1);
$state2 = $this->transformer->transformOut($encrypted2);
expect($state1->name)->toBe($state2->name);
expect($state1->value)->toBe($state2->value);
});
it('handles state with special characters', function () {
$state = new TransformerTestState(
name: "Special: 🔐 <script>alert('xss')</script>",
value: 123
);
$encrypted = $this->transformer->transformIn($state);
$restored = $this->transformer->transformOut($encrypted);
expect($restored->name)->toBe($state->name);
});
});
describe('EncryptionTransformer Integration', function () {
it('works in transformer pipeline with multiple transformers', function () {
// Simulate transformer pipeline
$random = new SecureRandomGenerator();
$crypto = new CryptographicUtilities($random);
$encryptionKey = $random->bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES);
$encryptor = new StateEncryptor($encryptionKey, $crypto, $random);
$serializer = new EncryptedStateSerializer($encryptor);
$encryptionTransformer = new EncryptionTransformer(
$serializer,
TransformerTestState::class
);
$state = new TransformerTestState('Pipeline Test', 789);
// transformIn pipeline (high to low priority)
$data = $encryptionTransformer->transformIn($state);
// transformOut pipeline (reverse order)
$restored = $encryptionTransformer->transformOut($data);
expect($restored)->toBeInstanceOf(TransformerTestState::class);
expect($restored->name)->toBe('Pipeline Test');
expect($restored->value)->toBe(789);
});
it('maintains type safety through transformation', function () {
$random = new SecureRandomGenerator();
$crypto = new CryptographicUtilities($random);
$encryptionKey = $random->bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES);
$encryptor = new StateEncryptor($encryptionKey, $crypto, $random);
$serializer = new EncryptedStateSerializer($encryptor);
$transformer = new EncryptionTransformer(
$serializer,
TransformerTestState::class
);
$state = new TransformerTestState('Type Safety', 0);
$encrypted = $transformer->transformIn($state);
$restored = $transformer->transformOut($encrypted);
// Type checks
expect($restored)->toBeInstanceOf(SerializableState::class);
expect($restored)->toBeInstanceOf(TransformerTestState::class);
expect($restored->value)->toBeInt();
expect($restored->name)->toBeString();
});
});