@@ -30,13 +30,84 @@ class EntrypointActionTest extends TestCase
3030 public function testGetEntrypointWithProviderProcessor (): void
3131 {
3232 $ expected = new Entrypoint (new ResourceNameCollection (['dummies ' ]));
33+
3334 $ resourceNameCollectionFactory = $ this ->createMock (ResourceNameCollectionFactoryInterface::class);
3435 $ resourceNameCollectionFactory ->method ('create ' )->willReturn (new ResourceNameCollection (['dummies ' ]));
36+
3537 $ provider = $ this ->createMock (ProviderInterface::class);
3638 $ provider ->expects ($ this ->once ())->method ('provide ' )->willReturn ($ expected );
39+
3740 $ processor = $ this ->createMock (ProcessorInterface::class);
3841 $ processor ->expects ($ this ->once ())->method ('process ' )->willReturnArgument (0 );
42+
3943 $ entrypoint = new EntrypointAction ($ resourceNameCollectionFactory , $ provider , $ processor );
4044 $ this ->assertEquals ($ expected , $ entrypoint (Request::create ('/ ' )));
4145 }
46+
47+ public function testInvokeCachesResourceNameCollection (): void
48+ {
49+ $ resourceNameCollectionFactory = $ this ->createMock (ResourceNameCollectionFactoryInterface::class);
50+ $ resourceNameCollectionFactory ->expects ($ this ->once ())
51+ ->method ('create ' )
52+ ->willReturn (new ResourceNameCollection (['Dummy ' ]));
53+
54+ $ provider = $ this ->createMock (ProviderInterface::class);
55+ $ processor = $ this ->createMock (ProcessorInterface::class);
56+
57+ $ action = new EntrypointAction ($ resourceNameCollectionFactory , $ provider , $ processor );
58+
59+ $ request = new Request ();
60+
61+ $ provider ->expects ($ this ->exactly (2 ))
62+ ->method ('provide ' )
63+ ->willReturn (new Entrypoint (new ResourceNameCollection (['Dummy ' ])));
64+
65+ $ processor ->expects ($ this ->exactly (2 ))
66+ ->method ('process ' );
67+
68+ $ action ($ request );
69+
70+ // Test that second call does not call factory again (lazy-loading/caching)
71+ $ action ($ request );
72+ }
73+
74+ /**
75+ * This test ensures that instances are isolated and don't leak state.
76+ * In Worker mode (FrankenPHP/Swoole), static properties would cause a state leak between instances.
77+ */
78+ public function testInstancesAreIsolated (): void
79+ {
80+ $ processor = $ this ->createMock (ProcessorInterface::class);
81+ $ provider = $ this ->createMock (ProviderInterface::class);
82+
83+ // Instance 1: configured with ResourceA
84+ $ factory1 = $ this ->createMock (ResourceNameCollectionFactoryInterface::class);
85+ $ factory1 ->method ('create ' )->willReturn (new ResourceNameCollection (['ResourceA ' ]));
86+ $ action1 = new EntrypointAction ($ factory1 , $ provider , $ processor );
87+
88+ // Instance 2: configured with ResourceB
89+ $ factory2 = $ this ->createMock (ResourceNameCollectionFactoryInterface::class);
90+ $ factory2 ->method ('create ' )->willReturn (new ResourceNameCollection (['ResourceB ' ]));
91+ $ action2 = new EntrypointAction ($ factory2 , $ provider , $ processor );
92+
93+ $ request = new Request ();
94+
95+ // 1. Trigger action 1
96+ $ action1 ($ request );
97+ // 2. Trigger action 2 (if static were used, this would overwrite action 1's state)
98+ $ action2 ($ request );
99+
100+ // Verification of isolation:
101+ $ this ->assertEquals (
102+ new ResourceNameCollection (['ResourceA ' ]),
103+ $ action1 ->provide ()->getResourceNameCollection (),
104+ "Instance 1 was polluted by Instance 2 (likely due to a static property) "
105+ );
106+
107+ $ this ->assertEquals (
108+ new ResourceNameCollection (['ResourceB ' ]),
109+ $ action2 ->provide ()->getResourceNameCollection (),
110+ "Instance 2 has incorrect state. "
111+ );
112+ }
42113}
0 commit comments