diff --git a/Spanner/composer.json b/Spanner/composer.json index d860984c5501..4ad8a64c57c5 100644 --- a/Spanner/composer.json +++ b/Spanner/composer.json @@ -17,7 +17,8 @@ "phpdocumentor/reflection-docblock": "^5.3", "erusev/parsedown": "^1.6", "google/cloud-pubsub": "^2.0", - "dg/bypass-finals": "^1.7" + "dg/bypass-finals": "^1.7", + "dms/phpunit-arraysubset-asserts": "^0.5.0" }, "suggest": { "ext-protobuf": "Provides a significant increase in throughput over the pure PHP protobuf implementation. See https://cloud.google.com/php/grpc for installation instructions.", diff --git a/Spanner/src/Instance.php b/Spanner/src/Instance.php index 4651a06b38e0..fc38dbbaad3f 100644 --- a/Spanner/src/Instance.php +++ b/Spanner/src/Instance.php @@ -119,7 +119,7 @@ public function __construct( private InstanceAdminClient $instanceAdminClient, private DatabaseAdminClient $databaseAdminClient, private Serializer $serializer, - private stirng $projectId, + private string $projectId, private string $name, private bool $returnInt64AsObject = false, private array $info = [], diff --git a/Spanner/src/Operation.php b/Spanner/src/Operation.php index eb6fc0cf063e..e3789f157058 100644 --- a/Spanner/src/Operation.php +++ b/Spanner/src/Operation.php @@ -22,6 +22,7 @@ use Google\ApiCore\ArrayTrait; use Google\Cloud\Core\TimeTrait; use Google\Cloud\Core\ValidateTrait; +use Google\Cloud\Core\RequestProcessorTrait; use Google\Cloud\Spanner\Batch\QueryPartition; use Google\Cloud\Spanner\Batch\ReadPartition; use Google\Cloud\Spanner\Session\Session; @@ -55,6 +56,7 @@ class Operation use ApiHelperTrait; use ArrayTrait; use RequestTrait; + use RequestProcessorTrait; use MutationTrait; use TimeTrait; use ValidateTrait; @@ -659,7 +661,7 @@ public function createSession($databaseName, array $options = []) ]]; $request = $this->serializer->decodeMessage(new CreateSessionRequest(), $data); - $callOptions = $this->addResourcePrefixHeader($callOptions, $this->getDatabaseNameFromSession($session)); + $callOptions = $this->addResourcePrefixHeader($callOptions, $databaseName); $callOptions = $this->addLarHeader($callOptions, $this->routeToLeader); $response = $this->spannerClient->createSession($request, $callOptions); diff --git a/Spanner/src/Transaction.php b/Spanner/src/Transaction.php index 2a120784aa6c..0580e285a143 100644 --- a/Spanner/src/Transaction.php +++ b/Spanner/src/Transaction.php @@ -101,8 +101,8 @@ public function __construct( private Session $session, private ?string $transactionId = null, private bool $isRetry = false, - private ?string $tag = null, - private array $options = [], + ?string $tag = null, + array $options = [], private ?ValueMapper $mapper = null ) { $this->type = ($transactionId || isset($options['begin'])) @@ -116,6 +116,8 @@ public function __construct( } $this->context = SessionPoolInterface::CONTEXT_READWRITE; + $this->options = $options; + $this->tag =$tag; } /** diff --git a/Spanner/tests/RequestHandlingTestTrait.php b/Spanner/tests/RequestHandlingTestTrait.php deleted file mode 100644 index 1e8ab7203d19..000000000000 --- a/Spanner/tests/RequestHandlingTestTrait.php +++ /dev/null @@ -1,115 +0,0 @@ -prophesize(RequestHandler::class); - } - - private function mockSendRequest( - string $clientClass, - string $method, - ?callable $requestCondition, - mixed $response, - int $timesCalled = 1, - ?callable $optionalArrayCondition = null - ) { - if (is_null($requestCondition)) { - $requestCondition = function ($args) { - return true; - }; - } - if (is_null($optionalArrayCondition)) { - $optionalArrayCondition = function ($args) { - return true; - }; - } - - $handler = $this->requestHandler - ->sendRequest( - $clientClass, - $method, - Argument::that($requestCondition), - Argument::that($optionalArrayCondition) - ); - - if ($response instanceof \Exception) { - $handler->willThrow($response); - } elseif (is_callable($response)) { - $handler->will($response); - } else { - $handler->willReturn($response); - } - - if ($timesCalled >= 0) { - $handler->shouldBeCalledTimes($timesCalled); - } - } - - private function getSerializer() - { - return new Serializer([], [ - 'google.protobuf.Value' => function ($v) { - return $this->flattenValue($v); - }, - 'google.protobuf.ListValue' => function ($v) { - return $this->flattenListValue($v); - }, - 'google.protobuf.Struct' => function ($v) { - return $this->flattenStruct($v); - }, - 'google.protobuf.Timestamp' => function ($v) { - return $this->formatTimestampFromApi($v); - } - ]); - } - - private function getOperationResponseMock() - { - $operation = $this->serializer->decodeMessage( - new \Google\LongRunning\Operation(), - ['metadata' => [ - 'typeUrl' => 'type.googleapis.com/google.spanner.admin.database.v1.CreateDatabaseMetadata' - ]] - ); - $operationResponse = $this->prophesize(OperationResponse::class); - $operationResponse->getLastProtoResponse()->willReturn($operation); - $operationResponse->isDone()->willReturn(false); - $operationResponse->getError()->willReturn(null); - $operationResponse->withResultFunction(Argument::any()) - ->willReturn($operationResponse->reveal()); - - return $operationResponse; - } -} diff --git a/Spanner/tests/Snippet/DatabaseTest.php b/Spanner/tests/Snippet/DatabaseTest.php index 4533a5a357a7..3737ed173e03 100644 --- a/Spanner/tests/Snippet/DatabaseTest.php +++ b/Spanner/tests/Snippet/DatabaseTest.php @@ -484,7 +484,7 @@ public function testRunTransaction() ]) ); - $this->mockSendRequest(SpannerClient::class, 'rollback', null, null, 0); + $this->spannerClient->rollback(Argument::cetera())->shouldNotBeCalled(); $this->refreshOperation($this->database, $this->requestHandler->reveal(), $this->serializer); @@ -499,9 +499,9 @@ public function testRunTransaction() public function testRunTransactionRollback() { - $this->mockSendRequest(SpannerClient::class, 'beginTransaction', null, null, 0); + $this->spannerClient->beginTransaction(Argument::cetera())->shouldNotBeCalled(); - $this->mockSendRequest(SpannerClient::class, 'commit', null, null, 0); + $this->spannerClient->commit(Argument::cetera())->shouldNotBeCalled(); $this->mockSendRequest(SpannerClient::class, 'rollback', null, null, 1); diff --git a/Spanner/tests/Unit/DatabaseTest.php b/Spanner/tests/Unit/DatabaseTest.php index c6031be919f3..5e8d1ff07063 100644 --- a/Spanner/tests/Unit/DatabaseTest.php +++ b/Spanner/tests/Unit/DatabaseTest.php @@ -832,9 +832,9 @@ public function testSnapshotNestedTransaction() // Begin transaction RPC is skipped when begin is inlined // and invoked only if `begin` fails or if commit is the // sole operation in the transaction. - $this->mockSendRequest(SpannerClient::class, 'beginTransaction', null, null, 0); + $this->spannerClient->beginTransaction(Argument::cetera())->shouldNotBeCalled(); - $this->mockSendRequest(SpannerClient::class, 'rollback', null, null, 0); + $this->spannerClient->rollback(Argument::cetera())->shouldNotBeCalled(); $this->refreshOperation($this->database, $this->requestHandler->reveal(), $this->serializer); @@ -900,9 +900,9 @@ public function testRunTransactionNoCommit() { $this->expectException(\InvalidArgumentException::class); - $this->mockSendRequest(SpannerClient::class, 'beginTransaction', null, null, 0); + $this->spannerClient->beginTransaction(Argument::cetera())->shouldNotBeCalled(); - $this->mockSendRequest(SpannerClient::class, 'rollback', null, null, 0); + $this->spannerClient->rollback(Argument::cetera())->shouldNotBeCalled(); $this->refreshOperation($this->database, $this->requestHandler->reveal(), $this->serializer); @@ -913,9 +913,9 @@ public function testRunTransactionNestedTransaction() { $this->expectException(\BadMethodCallException::class); - $this->mockSendRequest(SpannerClient::class, 'beginTransaction', null, null, 0); + $this->spannerClient->beginTransaction(Argument::cetera())->shouldNotBeCalled(); - $this->mockSendRequest(SpannerClient::class, 'rollback', null, null, 0); + $this->spannerClient->rollback(Argument::cetera())->shouldNotBeCalled(); $this->refreshOperation($this->database, $this->requestHandler->reveal(), $this->serializer); @@ -1074,9 +1074,9 @@ public function testTransactionNestedTransaction() { $this->expectException(\BadMethodCallException::class); - $this->mockSendRequest(SpannerClient::class, 'beginTransaction', null, null, 0); + $this->spannerClient->beginTransaction(Argument::cetera())->shouldNotBeCalled(); - $this->mockSendRequest(SpannerClient::class, 'rollback', null, null, 0); + $this->spannerClient->rollback(Argument::cetera())->shouldNotBeCalled(); $this->refreshOperation($this->database, $this->requestHandler->reveal(), $this->serializer); @@ -2099,7 +2099,7 @@ function ($args) use ($sql) { $error, Database::MAX_RETRIES ); - $this->mockSendRequest(SpannerClient::class, 'commit', null, null, 0); + $this->spannerClient->commit(Argument::cetera())->shouldNotBeCalled(); $this->refreshOperation($this->database, $this->requestHandler->reveal(), $this->serializer); $this->database->runTransaction(function ($t) use ($sql) { @@ -2336,7 +2336,7 @@ private function resultGeneratorWithError() private function stubCommit($withTransaction = true) { if ($withTransaction) { - $this->mockSendRequest(SpannerClient::class, 'beginTransaction', null, null, 0); + $this->spannerClient->beginTransaction(Argument::cetera())->shouldNotBeCalled(); } else { $this->mockSendRequest( SpannerClient::class, diff --git a/Spanner/tests/Unit/InstanceConfigurationTest.php b/Spanner/tests/Unit/InstanceConfigurationTest.php index 19d8cb183f51..fbe7f1233bc5 100644 --- a/Spanner/tests/Unit/InstanceConfigurationTest.php +++ b/Spanner/tests/Unit/InstanceConfigurationTest.php @@ -21,6 +21,7 @@ use Google\ApiCore\ApiException; use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Core\Testing\TestHelpers; +use Google\Cloud\Core\Serializer; use Google\Cloud\Spanner\Admin\Instance\V1\Client\InstanceAdminClient; use Google\Cloud\Spanner\Admin\Instance\V1\InstanceConfig; use Google\Cloud\Spanner\Admin\Instance\V1\DeleteInstanceConfigRequest; diff --git a/Spanner/tests/Unit/OperationTest.php b/Spanner/tests/Unit/OperationTest.php index acb969812f40..4e4b12b96544 100644 --- a/Spanner/tests/Unit/OperationTest.php +++ b/Spanner/tests/Unit/OperationTest.php @@ -465,7 +465,7 @@ function ($args) { public function testSnapshotSingleUse() { - $this->mockSendRequest(SpannerClient::class, 'beginTransaction', null, null, 0); + $this->spannerClient->beginTransaction(Argument::cetera())->shouldNotBeCalled(); $this->operation->___setProperty('requestHandler', $this->requestHandler->reveal()); $this->operation->___setProperty('serializer', $this->serializer); diff --git a/Spanner/tests/Unit/TransactionTypeTest.php b/Spanner/tests/Unit/TransactionTypeTest.php index 3340ea1b3507..c708f1546f92 100644 --- a/Spanner/tests/Unit/TransactionTypeTest.php +++ b/Spanner/tests/Unit/TransactionTypeTest.php @@ -18,12 +18,13 @@ namespace Google\Cloud\Spanner\Tests\Unit; use Google\ApiCore\Serializer; +use Google\ApiCore\ServerStream; use Google\Cloud\Core\ApiHelperTrait; -use Google\Cloud\Core\RequestHandler; use Google\Cloud\Core\Testing\GrpcTestTrait; use Google\Cloud\Core\Testing\TestHelpers; use Google\Cloud\Core\TimeTrait; -use Google\Cloud\Spanner\Admin\Instance\V1\InstanceAdminClient; +use Google\Cloud\Spanner\Admin\Database\V1\Client\DatabaseAdminClient; +use Google\Cloud\Spanner\Admin\Instance\V1\Client\InstanceAdminClient; use Google\Cloud\Spanner\Database; use Google\Cloud\Spanner\Instance; use Google\Cloud\Spanner\KeySet; @@ -34,11 +35,16 @@ use Google\Cloud\Spanner\Timestamp; use Google\Cloud\Spanner\Transaction; use Google\Cloud\Spanner\V1\BeginTransactionRequest; +use Google\Cloud\Spanner\V1\BeginTransactionResponse; use Google\Cloud\Spanner\V1\Client\SpannerClient; use Google\Cloud\Spanner\V1\CommitRequest; +use Google\Cloud\Spanner\V1\CommitResponse; +use Google\Cloud\Spanner\V1\CreateSessionRequest; use Google\Cloud\Spanner\V1\ExecuteSqlRequest; use Google\Cloud\Spanner\V1\ReadRequest; use Google\Cloud\Spanner\V1\RollbackRequest; +use Google\Cloud\Spanner\V1\Session; +use Google\Cloud\Spanner\V1\Transaction as TransactionProto; use Google\Cloud\Spanner\V1\TransactionOptions; use Google\Cloud\Spanner\V1\TransactionSelector; use Google\Cloud\Spanner\V1\TransactionOptions\PBReadOnly; @@ -59,7 +65,6 @@ class TransactionTypeTest extends TestCase use GrpcTestTrait; use ProphecyTrait; use ResultTestTrait; - use RequestHandlingTestTrait; use TimeTrait; const PROJECT = 'my-project'; @@ -68,7 +73,7 @@ class TransactionTypeTest extends TestCase const TRANSACTION = 'my-transaction'; const SESSION = 'my-session'; - private $requestHandler; + private $spannerClient; private $serializer; private $timestamp; @@ -78,50 +83,51 @@ public function setUp(): void $this->timestamp = (new Timestamp(\DateTime::createFromFormat('U', time()), 500000005))->formatAsString(); - $this->requestHandler = $this->getRequestHandlerStub(); - $this->serializer = $this->getSerializer(); - $this->mockSendRequest( - SpannerClient::class, - 'createSession', - function ($args) { - Argument::type(BeginTransactionRequest::class); + $this->spannerClient = $this->prophesize(SpannerClient::class); + $this->serializer = new Serializer(); + $this->spannerClient->createSession( + Argument::that(function (CreateSessionRequest $request) { $this->assertEquals( - $args->getDatabase(), + $request->getDatabase(), SpannerClient::databaseName(self::PROJECT, self::INSTANCE, self::DATABASE) ); return true; - }, - ['name' => $this->getFullyQualifiedSessionName()], - -1 - ); - $this->mockSendRequest(SpannerClient::class, 'deleteSession', null, [], -1); + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new Session(['name' => $this->getFullyQualifiedSessionName()])); + + $this->spannerClient->deleteSession() + ->shouldBeCalled(); } public function testDatabaseRunTransactionPreAllocate() { - $this->mockSendRequest( - SpannerClient::class, - 'beginTransaction', - function ($args) { + $this->spannerClient->beginTransaction( + Argument::that(function ($request) { Argument::type(BeginTransactionRequest::class); - $this->assertEquals($args->getSession(), $this->getFullyQualifiedSessionName()); + $this->assertEquals($request->getSession(), $this->getFullyQualifiedSessionName()); return true; - }, - ['id' => self::TRANSACTION] - ); - $this->mockSendRequest( - SpannerClient::class, - 'commit', - function ($args) { + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new TransactionProto(['id' => self::TRANSACTION])); + + $this->spannerClient->commit( + Argument::that(function ($request) { Argument::type(CommitRequest::class); - $this->assertEquals($args->getTransactionId(), self::TRANSACTION); + $this->assertEquals($request->getTransactionId(), self::TRANSACTION); return true; - }, - ['commitTimestamp' => $this->timestamp] - ); + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new CommitResponse(['commit_timestamp' => $this->timestamp])); $database = $this->database( - $this->requestHandler->reveal(), + $this->spannerClient->reveal(), $this->serializer ); @@ -133,24 +139,24 @@ function ($args) { public function testDatabaseRunTransactionSingleUse() { - $this->mockSendRequest(SpannerClient::class, 'beginTransaction', null, null, 0); + $this->spannerClient->beginTransaction(Argument::cetera())->shouldNotBeCalled(); - $this->mockSendRequest( - SpannerClient::class, - 'commit', - function ($args) { + $this->spannerClient->commit( + Argument::that(function ($request) { Argument::type(CommitRequest::class); $this->assertEquals( - $args->getSingleUseTransaction(), + $request->getSingleUseTransaction(), $this->createTransactionOptions() ); return true; - }, - ['commitTimestamp' => $this->timestamp] - ); + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new CommitResponse(['commit_timestamp' => $this->timestamp])); $database = $this->database( - $this->requestHandler->reveal(), + $this->spannerClient->reveal(), $this->serializer ); @@ -163,18 +169,18 @@ function ($args) { public function testDatabaseTransactionPreAllocate() { - $this->mockSendRequest( - SpannerClient::class, - 'beginTransaction', - function ($args) { + $this->spannerClient->beginTransaction( + Argument::that(function ($request) { Argument::type(BeginTransactionRequest::class); - $this->assertEquals($args->getOptions(), $this->createTransactionOptions()); + $this->assertEquals($request->getOptions(), $this->createTransactionOptions()); return true; - }, - ['id' => self::TRANSACTION] - ); + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new TransactionProto(['id' => self::TRANSACTION])); - $database = $this->database($this->requestHandler->reveal(), $this->serializer); + $database = $this->database($this->spannerClient->reveal()); $transaction = $database->transaction(); $this->assertInstanceOf(Transaction::class, $transaction); @@ -183,10 +189,10 @@ function ($args) { public function testDatabaseTransactionSingleUse() { - $this->mockSendRequest(SpannerClient::class, 'beginTransaction', null, null, 0); + $this->spannerClient->beginTransaction(Argument::cetera())->shouldNotBeCalled(); $database = $this->database( - $this->requestHandler->reveal(), + $this->spannerClient->reveal(), $this->serializer ); @@ -198,22 +204,22 @@ public function testDatabaseTransactionSingleUse() public function testDatabaseSnapshotPreAllocate() { - $this->mockSendRequest( - SpannerClient::class, - 'beginTransaction', - function ($args) { + $this->spannerClient->beginTransaction( + Argument::that(function ($request) { Argument::type(BeginTransactionRequest::class); $this->assertEquals( - $args->getOptions(), + $request->getOptions(), $this->createTransactionOptions(['readOnly' => ['strong' => true]]) ); return true; - }, - ['id' => self::TRANSACTION] - ); + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new TransactionProto(['id' => self::TRANSACTION])); $database = $database = $this->database( - $this->requestHandler->reveal(), + $this->spannerClient->reveal(), $this->serializer ); @@ -225,10 +231,10 @@ function ($args) { public function testDatabaseSnapshotSingleUse() { - $this->mockSendRequest(SpannerClient::class, 'beginTransaction', null, null, 0); + $this->spannerClient->beginTransaction(Argument::cetera())->shouldNotBeCalled(); $database = $database = $this->database( - $this->requestHandler->reveal(), + $this->spannerClient->reveal(), $this->serializer ); @@ -250,32 +256,32 @@ public function testDatabaseSingleUseSnapshotMinReadTimestampAndMaxStaleness($ch $timestamp = new Timestamp($time[0], $time[1]); $duration = new Duration(['seconds' => $seconds, 'nanos' => $nanos]); - $this->mockSendRequest(SpannerClient::class, 'beginTransaction', null, null, 0); - $transaction = [ - 'singleUse' => [ - 'readOnly' => [ - 'minReadTimestamp' => $this->formatTimestampForApi($this->timestamp), - 'maxStaleness' => [ - 'seconds' => $seconds, - 'nanos' => $nanos - ] - ] - ] - ]; - $selector = $this->serializer->decodeMessage(new TransactionSelector, $transaction); + $this->spannerClient->beginTransaction(Argument::cetera())->shouldNotBeCalled(); + + $transaction = new TransactionSelector([ + 'single_use' => new TransactionOptions([ + 'read_only' => new PBReadOnly([ + 'min_read_timestamp' => new ProtoTimestamp([ + 'seconds' => $time[0]->format('U'), + 'nanos' => $time[1] + ]), + 'max_staleness' => $duration, + ]) + ]) + ]); - $this->mockSendRequest( - SpannerClient::class, - 'executeStreamingSql', - function ($args) use ($selector) { + $this->spannerClient->executeStreamingSql( + Argument::that(function ($request) use ($transaction) { Argument::type(ExecuteSqlRequest::class); - $this->assertEquals($args->getTransaction(), $selector); + $this->assertEquals($request->getTransaction(), $transaction); return true; - }, - $this->resultGenerator($chunks) - ); + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream($chunks)); - $database = $this->database($this->requestHandler->reveal(), $this->serializer); + $database = $this->database($this->spannerClient->reveal()); $snapshot = $database->snapshot([ 'singleUse' => true, 'minReadTimestamp' => $timestamp, @@ -292,10 +298,10 @@ public function testDatabasePreAllocatedSnapshotMinReadTimestamp() $time = $this->parseTimeString($this->timestamp); $timestamp = new Timestamp($time[0], $time[1]); - $this->mockSendRequest(SpannerClient::class, 'beginTransaction', null, null, 0); - $this->mockSendRequest(SpannerClient::class, 'executeStreamingSql', null, null, 0); + $this->spannerClient->beginTransaction(Argument::cetera())->shouldNotBeCalled(); + $this->spannerClient->executeStreamingSql(Argument::cetera())->shouldNotBeCalled(); - $database = $this->database($this->requestHandler->reveal(), $this->serializer); + $database = $this->database($this->spannerClient->reveal()); $snapshot = $database->snapshot([ 'minReadTimestamp' => $timestamp, @@ -311,10 +317,10 @@ public function testDatabasePreAllocatedSnapshotMaxStaleness() $duration = new Duration(['seconds' => $seconds, 'nanos' => $nanos]); - $this->mockSendRequest(SpannerClient::class, 'beginTransaction', null, null, 0); - $this->mockSendRequest(SpannerClient::class, 'executeStreamingSql', null, null, 0); + $this->spannerClient->beginTransaction(Argument::cetera())->shouldNotBeCalled(); + $this->spannerClient->executeStreamingSql(Argument::cetera())->shouldNotBeCalled(); - $database = $this->database($this->requestHandler->reveal(), $this->serializer); + $database = $this->database($this->spannerClient->reveal()); $snapshot = $database->snapshot([ 'maxStaleness' => $duration @@ -332,32 +338,32 @@ public function testDatabaseSnapshotSingleUseReadTimestampAndExactStaleness($chu $time = $this->parseTimeString($this->timestamp); $timestamp = new Timestamp($time[0], $time[1]); $duration = new Duration(['seconds' => $seconds, 'nanos' => $nanos]); - $transaction = [ - 'singleUse' => [ - 'readOnly' => [ - 'minReadTimestamp' => $this->formatTimestampForApi($this->timestamp), - 'exactStaleness' => [ - 'seconds' => $seconds, - 'nanos' => $nanos - ] - ] - ] - ]; - $selector = $this->serializer->decodeMessage(new TransactionSelector, $transaction); + $transaction = new TransactionSelector([ + 'single_use' => new TransactionOptions([ + 'read_only' => new PBReadOnly([ + 'min_read_timestamp' => new ProtoTimestamp([ + 'seconds' => $time[0]->format('U'), + 'nanos' => $time[1] + ]), + 'exact_staleness' => $duration, + ]) + ]) + ]); + + $this->spannerClient->beginTransaction(Argument::cetera())->shouldNotBeCalled(); - $this->mockSendRequest(SpannerClient::class, 'beginTransaction', null, null, 0); - $this->mockSendRequest( - SpannerClient::class, - 'executeStreamingSql', - function ($args) use ($selector) { + $this->spannerClient->executeStreamingSql( + Argument::that(function ($request) use ($transaction) { Argument::type(ExecuteSqlRequest::class); - $this->assertEquals($args->getTransaction(), $selector); + $this->assertEquals($request->getTransaction(), $transaction); return true; - }, - $this->resultGenerator($chunks) - ); + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream($chunks)); - $database = $this->database($this->requestHandler->reveal(), $this->serializer); + $database = $this->database($this->spannerClient->reveal()); $snapshot = $database->snapshot([ 'singleUse' => true, @@ -388,31 +394,32 @@ public function testDatabaseSnapshotPreAllocateReadTimestampAndExactStaleness($c ] ] ]; - $this->mockSendRequest( - SpannerClient::class, - 'beginTransaction', - function ($args) use ($options) { + $this->spannerClient->beginTransaction( + Argument::that(function ($request) use ($options) { Argument::type(BeginTransactionRequest::class); $this->assertEquals( - $args->getOptions(), + $request->getOptions(), $this->createTransactionOptions($options) ); return true; - }, - ['id' => self::TRANSACTION] - ); - $this->mockSendRequest( - SpannerClient::class, - 'executeStreamingSql', - function ($args) { + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new TransactionProto(['id' => self::TRANSACTION])); + + $this->spannerClient->executeStreamingSql( + Argument::that(function ($request) { Argument::type(ExecuteSqlRequest::class); - $this->assertEquals($args->getTransaction()->getId(), self::TRANSACTION); + $this->assertEquals($request->getTransaction()->getId(), self::TRANSACTION); return true; - }, - $this->resultGenerator($chunks) - ); + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream($chunks)); - $database = $this->database($this->requestHandler->reveal(), $this->serializer); + $database = $this->database($this->spannerClient->reveal()); $snapshot = $database->snapshot([ 'readTimestamp' => $timestamp, @@ -427,26 +434,25 @@ function ($args) { */ public function testDatabaseSingleUseSnapshotStrongConsistency($chunks) { - $this->mockSendRequest(SpannerClient::class, 'beginTransaction', null, null, 0); - $transaction = [ - 'singleUse' => [ - 'readOnly' => [ + $this->spannerClient->beginTransaction(Argument::cetera())->shouldNotBeCalled(); + $transaction = new TransactionSelector([ + 'single_use' => new TransactionOptions([ + 'read_only' => new PBReadOnly([ 'strong' => true - ] - ] - ]; - $selector = $this->serializer->decodeMessage(new TransactionSelector, $transaction); - $this->mockSendRequest( - SpannerClient::class, - 'executeStreamingSql', - function ($args) use ($selector) { + ]) + ]) + ]); + $this->spannerClient->executeStreamingSql( + Argument::that(function ($request) use ($transaction) { Argument::type(executeSqlRequest::class); - $this->assertEquals($args->getTransaction(), $selector); + $this->assertEquals($request->getTransaction(), $transaction); return true; - }, - $this->resultGenerator($chunks) - ); - $database = $this->database($this->requestHandler->reveal(), $this->serializer); + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream($chunks)); + $database = $this->database($this->spannerClient->reveal()); $snapshot = $database->snapshot([ 'singleUse' => true, @@ -466,31 +472,32 @@ public function testDatabasePreAllocatedSnapshotStrongConsistency($chunks) 'strong' => true ] ]; - $this->mockSendRequest( - SpannerClient::class, - 'beginTransaction', - function ($args) use ($options) { + $this->spannerClient->beginTransaction( + Argument::that(function ($request) use ($options) { Argument::type(BeginTransactionRequest::class); $this->assertEquals( - $args->getOptions(), + $request->getOptions(), $this->createTransactionOptions($options) ); return true; - }, - ['id' => self::TRANSACTION] - ); - $this->mockSendRequest( - SpannerClient::class, - 'executeStreamingSql', - function ($args) { + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new TransactionProto(['id' => self::TRANSACTION])); + + $this->spannerClient->executeStreamingSql( + Argument::that(function ($request) { Argument::type(executeSqlRequest::class); - $this->assertEquals($args->getTransaction()->getId(), self::TRANSACTION); + $this->assertEquals($request->getTransaction()->getId(), self::TRANSACTION); return true; - }, - $this->resultGenerator($chunks) - ); + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream($chunks)); - $database = $this->database($this->requestHandler->reveal(), $this->serializer); + $database = $this->database($this->spannerClient->reveal()); $snapshot = $database->snapshot([ 'strong' => true @@ -504,27 +511,26 @@ function ($args) { */ public function testDatabaseSingleUseSnapshotDefaultsToStrongConsistency($chunks) { - $this->mockSendRequest(SpannerClient::class, 'beginTransaction', null, null, 0); - $transaction = [ - 'singleUse' => [ - 'readOnly' => [ + $this->spannerClient->beginTransaction(Argument::cetera())->shouldNotBeCalled(); + $transaction = new TransactionSelector([ + 'single_use' => new TransactionOptions([ + 'read_only' => new PBReadOnly([ 'strong' => true - ] - ] - ]; - $selector = $this->serializer->decodeMessage(new TransactionSelector, $transaction); - $this->mockSendRequest( - SpannerClient::class, - 'executeStreamingSql', - function ($args) use ($selector) { + ]) + ]) + ]); + $this->spannerClient->executeStreamingSql( + Argument::that(function ($request) use ($transaction) { Argument::type(executeSqlRequest::class); - $this->assertEquals($args->getTransaction(), $selector); + $this->assertEquals($request->getTransaction(), $transaction); return true; - }, - $this->resultGenerator($chunks) - ); + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream($chunks)); - $database = $this->database($this->requestHandler->reveal(), $this->serializer); + $database = $this->database($this->spannerClient->reveal()); $snapshot = $database->snapshot([ 'singleUse' => true, @@ -543,31 +549,32 @@ public function testDatabasePreAllocatedSnapshotDefaultsToStrongConsistency($chu 'strong' => true ] ]; - $this->mockSendRequest( - SpannerClient::class, - 'beginTransaction', - function ($args) use ($options) { + $this->spannerClient->beginTransaction( + Argument::that(function ($request) use ($options) { Argument::type(BeginTransactionRequest::class); $this->assertEquals( - $args->getOptions(), + $request->getOptions(), $this->createTransactionOptions($options) ); return true; - }, - ['id' => self::TRANSACTION] - ); - $this->mockSendRequest( - SpannerClient::class, - 'executeStreamingSql', - function ($args) { + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new TransactionProto(['id' => self::TRANSACTION])); + + $this->spannerClient->executeStreamingSql( + Argument::that(function ($request) { Argument::type(executeSqlRequest::class); - $this->assertEquals($args->getTransaction()->getId(), self::TRANSACTION); + $this->assertEquals($request->getTransaction()->getId(), self::TRANSACTION); return true; - }, - $this->resultGenerator($chunks) - ); + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream($chunks)); - $database = $this->database($this->requestHandler->reveal(), $this->serializer); + $database = $this->database($this->spannerClient->reveal()); $snapshot = $database->snapshot(); @@ -584,31 +591,32 @@ public function testDatabaseSnapshotReturnReadTimestamp($chunks) 'returnReadTimestamp' => true ] ]; - $this->mockSendRequest( - SpannerClient::class, - 'beginTransaction', - function ($args) use ($options) { + $this->spannerClient->beginTransaction( + Argument::that(function ($request) use ($options) { Argument::type(BeginTransactionRequest::class); $this->assertEquals( - $args->getOptions(), + $request->getOptions(), $this->createTransactionOptions($options) ); return true; - }, - ['id' => self::TRANSACTION] - ); - $this->mockSendRequest( - SpannerClient::class, - 'executeStreamingSql', - function ($args) { + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new TransactionProto(['id' => self::TRANSACTION])); + + $this->spannerClient->executeStreamingSql( + Argument::that(function ($request) { Argument::type(executeSqlRequest::class); - $this->assertEquals($args->getTransaction()->getId(), self::TRANSACTION); + $this->assertEquals($request->getTransaction()->getId(), self::TRANSACTION); return true; - }, - $this->resultGenerator($chunks) - ); + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream($chunks)); - $database = $this->database($this->requestHandler->reveal(), $this->serializer); + $database = $this->database($this->spannerClient->reveal()); $snapshot = $database->snapshot([ 'returnReadTimestamp' => true @@ -619,21 +627,21 @@ function ($args) { public function testDatabaseInsertSingleUseReadWrite() { - $this->mockSendRequest( - SpannerClient::class, - 'commit', - function ($args) { + $this->spannerClient->commit( + Argument::that(function ($request) { Argument::type(CommitRequest::class); $this->assertEquals( - $args->getSingleUseTransaction(), + $request->getSingleUseTransaction(), $this->createTransactionOptions() ); return true; - }, - ['commitTimestamp' => $this->timestamp] - ); + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new CommitResponse(['commit_timestamp' => $this->timestamp])); - $database = $this->database($this->requestHandler->reveal(), $this->serializer); + $database = $this->database($this->spannerClient->reveal()); $database->insert('Table', [ 'column' => 'value' @@ -715,27 +723,26 @@ public function testDatabaseDeleteSingleUseReadWrite() */ public function testDatabaseExecuteSingleUseReadOnly($chunks) { - $this->mockSendRequest(SpannerClient::class, 'beginTransaction', null, null, 0); - $transaction = [ - 'singleUse' => [ - 'readOnly' => [ + $this->spannerClient->beginTransaction(Argument::cetera())->shouldNotBeCalled(); + $transaction = new TransactionSelector([ + 'single_use' => new TransactionOptions([ + 'read_only' => new PBReadOnly([ 'strong' => true - ] - ] - ]; - $selector = $this->serializer->decodeMessage(new TransactionSelector, $transaction); - $this->mockSendRequest( - SpannerClient::class, - 'executeStreamingSql', - function ($args) use ($selector) { - Argument::type(executeSqlRequest::class); - $this->assertEquals($args->getTransaction(), $selector); + ]) + ]) + ]); + $this->spannerClient->executeStreamingSql( + Argument::that(function ($request) use ($transaction) { + Argument::type(ExecuteSqlRequest::class); + $this->assertEquals($request->getTransaction(), $transaction); return true; - }, - $this->resultGenerator($chunks) - ); + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream($chunks)); - $database = $this->database($this->requestHandler->reveal(), $this->serializer); + $database = $this->database($this->spannerClient->reveal()); $database->execute('SELECT * FROM Table')->rows()->current(); } @@ -744,27 +751,26 @@ function ($args) use ($selector) { */ public function testDatabaseExecuteBeginReadOnly($chunks) { - $this->mockSendRequest(SpannerClient::class, 'beginTransaction', null, null, 0); - $transaction = [ - 'begin' => [ - 'readOnly' => [ + $this->spannerClient->beginTransaction(Argument::cetera())->shouldNotBeCalled(); + $transaction = new TransactionSelector([ + 'begin' => new TransactionOptions([ + 'read_only' => new PBReadOnly([ 'strong' => true - ] - ] - ]; - $selector = $this->serializer->decodeMessage(new TransactionSelector, $transaction); - $this->mockSendRequest( - SpannerClient::class, - 'executeStreamingSql', - function ($args) use ($selector) { + ]) + ]) + ]); + $this->spannerClient->executeStreamingSql( + Argument::that(function ($request) use ($transaction) { Argument::type(executeSqlRequest::class); - $this->assertEquals($args->getTransaction(), $selector); + $this->assertEquals($request->getTransaction(), $transaction); return true; - }, - $this->resultGenerator($chunks) - ); + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream($chunks)); - $database = $this->database($this->requestHandler->reveal(), $this->serializer); + $database = $this->database($this->spannerClient->reveal()); $database->execute('SELECT * FROM Table', [ 'begin' => true ])->rows()->current(); @@ -775,25 +781,24 @@ function ($args) use ($selector) { */ public function testDatabaseExecuteBeginReadWrite($chunks) { - $this->mockSendRequest(SpannerClient::class, 'beginTransaction', null, null, 0); - $transaction = [ - 'begin' => [ - 'readWrite' => [] - ] - ]; - $selector = $this->serializer->decodeMessage(new TransactionSelector, $transaction); - $this->mockSendRequest( - SpannerClient::class, - 'executeStreamingSql', - function ($args) use ($selector) { + $this->spannerClient->beginTransaction(Argument::cetera())->shouldNotBeCalled(); + $transaction = new TransactionSelector([ + 'begin' => new TransactionOptions([ + 'read_write' => new ReadWrite() + ]) + ]); + $this->spannerClient->executeStreamingSql( + Argument::that(function ($request) use ($transaction) { Argument::type(executeSqlRequest::class); - $this->assertEquals($args->getTransaction(), $selector); + $this->assertEquals($request->getTransaction(), $transaction); return true; - }, - $this->resultGenerator($chunks) - ); + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream($chunks)); - $database = $this->database($this->requestHandler->reveal(), $this->serializer); + $database = $this->database($this->spannerClient->reveal()); $database->execute('SELECT * FROM Table', [ 'begin' => true, 'transactionType' => SessionPoolInterface::CONTEXT_READWRITE @@ -805,27 +810,26 @@ function ($args) use ($selector) { */ public function testDatabaseReadSingleUseReadOnly($chunks) { - $this->mockSendRequest(SpannerClient::class, 'beginTransaction', null, null, 0); - $transaction = [ - 'singleUse' => [ - 'readOnly' => [ + $this->spannerClient->beginTransaction(Argument::cetera())->shouldNotBeCalled(); + $transaction = new TransactionSelector([ + 'single_use' => new TransactionOptions([ + 'read_only' => new PBReadOnly([ 'strong' => true - ] - ] - ]; - $selector = $this->serializer->decodeMessage(new TransactionSelector, $transaction); - $this->mockSendRequest( - SpannerClient::class, - 'streamingRead', - function ($args) use ($selector) { + ]) + ]) + ]); + $this->spannerClient->streamingRead( + Argument::that(function ($request) use ($transaction) { Argument::type(readRequest::class); - $this->assertEquals($args->getTransaction(), $selector); + $this->assertEquals($request->getTransaction(), $transaction); return true; - }, - $this->resultGenerator($chunks) - ); + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream($chunks)); - $database = $this->database($this->requestHandler->reveal(), $this->serializer); + $database = $this->database($this->spannerClient->reveal()); $database->read('Table', new KeySet, [])->rows()->current(); } @@ -834,27 +838,26 @@ function ($args) use ($selector) { */ public function testDatabaseReadBeginReadOnly($chunks) { - $this->mockSendRequest(SpannerClient::class, 'beginTransaction', null, null, 0); - $transaction = [ - 'begin' => [ - 'readOnly' => [ + $this->spannerClient->beginTransaction(Argument::cetera())->shouldNotBeCalled(); + $transaction = new TransactionSelector([ + 'begin' => new TransactionOptions([ + 'read_only' => new PBReadOnly([ 'strong' => true - ] - ] - ]; - $selector = $this->serializer->decodeMessage(new TransactionSelector, $transaction); - $this->mockSendRequest( - SpannerClient::class, - 'streamingRead', - function ($args) use ($selector) { + ]) + ]) + ]); + $this->spannerClient->streamingRead( + Argument::that(function ($request) use ($transaction) { Argument::type(ReadRequest::class); - $this->assertEquals($args->getTransaction(), $selector); + $this->assertEquals($request->getTransaction(), $transaction); return true; - }, - $this->resultGenerator($chunks) - ); + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream($chunks)); - $database = $this->database($this->requestHandler->reveal(), $this->serializer); + $database = $this->database($this->spannerClient->reveal()); $database->read('Table', new KeySet, [], [ 'begin' => true ])->rows()->current(); @@ -865,25 +868,24 @@ function ($args) use ($selector) { */ public function testDatabaseReadBeginReadWrite($chunks) { - $this->mockSendRequest(SpannerClient::class, 'beginTransaction', null, null, 0); - $transaction = [ - 'begin' => [ - 'readWrite' => [] - ] - ]; - $selector = $this->serializer->decodeMessage(new TransactionSelector, $transaction); - $this->mockSendRequest( - SpannerClient::class, - 'streamingRead', - function ($args) use ($selector) { + $this->spannerClient->beginTransaction(Argument::cetera())->shouldNotBeCalled(); + $transaction = new TransactionSelector([ + 'begin' => new TransactionOptions([ + 'read_write' => new ReadWrite(), + ]) + ]); + $this->spannerClient->streamingRead( + Argument::that(function ($request) use ($transaction) { Argument::type(ReadRequest::class); - $this->assertEquals($args->getTransaction(), $selector); + $this->assertEquals($request->getTransaction(), $transaction); return true; - }, - $this->resultGenerator($chunks) - ); + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn($this->resultGeneratorStream($chunks)); - $database = $this->database($this->requestHandler->reveal(), $this->serializer); + $database = $this->database($this->spannerClient->reveal()); $database->read('Table', new KeySet, [], [ 'begin' => true, 'transactionType' => SessionPoolInterface::CONTEXT_READWRITE @@ -892,35 +894,35 @@ function ($args) use ($selector) { public function testTransactionPreAllocatedRollback() { - $this->mockSendRequest( - SpannerClient::class, - 'beginTransaction', - function ($args) { + $this->spannerClient->beginTransaction( + Argument::that(function ($request) { Argument::type(BeginTransactionRequest::class); - $this->assertEquals($args->getOptions(), $this->createTransactionOptions()); + $this->assertEquals($request->getOptions(), $this->createTransactionOptions()); return true; - }, - ['id' => self::TRANSACTION] - ); + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new TransactionProto(['id' => self::TRANSACTION])); + $session = SpannerClient::sessionName( self::PROJECT, self::INSTANCE, self::DATABASE, self::SESSION ); - $this->mockSendRequest( - SpannerClient::class, - 'rollback', - function ($args) use ($session) { + $this->spannerClient->rollback( + Argument::that(function ($request) use ($session) { Argument::type(RollbackRequest::class); - $this->assertEquals($args->getTransactionId(), self::TRANSACTION); - $this->assertEquals($args->getSession(), $session); + $this->assertEquals($request->getTransactionId(), self::TRANSACTION); + $this->assertEquals($request->getSession(), $session); return true; - }, - ['id' => self::TRANSACTION] - ); + }), + Argument::type('array') + ) + ->shouldBeCalledOnce(); - $database = $this->database($this->requestHandler->reveal(), $this->serializer); + $database = $this->database($this->spannerClient->reveal()); $t = $database->transaction(); $t->rollback(); } @@ -929,30 +931,28 @@ public function testTransactionSingleUseRollback() { $this->expectException(\BadMethodCallException::class); - $this->mockSendRequest(SpannerClient::class, 'beginTransaction', null, null, 0); - $this->mockSendRequest(SpannerClient::class, 'rollback', null, null, 0); + $this->spannerClient->beginTransaction(Argument::cetera())->shouldNotBeCalled(); + $this->spannerClient->rollback(Argument::cetera())->shouldNotBeCalled(); - $database = $this->database($this->requestHandler->reveal(), $this->serializer); + $database = $this->database($this->spannerClient->reveal()); $t = $database->transaction(['singleUse' => true]); $t->rollback(); } - private function database(RequestHandler $requestHandler, Serializer $serializer) + private function database(SpannerClient $spannerClient) { - $operation = new Operation($requestHandler, $serializer, false); $instance = $this->prophesize(Instance::class); $instance->name()->willReturn(InstanceAdminClient::instanceName(self::PROJECT, self::INSTANCE)); $instance->directedReadOptions()->willReturn([]); - $database = TestHelpers::stub(Database::class, [ - $requestHandler, - $serializer, + $database = new Database( + $spannerClient, + $this->prophesize(DatabaseAdminClient::class)->reveal(), + $this->serializer, $instance->reveal(), self::PROJECT, self::DATABASE - ], ['operation']); - - $database->___setProperty('operation', $operation); + ); return $database; } @@ -988,20 +988,32 @@ private function createTransactionOptions($options = []) private function createMockedCommitDatabase() { - $this->mockSendRequest( - SpannerClient::class, - 'commit', - function ($args) { + $time = $this->parseTimeString($this->timestamp); + $timestamp = new ProtoTimestamp(['seconds' => $time[0]->format('U'), 'nanos' => $time[1]]); + + $this->spannerClient->commit( + Argument::that(function ($request) { Argument::type(CommitRequest::class); $this->assertEquals( - $args->getSingleUseTransaction(), + $request->getSingleUseTransaction(), $this->createTransactionOptions() ); return true; - }, - ['commitTimestamp' => $this->timestamp] - ); + }), + Argument::type('array') + ) + ->shouldBeCalledOnce() + ->willReturn(new CommitResponse(['commit_timestamp' => $timestamp])); + + return $this->database($this->spannerClient->reveal()); + } + + private function resultGeneratorStream(array $chunks) + { + $this->stream = $this->prophesize(ServerStream::class); + $this->stream->readAll() + ->willReturn($this->resultGenerator($chunks)); - return $this->database($this->requestHandler->reveal(), $this->serializer); + return $this->stream->reveal(); } }