Skip to content

Commit 30dd874

Browse files
[11.x] Http Client: fake connection exception (#53485)
* Support faking connection exception * Fix linting issues * Fix another linting issue * formatting --------- Co-authored-by: Taylor Otwell <[email protected]>
1 parent 923ee74 commit 30dd874

File tree

3 files changed

+95
-6
lines changed

3 files changed

+95
-6
lines changed

src/Illuminate/Http/Client/Factory.php

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Illuminate\Http\Client;
44

55
use Closure;
6+
use GuzzleHttp\Exception\ConnectException;
67
use GuzzleHttp\Middleware;
78
use GuzzleHttp\Promise\Create;
89
use GuzzleHttp\Promise\PromiseInterface;
@@ -164,6 +165,22 @@ public static function response($body = null, $status = 200, $headers = [])
164165
return Create::promiseFor($response);
165166
}
166167

168+
/**
169+
* Create a new connection exception for use during stubbing.
170+
*
171+
* @param string|null $message
172+
* @return \GuzzleHttp\Promise\PromiseInterface
173+
*/
174+
public static function failedConnection($message = null)
175+
{
176+
return function ($request) use ($message) {
177+
return Create::rejectionFor(new ConnectException(
178+
$message ?? "cURL error 6: Could not resolve host: {$request->toPsrRequest()->getUri()->getHost()} (see https://curl.haxx.se/libcurl/c/libcurl-errors.html) for {$request->toPsrRequest()->getUri()}.",
179+
$request->toPsrRequest(),
180+
));
181+
};
182+
}
183+
167184
/**
168185
* Get an invokable object that returns a sequence of responses in order for use during stubbing.
169186
*
@@ -203,9 +220,11 @@ public function fake($callback = null)
203220

204221
$this->stubCallbacks = $this->stubCallbacks->merge(collect([
205222
function ($request, $options) use ($callback) {
206-
$response = $callback instanceof Closure
207-
? $callback($request, $options)
208-
: $callback;
223+
$response = $callback;
224+
225+
while ($response instanceof Closure) {
226+
$response = $response($request, $options);
227+
}
209228

210229
if ($response instanceof PromiseInterface) {
211230
$options['on_stats'](new TransferStats(

src/Illuminate/Http/Client/ResponseSequence.php

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Illuminate\Http\Client;
44

5+
use Closure;
56
use Illuminate\Support\Traits\Macroable;
67
use OutOfBoundsException;
78

@@ -71,7 +72,7 @@ public function pushStatus(int $status, array $headers = [])
7172
}
7273

7374
/**
74-
* Push response with the contents of a file as the body to the sequence.
75+
* Push a response with the contents of a file as the body to the sequence.
7576
*
7677
* @param string $filePath
7778
* @param int $status
@@ -87,6 +88,19 @@ public function pushFile(string $filePath, int $status = 200, array $headers = [
8788
);
8889
}
8990

91+
/**
92+
* Push a connection exception to the sequence.
93+
*
94+
* @param string|null $message
95+
* @return $this
96+
*/
97+
public function pushFailedConnection($message = null)
98+
{
99+
return $this->pushResponse(
100+
Factory::failedConnection($message)
101+
);
102+
}
103+
90104
/**
91105
* Push a response to the sequence.
92106
*
@@ -137,11 +151,12 @@ public function isEmpty()
137151
/**
138152
* Get the next response in the sequence.
139153
*
154+
* @param \Illuminate\Http\Client\Request $request
140155
* @return mixed
141156
*
142157
* @throws \OutOfBoundsException
143158
*/
144-
public function __invoke()
159+
public function __invoke($request)
145160
{
146161
if ($this->failWhenEmpty && $this->isEmpty()) {
147162
throw new OutOfBoundsException('A request was made, but the response sequence is empty.');
@@ -151,6 +166,8 @@ public function __invoke()
151166
return value($this->emptyResponse ?? Factory::response());
152167
}
153168

154-
return array_shift($this->responses);
169+
$response = array_shift($this->responses);
170+
171+
return $response instanceof Closure ? $response($request) : $response;
155172
}
156173
}

tests/Http/HttpClientTest.php

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2196,6 +2196,59 @@ public function testRequestsWillBeWaitingSleepMillisecondsReceivedInBackoffArray
21962196
]);
21972197
}
21982198

2199+
public function testFakeConnectionException()
2200+
{
2201+
$this->factory->fake($this->factory->failedConnection('Fake'));
2202+
2203+
$this->expectException(ConnectionException::class);
2204+
$this->expectExceptionMessage('Fake');
2205+
2206+
$this->factory->post('https://example.com');
2207+
}
2208+
2209+
public function testFakeConnectionExceptionWithinFakeClosure()
2210+
{
2211+
$this->factory->fake(fn () => $this->factory->failedConnection('Fake'));
2212+
2213+
$this->expectException(ConnectionException::class);
2214+
$this->expectExceptionMessage('Fake');
2215+
2216+
$this->factory->post('https://example.com');
2217+
}
2218+
2219+
public function testFakeConnectionExceptionWithinArray()
2220+
{
2221+
$this->factory->fake(['*' => $this->factory->failedConnection('Fake')]);
2222+
2223+
$this->expectException(ConnectionException::class);
2224+
$this->expectExceptionMessage('Fake');
2225+
2226+
$this->factory->post('https://example.com');
2227+
}
2228+
2229+
public function testFakeConnectionExceptionWithinSequence()
2230+
{
2231+
$this->factory->fake([
2232+
'*' => $this->factory->sequence()
2233+
->pushFailedConnection('Fake')
2234+
->push('Success'),
2235+
]);
2236+
2237+
$exception = null;
2238+
2239+
$response = $this->factory->retry(3, function ($attempt, $e) use (&$exception) {
2240+
$exception = $e;
2241+
2242+
return true;
2243+
})->post('https://example.com');
2244+
2245+
$this->assertSame('Success', $response->body());
2246+
2247+
$this->assertNotNull($exception);
2248+
$this->assertInstanceOf(ConnectionException::class, $exception);
2249+
$this->assertSame('Fake', $exception->getMessage());
2250+
}
2251+
21992252
public function testMiddlewareRunsWhenFaked()
22002253
{
22012254
$this->factory->fake(function (Request $request) {

0 commit comments

Comments
 (0)