Files
michaelschiemer/tests/Framework/Logging/LogContextManagerTest.php
Michael Schiemer 5050c7d73a 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
2025-10-05 11:05:04 +02:00

313 lines
11 KiB
PHP

<?php
declare(strict_types=1);
namespace Tests\Framework\Logging;
use App\Framework\Logging\LogContextManager;
use App\Framework\Logging\ValueObjects\LogContext;
use App\Framework\Logging\ValueObjects\RequestContext;
use App\Framework\Logging\ValueObjects\UserContext;
use App\Framework\Random\SecureRandomGenerator;
use App\Framework\Tracing\TraceContext;
describe('LogContextManager', function () {
beforeEach(function () {
// Create fresh instance for each test
$this->contextManager = new LogContextManager();
TraceContext::clear();
});
afterEach(function () {
// Clean up after each test
TraceContext::clear();
});
it('starts with empty context', function () {
$context = $this->contextManager->getCurrentContext();
expect($context->structured)->toBe([]);
expect($context->tags)->toBe([]);
expect($context->trace)->toBeNull();
expect($context->user)->toBeNull();
expect($context->request)->toBeNull();
});
it('can set and get global context', function () {
$globalContext = LogContext::withData(['app' => 'test'])
->addTags('global');
$this->contextManager->setGlobalContext($globalContext);
$current = $this->contextManager->getCurrentContext();
expect($current->structured)->toBe(['app' => 'test']);
expect($current->tags)->toBe(['global']);
});
it('can set and get request context', function () {
$requestContext = LogContext::withData(['request' => 'test'])
->addTags('request');
$this->contextManager->setRequestContext($requestContext);
$current = $this->contextManager->getCurrentContext();
expect($current->structured)->toBe(['request' => 'test']);
expect($current->tags)->toBe(['request']);
});
it('merges global and request contexts', function () {
$globalContext = LogContext::withData(['app' => 'test'])
->addTags('global');
$requestContext = LogContext::withData(['request' => 'test'])
->addTags('request');
$this->contextManager->setGlobalContext($globalContext);
$this->contextManager->setRequestContext($requestContext);
$current = $this->contextManager->getCurrentContext();
expect($current->structured)->toBe(['app' => 'test', 'request' => 'test']);
expect($current->tags)->toBe(['global', 'request']);
});
it('can add data to global context', function () {
$this->contextManager->addGlobalData('key1', 'value1');
$this->contextManager->addGlobalData('key2', 'value2');
$current = $this->contextManager->getCurrentContext();
expect($current->structured)->toBe(['key1' => 'value1', 'key2' => 'value2']);
});
it('can add tags to global context', function () {
$this->contextManager->addGlobalTags('tag1', 'tag2');
$this->contextManager->addGlobalTags('tag3');
$current = $this->contextManager->getCurrentContext();
expect($current->tags)->toBe(['tag1', 'tag2', 'tag3']);
});
it('can add data to request context', function () {
$this->contextManager->addRequestData('req1', 'value1');
$this->contextManager->addRequestData('req2', 'value2');
$current = $this->contextManager->getCurrentContext();
expect($current->structured)->toBe(['req1' => 'value1', 'req2' => 'value2']);
});
it('can add tags to request context', function () {
$this->contextManager->addRequestTags('reqtag1', 'reqtag2');
$this->contextManager->addRequestTags('reqtag3');
$current = $this->contextManager->getCurrentContext();
expect($current->tags)->toBe(['reqtag1', 'reqtag2', 'reqtag3']);
});
it('includes current trace context automatically', function () {
$trace = TraceContext::start(new SecureRandomGenerator());
$current = $this->contextManager->getCurrentContext();
expect($current->trace)->toBe($trace);
});
it('can add user context', function () {
$userContext = UserContext::authenticated('123', 'john');
$this->contextManager->withUser($userContext);
$current = $this->contextManager->getCurrentContext();
expect($current->user)->toBe($userContext);
});
it('can add request context through manager', function () {
$requestContext = RequestContext::empty();
$this->contextManager->withRequest($requestContext);
$current = $this->contextManager->getCurrentContext();
expect($current->request)->toBe($requestContext);
});
it('can add trace context through manager', function () {
$traceContext = TraceContext::start(new SecureRandomGenerator());
$this->contextManager->withTrace($traceContext);
$current = $this->contextManager->getCurrentContext();
expect($current->trace)->toBe($traceContext);
});
it('can clear request context only', function () {
$this->contextManager->setGlobalContext(LogContext::withData(['global' => 'data']));
$this->contextManager->setRequestContext(LogContext::withData(['request' => 'data']));
$this->contextManager->clearRequestContext();
$current = $this->contextManager->getCurrentContext();
expect($current->structured)->toBe(['global' => 'data']);
});
it('can clear global context only', function () {
$this->contextManager->setGlobalContext(LogContext::withData(['global' => 'data']));
$this->contextManager->setRequestContext(LogContext::withData(['request' => 'data']));
$this->contextManager->clearGlobalContext();
$current = $this->contextManager->getCurrentContext();
expect($current->structured)->toBe(['request' => 'data']);
});
it('can clear all contexts', function () {
$this->contextManager->setGlobalContext(LogContext::withData(['global' => 'data']));
$this->contextManager->setRequestContext(LogContext::withData(['request' => 'data']));
$this->contextManager->clearContext();
$current = $this->contextManager->getCurrentContext();
expect($current->structured)->toBe(['global' => 'data']); // Global remains
});
it('can execute callback with temporary context', function () {
$originalData = ['original' => 'data'];
$this->contextManager->setRequestContext(LogContext::withData($originalData));
$temporaryContext = LogContext::withData(['temp' => 'data']);
$result = $this->contextManager->withTemporaryContext($temporaryContext, function () {
$current = $this->contextManager->getCurrentContext();
return $current->structured;
});
expect($result)->toBe(['original' => 'data', 'temp' => 'data']);
// Original context should be restored
$current = $this->contextManager->getCurrentContext();
expect($current->structured)->toBe($originalData);
});
it('can execute callback with temporary tags', function () {
$this->contextManager->setRequestContext(LogContext::withTags('original'));
$result = $this->contextManager->withTemporaryTags(['temp1', 'temp2'], function () {
$current = $this->contextManager->getCurrentContext();
return $current->tags;
});
expect($result)->toBe(['original', 'temp1', 'temp2']);
// Original context should be restored
$current = $this->contextManager->getCurrentContext();
expect($current->tags)->toBe(['original']);
});
it('can execute callback with temporary data', function () {
$this->contextManager->setRequestContext(LogContext::withData(['original' => 'value']));
$result = $this->contextManager->withTemporaryData(['temp' => 'value'], function () {
$current = $this->contextManager->getCurrentContext();
return $current->structured;
});
expect($result)->toBe(['original' => 'value', 'temp' => 'value']);
// Original context should be restored
$current = $this->contextManager->getCurrentContext();
expect($current->structured)->toBe(['original' => 'value']);
});
it('restores context even when callback throws exception', function () {
$originalContext = LogContext::withData(['original' => 'data']);
$this->contextManager->setRequestContext($originalContext);
$temporaryContext = LogContext::withData(['temp' => 'data']);
try {
$this->contextManager->withTemporaryContext($temporaryContext, function () {
throw new \RuntimeException('Test exception');
});
} catch (\RuntimeException $e) {
// Expected
}
// Original context should be restored
$current = $this->contextManager->getCurrentContext();
expect($current->structured)->toBe(['original' => 'data']);
});
it('can initialize request context from globals', function () {
// Mock global variables
$_SERVER['REQUEST_METHOD'] = 'GET';
$_SERVER['REQUEST_URI'] = '/test';
$_SERVER['HTTP_HOST'] = 'example.com';
$_SERVER['SERVER_NAME'] = 'example.com';
$_SERVER['SERVER_PORT'] = '80';
// Test direct RequestContext creation first
$requestContext = \App\Framework\Logging\ValueObjects\RequestContext::fromGlobals();
expect($requestContext)->not->toBeNull();
expect($requestContext->getMethod())->toBe('GET');
$context = $this->contextManager->initializeRequestContext();
expect($context->request)->not->toBeNull();
expect($context->request->getMethod())->toBe('GET');
expect($context->request->getUri())->toBe('/test');
expect($context->request->getHost())->toBe('example.com');
// Should be set as current request context
$current = $this->contextManager->getCurrentContext();
expect($current->request)->toBe($context->request);
// Cleanup
unset($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI'], $_SERVER['HTTP_HOST'], $_SERVER['SERVER_NAME'], $_SERVER['SERVER_PORT']);
});
it('provides debug information', function () {
$this->contextManager->setGlobalContext(LogContext::withData(['global' => 'data']));
$this->contextManager->setRequestContext(LogContext::withData(['request' => 'data']));
$debug = $this->contextManager->getDebugInfo();
expect($debug)->toHaveKeys([
'has_global_context',
'has_request_context',
'global_context',
'request_context',
'current_trace',
'combined_context',
]);
expect($debug['has_global_context'])->toBeTrue();
expect($debug['has_request_context'])->toBeTrue();
});
it('can check for trace context', function () {
expect($this->contextManager->hasTraceContext())->toBeFalse();
TraceContext::start(new SecureRandomGenerator());
expect($this->contextManager->hasTraceContext())->toBeTrue();
});
it('can check for user context', function () {
expect($this->contextManager->hasUserContext())->toBeFalse();
$this->contextManager->withUser(UserContext::authenticated('123'));
expect($this->contextManager->hasUserContext())->toBeTrue();
});
it('can check for request context', function () {
expect($this->contextManager->hasRequestContext())->toBeFalse();
$this->contextManager->withRequest(RequestContext::empty());
expect($this->contextManager->hasRequestContext())->toBeTrue();
});
});