Skip to content

Commit 1282c20

Browse files
committed
PHPLIB-468: Integrate spec tests for Convenient Transactions API
1 parent 0d09f2a commit 1282c20

12 files changed

+3854
-22
lines changed

tests/SpecTests/ErrorExpectation.php

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use Exception;
66
use MongoDB\Driver\Exception\BulkWriteException;
77
use MongoDB\Driver\Exception\CommandException;
8+
use MongoDB\Driver\Exception\ExecutionTimeoutException;
89
use MongoDB\Driver\Exception\RuntimeException;
910
use MongoDB\Exception\InvalidArgumentException;
1011
use MongoDB\Tests\TestCase;
@@ -26,9 +27,10 @@ final class ErrorExpectation
2627
*/
2728
private static $codeNameMap = [
2829
'Interrupted' => 11601,
29-
'WriteConflict' => 112,
30+
'MaxTimeMSExpired' => 50,
3031
'NoSuchTransaction' => 251,
3132
'OperationNotSupportedInTransaction' => 263,
33+
'WriteConflict' => 112,
3234
];
3335

3436
/** @var integer */
@@ -198,7 +200,7 @@ private function assertCodeName(TestCase $test, Exception $actual = null)
198200
* around this be comparing the error code against a map.
199201
*
200202
* TODO: Remove this once PHPC-1386 is resolved. */
201-
if ($actual instanceof BulkWriteException) {
203+
if ($actual instanceof BulkWriteException || $actual instanceof ExecutionTimeoutException) {
202204
$test->assertArrayHasKey($this->codeName, self::$codeNameMap);
203205
$test->assertSame(self::$codeNameMap[$this->codeName], $actual->getCode());
204206

@@ -207,6 +209,14 @@ private function assertCodeName(TestCase $test, Exception $actual = null)
207209

208210
$test->assertInstanceOf(CommandException::class, $actual);
209211
$result = $actual->getResultDocument();
212+
213+
if (isset($result->writeConcernError)) {
214+
$test->assertObjectHasAttribute('codeName', $result->writeConcernError);
215+
$test->assertSame($this->codeName, $result->writeConcernError->codeName);
216+
217+
return;
218+
}
219+
210220
$test->assertObjectHasAttribute('codeName', $result);
211221
$test->assertSame($this->codeName, $result->codeName);
212222
}

tests/SpecTests/Operation.php

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
use function fclose;
2121
use function fopen;
2222
use function MongoDB\is_last_pipeline_operator_write;
23+
use function MongoDB\with_transaction;
2324
use function stream_get_contents;
2425
use function strtolower;
2526

@@ -127,6 +128,29 @@ public static function fromCommandMonitoring(stdClass $operation)
127128
return $o;
128129
}
129130

