assertEquals(\RuntimeException::class, $context->class); $this->assertEquals('RuntimeException', $context->getShortClass()); $this->assertEquals('Test error', $context->message); $this->assertEquals(123, $context->code); $this->assertNotEmpty($context->file); $this->assertGreaterThan(0, $context->line); } public function test_captures_stack_trace(): void { $exception = $this->createException(); $context = ExceptionContext::fromThrowable($exception); $this->assertNotEmpty($context->stackTrace); $this->assertContainsOnlyInstancesOf( \App\Framework\Logging\ValueObjects\StackFrame::class, $context->stackTrace ); } public function test_captures_previous_exception(): void { $previous = new \RuntimeException('Previous error'); $exception = new \RuntimeException('Current error', 0, $previous); $context = ExceptionContext::fromThrowable($exception); $this->assertNotNull($context->previous); $this->assertEquals('Previous error', $context->previous->message); $this->assertEquals(1, $context->getChainLength()); } public function test_captures_exception_chain(): void { $first = new \RuntimeException('First'); $second = new \RuntimeException('Second', 0, $first); $third = new \RuntimeException('Third', 0, $second); $context = ExceptionContext::fromThrowable($third); $this->assertEquals(2, $context->getChainLength()); $chain = $context->getChain(); $this->assertCount(3, $chain); $this->assertEquals('Third', $chain[0]->message); $this->assertEquals('Second', $chain[1]->message); $this->assertEquals('First', $chain[2]->message); } public function test_get_top_frames(): void { $exception = $this->createException(); $context = ExceptionContext::fromThrowable($exception); $topFrames = $context->getTopFrames(3); $this->assertCount(min(3, count($context->stackTrace)), $topFrames); } public function test_to_array(): void { $exception = new \RuntimeException('Test'); $context = ExceptionContext::fromThrowable($exception); $array = $context->toArray(); $this->assertArrayHasKey('class', $array); $this->assertArrayHasKey('message', $array); $this->assertArrayHasKey('file', $array); $this->assertArrayHasKey('line', $array); $this->assertArrayHasKey('stack_trace', $array); } public function test_to_compact_array(): void { $exception = new \RuntimeException('Test'); $context = ExceptionContext::fromThrowable($exception); $array = $context->toCompactArray(); $this->assertArrayHasKey('class', $array); $this->assertArrayHasKey('message', $array); $this->assertArrayHasKey('location', $array); $this->assertArrayNotHasKey('stack_trace', $array); } public function test_get_summary(): void { $exception = new \RuntimeException('Test error'); $context = ExceptionContext::fromThrowable($exception); $summary = $context->getSummary(); $this->assertStringContainsString('RuntimeException', $summary); $this->assertStringContainsString('Test error', $summary); } public function test_json_serializable(): void { $exception = new \RuntimeException('Test'); $context = ExceptionContext::fromThrowable($exception); $json = json_encode($context); $this->assertNotFalse($json); $decoded = json_decode($json, true); $this->assertArrayHasKey('class', $decoded); $this->assertArrayHasKey('message', $decoded); } private function createException(): \Exception { try { $this->throwTestException(); } catch (\Exception $e) { return $e; } } private function throwTestException(): void { throw new \RuntimeException('Test exception'); } }