Skip to content

Commit 3a84482

Browse files
authored
Merge pull request #89 from clue-labs/exception
Improve Exception messages for connection issues
2 parents b8582fa + 20859c2 commit 3a84482

File tree

5 files changed

+139
-10
lines changed

5 files changed

+139
-10
lines changed

examples/cli.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,10 @@
5050
});
5151
}, function (Exception $error) {
5252
echo 'CONNECTION ERROR: ' . $error->getMessage() . PHP_EOL;
53+
if ($error->getPrevious()) {
54+
echo $error->getPrevious()->getMessage() . PHP_EOL;
55+
}
5356
exit(1);
5457
});
5558

56-
5759
$loop->run();

examples/incr.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@
1212

1313
$client->get('test')->then(function ($result) {
1414
var_dump($result);
15+
}, function (Exception $e) {
16+
echo 'Error: ' . $e->getMessage() . PHP_EOL;
17+
if ($e->getPrevious()) {
18+
echo $e->getPrevious()->getMessage() . PHP_EOL;
19+
}
20+
exit(1);
1521
});
1622

1723
$client->end();

examples/publish.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,13 @@
1212

1313
$client = $factory->createLazyClient('localhost');
1414
$client->publish($channel, $message)->then(function ($received) {
15-
echo 'successfully published. Received by ' . $received . PHP_EOL;
15+
echo 'Successfully published. Received by ' . $received . PHP_EOL;
16+
}, function (Exception $e) {
17+
echo 'Unable to publish: ' . $e->getMessage() . PHP_EOL;
18+
if ($e->getPrevious()) {
19+
echo $e->getPrevious()->getMessage() . PHP_EOL;
20+
}
21+
exit(1);
1622
});
1723

1824
$client->end();

