Files
michaelschiemer/tests/Unit/Framework/Logging/ValueObjects/LogContextTest.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

423 lines
14 KiB
PHP

<?php
declare(strict_types=1);
use App\Framework\Logging\ValueObjects\LogContext;
use App\Framework\Logging\ValueObjects\UserContext;
use App\Framework\Logging\ValueObjects\RequestContext;
use App\Framework\Tracing\TraceContext;
use App\Framework\Random\SecureRandomGenerator;
describe('LogContext', function () {
describe('empty()', function () {
it('creates empty LogContext', function () {
$context = LogContext::empty();
expect($context->structured)->toBe([]);
expect($context->tags)->toBe([]);
expect($context->trace)->toBeNull();
expect($context->user)->toBeNull();
expect($context->request)->toBeNull();
expect($context->metadata)->toBe([]);
});
});
describe('withData()', function () {
it('creates LogContext with structured data', function () {
$data = ['user_id' => 123, 'action' => 'login'];
$context = LogContext::withData($data);
expect($context->structured)->toBe($data);
expect($context->tags)->toBe([]);
});
});
describe('withTags()', function () {
it('creates LogContext with tags', function () {
$context = LogContext::withTags('security', 'authentication');
expect($context->tags)->toBe(['security', 'authentication']);
expect($context->structured)->toBe([]);
});
it('accepts variadic tags', function () {
$context = LogContext::withTags('tag1', 'tag2', 'tag3');
expect($context->tags)->toHaveCount(3);
expect($context->tags)->toContain('tag1', 'tag2', 'tag3');
});
});
describe('addData()', function () {
it('adds single data entry', function () {
$context = LogContext::empty()->addData('key', 'value');
expect($context->structured)->toBe(['key' => 'value']);
});
it('preserves existing data when adding new', function () {
$context = LogContext::withData(['existing' => 'data'])
->addData('new', 'value');
expect($context->structured)->toBe([
'existing' => 'data',
'new' => 'value',
]);
});
it('returns new instance (immutability)', function () {
$original = LogContext::withData(['original' => 'data']);
$modified = $original->addData('new', 'value');
expect($original->structured)->toBe(['original' => 'data']);
expect($modified->structured)->toBe([
'original' => 'data',
'new' => 'value',
]);
expect($original)->not->toBe($modified);
});
});
describe('mergeData()', function () {
it('merges multiple data entries', function () {
$context = LogContext::withData(['existing' => 'data'])
->mergeData(['new1' => 'value1', 'new2' => 'value2']);
expect($context->structured)->toBe([
'existing' => 'data',
'new1' => 'value1',
'new2' => 'value2',
]);
});
it('overwrites existing keys', function () {
$context = LogContext::withData(['key' => 'old'])
->mergeData(['key' => 'new']);
expect($context->structured['key'])->toBe('new');
});
});
describe('addTags()', function () {
it('adds tags to existing tags', function () {
$context = LogContext::withTags('tag1')
->addTags('tag2', 'tag3');
expect($context->tags)->toHaveCount(3);
expect($context->tags)->toContain('tag1', 'tag2', 'tag3');
});
it('removes duplicate tags', function () {
$context = LogContext::withTags('tag1', 'tag2')
->addTags('tag2', 'tag3');
expect($context->tags)->toHaveCount(3);
expect($context->tags)->toContain('tag1', 'tag2', 'tag3');
});
it('returns new instance (immutability)', function () {
$original = LogContext::withTags('tag1');
$modified = $original->addTags('tag2');
expect($original->tags)->toBe(['tag1']);
expect($modified->tags)->toHaveCount(2);
expect($original)->not->toBe($modified);
});
});
describe('withTrace()', function () {
it('sets trace context', function () {
$random = new SecureRandomGenerator();
$trace = TraceContext::start($random, 'trace-id-123');
$context = LogContext::empty()->withTrace($trace);
expect($context->trace)->toBe($trace);
// Cleanup
TraceContext::clear();
});
});
describe('withUser()', function () {
it('sets user context', function () {
$user = new UserContext('user-123', 'john@example.com');
$context = LogContext::empty()->withUser($user);
expect($context->user)->toBe($user);
});
});
describe('withRequest()', function () {
it('sets request context', function () {
$request = RequestContext::empty();
$context = LogContext::empty()->withRequest($request);
expect($context->request)->toBe($request);
});
});
describe('addMetadata()', function () {
it('adds metadata entry', function () {
$context = LogContext::empty()->addMetadata('key', 'value');
expect($context->metadata)->toBe(['key' => 'value']);
});
it('preserves existing metadata', function () {
$context = LogContext::empty()
->addMetadata('key1', 'value1')
->addMetadata('key2', 'value2');
expect($context->metadata)->toBe([
'key1' => 'value1',
'key2' => 'value2',
]);
});
});
describe('merge()', function () {
it('merges two LogContexts', function () {
$context1 = LogContext::withData(['key1' => 'value1'])
->addTags('tag1');
$context2 = LogContext::withData(['key2' => 'value2'])
->addTags('tag2');
$merged = $context1->merge($context2);
expect($merged->structured)->toBe([
'key1' => 'value1',
'key2' => 'value2',
]);
expect($merged->tags)->toContain('tag1', 'tag2');
});
it('overwrites structured data on merge', function () {
$context1 = LogContext::withData(['key' => 'old']);
$context2 = LogContext::withData(['key' => 'new']);
$merged = $context1->merge($context2);
expect($merged->structured['key'])->toBe('new');
});
it('removes duplicate tags on merge', function () {
$context1 = LogContext::withTags('tag1', 'tag2');
$context2 = LogContext::withTags('tag2', 'tag3');
$merged = $context1->merge($context2);
expect($merged->tags)->toHaveCount(3);
expect($merged->tags)->toContain('tag1', 'tag2', 'tag3');
});
it('prefers other context for trace/user/request', function () {
$random = new SecureRandomGenerator();
$trace1 = TraceContext::start($random, 'trace-1');
TraceContext::clear();
$trace2 = TraceContext::start($random, 'trace-2');
$user1 = new UserContext('user-1', 'user1@example.com');
$user2 = new UserContext('user-2', 'user2@example.com');
$context1 = LogContext::empty()
->withTrace($trace1)
->withUser($user1);
$context2 = LogContext::empty()
->withTrace($trace2)
->withUser($user2);
$merged = $context1->merge($context2);
expect($merged->trace)->toBe($trace2);
expect($merged->user)->toBe($user2);
// Cleanup
TraceContext::clear();
});
it('keeps original trace/user/request when other is null', function () {
$random = new SecureRandomGenerator();
$trace = TraceContext::start($random, 'trace-1');
$user = new UserContext('user-1', 'user1@example.com');
$context1 = LogContext::empty()
->withTrace($trace)
->withUser($user);
$context2 = LogContext::withData(['key' => 'value']);
$merged = $context1->merge($context2);
expect($merged->trace)->toBe($trace);
expect($merged->user)->toBe($user);
// Cleanup
TraceContext::clear();
});
});
describe('hasStructuredData()', function () {
it('returns true when structured data exists', function () {
$context = LogContext::withData(['key' => 'value']);
expect($context->hasStructuredData())->toBeTrue();
});
it('returns false when no structured data', function () {
$context = LogContext::empty();
expect($context->hasStructuredData())->toBeFalse();
});
});
describe('hasTags()', function () {
it('returns true when tags exist', function () {
$context = LogContext::withTags('tag1');
expect($context->hasTags())->toBeTrue();
});
it('returns false when no tags', function () {
$context = LogContext::empty();
expect($context->hasTags())->toBeFalse();
});
});
describe('hasTag()', function () {
it('returns true when specific tag exists', function () {
$context = LogContext::withTags('security', 'auth');
expect($context->hasTag('security'))->toBeTrue();
expect($context->hasTag('auth'))->toBeTrue();
});
it('returns false when tag does not exist', function () {
$context = LogContext::withTags('security');
expect($context->hasTag('performance'))->toBeFalse();
});
it('uses strict comparison', function () {
$context = LogContext::withTags('123');
expect($context->hasTag('123'))->toBeTrue();
// hasTag expects string, so we don't test with int
});
});
describe('toArray()', function () {
it('returns empty array for empty context', function () {
$context = LogContext::empty();
expect($context->toArray())->toBe([]);
});
it('includes structured data when present', function () {
$context = LogContext::withData(['key' => 'value']);
$array = $context->toArray();
expect($array)->toHaveKey('structured');
expect($array['structured'])->toBe(['key' => 'value']);
});
it('includes tags when present', function () {
$context = LogContext::withTags('tag1', 'tag2');
$array = $context->toArray();
expect($array)->toHaveKey('tags');
expect($array['tags'])->toBe(['tag1', 'tag2']);
});
it('includes trace when present', function () {
$random = new SecureRandomGenerator();
$trace = TraceContext::start($random, 'trace-123');
$context = LogContext::empty()->withTrace($trace);
$array = $context->toArray();
expect($array)->toHaveKey('trace');
expect($array['trace'])->toHaveKey('trace_id');
expect($array['trace']['trace_id'])->toBe('trace-123');
// Cleanup
TraceContext::clear();
});
it('includes user when present', function () {
$user = new UserContext('user-123', 'john@example.com');
$context = LogContext::empty()->withUser($user);
$array = $context->toArray();
expect($array)->toHaveKey('user');
expect($array['user'])->toBeArray();
});
it('includes request when present', function () {
$request = RequestContext::empty();
$context = LogContext::empty()->withRequest($request);
$array = $context->toArray();
expect($array)->toHaveKey('request');
expect($array['request'])->toBeArray();
});
it('includes metadata when present', function () {
$context = LogContext::empty()->addMetadata('key', 'value');
$array = $context->toArray();
expect($array)->toHaveKey('metadata');
expect($array['metadata'])->toBe(['key' => 'value']);
});
it('includes all sections when all are present', function () {
$random = new SecureRandomGenerator();
$trace = TraceContext::start($random, 'trace-123');
$user = new UserContext('user-123', 'john@example.com');
$request = RequestContext::empty();
$context = LogContext::withData(['key' => 'value'])
->addTags('security')
->withTrace($trace)
->withUser($user)
->withRequest($request)
->addMetadata('meta', 'data');
$array = $context->toArray();
expect($array)->toHaveKeys(['structured', 'tags', 'trace', 'user', 'request', 'metadata']);
// Cleanup
TraceContext::clear();
});
});
describe('immutability', function () {
it('is immutable through fluent API', function () {
$original = LogContext::empty();
$modified = $original
->addData('key', 'value')
->addTags('tag')
->addMetadata('meta', 'value');
// Original remains unchanged
expect($original->structured)->toBe([]);
expect($original->tags)->toBe([]);
expect($original->metadata)->toBe([]);
// Modified has all changes
expect($modified->structured)->toBe(['key' => 'value']);
expect($modified->tags)->toBe(['tag']);
expect($modified->metadata)->toBe(['meta' => 'value']);
});
});
});