assertSame($client, $pipeline->getClient()); } /** * @group disconnected */ public function testCallDoesNotSendCommandsWithoutExecute(): void { $connection = $this->getMockBuilder('Predis\Connection\NodeConnectionInterface')->getMock(); $connection ->expects($this->never()) ->method('writeRequest'); $connection ->expects($this->never()) ->method('readResponse'); $pipeline = new Pipeline(new Client($connection)); $pipeline->echo('one'); $pipeline->echo('two'); $pipeline->echo('three'); } /** * @group disconnected */ public function testCallReturnsPipelineForFluentInterface(): void { $connection = $this->getMockBuilder('Predis\Connection\NodeConnectionInterface')->getMock(); $connection ->expects($this->never()) ->method('writeRequest'); $connection ->expects($this->never()) ->method('readResponse'); $pipeline = new Pipeline(new Client($connection)); $this->assertSame($pipeline, $pipeline->echo('one')); $this->assertSame($pipeline, $pipeline->echo('one')->echo('two')->echo('three')); } /** * @group disconnected */ public function testDoesNotParseComplexResponseObjects(): void { $object = $this->getMockBuilder('Predis\Response\ResponseInterface')->getMock(); $connection = $this->getMockBuilder('Predis\Connection\NodeConnectionInterface')->getMock(); $connection ->expects($this->once()) ->method('readResponse') ->willReturn($object); $connection ->expects($this->exactly(2)) ->method('getParameters') ->willReturn(new Parameters(['protocol' => 2])); $pipeline = new Pipeline(new Client($connection)); $pipeline->ping(); $this->assertSame([$object], $pipeline->execute()); } /** * @group disconnected */ public function testThrowsServerExceptionOnResponseErrorByDefault(): void { $this->expectException('Predis\Response\ServerException'); $this->expectExceptionMessage('ERR Test error'); $error = new Response\Error('ERR Test error'); $connection = $this->getMockBuilder('Predis\Connection\NodeConnectionInterface')->getMock(); $connection ->expects($this->once()) ->method('readResponse') ->willReturn($error); $connection ->expects($this->exactly(2)) ->method('getParameters') ->willReturn(new Parameters(['protocol' => 2])); $pipeline = new Pipeline(new Client($connection)); $pipeline->ping(); $pipeline->ping(); $pipeline->execute(); } /** * @group disconnected */ public function testReturnsResponseErrorWithClientExceptionsSetToFalse(): void { $error = $this->getMockBuilder('Predis\Response\ErrorInterface')->getMock(); $connection = $this->getMockBuilder('Predis\Connection\NodeConnectionInterface')->getMock(); $connection ->expects($this->exactly(2)) ->method('readResponse') ->willReturn($error); $connection ->expects($this->exactly(2)) ->method('getParameters') ->willReturn(new Parameters(['protocol' => 2])); $client = new Client($connection, ['exceptions' => false]); $pipeline = new Pipeline($client); $pipeline->ping(); $pipeline->ping(); $this->assertSame([$error, $error], $pipeline->execute()); } /** * @group disconnected */ public function testExecuteReturnsPipelineForFluentInterface(): void { $connection = $this->getMockBuilder('Predis\Connection\NodeConnectionInterface')->getMock(); $command = $this->getCommandFactory()->create('echo', ['one']); $pipeline = new Pipeline(new Client($connection)); $this->assertSame($pipeline, $pipeline->executeCommand($command)); } /** * @group disconnected */ public function testExecuteCommandDoesNotSendCommandsWithoutExecute(): void { $connection = $this->getMockBuilder('Predis\Connection\NodeConnectionInterface')->getMock(); $connection ->expects($this->never()) ->method('writeRequest'); $connection ->expects($this->never()) ->method('readResponse'); $commands = $this->getCommandFactory(); $pipeline = new Pipeline(new Client($connection)); $pipeline->executeCommand($commands->create('echo', ['one'])); $pipeline->executeCommand($commands->create('echo', ['two'])); $pipeline->executeCommand($commands->create('echo', ['three'])); } /** * @group disconnected */ public function testExecuteWithEmptyBuffer(): void { $connection = $this->getMockBuilder('Predis\Connection\NodeConnectionInterface')->getMock(); $connection ->expects($this->never()) ->method('writeRequest'); $connection ->expects($this->never()) ->method('readResponse'); $pipeline = new Pipeline(new Client($connection)); $this->assertSame([], $pipeline->execute()); } /** * @group disconnected */ public function testExecuteWithFilledBuffer(): void { $command1 = new ECHO_(); $command1->setArguments(['one']); $command2 = new ECHO_(); $command2->setArguments(['two']); $command3 = new ECHO_(); $command3->setArguments(['three']); $buffer = $command1->serializeCommand() . $command2->serializeCommand() . $command3->serializeCommand(); $connection = $this->getMockBuilder('Predis\Connection\NodeConnectionInterface')->getMock(); $connection ->expects($this->once()) ->method('write') ->with($buffer); $connection ->expects($this->exactly(3)) ->method('readResponse') ->willReturnCallback($this->getReadCallback()); $connection ->expects($this->exactly(2)) ->method('getParameters') ->willReturn(new Parameters(['protocol' => 2])); $pipeline = new Pipeline(new Client($connection)); $pipeline->echo('one'); $pipeline->echo('two'); $pipeline->echo('three'); $pipeline->flushPipeline(); $this->assertSame(['one', 'two', 'three'], $pipeline->execute()); } /** * @group disconnected */ public function testFlushWithFalseArgumentDiscardsBuffer(): void { $pipeline = new Pipeline(new Client()); $pipeline->echo('one'); $pipeline->echo('two'); $pipeline->echo('three'); $pipeline->flushPipeline(false); $this->assertSame([], $pipeline->execute()); } /** * @group disconnected */ public function testFlushHandlesPartialBuffers(): void { $command1 = new ECHO_(); $command1->setArguments(['one']); $command2 = new ECHO_(); $command2->setArguments(['two']); $buffer1 = $command1->serializeCommand() . $command2->serializeCommand(); $command3 = new ECHO_(); $command3->setArguments(['three']); $command4 = new ECHO_(); $command4->setArguments(['four']); $buffer2 = $command3->serializeCommand() . $command4->serializeCommand(); $connection = $this->getMockBuilder('Predis\Connection\NodeConnectionInterface')->getMock(); $connection ->expects($this->exactly(2)) ->method('write') ->withConsecutive([$buffer1], [$buffer2]); $connection ->expects($this->exactly(4)) ->method('readResponse') ->willReturnCallback($this->getReadCallback()); $connection ->expects($this->exactly(4)) ->method('getParameters') ->willReturn(new Parameters(['protocol' => 2])); $pipeline = new Pipeline(new Client($connection)); $pipeline->echo('one'); $pipeline->echo('two'); $pipeline->flushPipeline(); $pipeline->echo('three'); $pipeline->echo('four'); $this->assertSame(['one', 'two', 'three', 'four'], $pipeline->execute()); } /** * @group disconnected */ public function testSwitchesToMasterWithReplicationConnection(): void { $nodeConnection = $this->getMockBuilder('Predis\Connection\NodeConnectionInterface')->getMock(); $nodeConnection ->expects($this->exactly(3)) ->method('write') ->with((new PING())->serializeCommand()); $pong = new Response\Status('PONG'); $connection = $this->getMockBuilder('Predis\Connection\Replication\ReplicationInterface')->getMock(); $connection ->expects($this->once()) ->method('switchToMaster'); $connection ->expects($this->exactly(6)) ->method('getConnectionByCommand') ->willReturn($nodeConnection); $nodeConnection ->expects($this->exactly(3)) ->method('readResponse') ->willReturn($pong); $connection ->expects($this->exactly(3)) ->method('getParameters') ->willReturn(new Parameters(['protocol' => 2])); $pipeline = new Pipeline(new Client($connection)); $pipeline->ping(); $pipeline->ping(); $pipeline->ping(); $this->assertSame([$pong, $pong, $pong], $pipeline->execute()); } /** * @group disconnected */ public function testExecuteAcceptsCallableArgument(): void { $test = $this; $pipeline = new Pipeline(new Client()); $callable = static function (Pipeline $pipe) use ($test, $pipeline) { $test->assertSame($pipeline, $pipe); $pipe->flushPipeline(false); }; $pipeline->execute($callable); } /** * @group disconnected */ public function testExecuteDoesNotAcceptNonCallableArgument(): void { $this->expectException('InvalidArgumentException'); $noncallable = new stdClass(); $pipeline = new Pipeline(new Client()); $pipeline->execute($noncallable); } /** * @group disconnected */ public function testExecuteInsideCallableArgumentThrowsException(): void { $this->expectException('Predis\ClientException'); $pipeline = new Pipeline(new Client()); $pipeline->execute(static function (Pipeline $pipe) { $pipe->execute(); }); } /** * @group disconnected */ public function testExecuteWithCallableArgumentRunsPipelineInCallable(): void { $command1 = new ECHO_(); $command1->setArguments(['one']); $command2 = new ECHO_(); $command2->setArguments(['two']); $command3 = new ECHO_(); $command3->setArguments(['three']); $command4 = new ECHO_(); $command4->setArguments(['four']); $buffer = $command1->serializeCommand() . $command2->serializeCommand() . $command3->serializeCommand() . $command4->serializeCommand(); $connection = $this->getMockBuilder('Predis\Connection\NodeConnectionInterface')->getMock(); $connection ->expects($this->once()) ->method('write') ->with($buffer); $connection ->expects($this->exactly(4)) ->method('readResponse') ->willReturnCallback($this->getReadCallback()); $connection ->expects($this->exactly(2)) ->method('getParameters') ->willReturn(new Parameters(['protocol' => 2])); $pipeline = new Pipeline(new Client($connection)); $responses = $pipeline->execute(static function (Pipeline $pipe) { $pipe->echo('one'); $pipe->echo('two'); $pipe->echo('three'); $pipe->echo('four'); }); $this->assertSame(['one', 'two', 'three', 'four'], $responses); } /** * @group disconnected */ public function testExecuteWithCallableArgumentHandlesExceptions(): void { $connection = $this->getMockBuilder('Predis\Connection\NodeConnectionInterface')->getMock(); $connection ->expects($this->never()) ->method('writeRequest'); $connection ->expects($this->never()) ->method('readResponse'); $exception = null; $responses = null; $pipeline = new Pipeline(new Client($connection)); try { $responses = $pipeline->execute(static function (Pipeline $pipe) { $pipe->echo('one'); $pipe->echo('two'); throw new ClientException('TEST'); }); } catch (Exception $exception) { // NOOP } $this->assertInstanceOf('Predis\ClientException', $exception); $this->assertSame('TEST', $exception->getMessage()); $this->assertNull($responses); } /** * @group disconnected * @throws Exception */ public function testRetryStandalonePipelineOnRetryableErrors(): void { $mockStream = $this->getMockBuilder(StreamInterface::class)->getMock(); $mockStreamFactory = $this->getMockBuilder(StreamFactoryInterface::class)->getMock(); $parameters = new Parameters([ 'retry' => new Retry(new ExponentialBackoff(1000, 10000), 3), ]); $mockStream ->expects($this->atLeast(3)) ->method('close') ->withAnyParameters(); $mockStream ->expects($this->exactly(4)) ->method('write') ->withAnyParameters() ->willReturnOnConsecutiveCalls( $this->throwException(new RuntimeException('', 2)), $this->throwException(new RuntimeException('', 2)), $this->throwException(new RuntimeException('', 2)), 1000 ); $mockStream ->expects($this->exactly(3)) ->method('read') ->withAnyParameters() ->willReturn("+PONG\r\n"); $mockStreamFactory ->expects($this->exactly(4)) ->method('createStream') ->withAnyParameters() ->willReturn($mockStream); $connection = new StreamConnection($parameters, $mockStreamFactory); $pipeline = new Pipeline(new Client($connection)); $responses = $pipeline->execute(static function (Pipeline $pipe) { $pipe->ping(); $pipe->ping(); $pipe->ping(); }); $this->assertEquals(['PONG', 'PONG', 'PONG'], $responses); } /** * @group disconnected * @throws Exception */ public function testRetryClusterPipelineOnRetryableErrors(): void { $mockStream = $this->getMockBuilder(StreamInterface::class)->getMock(); $mockStreamFactory = $this->getMockBuilder(StreamFactoryInterface::class)->getMock(); $mockConnectionFactory = $this->getMockBuilder(Factory::class)->getMock(); $parameters = new Parameters([ 'retry' => new Retry(new ExponentialBackoff(1000, 10000), 3), ]); $mockStream ->expects($this->atLeast(3)) ->method('close') ->withAnyParameters(); $mockStream ->expects($this->exactly(6)) ->method('write') ->withAnyParameters() ->willReturnOnConsecutiveCalls( $this->throwException(new RuntimeException('', 2)), $this->throwException(new RuntimeException('', 2)), $this->throwException(new RuntimeException('', 2)), 1000, 1000, 1000 ); $mockStream ->expects($this->exactly(3)) ->method('read') ->withAnyParameters() ->willReturn("+OK\r\n"); $mockStreamFactory ->expects($this->exactly(4)) ->method('createStream') ->withAnyParameters() ->willReturn($mockStream); $streamConnection = new StreamConnection($parameters, $mockStreamFactory); $connection = new RedisCluster($mockConnectionFactory, $parameters); $connection->add($streamConnection); $pipeline = new Pipeline(new Client($connection)); $responses = $pipeline->execute(static function (Pipeline $pipe) { $pipe->set('key', 'value'); $pipe->set('key', 'value'); $pipe->set('key', 'value'); }); $this->assertEquals(['OK', 'OK', 'OK'], $responses); } /** * @group disconnected * @throws Exception */ public function testExecutePipelineInvokesOnAggregateConnectionFailCallbackOnConnectionException(): void { $mockStream = $this->getMockBuilder(StreamInterface::class)->getMock(); $mockStreamFactory = $this->getMockBuilder(StreamFactoryInterface::class)->getMock(); $mockConnectionFactory = $this->getMockBuilder(Factory::class)->getMock(); $parameters = new Parameters([ 'retry' => new Retry(new ExponentialBackoff(1000, 10000), 3), ]); $streamConnection = new StreamConnection($parameters, $mockStreamFactory); $connection = $this->getMockBuilder(RedisCluster::class) ->setConstructorArgs([$mockConnectionFactory, $parameters]) ->onlyMethods(['getConnectionByCommand', 'remove', 'askSlotMap']) ->getMock(); // Disable useClusterSlots to avoid askSlotMap() calls $connection->useClusterSlots = false; $mockStream ->expects($this->atLeast(3)) ->method('close') ->withAnyParameters(); $mockStream ->expects($this->exactly(6)) ->method('write') ->withAnyParameters() ->willReturnOnConsecutiveCalls( $this->throwException(new \Predis\Connection\ConnectionException($streamConnection, 'Connection failed')), $this->throwException(new \Predis\Connection\ConnectionException($streamConnection, 'Connection failed')), $this->throwException(new \Predis\Connection\ConnectionException($streamConnection, 'Connection failed')), 1000, 1000, 1000 ); $mockStream ->expects($this->exactly(3)) ->method('read') ->withAnyParameters() ->willReturn("+OK\r\n"); $mockStreamFactory ->expects($this->exactly(4)) ->method('createStream') ->withAnyParameters() ->willReturn($mockStream); $connection ->expects($this->exactly(9)) ->method('getConnectionByCommand') ->willReturn($streamConnection); // Verify that remove() is called on the aggregate connection during retry $connection ->expects($this->exactly(3)) ->method('remove') ->with($streamConnection); // Verify that askSlotMap() is NOT called since useClusterSlots is false $connection ->expects($this->never()) ->method('askSlotMap'); $pipeline = new Pipeline(new Client($connection)); $responses = $pipeline->execute(static function (Pipeline $pipe) { $pipe->set('key', 'value'); $pipe->set('key', 'value'); $pipe->set('key', 'value'); }); $this->assertEquals(['OK', 'OK', 'OK'], $responses); } /** * @group disconnected * @throws Exception */ public function testExecutePipelineInvokesOnAggregateConnectionFailCallbackOnTimeoutException(): void { $mockStream = $this->getMockBuilder(StreamInterface::class)->getMock(); $mockStreamFactory = $this->getMockBuilder(StreamFactoryInterface::class)->getMock(); $mockConnectionFactory = $this->getMockBuilder(Factory::class)->getMock(); $parameters = new Parameters([ 'retry' => new Retry(new ExponentialBackoff(1000, 10000), 3), ]); $streamConnection = new StreamConnection($parameters, $mockStreamFactory); $connection = $this->getMockBuilder(RedisCluster::class) ->setConstructorArgs([$mockConnectionFactory, $parameters]) ->onlyMethods(['getConnectionByCommand', 'remove']) ->getMock(); // Disable useClusterSlots to avoid askSlotMap() calls $connection->useClusterSlots = false; $mockStream ->expects($this->atLeast(3)) ->method('close') ->withAnyParameters(); $mockStream ->expects($this->exactly(6)) ->method('write') ->withAnyParameters() ->willReturnOnConsecutiveCalls( $this->throwException(new TimeoutException($streamConnection, 0)), $this->throwException(new TimeoutException($streamConnection, 0)), $this->throwException(new TimeoutException($streamConnection, 0)), 1000, 1000, 1000 ); $mockStream ->expects($this->exactly(3)) ->method('read') ->withAnyParameters() ->willReturn("+OK\r\n"); $mockStreamFactory ->expects($this->exactly(4)) ->method('createStream') ->withAnyParameters() ->willReturn($mockStream); $connection ->expects($this->exactly(9)) ->method('getConnectionByCommand') ->willReturn($streamConnection); // Verify that remove() is NOT called for TimeoutException $connection ->expects($this->never()) ->method('remove'); $pipeline = new Pipeline(new Client($connection)); $responses = $pipeline->execute(static function (Pipeline $pipe) { $pipe->set('key', 'value'); $pipe->set('key', 'value'); $pipe->set('key', 'value'); }); $this->assertEquals(['OK', 'OK', 'OK'], $responses); } /** * @group disconnected * @throws Exception */ public function testRetryReplicationPipelineOnRetryableErrors(): void { $mockStream = $this->getMockBuilder(StreamInterface::class)->getMock(); $mockStreamFactory = $this->getMockBuilder(StreamFactoryInterface::class)->getMock(); $parameters = new Parameters([ 'retry' => new Retry(new ExponentialBackoff(1000, 10000), 3), 'role' => 'master', ]); $mockStream ->expects($this->atLeast(3)) ->method('close') ->withAnyParameters(); $mockStream ->expects($this->exactly(6)) ->method('write') ->withAnyParameters() ->willReturnOnConsecutiveCalls( $this->throwException(new RuntimeException('', 2)), $this->throwException(new RuntimeException('', 2)), $this->throwException(new RuntimeException('', 2)), 1000, 1000, 1000 ); $mockStream ->expects($this->exactly(3)) ->method('read') ->withAnyParameters() ->willReturn("+OK\r\n"); $mockStreamFactory ->expects($this->exactly(4)) ->method('createStream') ->withAnyParameters() ->willReturn($mockStream); $streamConnection = new StreamConnection($parameters, $mockStreamFactory); $connection = new MasterSlaveReplication(new ReplicationStrategy()); $connection->add($streamConnection); $pipeline = new Pipeline(new Client($connection)); $responses = $pipeline->execute(static function (Pipeline $pipe) { $pipe->set('key', 'value'); $pipe->set('key', 'value'); $pipe->set('key', 'value'); }); $this->assertEquals(['OK', 'OK', 'OK'], $responses); } // ******************************************************************** // // ---- INTEGRATION TESTS --------------------------------------------- // // ******************************************************************** // /** * @group connected */ public function testIntegrationWithFluentInterface(): void { $pipeline = $this->getClient()->pipeline(); $results = $pipeline ->echo('one') ->echo('two') ->echo('three') ->execute(); $this->assertSame(['one', 'two', 'three'], $results); } /** * @group connected * @requiresRedisVersion >= 6.0.0 */ public function testIntegrationWithFluentInterfaceResp3(): void { $pipeline = $this->getClient(['protocol' => 3])->pipeline(); $results = $pipeline ->echo('one') ->echo('two') ->echo('three') ->execute(); $this->assertSame(['one', 'two', 'three'], $results); } /** * @group connected */ public function testIntegrationWithCallableBlock(): void { $client = $this->getClient(); $results = $client->pipeline(static function (Pipeline $pipe) { $pipe->set('foo', 'bar'); $pipe->get('foo'); }); $this->assertEquals(['OK', 'bar'], $results); $this->assertSame(1, $client->exists('foo')); } /** * @group connected */ public function testOutOfBandMessagesInsidePipeline(): void { $oob = null; $client = $this->getClient(); $results = $client->pipeline(static function (Pipeline $pipe) use (&$oob) { $pipe->set('foo', 'bar'); $oob = $pipe->getClient()->echo('oob message'); $pipe->get('foo'); }); $this->assertEquals(['OK', 'bar'], $results); $this->assertSame('oob message', $oob); $this->assertSame(1, $client->exists('foo')); } /** * @group connected */ public function testIntegrationWithClientExceptionInCallableBlock(): void { $exception = null; $client = $this->getClient(); try { $client->pipeline(static function (Pipeline $pipe) { $pipe->set('foo', 'bar'); throw new ClientException('TEST'); }); } catch (Exception $exception) { // NOOP } $this->assertInstanceOf('Predis\ClientException', $exception); $this->assertSame('TEST', $exception->getMessage()); $this->assertSame(0, $client->exists('foo')); } /** * @group connected */ public function testIntegrationWithServerExceptionInCallableBlock(): void { $exception = null; $client = $this->getClient(); try { $client->pipeline(static function (Pipeline $pipe) { $pipe->set('foo', 'bar'); // LPUSH on a string key fails, but won't stop // the pipeline to send the commands. $pipe->lpush('foo', 'bar'); $pipe->set('hoge', 'piyo'); }); } catch (Exception $exception) { // NOOP } $this->assertInstanceOf('Predis\Response\ServerException', $exception); $this->assertSame(1, $client->exists('foo')); $this->assertSame(1, $client->exists('hoge')); } /** * @group connected */ public function testIntegrationWithServerErrorInCallableBlock(): void { $client = $this->getClient([], ['exceptions' => false]); $results = $client->pipeline(static function (Pipeline $pipe) { $pipe->set('foo', 'bar'); $pipe->lpush('foo', 'bar'); // LPUSH on a string key fails. $pipe->get('foo'); }); $this->assertEquals('OK', $results[0]); $this->assertInstanceOf('Predis\Response\Error', $results[1]); $this->assertSame('bar', $results[2]); } /** * @group connected * @group cluster * @group relay-incompatible * @requiresRedisVersion >= 6.2.0 */ public function testClusterExecutePipeline(): void { $client = $this->getClient(); $results = $client->pipeline(static function (Pipeline $pipe) { $pipe->set('foo', 'bar'); $pipe->set('bar', 'foo'); $pipe->set('baz', 'baz'); $pipe->get('foo'); $pipe->get('bar'); $pipe->get('baz'); }); $expectedResults = [ new Response\Status('OK'), new Response\Status('OK'), new Response\Status('OK'), 'bar', 'foo', 'baz', ]; $this->assertSameValues($expectedResults, $results); } /** * @group connected * @group relay-incompatible * @requiresRedisVersion >= 6.2.0 * @return void */ public function testStandaloneRetryPipelineOnTimeoutException(): void { $client = $this->getClient([ 'retry' => new Retry(new ExponentialBackoff(100, 1000), 3), 'read_write_timeout' => 0.1, ]); $this->expectException(TimeoutException::class); $client->pipeline(static function (Pipeline $pipe) use (&$retries) { $pipe->incr('test_key'); $pipe->blpop('foo', 3); }); $this->assertEquals(3, $client->get('test_key')); } /** * @group connected * @group cluster * @group relay-incompatible * @requiresRedisVersion >= 6.2.0 * @return void */ public function testClusterRetryPipelineOnTimeoutException(): void { $retries = 0; $client = $this->getClient([], [ 'parameters' => [ 'retry' => new Retry(new ExponentialBackoff(100, 1000), 3), 'read_write_timeout' => 0.1, ], ]); $this->expectException(TimeoutException::class); $client->pipeline(static function (Pipeline $pipe) use (&$retries) { ++$retries; $pipe->blpop('foo', 3); }); $this->assertEquals(3, $retries); } /** * @group connected * @group relay-incompatible */ public function testReplicationExecutesPipelineWithCRLFValues(): void { $parameters = $this->getDefaultParametersArray(); $client = new Client( ["tcp://{$parameters['host']}:{$parameters['port']}?role=master&database={$parameters['database']}&password={$parameters['password']}"], ['replication' => 'predis'] ); $results = $client->pipeline(static function (Pipeline $pipe) { $pipe->set('foo', "bar\r\nbaz"); $pipe->get('foo'); }); $expectedResults = [ new Response\Status('OK'), "bar\r\nbaz", ]; $this->assertSameValues($expectedResults, $results); } // ******************************************************************** // // ---- HELPER METHODS ------------------------------------------------ // // ******************************************************************** // /** * Returns a client instance connected to the specified Redis server. * * @param array $parameters Additional connection parameters * @param array $options Additional client options * * @return ClientInterface */ protected function getClient(array $parameters = [], array $options = []): ClientInterface { return $this->createClient($parameters, $options); } /** * Helper method returning a callback used to responses to ECHO command. * * @return callable */ protected function getReadCallback(): callable { return static function (CommandInterface $command) { if (($id = $command->getId()) !== 'ECHO') { throw new InvalidArgumentException("Expected ECHO, got {$id}"); } [$echoed] = $command->getArguments(); return $echoed; }; } }