33namespace Overtrue \LaravelOpenTelemetry \Tests \Hooks \Illuminate \Http ;
44
55use Mockery ;
6+ use OpenTelemetry \API \Instrumentation \CachedInstrumentation ;
67use Overtrue \LaravelOpenTelemetry \Facades \Measure ;
78use Overtrue \LaravelOpenTelemetry \Hooks \Illuminate \Http \Kernel ;
89use Overtrue \LaravelOpenTelemetry \Tests \TestCase ;
@@ -24,8 +25,9 @@ public function test_adds_trace_id_to_response_header()
2425 $ expectedTraceId = '12345678901234567890123456789012 ' ;
2526 Measure::shouldReceive ('traceId ' )->andReturn ($ expectedTraceId );
2627
27- // Create hook - 在测试环境中直接实例化而不是通过 hook() 方法
28- $ hook = new Kernel ;
28+ // Create hook
29+ $ instrumentation = new CachedInstrumentation ('test ' );
30+ $ hook = Kernel::hook ($ instrumentation );
2931
3032 // Create response
3133 $ response = new Response ;
@@ -47,8 +49,9 @@ public function test_does_not_add_header_when_config_is_null()
4749 // Set configuration to null
4850 config (['otel.response_trace_header_name ' => null ]);
4951
50- // Create hook - 在测试环境中直接实例化
51- $ hook = new Kernel ;
52+ // Create hook
53+ $ instrumentation = new CachedInstrumentation ('test ' );
54+ $ hook = Kernel::hook ($ instrumentation );
5255
5356 // Create response
5457 $ response = new Response ;
@@ -70,8 +73,9 @@ public function test_does_not_add_header_when_config_is_empty()
7073 // Set configuration to empty string
7174 config (['otel.response_trace_header_name ' => '' ]);
7275
73- // Create hook - 在测试环境中直接实例化
74- $ hook = new Kernel ;
76+ // Create hook
77+ $ instrumentation = new CachedInstrumentation ('test ' );
78+ $ hook = Kernel::hook ($ instrumentation );
7579
7680 // Create response
7781 $ response = new Response ;
@@ -96,8 +100,9 @@ public function test_does_not_add_header_when_trace_id_is_empty()
96100 // Mock empty trace ID
97101 Measure::shouldReceive ('traceId ' )->andReturn ('' );
98102
99- // Create hook - 在测试环境中直接实例化
100- $ hook = new Kernel ;
103+ // Create hook
104+ $ instrumentation = new CachedInstrumentation ('test ' );
105+ $ hook = Kernel::hook ($ instrumentation );
101106
102107 // Create response
103108 $ response = new Response ;
@@ -123,8 +128,9 @@ public function test_does_not_add_header_when_trace_id_is_all_zeros()
123128 $ allZerosTraceId = '00000000000000000000000000000000 ' ;
124129 Measure::shouldReceive ('traceId ' )->andReturn ($ allZerosTraceId );
125130
126- // Create hook - 在测试环境中直接实例化
127- $ hook = new Kernel ;
131+ // Create hook
132+ $ instrumentation = new CachedInstrumentation ('test ' );
133+ $ hook = Kernel::hook ($ instrumentation );
128134
129135 // Create response
130136 $ response = new Response ;
@@ -149,8 +155,9 @@ public function test_handles_exception_gracefully()
149155 // Mock Measure::traceId() to throw exception
150156 Measure::shouldReceive ('traceId ' )->andThrow (new \Exception ('Test exception ' ));
151157
152- // Create hook - 在测试环境中直接实例化
153- $ hook = new Kernel ;
158+ // Create hook
159+ $ instrumentation = new CachedInstrumentation ('test ' );
160+ $ hook = Kernel::hook ($ instrumentation );
154161
155162 // Create response
156163 $ response = new Response ;
@@ -176,8 +183,9 @@ public function test_uses_custom_header_name()
176183 $ expectedTraceId = '98765432109876543210987654321098 ' ;
177184 Measure::shouldReceive ('traceId ' )->andReturn ($ expectedTraceId );
178185
179- // Create hook - 在测试环境中直接实例化
180- $ hook = new Kernel ;
186+ // Create hook
187+ $ instrumentation = new CachedInstrumentation ('test ' );
188+ $ hook = Kernel::hook ($ instrumentation );
181189
182190 // Create response
183191 $ response = new Response ;
@@ -195,6 +203,205 @@ public function test_uses_custom_header_name()
195203 $ this ->assertNull ($ response ->headers ->get ('X-Trace-Id ' )); // Default header should be empty
196204 }
197205
206+ public function test_detects_frankenphp_worker_mode_correctly ()
207+ {
208+ // Create hook
209+ $ instrumentation = new CachedInstrumentation ('test ' );
210+ $ hook = Kernel::hook ($ instrumentation );
211+
212+ // Use reflection to access private method
213+ $ reflection = new \ReflectionClass ($ hook );
214+ $ method = $ reflection ->getMethod ('isFrankenPhpWorkerMode ' );
215+ $ method ->setAccessible (true );
216+
217+ // Test non-FrankenPHP environment
218+ $ result = $ method ->invoke ($ hook );
219+ $ this ->assertFalse ($ result );
220+
221+ // Note: Testing true case would require mocking global functions like php_sapi_name()
222+ // which is complex in unit tests, better tested in integration tests
223+ }
224+
225+ public function test_handles_worker_request_start_with_event_function ()
226+ {
227+ // Create hook
228+ $ instrumentation = new CachedInstrumentation ('test ' );
229+ $ hook = Kernel::hook ($ instrumentation );
230+
231+ // Use reflection to access private method
232+ $ reflection = new \ReflectionClass ($ hook );
233+ $ method = $ reflection ->getMethod ('handleWorkerRequestStart ' );
234+ $ method ->setAccessible (true );
235+
236+ // Mock global event function
237+ if (! function_exists ('event ' )) {
238+ eval ('function event($name) { /* Mock implementation */ } ' );
239+ }
240+
241+ // Execute method - should not throw exception
242+ $ method ->invoke ($ hook );
243+
244+ // Test passes if no exception is thrown
245+ $ this ->assertTrue (true );
246+ }
247+
248+ public function test_handles_worker_request_end_with_cleanup ()
249+ {
250+ // Create hook
251+ $ instrumentation = new CachedInstrumentation ('test ' );
252+ $ hook = Kernel::hook ($ instrumentation );
253+
254+ // Use reflection to access private method
255+ $ reflection = new \ReflectionClass ($ hook );
256+ $ method = $ reflection ->getMethod ('handleWorkerRequestEnd ' );
257+ $ method ->setAccessible (true );
258+
259+ // Execute method - should not throw exception
260+ $ method ->invoke ($ hook );
261+
262+ // Test passes if no exception is thrown
263+ $ this ->assertTrue (true );
264+ }
265+
266+ public function test_resets_opentelemetry_context_safely ()
267+ {
268+ // Create hook
269+ $ instrumentation = new CachedInstrumentation ('test ' );
270+ $ hook = Kernel::hook ($ instrumentation );
271+
272+ // Use reflection to access private method
273+ $ reflection = new \ReflectionClass ($ hook );
274+ $ method = $ reflection ->getMethod ('resetOpenTelemetryContext ' );
275+ $ method ->setAccessible (true );
276+
277+ // Execute method - should not throw exception even if span operations fail
278+ $ method ->invoke ($ hook );
279+
280+ // Test passes if no exception is thrown
281+ $ this ->assertTrue (true );
282+ }
283+
284+ public function test_cleans_up_worker_request_resources ()
285+ {
286+ // Create hook
287+ $ instrumentation = new CachedInstrumentation ('test ' );
288+ $ hook = Kernel::hook ($ instrumentation );
289+
290+ // Use reflection to access private method
291+ $ reflection = new \ReflectionClass ($ hook );
292+ $ method = $ reflection ->getMethod ('cleanupWorkerRequestResources ' );
293+ $ method ->setAccessible (true );
294+
295+ // Execute method - should not throw exception
296+ $ method ->invoke ($ hook );
297+
298+ // Test passes if no exception is thrown
299+ $ this ->assertTrue (true );
300+ }
301+
302+ public function test_resets_global_state_safely ()
303+ {
304+ // Create hook
305+ $ instrumentation = new CachedInstrumentation ('test ' );
306+ $ hook = Kernel::hook ($ instrumentation );
307+
308+ // Use reflection to access private method
309+ $ reflection = new \ReflectionClass ($ hook );
310+ $ method = $ reflection ->getMethod ('resetGlobalState ' );
311+ $ method ->setAccessible (true );
312+
313+ // Execute method - should not throw exception
314+ $ method ->invoke ($ hook );
315+
316+ // Test passes if no exception is thrown
317+ $ this ->assertTrue (true );
318+ }
319+
320+ public function test_instrument_method_registers_hooks ()
321+ {
322+ // Create hook
323+ $ instrumentation = new CachedInstrumentation ('test ' );
324+ $ hook = Kernel::hook ($ instrumentation );
325+
326+ // Execute instrument method - should not throw exception
327+ $ hook ->instrument ();
328+
329+ // Test passes if no exception is thrown during hook registration
330+ $ this ->assertTrue (true );
331+ }
332+
333+ public function test_handles_worker_start_exceptions_gracefully ()
334+ {
335+ // Create hook
336+ $ instrumentation = new CachedInstrumentation ('test ' );
337+ $ hook = Kernel::hook ($ instrumentation );
338+
339+ // Use reflection to test private method that handles exceptions
340+ $ reflection = new \ReflectionClass ($ hook );
341+ $ startMethod = $ reflection ->getMethod ('handleWorkerRequestStart ' );
342+ $ startMethod ->setAccessible (true );
343+
344+ // This should not throw exception even if internal operations fail
345+ $ startMethod ->invoke ($ hook );
346+
347+ // Test passes if no exception propagates
348+ $ this ->assertTrue (true );
349+ }
350+
351+ public function test_handles_worker_end_exceptions_gracefully ()
352+ {
353+ // Create hook
354+ $ instrumentation = new CachedInstrumentation ('test ' );
355+ $ hook = Kernel::hook ($ instrumentation );
356+
357+ // Use reflection to test private method that handles exceptions
358+ $ reflection = new \ReflectionClass ($ hook );
359+ $ endMethod = $ reflection ->getMethod ('handleWorkerRequestEnd ' );
360+ $ endMethod ->setAccessible (true );
361+
362+ // This should not throw exception even if internal operations fail
363+ $ endMethod ->invoke ($ hook );
364+
365+ // Test passes if no exception propagates
366+ $ this ->assertTrue (true );
367+ }
368+
369+ public function test_handles_context_reset_exceptions_gracefully ()
370+ {
371+ // Create hook
372+ $ instrumentation = new CachedInstrumentation ('test ' );
373+ $ hook = Kernel::hook ($ instrumentation );
374+
375+ // Use reflection to test private method that handles exceptions
376+ $ reflection = new \ReflectionClass ($ hook );
377+ $ resetMethod = $ reflection ->getMethod ('resetOpenTelemetryContext ' );
378+ $ resetMethod ->setAccessible (true );
379+
380+ // This should not throw exception even if span operations fail
381+ $ resetMethod ->invoke ($ hook );
382+
383+ // Test passes if no exception propagates
384+ $ this ->assertTrue (true );
385+ }
386+
387+ public function test_handles_cleanup_exceptions_gracefully ()
388+ {
389+ // Create hook
390+ $ instrumentation = new CachedInstrumentation ('test ' );
391+ $ hook = Kernel::hook ($ instrumentation );
392+
393+ // Use reflection to test private method that handles exceptions
394+ $ reflection = new \ReflectionClass ($ hook );
395+ $ cleanupMethod = $ reflection ->getMethod ('cleanupWorkerRequestResources ' );
396+ $ cleanupMethod ->setAccessible (true );
397+
398+ // This should not throw exception even if cleanup operations fail
399+ $ cleanupMethod ->invoke ($ hook );
400+
401+ // Test passes if no exception propagates
402+ $ this ->assertTrue (true );
403+ }
404+
198405 protected function tearDown (): void
199406 {
200407 Mockery::close ();
0 commit comments