131+
/**
132+
* This method is exclusively used to prepare nested operations for the
133+
* withTransaction session operation
134+
*
135+
* @return Operation
136+
*/
137+
private static function fromConvenientTransactions(stdClass $operation)
138+
{
139+
$o = new self($operation);
140+
141+
if (isset($operation->error)) {
142+
$o->errorExpectation = ErrorExpectation::fromTransactions($operation);
143+
}
144+
145+
$o->resultExpectation = ResultExpectation::fromTransactions($operation, $o->getResultAssertionType());
146+
147+
if (isset($operation->collectionOptions)) {
148+
$o->collectionOptions = (array) $operation->collectionOptions;
149+
}
150+
151+
return $o;
152+
}
153+
130154
public static function fromCrud(stdClass $operation)
131155
{
132156
$o = new self($operation);
@@ -177,16 +201,17 @@ public static function fromTransactions(stdClass $operation)
177201
/**
178202
* Execute the operation and assert its outcome.
179203
*
180-
* @param FunctionalTestCase $test Test instance
181-
* @param Context $context Execution context
204+
* @param FunctionalTestCase $test Test instance
205+
* @param Context $context Execution context
206+
* @param bool $bubbleExceptions If true, any exception that was caught is rethrown
182207
*/
183-
public function assert(FunctionalTestCase $test, Context $context)
208+
public function assert(FunctionalTestCase $test, Context $context, $bubbleExceptions = false)
184209
{
185210
$result = null;
186211
$exception = null;
187212

188213
try {
189-
$result = $this->execute($context);
214+
$result = $this->execute($test, $context);
190215

191216
/* Eagerly iterate the results of a cursor. This both allows an
192217
* exception to be thrown sooner and ensures that any expected
@@ -211,6 +236,10 @@ public function assert(FunctionalTestCase $test, Context $context)
211236
if (isset($this->resultExpectation)) {
212237
$this->resultExpectation->assert($test, $result);
213238
}
239+
240+
if ($exception && $bubbleExceptions) {
241+
throw $exception;
242+
}
214243
}
215244

216245
/**
@@ -220,7 +249,7 @@ public function assert(FunctionalTestCase $test, Context $context)
220249
* @return mixed
221250
* @throws LogicException if the operation is unsupported
222251
*/
223-
private function execute(Context $context)
252+
private function execute(FunctionalTestCase $test, Context $context)
224253
{
225254
switch ($this->object) {
226255
case self::OBJECT_CLIENT:
@@ -248,9 +277,9 @@ private function execute(Context $context)
248277

249278
return $this->executeForDatabase($database, $context);
250279
case self::OBJECT_SESSION0:
251-
return $this->executeForSession($context->session0, $context);
280+
return $this->executeForSession($context->session0, $test, $context);
252281
case self::OBJECT_SESSION1:
253-
return $this->executeForSession($context->session1, $context);
282+
return $this->executeForSession($context->session1, $test, $context);
254283
default:
255284
throw new LogicException('Unsupported object: ' . $this->object);
256285
}
@@ -479,12 +508,13 @@ private function executeForGridFSBucket(Bucket $bucket, Context $context)
479508
/**
480509
* Executes the session operation and return its result.
481510
*
482-
* @param Session $session
483-
* @param Context $context Execution context
511+
* @param Session $session
512+
* @param FunctionalTestCase $test
513+
* @param Context $context Execution context
484514
* @return mixed
485515
* @throws LogicException if the session operation is unsupported
486516
*/
487-
private function executeForSession(Session $session, Context $context)
517+
private function executeForSession(Session $session, FunctionalTestCase $test, Context $context)
488518
{
489519
switch ($this->name) {
490520
case 'abortTransaction':
@@ -495,6 +525,21 @@ private function executeForSession(Session $session, Context $context)
495525
$options = isset($this->arguments['options']) ? (array) $this->arguments['options'] : [];
496526

497527
return $session->startTransaction($context->prepareOptions($options));
528+
case 'withTransaction':
529+
/** @var self[] $callbackOperations */
530+
$callbackOperations = array_map(function ($operation) {
531+
return self::fromConvenientTransactions($operation);
532+
}, $this->arguments['callback']->operations);
533+
534+
$callback = function () use ($callbackOperations, $test, $context) {
535+
foreach ($callbackOperations as $operation) {
536+
$operation->assert($test, $context, true);
537+
}
538+
};
539+
540+
$options = isset($this->arguments['options']) ? (array) $this->arguments['options'] : [];
541+
542+
return with_transaction($session, $callback, $context->prepareOptions($options));
498543
default:
499544
throw new LogicException('Unsupported session operation: ' . $this->name);
500545
}

tests/SpecTests/TransactionsSpecTest.php

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use stdClass;
1313
use Symfony\Bridge\PhpUnit\SetUpTearDownTrait;
1414
use function basename;
15+
use function dirname;
1516
use function file_get_contents;
1617
use function get_object_vars;
1718
use function glob;
@@ -34,17 +35,17 @@ class TransactionsSpecTest extends FunctionalTestCase
3435
* @var array
3536
*/
3637
private static $incompleteTests = [
37-
'read-pref: default readPreference' => 'PHPLIB does not properly inherit readPreference for transactions',
38-
'read-pref: primary readPreference' => 'PHPLIB does not properly inherit readPreference for transactions',
39-
'run-command: run command with secondary read preference in client option and primary read preference in transaction options' => 'PHPLIB does not properly inherit readPreference for transactions',
40-
'transaction-options: transaction options inherited from client' => 'PHPLIB does not properly inherit readConcern for transactions',
41-
'transaction-options: readConcern local in defaultTransactionOptions' => 'PHPLIB does not properly inherit readConcern for transactions',
42-
'transaction-options: readConcern snapshot in startTransaction options' => 'PHPLIB does not properly inherit readConcern for transactions',
38+
'transactions/read-pref: default readPreference' => 'PHPLIB does not properly inherit readPreference for transactions',
39+
'transactions/read-pref: primary readPreference' => 'PHPLIB does not properly inherit readPreference for transactions',
40+
'transactions/run-command: run command with secondary read preference in client option and primary read preference in transaction options' => 'PHPLIB does not properly inherit readPreference for transactions',
41+
'transactions/transaction-options: transaction options inherited from client' => 'PHPLIB does not properly inherit readConcern for transactions',
42+
'transactions/transaction-options: readConcern local in defaultTransactionOptions' => 'PHPLIB does not properly inherit readConcern for transactions',
43+
'transactions/transaction-options: readConcern snapshot in startTransaction options' => 'PHPLIB does not properly inherit readConcern for transactions',
4344
];
4445

45-
private static function doSetUpBeforeClass()
46+
private function doSetUp()
4647
{
47-
parent::setUpBeforeClass();
48+
parent::setUp();
4849

4950
static::killAllSessions();
5051
}
@@ -184,9 +185,9 @@ public function provideTests()
184185
{
185186
$testArgs = [];
186187

187-
foreach (glob(__DIR__ . '/transactions/*.json') as $filename) {
188+
foreach (glob(__DIR__ . '/transactions*/*.json') as $filename) {
188189
$json = $this->decodeJson(file_get_contents($filename));
189-
$group = basename($filename, '.json');
190+
$group = basename(dirname($filename)) . '/' . basename($filename, '.json');
190191
$runOn = isset($json->runOn) ? $json->runOn : null;
191192
$data = isset($json->data) ? $json->data : [];
192193
$databaseName = isset($json->database_name) ? $json->database_name : null;

0 commit comments

Comments
 (0)