src/Factory.php

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public function createClient($target)
5555
$connecting = $this->connector->connect($parts['authority']);
5656
$deferred = new Deferred(function ($_, $reject) use ($connecting) {
5757
// connection cancelled, start with rejecting attempt, then clean up
58-
$reject(new \RuntimeException('Connection to database server cancelled'));
58+
$reject(new \RuntimeException('Connection to Redis server cancelled'));
5959

6060
// either close successful connection or cancel pending connection attempt
6161
$connecting->then(function (ConnectionInterface $connection) {
@@ -67,6 +67,12 @@ public function createClient($target)
6767
$protocol = $this->protocol;
6868
$promise = $connecting->then(function (ConnectionInterface $stream) use ($protocol) {
6969
return new StreamingClient($stream, $protocol->createResponseParser(), $protocol->createSerializer());
70+
}, function (\Exception $e) {
71+
throw new \RuntimeException(
72+
'Connection to Redis server failed because underlying transport connection failed',
73+
0,
74+
$e
75+
);
7076
});
7177

7278
if (isset($parts['auth'])) {
@@ -77,7 +83,12 @@ function () use ($client) {
7783
},
7884
function ($error) use ($client) {
7985
$client->close();
80-
throw $error;
86+
87+
throw new \RuntimeException(
88+
'Connection to Redis server failed because AUTH command failed',
89+
0,
90+
$error
91+
);
8192
}
8293
);
8394
});
@@ -91,7 +102,12 @@ function () use ($client) {
91102
},
92103
function ($error) use ($client) {
93104
$client->close();
94-
throw $error;
105+
106+
throw new \RuntimeException(
107+
'Connection to Redis server failed because SELECT command failed',
108+
0,
109+
$error
110+
);
95111
}
96112
);
97113
});
@@ -108,7 +124,7 @@ function ($error) use ($client) {
108124
return \React\Promise\Timer\timeout($deferred->promise(), $timeout, $this->loop)->then(null, function ($e) {
109125
if ($e instanceof TimeoutException) {
110126
throw new \RuntimeException(
111-
'Connection to database server timed out after ' . $e->getTimeout() . ' seconds'
127+
'Connection to Redis server timed out after ' . $e->getTimeout() . ' seconds'
112128
);
113129
}
114130
throw $e;

tests/FactoryStreamingClientTest.php

Lines changed: 103 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,43 @@ public function testWillWriteAuthCommandIfRedisUnixUriContainsUserInfo()
131131
$this->factory->createClient('redis+unix://hello:world@/tmp/redis.sock');
132132
}
133133

134+
public function testWillResolveWhenAuthCommandReceivesOkResponseIfRedisUriContainsUserInfo()
135+
{
136+
$stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('write'))->getMock();
137+
$stream->expects($this->once())->method('write')->with("*2\r\n$4\r\nauth\r\n$5\r\nworld\r\n");
138+
139+
$this->connector->expects($this->once())->method('connect')->willReturn(Promise\resolve($stream));
140+
$promise = $this->factory->createClient('redis://:world@localhost');
141+
142+
$stream->emit('data', array("+OK\r\n"));
143+
144+
$promise->then($this->expectCallableOnceWith($this->isInstanceOf('Clue\React\Redis\Client')));
145+
}
146+
147+
public function testWillRejectAndCloseAutomaticallyWhenAuthCommandReceivesErrorResponseIfRedisUriContainsUserInfo()
148+
{
149+
$stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('write', 'close'))->getMock();
150+
$stream->expects($this->once())->method('write')->with("*2\r\n$4\r\nauth\r\n$5\r\nworld\r\n");
151+
$stream->expects($this->once())->method('close');
152+
153+
$this->connector->expects($this->once())->method('connect')->willReturn(Promise\resolve($stream));
154+
$promise = $this->factory->createClient('redis://:world@localhost');
155+
156+
$stream->emit('data', array("-ERR invalid password\r\n"));
157+
158+
$promise->then(null, $this->expectCallableOnceWith(
159+
$this->logicalAnd(
160+
$this->isInstanceOf('RuntimeException'),
161+
$this->callback(function (\Exception $e) {
162+
return $e->getMessage() === 'Connection to Redis server failed because AUTH command failed';
163+
}),
164+
$this->callback(function (\Exception $e) {
165+
return $e->getPrevious()->getMessage() === 'ERR invalid password';
166+
})
167+
)
168+
));
169+
}
170+
134171
public function testWillWriteSelectCommandIfRedisUnixUriContainsDbQueryParameter()
135172
{
136173
$stream = $this->getMockBuilder('React\Socket\ConnectionInterface')->getMock();
@@ -140,19 +177,63 @@ public function testWillWriteSelectCommandIfRedisUnixUriContainsDbQueryParameter
140177
$this->factory->createClient('redis+unix:///tmp/redis.sock?db=demo');
141178
}
142179

