- 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
313 lines
11 KiB
PHP
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();
|
|
});
|
|
});
|