ClientTest.php 35 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019
  1. <?php
  2. /*
  3. * This file is part of the Predis package.
  4. *
  5. * (c) Daniele Alessandri <suppakilla@gmail.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Predis;
  11. use PredisTestCase;
  12. /**
  13. *
  14. */
  15. class ClientTest extends PredisTestCase
  16. {
  17. /**
  18. * @group disconnected
  19. */
  20. public function testConstructorWithoutArguments()
  21. {
  22. $client = new Client();
  23. $connection = $client->getConnection();
  24. $this->assertInstanceOf('Predis\Connection\NodeConnectionInterface', $connection);
  25. $parameters = $connection->getParameters();
  26. $this->assertSame($parameters->host, '127.0.0.1');
  27. $this->assertSame($parameters->port, 6379);
  28. $options = $client->getOptions();
  29. $this->assertSame($options->commands, $client->getCommandFactory());
  30. $this->assertFalse($client->isConnected());
  31. }
  32. /**
  33. * @group disconnected
  34. */
  35. public function testConstructorWithNullArgument()
  36. {
  37. $client = new Client(null);
  38. $connection = $client->getConnection();
  39. $this->assertInstanceOf('Predis\Connection\NodeConnectionInterface', $connection);
  40. $parameters = $connection->getParameters();
  41. $this->assertSame($parameters->host, '127.0.0.1');
  42. $this->assertSame($parameters->port, 6379);
  43. $options = $client->getOptions();
  44. $this->assertSame($options->commands, $client->getCommandFactory());
  45. $this->assertFalse($client->isConnected());
  46. }
  47. /**
  48. * @group disconnected
  49. */
  50. public function testConstructorWithNullAndNullArguments()
  51. {
  52. $client = new Client(null, null);
  53. $connection = $client->getConnection();
  54. $this->assertInstanceOf('Predis\Connection\NodeConnectionInterface', $connection);
  55. $parameters = $connection->getParameters();
  56. $this->assertSame($parameters->host, '127.0.0.1');
  57. $this->assertSame($parameters->port, 6379);
  58. $options = $client->getOptions();
  59. $this->assertSame($options->commands, $client->getCommandFactory());
  60. $this->assertFalse($client->isConnected());
  61. }
  62. /**
  63. * @group disconnected
  64. */
  65. public function testConstructorWithArrayArgument()
  66. {
  67. $client = new Client($arg1 = array('host' => 'localhost', 'port' => 7000));
  68. $parameters = $client->getConnection()->getParameters();
  69. $this->assertSame($parameters->host, $arg1['host']);
  70. $this->assertSame($parameters->port, $arg1['port']);
  71. }
  72. /**
  73. * @group disconnected
  74. * @expectedException \InvalidArgumentException
  75. * @expectedExceptionMessage Array of connection parameters requires `cluster`, `replication` or `aggregate` client option
  76. */
  77. public function testConstructorThrowsExceptionWithArrayOfParametersArgumentAndMissingOption()
  78. {
  79. $arg1 = array(
  80. array('host' => 'localhost', 'port' => 7000),
  81. array('host' => 'localhost', 'port' => 7001),
  82. );
  83. $client = new Client($arg1);
  84. }
  85. /**
  86. * @group disconnected
  87. */
  88. public function testConstructorWithArrayOfArrayArgumentAndClusterOption()
  89. {
  90. $arg1 = array(
  91. array('host' => 'localhost', 'port' => 7000),
  92. array('host' => 'localhost', 'port' => 7001),
  93. );
  94. $client = new Client($arg1, array(
  95. 'aggregate' => $this->getAggregateInitializer($arg1),
  96. ));
  97. $this->assertInstanceOf('Predis\Connection\AggregateConnectionInterface', $client->getConnection());
  98. }
  99. /**
  100. * @group disconnected
  101. */
  102. public function testConstructorWithStringArgument()
  103. {
  104. $client = new Client('tcp://localhost:7000');
  105. $parameters = $client->getConnection()->getParameters();
  106. $this->assertSame($parameters->host, 'localhost');
  107. $this->assertSame($parameters->port, 7000);
  108. }
  109. /**
  110. * @group disconnected
  111. */
  112. public function testConstructorWithArrayOfStringArgument()
  113. {
  114. $arg1 = array('tcp://localhost:7000', 'tcp://localhost:7001');
  115. $client = new Client($arg1, array(
  116. 'aggregate' => $this->getAggregateInitializer($arg1),
  117. ));
  118. $this->assertInstanceOf('Predis\Connection\AggregateConnectionInterface', $client->getConnection());
  119. }
  120. /**
  121. * @group disconnected
  122. */
  123. public function testConstructorWithArrayOfConnectionsArgument()
  124. {
  125. $arg1 = array(
  126. $this->getMock('Predis\Connection\NodeConnectionInterface'),
  127. $this->getMock('Predis\Connection\NodeConnectionInterface'),
  128. );
  129. $client = new Client($arg1, array(
  130. 'aggregate' => $this->getAggregateInitializer($arg1),
  131. ));
  132. $this->assertInstanceOf('Predis\Connection\AggregateConnectionInterface', $client->getConnection());
  133. }
  134. /**
  135. * @group disconnected
  136. */
  137. public function testConstructorWithConnectionArgument()
  138. {
  139. $factory = new Connection\Factory();
  140. $connection = $factory->create('tcp://localhost:7000');
  141. $client = new Client($connection);
  142. $this->assertInstanceOf('Predis\Connection\NodeConnectionInterface', $client->getConnection());
  143. $this->assertSame($connection, $client->getConnection());
  144. $parameters = $client->getConnection()->getParameters();
  145. $this->assertSame($parameters->host, 'localhost');
  146. $this->assertSame($parameters->port, 7000);
  147. }
  148. /**
  149. * @group disconnected
  150. */
  151. public function testConstructorWithClusterArgument()
  152. {
  153. $cluster = new Connection\Cluster\PredisCluster();
  154. $factory = new Connection\Factory();
  155. $factory->aggregate($cluster, array('tcp://localhost:7000', 'tcp://localhost:7001'));
  156. $client = new Client($cluster);
  157. $this->assertInstanceOf('Predis\Connection\Cluster\ClusterInterface', $client->getConnection());
  158. $this->assertSame($cluster, $client->getConnection());
  159. }
  160. /**
  161. * @group disconnected
  162. */
  163. public function testConstructorWithReplicationArgument()
  164. {
  165. $replication = new Connection\Replication\MasterSlaveReplication();
  166. $factory = new Connection\Factory();
  167. $factory->aggregate($replication, array('tcp://host1?alias=master', 'tcp://host2?alias=slave'));
  168. $client = new Client($replication);
  169. $this->assertInstanceOf('Predis\Connection\Replication\ReplicationInterface', $client->getConnection());
  170. $this->assertSame($replication, $client->getConnection());
  171. }
  172. /**
  173. * @group disconnected
  174. */
  175. public function testConstructorWithCallableArgument()
  176. {
  177. $connection = $this->getMock('Predis\Connection\ConnectionInterface');
  178. $callable = $this->getMock('stdClass', array('__invoke'));
  179. $callable->expects($this->once())
  180. ->method('__invoke')
  181. ->with($this->isInstanceOf('Predis\Configuration\OptionsInterface'))
  182. ->will($this->returnValue($connection));
  183. $client = new Client($callable);
  184. $this->assertSame($connection, $client->getConnection());
  185. }
  186. /**
  187. * @group disconnected
  188. * @expectedException \InvalidArgumentException
  189. * @expectedExceptionMessage Callable parameters must return a valid connection
  190. */
  191. public function testConstructorWithCallableConnectionInitializerThrowsExceptionOnInvalidReturnType()
  192. {
  193. $wrongType = $this->getMock('stdClass');
  194. $callable = $this->getMock('stdClass', array('__invoke'));
  195. $callable->expects($this->once())
  196. ->method('__invoke')
  197. ->with($this->isInstanceOf('Predis\Configuration\OptionsInterface'))
  198. ->will($this->returnValue($wrongType));
  199. new Client($callable);
  200. }
  201. /**
  202. * @group disconnected
  203. */
  204. public function testConstructorWithNullAndArrayArgument()
  205. {
  206. $connections = $this->getMock('Predis\Connection\FactoryInterface');
  207. $arg2 = array('prefix' => 'prefix:', 'connections' => $connections);
  208. $client = new Client(null, $arg2);
  209. $this->assertInstanceOf('Predis\Command\FactoryInterface', $commands = $client->getCommandFactory());
  210. $this->assertInstanceOf('Predis\Command\Processor\KeyPrefixProcessor', $commands->getProcessor());
  211. $this->assertSame('prefix:', $commands->getProcessor()->getPrefix());
  212. }
  213. /**
  214. * @group disconnected
  215. */
  216. public function testConstructorWithArrayAndOptionReplication()
  217. {
  218. $arg1 = array('tcp://host1?alias=master', 'tcp://host2?alias=slave');
  219. $arg2 = array('replication' => 'predis');
  220. $client = new Client($arg1, $arg2);
  221. $this->assertInstanceOf('Predis\Connection\Replication\ReplicationInterface', $connection = $client->getConnection());
  222. $this->assertSame('host1', $connection->getConnectionById('master')->getParameters()->host);
  223. $this->assertSame('host2', $connection->getConnectionById('slave')->getParameters()->host);
  224. }
  225. /**
  226. * @group disconnected
  227. */
  228. public function testClusterOptionHasPrecedenceOverReplicationOptionAndAggregateOption()
  229. {
  230. $arg1 = array('tcp://host1', 'tcp://host2');
  231. $connection = $this->getMock('Predis\Connection\AggregateConnectionInterface');
  232. $fncluster = $this->getMock('stdClass', array('__invoke'));
  233. $fncluster->expects($this->once())
  234. ->method('__invoke')
  235. ->with($this->isInstanceOf('Predis\Configuration\OptionsInterface'), $arg1)
  236. ->will($this->returnValue($connection));
  237. $fnreplication = $this->getMock('stdClass', array('__invoke'));
  238. $fnreplication->expects($this->never())->method('__invoke');
  239. $fnaggregate = $this->getMock('stdClass', array('__invoke'));
  240. $fnaggregate->expects($this->never())->method('__invoke');
  241. $arg2 = array(
  242. 'cluster' => $fncluster,
  243. 'replication' => $fnreplication,
  244. 'aggregate' => $fnaggregate,
  245. );
  246. $client = new Client($arg1, $arg2);
  247. $this->assertSame($connection, $client->getConnection());
  248. }
  249. /**
  250. * @group disconnected
  251. */
  252. public function testReplicationOptionHasPrecedenceOverAggregateOption()
  253. {
  254. $arg1 = array('tcp://host1', 'tcp://host2');
  255. $connection = $this->getMock('Predis\Connection\AggregateConnectionInterface');
  256. $fnreplication = $this->getMock('stdClass', array('__invoke'));
  257. $fnreplication->expects($this->once())
  258. ->method('__invoke')
  259. ->with($this->isInstanceOf('Predis\Configuration\OptionsInterface'), $arg1)
  260. ->will($this->returnValue($connection));
  261. $fnaggregate = $this->getMock('stdClass', array('__invoke'));
  262. $fnaggregate->expects($this->never())->method('__invoke');
  263. $arg2 = array(
  264. 'replication' => $fnreplication,
  265. 'aggregate' => $fnaggregate,
  266. );
  267. $client = new Client($arg1, $arg2);
  268. $this->assertSame($connection, $client->getConnection());
  269. }
  270. /**
  271. * @group disconnected
  272. */
  273. public function testAggregateOptionDoesNotTriggerAggregationInClient()
  274. {
  275. $arg1 = array('tcp://host1', 'tcp://host2');
  276. $connection = $this->getMock('Predis\Connection\AggregateConnectionInterface');
  277. $fnaggregate = $this->getMock('stdClass', array('__invoke'));
  278. $fnaggregate->expects($this->once())
  279. ->method('__invoke')
  280. ->with($this->isInstanceOf('Predis\Configuration\OptionsInterface'), $arg1)
  281. ->will($this->returnValue($connection));
  282. $connections = $this->getMock('Predis\Connection\FactoryInterface');
  283. $connections->expects($this->never())
  284. ->method('aggregate');
  285. $arg2 = array('aggregate' => $fnaggregate, 'connections' => $connections);
  286. $client = new Client($arg1, $arg2);
  287. $this->assertSame($connection, $client->getConnection());
  288. }
  289. /**
  290. * @group disconnected
  291. */
  292. public function testConnectAndDisconnect()
  293. {
  294. $connection = $this->getMock('Predis\Connection\ConnectionInterface');
  295. $connection->expects($this->once())->method('connect');
  296. $connection->expects($this->once())->method('disconnect');
  297. $client = new Client($connection);
  298. $client->connect();
  299. $client->disconnect();
  300. }
  301. /**
  302. * @group disconnected
  303. */
  304. public function testIsConnectedChecksConnectionState()
  305. {
  306. $connection = $this->getMock('Predis\Connection\ConnectionInterface');
  307. $connection->expects($this->once())->method('isConnected');
  308. $client = new Client($connection);
  309. $client->isConnected();
  310. }
  311. /**
  312. * @group disconnected
  313. */
  314. public function testQuitIsAliasForDisconnect()
  315. {
  316. $connection = $this->getMock('Predis\Connection\ConnectionInterface');
  317. $connection->expects($this->once())->method('disconnect');
  318. $client = new Client($connection);
  319. $client->quit();
  320. }
  321. /**
  322. * @group disconnected
  323. */
  324. public function testCreatesNewCommandUsingSpecifiedCommandFactory()
  325. {
  326. $ping = $this->getCommandFactory()->createCommand('ping', array());
  327. $commands = $this->getMock('Predis\Command\FactoryInterface');
  328. $commands->expects($this->once())
  329. ->method('createCommand')
  330. ->with('ping', array())
  331. ->will($this->returnValue($ping));
  332. $client = new Client(null, array('commands' => $commands));
  333. $this->assertSame($ping, $client->createCommand('ping', array()));
  334. }
  335. /**
  336. * @group disconnected
  337. */
  338. public function testExecuteCommandReturnsParsedResponses()
  339. {
  340. $commands = $this->getCommandFactory();
  341. $ping = $commands->createCommand('ping', array());
  342. $hgetall = $commands->createCommand('hgetall', array('metavars', 'foo', 'hoge'));
  343. $connection = $this->getMock('Predis\Connection\ConnectionInterface');
  344. $connection->expects($this->at(0))
  345. ->method('executeCommand')
  346. ->with($ping)
  347. ->will($this->returnValue(new Response\Status('PONG')));
  348. $connection->expects($this->at(1))
  349. ->method('executeCommand')
  350. ->with($hgetall)
  351. ->will($this->returnValue(array('foo', 'bar', 'hoge', 'piyo')));
  352. $client = new Client($connection);
  353. $this->assertEquals('PONG', $client->executeCommand($ping));
  354. $this->assertSame(array('foo' => 'bar', 'hoge' => 'piyo'), $client->executeCommand($hgetall));
  355. }
  356. /**
  357. * @group disconnected
  358. * @expectedException \Predis\Response\ServerException
  359. * @expectedExceptionMessage Operation against a key holding the wrong kind of value
  360. */
  361. public function testExecuteCommandThrowsExceptionOnRedisError()
  362. {
  363. $ping = $this->getCommandFactory()->createCommand('ping', array());
  364. $expectedResponse = new Response\Error('ERR Operation against a key holding the wrong kind of value');
  365. $connection = $this->getMock('Predis\Connection\ConnectionInterface');
  366. $connection->expects($this->once())
  367. ->method('executeCommand')
  368. ->will($this->returnValue($expectedResponse));
  369. $client = new Client($connection);
  370. $client->executeCommand($ping);
  371. }
  372. /**
  373. * @group disconnected
  374. */
  375. public function testExecuteCommandReturnsErrorResponseOnRedisError()
  376. {
  377. $ping = $this->getCommandFactory()->createCommand('ping', array());
  378. $expectedResponse = new Response\Error('ERR Operation against a key holding the wrong kind of value');
  379. $connection = $this->getMock('Predis\Connection\ConnectionInterface');
  380. $connection->expects($this->once())
  381. ->method('executeCommand')
  382. ->will($this->returnValue($expectedResponse));
  383. $client = new Client($connection, array('exceptions' => false));
  384. $response = $client->executeCommand($ping);
  385. $this->assertSame($response, $expectedResponse);
  386. }
  387. /**
  388. * @group disconnected
  389. */
  390. public function testCallingRedisCommandExecutesInstanceOfCommand()
  391. {
  392. $ping = $this->getCommandFactory()->createCommand('ping', array());
  393. $connection = $this->getMock('Predis\Connection\ConnectionInterface');
  394. $connection->expects($this->once())
  395. ->method('executeCommand')
  396. ->with($this->isInstanceOf('Predis\Command\Redis\PING'))
  397. ->will($this->returnValue('PONG'));
  398. $commands = $this->getMock('Predis\Command\FactoryInterface');
  399. $commands->expects($this->once())
  400. ->method('createCommand')
  401. ->with('ping', array())
  402. ->will($this->returnValue($ping));
  403. $options = array('commands' => $commands);
  404. $client = $this->getMock('Predis\Client', null, array($connection, $options));
  405. $this->assertEquals('PONG', $client->ping());
  406. }
  407. /**
  408. * @group disconnected
  409. * @expectedException \Predis\Response\ServerException
  410. * @expectedExceptionMessage Operation against a key holding the wrong kind of value
  411. */
  412. public function testCallingRedisCommandThrowsExceptionOnServerError()
  413. {
  414. $expectedResponse = new Response\Error('ERR Operation against a key holding the wrong kind of value');
  415. $connection = $this->getMock('Predis\Connection\ConnectionInterface');
  416. $connection->expects($this->once())
  417. ->method('executeCommand')
  418. ->with($this->isRedisCommand('PING'))
  419. ->will($this->returnValue($expectedResponse));
  420. $client = new Client($connection);
  421. $client->ping();
  422. }
  423. /**
  424. * @group disconnected
  425. */
  426. public function testCallingRedisCommandReturnsErrorResponseOnRedisError()
  427. {
  428. $expectedResponse = new Response\Error('ERR Operation against a key holding the wrong kind of value');
  429. $connection = $this->getMock('Predis\Connection\ConnectionInterface');
  430. $connection->expects($this->once())
  431. ->method('executeCommand')
  432. ->with($this->isRedisCommand('PING'))
  433. ->will($this->returnValue($expectedResponse));
  434. $client = new Client($connection, array('exceptions' => false));
  435. $response = $client->ping();
  436. $this->assertSame($response, $expectedResponse);
  437. }
  438. /**
  439. * @group disconnected
  440. */
  441. public function testRawCommand()
  442. {
  443. $connection = $this->getMock('Predis\Connection\ConnectionInterface');
  444. $connection->expects($this->at(0))
  445. ->method('executeCommand')
  446. ->with($this->isRedisCommand('SET', array('foo', 'bar')))
  447. ->will($this->returnValue(new Response\Status('OK')));
  448. $connection->expects($this->at(1))
  449. ->method('executeCommand')
  450. ->with($this->isRedisCommand('GET', array('foo')))
  451. ->will($this->returnValue('bar'));
  452. $connection->expects($this->at(2))
  453. ->method('executeCommand')
  454. ->with($this->isRedisCommand('PING'))
  455. ->will($this->returnValue('PONG'));
  456. $client = new Client($connection);
  457. $this->assertSame('OK', $client->executeRaw(array('SET', 'foo', 'bar')));
  458. $this->assertSame('bar', $client->executeRaw(array('GET', 'foo')));
  459. $error = true; // $error is always populated by reference.
  460. $this->assertSame('PONG', $client->executeRaw(array('PING'), $error));
  461. $this->assertFalse($error);
  462. }
  463. /**
  464. * @group disconnected
  465. */
  466. public function testRawCommandNeverAppliesPrefix()
  467. {
  468. $connection = $this->getMock('Predis\Connection\ConnectionInterface');
  469. $connection->expects($this->at(0))
  470. ->method('executeCommand')
  471. ->with($this->isRedisCommand('SET', array('foo', 'bar')))
  472. ->will($this->returnValue(new Response\Status('OK')));
  473. $connection->expects($this->at(1))
  474. ->method('executeCommand')
  475. ->with($this->isRedisCommand('GET', array('foo')))
  476. ->will($this->returnValue('bar'));
  477. $client = new Client($connection, array('prefix' => 'predis:'));
  478. $this->assertSame('OK', $client->executeRaw(array('SET', 'foo', 'bar')));
  479. $this->assertSame('bar', $client->executeRaw(array('GET', 'foo')));
  480. }
  481. /**
  482. * @group disconnected
  483. */
  484. public function testRawCommandNeverThrowsExceptions()
  485. {
  486. $message = 'ERR Mock error response';
  487. $response = new Response\Error($message);
  488. $connection = $this->getMock('Predis\Connection\ConnectionInterface');
  489. $connection->expects($this->once())
  490. ->method('executeCommand')
  491. ->with($this->isRedisCommand('PING'))
  492. ->will($this->returnValue($response));
  493. $client = new Client($connection, array('exceptions' => true));
  494. $this->assertSame($message, $client->executeRaw(array('PING'), $error));
  495. $this->assertTrue($error);
  496. }
  497. /**
  498. * @group disconnected
  499. * @expectedException \Predis\ClientException
  500. * @expectedExceptionMessage Command 'INVALIDCOMMAND' is not a registered Redis command.
  501. */
  502. public function testThrowsExceptionOnNonRegisteredRedisCommand()
  503. {
  504. $client = new Client();
  505. $client->invalidCommand();
  506. }
  507. /**
  508. * @group disconnected
  509. */
  510. public function testGetConnectionFromAggregateConnectionWithAlias()
  511. {
  512. $client = new Client(array('tcp://host1?alias=node01', 'tcp://host2?alias=node02'), array('cluster' => 'predis'));
  513. $this->assertInstanceOf('Predis\Connection\Cluster\ClusterInterface', $cluster = $client->getConnection());
  514. $this->assertInstanceOf('Predis\Connection\NodeConnectionInterface', $node01 = $client->getConnectionById('node01'));
  515. $this->assertInstanceOf('Predis\Connection\NodeConnectionInterface', $node02 = $client->getConnectionById('node02'));
  516. $this->assertSame('host1', $node01->getParameters()->host);
  517. $this->assertSame('host2', $node02->getParameters()->host);
  518. }
  519. /**
  520. * @group disconnected
  521. * @expectedException \Predis\NotSupportedException
  522. * @expectedExceptionMessage Retrieving connections by ID is supported only by aggregate connections.
  523. */
  524. public function testGetConnectionByIdWorksOnlyWithAggregateConnections()
  525. {
  526. $client = new Client();
  527. $client->getConnectionById('node01');
  528. }
  529. /**
  530. * @group disconnected
  531. */
  532. public function testOnMethodCreatesClientWithConnectionFromAggregateConnection()
  533. {
  534. $client = new Client(array('tcp://host1?alias=node01', 'tcp://host2?alias=node02'), array('prefix' => 'pfx:', 'cluster' => 'predis'));
  535. $this->assertInstanceOf('Predis\Connection\Cluster\ClusterInterface', $cluster = $client->getConnection());
  536. $this->assertInstanceOf('Predis\Connection\NodeConnectionInterface', $node01 = $client->getConnectionById('node01'));
  537. $this->assertInstanceOf('Predis\Connection\NodeConnectionInterface', $node02 = $client->getConnectionById('node02'));
  538. $clientNode02 = $client->on('node02');
  539. $this->assertInstanceOf('Predis\Client', $clientNode02);
  540. $this->assertSame($node02, $clientNode02->getConnection());
  541. $this->assertSame($client->getOptions(), $clientNode02->getOptions());
  542. }
  543. /**
  544. * @group disconnected
  545. */
  546. public function testOnMethodReturnsInstanceOfSubclass()
  547. {
  548. $nodes = array('tcp://host1?alias=node01', 'tcp://host2?alias=node02');
  549. $client = $this->getMock('Predis\Client', array('dummy'), array($nodes, array('cluster' => 'predis')), 'SubclassedClient');
  550. $this->assertInstanceOf('SubclassedClient', $client->on('node02'));
  551. }
  552. /**
  553. * @group disconnected
  554. */
  555. public function testOnMethodInvokesCallableInSecondArgumentAndReturnsItsReturnValue()
  556. {
  557. $test = $this;
  558. $client = new Client(array('tcp://host1?alias=node01', 'tcp://host2?alias=node02'), array('cluster' => 'predis'));
  559. $callable = $this->getMock('stdClass', array('__invoke'));
  560. $callable->expects($this->once())
  561. ->method('__invoke')
  562. ->with($this->callback(function ($clientNode) use ($test, $client) {
  563. $test->isInstanceOf('Predis\ClientInterface', $clientNode);
  564. $test->assertNotSame($client, $clientNode);
  565. $test->assertInstanceOf('Predis\Connection\NodeConnectionInterface', $connection = $clientNode->getConnection());
  566. $test->assertSame('node02', $connection->getParameters()->alias);
  567. return true;
  568. }))
  569. ->will($this->returnValue('value'));
  570. $this->assertSame('value', $client->on('node02', $callable));
  571. }
  572. /**
  573. * @group disconnected
  574. * @expectedException \Predis\NotSupportedException
  575. * @expectedExceptionMessage Retrieving connections by ID is supported only by aggregate connections
  576. */
  577. public function testOnMethodThrowsExceptionWithNodeConnection()
  578. {
  579. $client = new Client('tcp://127.0.0.1?alias=node01');
  580. $client->on('node01');
  581. }
  582. /**
  583. * @group disconnected
  584. * @expectedException \InvalidArgumentException
  585. * @expectedExceptionMessage Invalid connection ID: `nodeXX`
  586. */
  587. public function testOnMethodThrowsExceptionWithUnknownConnectionID()
  588. {
  589. $client = new Client(array('tcp://host1?alias=node01', 'tcp://host2?alias=node02'), array('cluster' => 'predis'));
  590. $client->on('nodeXX');
  591. }
  592. /**
  593. * @group disconnected
  594. */
  595. public function testPipelineWithoutArgumentsReturnsPipeline()
  596. {
  597. $client = new Client();
  598. $this->assertInstanceOf('Predis\Pipeline\Pipeline', $client->pipeline());
  599. }
  600. /**
  601. * @group disconnected
  602. */
  603. public function testPipelineWithArrayReturnsPipeline()
  604. {
  605. $client = new Client();
  606. $this->assertInstanceOf('Predis\Pipeline\Pipeline', $client->pipeline(array()));
  607. $this->assertInstanceOf('Predis\Pipeline\Atomic', $client->pipeline(array('atomic' => true)));
  608. $this->assertInstanceOf('Predis\Pipeline\FireAndForget', $client->pipeline(array('fire-and-forget' => true)));
  609. }
  610. /**
  611. * @group disconnected
  612. */
  613. public function testPipelineWithCallableExecutesPipeline()
  614. {
  615. $callable = $this->getMock('stdClass', array('__invoke'));
  616. $callable->expects($this->once())
  617. ->method('__invoke')
  618. ->with($this->isInstanceOf('Predis\Pipeline\Pipeline'));
  619. $client = new Client();
  620. $client->pipeline($callable);
  621. }
  622. /**
  623. * @group disconnected
  624. */
  625. public function testPubSubLoopWithoutArgumentsReturnsPubSubConsumer()
  626. {
  627. $client = new Client();
  628. $this->assertInstanceOf('Predis\PubSub\Consumer', $client->pubSubLoop());
  629. }
  630. /**
  631. * @group disconnected
  632. */
  633. public function testPubSubLoopWithArrayReturnsPubSubConsumerWithOptions()
  634. {
  635. $connection = $this->getMock('Predis\Connection\NodeConnectionInterface');
  636. $options = array('subscribe' => 'channel');
  637. $client = new Client($connection);
  638. $this->assertInstanceOf('Predis\PubSub\Consumer', $pubsub = $client->pubSubLoop($options));
  639. $reflection = new \ReflectionProperty($pubsub, 'options');
  640. $reflection->setAccessible(true);
  641. $this->assertSame($options, $reflection->getValue($pubsub));
  642. }
  643. /**
  644. * @group disconnected
  645. */
  646. public function testPubSubLoopWithArrayAndCallableExecutesPubSub()
  647. {
  648. // NOTE: we use a subscribe count of 0 in the fake message to trick
  649. // the context and to make it think that it can be closed
  650. // since there are no more subscriptions active.
  651. $message = array('subscribe', 'channel', 0);
  652. $options = array('subscribe' => 'channel');
  653. $connection = $this->getMock('Predis\Connection\NodeConnectionInterface');
  654. $connection->expects($this->once())
  655. ->method('read')
  656. ->will($this->returnValue($message));
  657. $callable = $this->getMock('stdClass', array('__invoke'));
  658. $callable->expects($this->once())
  659. ->method('__invoke');
  660. $client = new Client($connection);
  661. $client->pubSubLoop($options, $callable);
  662. }
  663. /**
  664. * @group disconnected
  665. */
  666. public function testTransactionWithoutArgumentsReturnsMultiExec()
  667. {
  668. $client = new Client();
  669. $this->assertInstanceOf('Predis\Transaction\MultiExec', $client->transaction());
  670. }
  671. /**
  672. * @group disconnected
  673. */
  674. public function testTransactionWithArrayReturnsMultiExecTransactionWithOptions()
  675. {
  676. $options = array('cas' => true, 'retry' => 3);
  677. $client = new Client();
  678. $this->assertInstanceOf('Predis\Transaction\MultiExec', $tx = $client->transaction($options));
  679. // I hate this part but reflection is the easiest way in this case.
  680. $property = new \ReflectionProperty($tx, 'modeCAS');
  681. $property->setAccessible(true);
  682. $this->assertSame($options['cas'], $property->getValue($tx));
  683. $property = new \ReflectionProperty($tx, 'attempts');
  684. $property->setAccessible(true);
  685. $this->assertSame($options['retry'], $property->getValue($tx));
  686. }
  687. /**
  688. * @group disconnected
  689. */
  690. public function testTransactionWithArrayAndCallableExecutesMultiExec()
  691. {
  692. // We use CAS here as we don't care about the actual MULTI/EXEC context.
  693. $options = array('cas' => true, 'retry' => 3);
  694. $connection = $this->getMock('Predis\Connection\NodeConnectionInterface');
  695. $connection->expects($this->once())
  696. ->method('executeCommand')
  697. ->will($this->returnValue(new Response\Status('QUEUED')));
  698. $txCallback = function ($tx) {
  699. $tx->ping();
  700. };
  701. $callable = $this->getMock('stdClass', array('__invoke'));
  702. $callable->expects($this->once())
  703. ->method('__invoke')
  704. ->will($this->returnCallback($txCallback));
  705. $client = new Client($connection);
  706. $client->transaction($options, $callable);
  707. }
  708. /**
  709. * @group disconnected
  710. */
  711. public function testMonitorReturnsMonitorConsumer()
  712. {
  713. $connection = $this->getMock('Predis\Connection\NodeConnectionInterface');
  714. $client = new Client($connection);
  715. $this->assertInstanceOf('Predis\Monitor\Consumer', $monitor = $client->monitor());
  716. }
  717. /**
  718. * @group disconnected
  719. */
  720. public function testClientResendScriptCommandUsingEvalOnNoScriptErrors()
  721. {
  722. $command = $this->getMockForAbstractClass('Predis\Command\ScriptCommand', array(), '', true, true, true, array('parseResponse'));
  723. $command->expects($this->once())
  724. ->method('getScript')
  725. ->will($this->returnValue('return redis.call(\'exists\', KEYS[1])'));
  726. $command->expects($this->once())
  727. ->method('parseResponse')
  728. ->with('OK')
  729. ->will($this->returnValue(true));
  730. $connection = $this->getMock('Predis\Connection\NodeConnectionInterface');
  731. $connection->expects($this->at(0))
  732. ->method('executeCommand')
  733. ->with($command)
  734. ->will($this->returnValue(new Response\Error('NOSCRIPT')));
  735. $connection->expects($this->at(1))
  736. ->method('executeCommand')
  737. ->with($this->isRedisCommand('EVAL'))
  738. ->will($this->returnValue('OK'));
  739. $client = new Client($connection);
  740. $this->assertTrue($client->executeCommand($command));
  741. }
  742. /**
  743. * @group disconnected
  744. */
  745. public function testGetIteratorWithTraversableConnections()
  746. {
  747. $connection1 = $this->getMockConnection('tcp://127.0.0.1:6381');
  748. $connection2 = $this->getMockConnection('tcp://127.0.0.1:6382');
  749. $connection3 = $this->getMockConnection('tcp://127.0.0.1:6383');
  750. $aggregate = new \Predis\Connection\Cluster\PredisCluster();
  751. $aggregate->add($connection1);
  752. $aggregate->add($connection2);
  753. $aggregate->add($connection3);
  754. $client = new Client($aggregate);
  755. $iterator = $client->getIterator();
  756. $this->assertInstanceOf('\Predis\Client', $nodeClient = $iterator->current());
  757. $this->assertSame($connection1, $nodeClient->getConnection());
  758. $this->assertSame('127.0.0.1:6381', $iterator->key());
  759. $iterator->next();
  760. $this->assertInstanceOf('\Predis\Client', $nodeClient = $iterator->current());
  761. $this->assertSame($connection2, $nodeClient->getConnection());
  762. $this->assertSame('127.0.0.1:6382', $iterator->key());
  763. $iterator->next();
  764. $this->assertInstanceOf('\Predis\Client', $nodeClient = $iterator->current());
  765. $this->assertSame($connection3, $nodeClient->getConnection());
  766. $this->assertSame('127.0.0.1:6383', $iterator->key());
  767. }
  768. /**
  769. * @group disconnected
  770. * @expectedException \Predis\ClientException
  771. * @expectedExceptionMessage The underlying connection is not traversable
  772. */
  773. public function testGetIteratorWithNonTraversableConnectionThrowsException()
  774. {
  775. $connection = $this->getMock('Predis\Connection\NodeConnectionInterface');
  776. $client = new Client($connection);
  777. $client->getIterator();
  778. }
  779. // ******************************************************************** //
  780. // ---- HELPER METHODS ------------------------------------------------ //
  781. // ******************************************************************** //
  782. /**
  783. * Returns an URI string representation of the specified connection parameters.
  784. *
  785. * @param array $parameters Array of connection parameters.
  786. *
  787. * @return string URI string.
  788. */
  789. protected function getParametersString(array $parameters)
  790. {
  791. $defaults = $this->getDefaultParametersArray();
  792. $scheme = isset($parameters['scheme']) ? $parameters['scheme'] : $defaults['scheme'];
  793. $host = isset($parameters['host']) ? $parameters['host'] : $defaults['host'];
  794. $port = isset($parameters['port']) ? $parameters['port'] : $defaults['port'];
  795. unset($parameters['scheme'], $parameters['host'], $parameters['port']);
  796. $uriString = "$scheme://$host:$port/?";
  797. foreach ($parameters as $k => $v) {
  798. $uriString .= "$k=$v&";
  799. }
  800. return $uriString;
  801. }
  802. /**
  803. * Returns a mock callable simulating an aggregate connection initializer.
  804. *
  805. * @param mixed $parameters Expected connection parameters
  806. *
  807. * @return callable
  808. */
  809. protected function getAggregateInitializer($parameters)
  810. {
  811. $connection = $this->getMock('Predis\Connection\AggregateConnectionInterface');
  812. $callable = $this->getMock('stdClass', array('__invoke'));
  813. $callable->expects($this->once())
  814. ->method('__invoke')
  815. ->with($this->isInstanceOf('Predis\Configuration\OptionsInterface'), $parameters)
  816. ->will($this->returnValue($connection));
  817. return $callable;
  818. }
  819. }