refactor(deployment): Remove WireGuard VPN dependency and restore public service access
Remove WireGuard integration from production deployment to simplify infrastructure: - Remove docker-compose-direct-access.yml (VPN-bound services) - Remove VPN-only middlewares from Grafana, Prometheus, Portainer - Remove WireGuard middleware definitions from Traefik - Remove WireGuard IPs (10.8.0.0/24) from Traefik forwarded headers All monitoring services now publicly accessible via subdomains: - grafana.michaelschiemer.de (with Grafana native auth) - prometheus.michaelschiemer.de (with Basic Auth) - portainer.michaelschiemer.de (with Portainer native auth) All services use Let's Encrypt SSL certificates via Traefik.
This commit is contained in:
@@ -0,0 +1,223 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Framework\ExceptionHandling\Context\ExceptionContextData;
|
||||
use App\Framework\ExceptionHandling\Context\ExceptionContextProvider;
|
||||
use App\Framework\ExceptionHandling\Factory\ExceptionFactory;
|
||||
use App\Framework\ExceptionHandling\Scope\ErrorScope;
|
||||
use App\Framework\ExceptionHandling\Scope\ErrorScopeContext;
|
||||
|
||||
describe('Exception Context Integration', function () {
|
||||
beforeEach(function () {
|
||||
$this->contextProvider = ExceptionContextProvider::instance();
|
||||
$this->errorScope = new ErrorScope();
|
||||
$this->factory = new ExceptionFactory($this->contextProvider, $this->errorScope);
|
||||
|
||||
// Clear any existing contexts
|
||||
$this->contextProvider->clear();
|
||||
});
|
||||
|
||||
it('creates slim exception with external context via WeakMap', function () {
|
||||
$context = ExceptionContextData::forOperation('user.create', 'UserService')
|
||||
->addData(['user_id' => '123', 'email' => 'test@example.com']);
|
||||
|
||||
$exception = $this->factory->create(
|
||||
RuntimeException::class,
|
||||
'User creation failed',
|
||||
$context
|
||||
);
|
||||
|
||||
// Exception is slim (pure PHP)
|
||||
expect($exception)->toBeInstanceOf(RuntimeException::class);
|
||||
expect($exception->getMessage())->toBe('User creation failed');
|
||||
|
||||
// Context is stored externally
|
||||
$storedContext = $this->contextProvider->get($exception);
|
||||
expect($storedContext)->not->toBeNull();
|
||||
expect($storedContext->operation)->toBe('user.create');
|
||||
expect($storedContext->component)->toBe('UserService');
|
||||
expect($storedContext->data)->toBe([
|
||||
'user_id' => '123',
|
||||
'email' => 'test@example.com'
|
||||
]);
|
||||
});
|
||||
|
||||
it('automatically enriches context from error scope', function () {
|
||||
// Enter HTTP scope
|
||||
$scopeContext = ErrorScopeContext::http(
|
||||
request: createMockRequest(),
|
||||
operation: 'api.request',
|
||||
component: 'ApiController'
|
||||
)->withUserId('user-456');
|
||||
|
||||
$this->errorScope->enter($scopeContext);
|
||||
|
||||
// Create exception without explicit context
|
||||
$exception = $this->factory->create(
|
||||
RuntimeException::class,
|
||||
'API request failed'
|
||||
);
|
||||
|
||||
// Context is enriched from scope
|
||||
$storedContext = $this->contextProvider->get($exception);
|
||||
expect($storedContext)->not->toBeNull();
|
||||
expect($storedContext->userId)->toBe('user-456');
|
||||
expect($storedContext->metadata)->toHaveKey('scope_type');
|
||||
expect($storedContext->metadata['scope_type'])->toBe('http');
|
||||
});
|
||||
|
||||
it('supports WeakMap automatic garbage collection', function () {
|
||||
$exception = new RuntimeException('Test exception');
|
||||
$context = ExceptionContextData::forOperation('test.operation');
|
||||
|
||||
$this->contextProvider->attach($exception, $context);
|
||||
|
||||
// Context exists
|
||||
expect($this->contextProvider->has($exception))->toBeTrue();
|
||||
|
||||
// Unset exception reference
|
||||
unset($exception);
|
||||
|
||||
// Force garbage collection
|
||||
gc_collect_cycles();
|
||||
|
||||
// WeakMap automatically cleaned up (we can't directly test this,
|
||||
// but stats should reflect fewer contexts after GC)
|
||||
$stats = $this->contextProvider->getStats();
|
||||
expect($stats)->toHaveKey('total_contexts');
|
||||
});
|
||||
|
||||
it('enhances existing exception with additional context', function () {
|
||||
$exception = new RuntimeException('Original error');
|
||||
$originalContext = ExceptionContextData::forOperation('operation.1')
|
||||
->addData(['step' => 1]);
|
||||
|
||||
$this->contextProvider->attach($exception, $originalContext);
|
||||
|
||||
// Enhance with additional context
|
||||
$additionalContext = ExceptionContextData::empty()
|
||||
->addData(['step' => 2, 'error_code' => 'E001']);
|
||||
|
||||
$this->factory->enhance($exception, $additionalContext);
|
||||
|
||||
// Context is merged
|
||||
$storedContext = $this->contextProvider->get($exception);
|
||||
expect($storedContext->data)->toBe([
|
||||
'step' => 2, // Overwrites
|
||||
'error_code' => 'E001' // Adds
|
||||
]);
|
||||
});
|
||||
|
||||
it('supports fiber-aware error scopes', function () {
|
||||
// Main scope
|
||||
$mainScope = ErrorScopeContext::generic(
|
||||
'main',
|
||||
'main.operation'
|
||||
);
|
||||
$this->errorScope->enter($mainScope);
|
||||
|
||||
// Create fiber scope
|
||||
$fiber = new Fiber(function () {
|
||||
$fiberScope = ErrorScopeContext::generic(
|
||||
'fiber',
|
||||
'fiber.operation'
|
||||
);
|
||||
$this->errorScope->enter($fiberScope);
|
||||
|
||||
$exception = $this->factory->create(
|
||||
RuntimeException::class,
|
||||
'Fiber error'
|
||||
);
|
||||
|
||||
// Context from fiber scope
|
||||
$context = $this->contextProvider->get($exception);
|
||||
expect($context->operation)->toBe('fiber.operation');
|
||||
|
||||
$this->errorScope->exit();
|
||||
});
|
||||
|
||||
$fiber->start();
|
||||
|
||||
// Main scope still active
|
||||
$exception = $this->factory->create(
|
||||
RuntimeException::class,
|
||||
'Main error'
|
||||
);
|
||||
$context = $this->contextProvider->get($exception);
|
||||
expect($context->operation)->toBe('main.operation');
|
||||
});
|
||||
|
||||
it('creates exception with convenience factory methods', function () {
|
||||
// forOperation
|
||||
$exception1 = $this->factory->forOperation(
|
||||
InvalidArgumentException::class,
|
||||
'Invalid user data',
|
||||
'user.validate',
|
||||
'UserValidator',
|
||||
['email' => 'invalid']
|
||||
);
|
||||
|
||||
$context1 = $this->contextProvider->get($exception1);
|
||||
expect($context1->operation)->toBe('user.validate');
|
||||
expect($context1->component)->toBe('UserValidator');
|
||||
expect($context1->data['email'])->toBe('invalid');
|
||||
|
||||
// withData
|
||||
$exception2 = $this->factory->withData(
|
||||
RuntimeException::class,
|
||||
'Database error',
|
||||
['query' => 'SELECT * FROM users']
|
||||
);
|
||||
|
||||
$context2 = $this->contextProvider->get($exception2);
|
||||
expect($context2->data['query'])->toBe('SELECT * FROM users');
|
||||
});
|
||||
|
||||
it('handles nested error scopes correctly', function () {
|
||||
// Outer scope
|
||||
$outerScope = ErrorScopeContext::http(
|
||||
request: createMockRequest(),
|
||||
operation: 'outer.operation'
|
||||
);
|
||||
$this->errorScope->enter($outerScope);
|
||||
|
||||
// Inner scope
|
||||
$innerScope = ErrorScopeContext::generic(
|
||||
'inner',
|
||||
'inner.operation'
|
||||
);
|
||||
$this->errorScope->enter($innerScope);
|
||||
|
||||
// Exception gets inner scope context
|
||||
$exception = $this->factory->create(
|
||||
RuntimeException::class,
|
||||
'Inner error'
|
||||
);
|
||||
|
||||
$context = $this->contextProvider->get($exception);
|
||||
expect($context->operation)->toBe('inner.operation');
|
||||
|
||||
// Exit inner scope
|
||||
$this->errorScope->exit();
|
||||
|
||||
// New exception gets outer scope context
|
||||
$exception2 = $this->factory->create(
|
||||
RuntimeException::class,
|
||||
'Outer error'
|
||||
);
|
||||
|
||||
$context2 = $this->contextProvider->get($exception2);
|
||||
expect($context2->operation)->toBe('outer.operation');
|
||||
});
|
||||
});
|
||||
|
||||
// Helper function to create mock request
|
||||
function createMockRequest(): \App\Framework\Http\HttpRequest
|
||||
{
|
||||
return new \App\Framework\Http\HttpRequest(
|
||||
method: \App\Framework\Http\Method::GET,
|
||||
path: '/test',
|
||||
id: new \App\Framework\Http\RequestId('test-secret')
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user