44
55namespace Tests \Feature ;
66
7- use Closure ;
87use Illuminate \Http \Request ;
98use Illuminate \Http \Response ;
109use Illuminate \Support \Facades \Config ;
1312
1413beforeEach (function () {
1514 // Reset circuit breaker state before each test
16- app ()->instance (CircuitBreaker::class, new CircuitBreaker () );
15+ app ()->instance (CircuitBreaker::class, new CircuitBreaker );
1716});
1817
1918describe ('CheckCircuitBreakers Middleware ' , function () {
2019 describe ('Handle Method ' , function () {
2120 it ('allows request through when no circuit breakers are specified ' , function () {
22- $ middleware = new CheckCircuitBreakers () ;
21+ $ middleware = new CheckCircuitBreakers ;
2322 $ request = Request::create ('/test ' , 'GET ' );
24-
23+
2524 $ next = function ($ req ) {
2625 return response ('success ' , 200 );
2726 };
28-
27+
2928 $ response = $ middleware ->handle ($ request , $ next );
30-
29+
3130 expect ($ response ->getStatusCode ())->toBe (200 )
3231 ->and ($ response ->getContent ())->toBe ('success ' );
3332 });
3433
3534 it ('allows request through when circuit breakers are closed ' , function () {
36- $ middleware = new CheckCircuitBreakers () ;
35+ $ middleware = new CheckCircuitBreakers ;
3736 $ request = Request::create ('/test ' , 'GET ' );
38-
37+
3938 $ next = function ($ req ) {
4039 return response ('success ' , 200 );
4140 };
42-
41+
4342 $ response = $ middleware ->handle ($ request , $ next , 'breaker1 ' , 'breaker2 ' );
44-
43+
4544 expect ($ response ->getStatusCode ())->toBe (200 )
4645 ->and ($ response ->getContent ())->toBe ('success ' );
4746 });
4847
4948 it ('denies request when first circuit breaker is open ' , function () {
5049 $ circuitBreaker = app (CircuitBreaker::class);
5150 $ circuitBreaker ->forceOpen ('breaker1 ' );
52-
53- $ middleware = new CheckCircuitBreakers () ;
51+
52+ $ middleware = new CheckCircuitBreakers ;
5453 $ request = Request::create ('/test ' , 'GET ' );
55-
54+
5655 $ nextCalled = false ;
5756 $ next = function ($ req ) use (&$ nextCalled ) {
5857 $ nextCalled = true ;
58+
5959 return response ('success ' , 200 );
6060 };
61-
61+
6262 $ response = $ middleware ->handle ($ request , $ next , 'breaker1 ' , 'breaker2 ' );
63-
63+
6464 expect ($ response ->getStatusCode ())->toBe (503 )
6565 ->and ($ nextCalled )->toBeFalse ()
6666 ->and ($ response ->headers ->get ('X-Circuit-Breaker ' ))->toBe ('breaker1 ' )
7171 it ('denies request when second circuit breaker is open ' , function () {
7272 $ circuitBreaker = app (CircuitBreaker::class);
7373 $ circuitBreaker ->forceOpen ('breaker2 ' );
74-
75- $ middleware = new CheckCircuitBreakers () ;
74+
75+ $ middleware = new CheckCircuitBreakers ;
7676 $ request = Request::create ('/test ' , 'GET ' );
77-
77+
7878 $ nextCalled = false ;
7979 $ next = function ($ req ) use (&$ nextCalled ) {
8080 $ nextCalled = true ;
81+
8182 return response ('success ' , 200 );
8283 };
83-
84+
8485 $ response = $ middleware ->handle ($ request , $ next , 'breaker1 ' , 'breaker2 ' );
85-
86+
8687 expect ($ response ->getStatusCode ())->toBe (503 )
8788 ->and ($ nextCalled )->toBeFalse ()
8889 ->and ($ response ->headers ->get ('X-Circuit-Breaker ' ))->toBe ('breaker2 ' )
9394 $ circuitBreaker = app (CircuitBreaker::class);
9495 $ circuitBreaker ->forceOpen ('breaker1 ' );
9596 $ circuitBreaker ->forceOpen ('breaker2 ' );
96-
97- $ middleware = new CheckCircuitBreakers () ;
97+
98+ $ middleware = new CheckCircuitBreakers ;
9899 $ request = Request::create ('/test ' , 'GET ' );
99-
100+
100101 $ next = function ($ req ) {
101102 return response ('success ' , 200 );
102103 };
103-
104+
104105 $ response = $ middleware ->handle ($ request , $ next , 'breaker1 ' , 'breaker2 ' );
105-
106+
106107 expect ($ response ->getStatusCode ())->toBe (503 )
107108 ->and ($ response ->headers ->get ('X-Circuit-Breaker ' ))->toBe ('breaker1 ' );
108109 });
109110 });
110111
111112 describe ('Deny Method ' , function () {
112113 it ('returns 503 response with correct headers ' , function () {
113- $ middleware = new CheckCircuitBreakers () ;
114-
114+ $ middleware = new CheckCircuitBreakers ;
115+
115116 // Use reflection to test the protected method
116117 $ reflection = new \ReflectionClass ($ middleware );
117118 $ denyMethod = $ reflection ->getMethod ('deny ' );
118119 $ denyMethod ->setAccessible (true );
119-
120+
120121 $ response = $ denyMethod ->invoke ($ middleware , 'test-breaker ' );
121-
122+
122123 expect ($ response )->toBeInstanceOf (Response::class)
123124 ->and ($ response ->getStatusCode ())->toBe (503 )
124125 ->and ($ response ->getContent ())->toBe ('' )
130131
131132 describe ('RetryAfter Method ' , function () {
132133 it ('returns default 300 seconds when state has no last failure time ' , function () {
133- $ middleware = new CheckCircuitBreakers () ;
134-
134+ $ middleware = new CheckCircuitBreakers ;
135+
135136 // Use reflection to test the protected method
136137 $ reflection = new \ReflectionClass ($ middleware );
137138 $ retryAfterMethod = $ reflection ->getMethod ('retryAfter ' );
138139 $ retryAfterMethod ->setAccessible (true );
139-
140+
140141 $ retryAfter = $ retryAfterMethod ->invoke ($ middleware , 'non-existent-breaker ' );
141-
142+
142143 expect ($ retryAfter )->toBe (300 );
143144 });
144145
145146 it ('calculates retry after based on elapsed time from failure ' , function () {
146147 $ circuitBreaker = app (CircuitBreaker::class);
147-
148+
148149 // Record a failure to create a state with lastFailureAt
149150 $ circuitBreaker ->recordFailure ('test-breaker ' , 300 );
150-
151- $ middleware = new CheckCircuitBreakers () ;
152-
151+
152+ $ middleware = new CheckCircuitBreakers ;
153+
153154 // Use reflection to test the protected method
154155 $ reflection = new \ReflectionClass ($ middleware );
155156 $ retryAfterMethod = $ reflection ->getMethod ('retryAfter ' );
156157 $ retryAfterMethod ->setAccessible (true );
157-
158+
158159 $ retryAfter = $ retryAfterMethod ->invoke ($ middleware , 'test-breaker ' );
159-
160+
160161 expect ($ retryAfter )->toBeInt ()
161162 ->and ($ retryAfter )->toBeGreaterThanOrEqual (0 )
162163 ->and ($ retryAfter )->toBeLessThanOrEqual (300 );
163164 });
164165
165166 it ('uses decay seconds from state when available ' , function () {
166167 $ circuitBreaker = app (CircuitBreaker::class);
167-
168+
168169 // Record a failure with custom decay seconds
169170 $ circuitBreaker ->recordFailure ('test-breaker ' , 600 );
170-
171- $ middleware = new CheckCircuitBreakers () ;
172-
171+
172+ $ middleware = new CheckCircuitBreakers ;
173+
173174 // Use reflection to test the protected method
174175 $ reflection = new \ReflectionClass ($ middleware );
175176 $ retryAfterMethod = $ reflection ->getMethod ('retryAfter ' );
176177 $ retryAfterMethod ->setAccessible (true );
177-
178+
178179 $ retryAfter = $ retryAfterMethod ->invoke ($ middleware , 'test-breaker ' );
179-
180+
180181 expect ($ retryAfter )->toBeInt ()
181182 ->and ($ retryAfter )->toBeGreaterThanOrEqual (0 )
182183 ->and ($ retryAfter )->toBeLessThanOrEqual (600 );
183184 });
184185
185186 it ('falls back to config when state has no decay seconds ' , function () {
186187 Config::set ('monitor.circuit_breaker.default_decay_seconds ' , 450 );
187-
188+
188189 $ circuitBreaker = app (CircuitBreaker::class);
189-
190+
190191 // Create a state but without decay seconds (by using the state directly)
191192 // First record a failure, then manually modify to remove decay seconds
192193 $ circuitBreaker ->recordFailure ('config-fallback-breaker ' , null );
193-
194- $ middleware = new CheckCircuitBreakers () ;
195-
194+
195+ $ middleware = new CheckCircuitBreakers ;
196+
196197 // Use reflection to test the protected method
197198 $ reflection = new \ReflectionClass ($ middleware );
198199 $ retryAfterMethod = $ reflection ->getMethod ('retryAfter ' );
199200 $ retryAfterMethod ->setAccessible (true );
200-
201+
201202 $ retryAfter = $ retryAfterMethod ->invoke ($ middleware , 'config-fallback-breaker ' );
202-
203+
203204 expect ($ retryAfter )->toBeInt ()
204205 ->and ($ retryAfter )->toBeGreaterThanOrEqual (0 )
205206 ->and ($ retryAfter )->toBeLessThanOrEqual (450 );
206207 });
207208
208209 it ('returns 0 when decay time has fully elapsed ' , function () {
209210 $ circuitBreaker = app (CircuitBreaker::class);
210-
211+
211212 // Create a state with an old failure time
212213 $ circuitBreaker ->recordFailure ('old-breaker ' , 1 );
213-
214+
214215 // Wait for decay to pass
215216 sleep (2 );
216-
217- $ middleware = new CheckCircuitBreakers () ;
218-
217+
218+ $ middleware = new CheckCircuitBreakers ;
219+
219220 // Use reflection to test the protected method
220221 $ reflection = new \ReflectionClass ($ middleware );
221222 $ retryAfterMethod = $ reflection ->getMethod ('retryAfter ' );
222223 $ retryAfterMethod ->setAccessible (true );
223-
224+
224225 $ retryAfter = $ retryAfterMethod ->invoke ($ middleware , 'old-breaker ' );
225-
226+
226227 expect ($ retryAfter )->toBeGreaterThanOrEqual (0 )
227228 ->and ($ retryAfter )->toBeLessThanOrEqual (1 );
228229 });
231232 describe ('Integration Tests ' , function () {
232233 it ('handles complete flow from open breaker to denied request ' , function () {
233234 $ circuitBreaker = app (CircuitBreaker::class);
234-
235+
235236 // Record enough failures to open the breaker
236237 for ($ i = 0 ; $ i < 5 ; $ i ++) {
237238 $ circuitBreaker ->recordFailure ('integration-breaker ' );
238239 }
239-
240- $ middleware = new CheckCircuitBreakers () ;
240+
241+ $ middleware = new CheckCircuitBreakers ;
241242 $ request = Request::create ('/api/test ' , 'GET ' );
242-
243+
243244 $ next = function ($ req ) {
244245 return response ()->json (['status ' => 'success ' ]);
245246 };
246-
247+
247248 $ response = $ middleware ->handle ($ request , $ next , 'integration-breaker ' );
248-
249+
249250 expect ($ response ->getStatusCode ())->toBe (503 )
250251 ->and ($ response ->headers ->get ('X-Circuit-Breaker ' ))->toBe ('integration-breaker ' )
251252 ->and ($ response ->headers ->get ('X-Circuit-Breaker-Status ' ))->toBe ('open ' )
252253 ->and ($ response ->headers ->has ('Retry-After ' ))->toBeTrue ();
253254 });
254255 });
255- });
256+ });
0 commit comments