180+
public function testWillResolveWhenSelectCommandReceivesOkResponseIfRedisUriContainsPath()
181+
{
182+
$stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('write'))->getMock();
183+
$stream->expects($this->once())->method('write')->with("*2\r\n$6\r\nselect\r\n$3\r\n123\r\n");
184+
185+
$this->connector->expects($this->once())->method('connect')->willReturn(Promise\resolve($stream));
186+
$promise = $this->factory->createClient('redis://localhost/123');
187+
188+
$stream->emit('data', array("+OK\r\n"));
189+
190+
$promise->then($this->expectCallableOnceWith($this->isInstanceOf('Clue\React\Redis\Client')));
191+
}
192+
193+
public function testWillRejectAndCloseAutomaticallyWhenSelectCommandReceivesErrorResponseIfRedisUriContainsPath()
194+
{
195+
$stream = $this->getMockBuilder('React\Socket\Connection')->disableOriginalConstructor()->setMethods(array('write', 'close'))->getMock();
196+
$stream->expects($this->once())->method('write')->with("*2\r\n$6\r\nselect\r\n$3\r\n123\r\n");
197+
$stream->expects($this->once())->method('close');
198+
199+
$this->connector->expects($this->once())->method('connect')->willReturn(Promise\resolve($stream));
200+
$promise = $this->factory->createClient('redis://localhost/123');
201+
202+
$stream->emit('data', array("-ERR DB index is out of range\r\n"));
203+
204+
$promise->then(null, $this->expectCallableOnceWith(
205+
$this->logicalAnd(
206+
$this->isInstanceOf('RuntimeException'),
207+
$this->callback(function (\Exception $e) {
208+
return $e->getMessage() === 'Connection to Redis server failed because SELECT command failed';
209+
}),
210+
$this->callback(function (\Exception $e) {
211+
return $e->getPrevious()->getMessage() === 'ERR DB index is out of range';
212+
})
213+
)
214+
));
215+
}
216+
143217
public function testWillRejectIfConnectorRejects()
144218
{
145219
$this->connector->expects($this->once())->method('connect')->with('127.0.0.1:2')->willReturn(Promise\reject(new \RuntimeException()));
146220
$promise = $this->factory->createClient('redis://127.0.0.1:2');
147221

148-
$this->expectPromiseReject($promise);
222+
$promise->then(null, $this->expectCallableOnceWith(
223+
$this->logicalAnd(
224+
$this->isInstanceOf('RuntimeException'),
225+
$this->callback(function (\Exception $e) {
226+
return $e->getMessage() === 'Connection to Redis server failed because underlying transport connection failed';
227+
})
228+
)
229+
));
149230
}
150231

151232
public function testWillRejectIfTargetIsInvalid()
152233
{
153234
$promise = $this->factory->createClient('http://invalid target');
154235

155-
$this->expectPromiseReject($promise);
236+
$promise->then(null, $this->expectCallableOnceWith($this->isInstanceOf('InvalidArgumentException')));
156237
}
157238

158239
public function testCancelWillRejectPromise()
@@ -173,6 +254,15 @@ public function testCancelWillCancelConnectorWhenConnectionIsPending()
173254

174255
$promise = $this->factory->createClient('redis://127.0.0.1:2');
175256
$promise->cancel();
257+
258+
$promise->then(null, $this->expectCallableOnceWith(
259+
$this->logicalAnd(
260+
$this->isInstanceOf('RuntimeException'),
261+
$this->callback(function (\Exception $e) {
262+
return $e->getMessage() === 'Connection to Redis server cancelled';
263+
})
264+
)
265+
));
176266
}
177267

178268
public function testCancelWillCloseConnectionWhenConnectionWaitsForSelect()
@@ -185,6 +275,15 @@ public function testCancelWillCloseConnectionWhenConnectionWaitsForSelect()
185275

186276
$promise = $this->factory->createClient('redis://127.0.0.1:2/123');
187277
$promise->cancel();
278+
279+
$promise->then(null, $this->expectCallableOnceWith(
280+
$this->logicalAnd(
281+
$this->isInstanceOf('RuntimeException'),
282+
$this->callback(function (\Exception $e) {
283+
return $e->getMessage() === 'Connection to Redis server cancelled';
284+
})
285+
)
286+
));
188287
}
189288

190289
public function testCreateClientWithTimeoutParameterWillStartTimerAndRejectOnExplicitTimeout()
@@ -205,9 +304,9 @@ public function testCreateClientWithTimeoutParameterWillStartTimerAndRejectOnExp
205304

206305
$promise->then(null, $this->expectCallableOnceWith(
207306
$this->logicalAnd(
208-
$this->isInstanceOf('Exception'),
307+
$this->isInstanceOf('RuntimeException'),
209308
$this->callback(function (\Exception $e) {
210-
return $e->getMessage() === 'Connection to database server timed out after 0 seconds';
309+
return $e->getMessage() === 'Connection to Redis server timed out after 0 seconds';
211310
})
212311
)
213312
));

0 commit comments

Comments
 (0)