Skip to content

Commit e7ef397

Browse files
committed
PHPLIB-536: Add spec tests for default write concern
1 parent 61528bc commit e7ef397

10 files changed

+1174
-16
lines changed

tests/SpecTests/CommandExpectations.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,16 @@ public static function fromCrud(array $expectedEvents)
102102
return $o;
103103
}
104104

105+
public static function fromReadWriteConcern(array $expectedEvents)
106+
{
107+
$o = new self($expectedEvents);
108+
109+
$o->ignoreCommandFailed = true;
110+
$o->ignoreCommandSucceeded = true;
111+
112+
return $o;
113+
}
114+
105115
public static function fromRetryableReads(array $expectedEvents)
106116
{
107117
$o = new self($expectedEvents);

tests/SpecTests/Context.php

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,21 @@ public static function fromCrud(stdClass $test, $databaseName, $collectionName)
166166
return $o;
167167
}
168168

169+
public static function fromReadWriteConcern(stdClass $test, $databaseName, $collectionName)
170+
{
171+
$o = new self($databaseName, $collectionName);
172+
173+
if (isset($test->outcome->collection->name)) {
174+
$o->outcomeCollectionName = $test->outcome->collection->name;
175+
}
176+
177+
$clientOptions = isset($test->clientOptions) ? (array) $test->clientOptions : [];
178+
179+
$o->client = new Client(FunctionalTestCase::getUri(), $clientOptions);
180+
181+
return $o;
182+
}
183+
169184
public static function fromRetryableReads(stdClass $test, $databaseName, $collectionName, $bucketName)
170185
{
171186
$o = new self($databaseName, $collectionName);
@@ -253,12 +268,13 @@ public function getClient()
253268
return $this->useEncryptedClient && $this->encryptedClient ? $this->encryptedClient : $this->client;
254269
}
255270

256-
public function getCollection(array $collectionOptions = [])
271+
public function getCollection(array $collectionOptions = [], array $databaseOptions = [])
257272
{
258273
return $this->selectCollection(
259274
$this->databaseName,
260275
$this->collectionName,
261-
$this->prepareOptions($collectionOptions)
276+
$collectionOptions,
277+
$databaseOptions
262278
);
263279
}
264280

@@ -312,13 +328,17 @@ public function prepareOptions(array $options)
312328
throw new LogicException('Unsupported writeConcern args: ' . implode(',', array_keys($diff)));
313329
}
314330

315-
$w = $writeConcern['w'];
316-
$wtimeout = $writeConcern['wtimeout'] ?? 0;
317-
$j = $writeConcern['j'] ?? null;
331+
if (! empty($writeConcern)) {
332+
$w = $writeConcern['w'];
333+
$wtimeout = $writeConcern['wtimeout'] ?? 0;
334+
$j = $writeConcern['j'] ?? null;
318335

319-
$options['writeConcern'] = isset($j)
320-
? new WriteConcern($w, $wtimeout, $j)
321-
: new WriteConcern($w, $wtimeout);
336+
$options['writeConcern'] = isset($j)
337+
? new WriteConcern($w, $wtimeout, $j)
338+
: new WriteConcern($w, $wtimeout);
339+
} else {
340+
unset($options['writeConcern']);
341+
}
322342
}
323343

324344
return $options;
@@ -380,13 +400,11 @@ public function replaceCommandSessionPlaceholder(stdClass $command)
380400
}
381401
}
382402

383-
public function selectCollection($databaseName, $collectionName, array $collectionOptions = [])
403+
public function selectCollection($databaseName, $collectionName, array $collectionOptions = [], array $databaseOptions = [])
384404
{
385-
return $this->getClient()->selectCollection(
386-
$databaseName,
387-
$collectionName,
388-
$this->prepareOptions($collectionOptions)
389-
);
405+
return $this
406+
->selectDatabase($databaseName, $databaseOptions)
407+
->selectCollection($collectionName, $this->prepareOptions($collectionOptions));
390408
}
391409

392410
public function selectDatabase($databaseName, array $databaseOptions = [])

tests/SpecTests/ErrorExpectation.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,11 @@ public static function fromCrud(stdClass $result)
9292
return $o;
9393
}
9494

95+
public static function fromReadWriteConcern(stdClass $operation)
96+
{
97+
return self::fromGenericOperation($operation);
98+
}
99+
95100
public static function fromRetryableReads(stdClass $operation)
96101
{
97102
$o = new self();

tests/SpecTests/Operation.php

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ final class Operation
6161
/** @var string|null */
6262
private $databaseName;
6363

64+
/** @var array */
65+
private $databaseOptions = [];
66+
6467
/** @var string */
6568
private $name;
6669

@@ -184,6 +187,24 @@ public static function fromCrud(stdClass $operation)
184187
return $o;
185188
}
186189

190+
public static function fromReadWriteConcern(stdClass $operation)
191+
{
192+
$o = new self($operation);
193+
194+
$o->errorExpectation = ErrorExpectation::fromReadWriteConcern($operation);
195+
$o->resultExpectation = ResultExpectation::fromReadWriteConcern($operation, $o->getResultAssertionType());
196+
197+
if (isset($operation->databaseOptions)) {
198+
$o->databaseOptions = (array) $operation->databaseOptions;
199+
}
200+
201+
if (isset($operation->collectionOptions)) {
202+
$o->collectionOptions = (array) $operation->collectionOptions;
203+
}
204+
205+
return $o;
206+
}
207+
187208
public static function fromRetryableReads(stdClass $operation)
188209
{
189210
$o = new self($operation);
@@ -277,7 +298,7 @@ private function execute(FunctionalTestCase $test, Context $context)
277298

278299
return $this->executeForClient($client, $context);
279300
case self::OBJECT_COLLECTION:
280-
$collection = $context->getCollection($this->collectionOptions);
301+
$collection = $context->getCollection($this->collectionOptions, $this->databaseOptions);
281302

282303
return $this->executeForCollection($collection, $context);
283304
case self::OBJECT_DATABASE:
@@ -289,7 +310,7 @@ private function execute(FunctionalTestCase $test, Context $context)
289310

290311
return $this->executeForGridFSBucket($bucket, $context);
291312
case self::OBJECT_SELECT_COLLECTION:
292-
$collection = $context->selectCollection($this->databaseName, $this->collectionName, $this->collectionOptions);
313+
$collection = $context->selectCollection($this->databaseName, $this->collectionName, $this->collectionOptions, $this->databaseOptions);
293314

294315
return $this->executeForCollection($collection, $context);
295316
case self::OBJECT_SELECT_DATABASE:
@@ -367,6 +388,11 @@ private function executeForCollection(Collection $collection, Context $context)
367388
$args['keys'],
368389
array_diff_key($args, ['keys' => 1])
369390
);
391+
case 'dropIndex':
392+
return $collection->dropIndex(
393+
$args['name'],
394+
array_diff_key($args, ['name' => 1])
395+
);
370396
case 'count':
371397
case 'countDocuments':
372398
case 'find':
@@ -738,6 +764,7 @@ private function getResultAssertionTypeForCollection()
738764
case 'countDocuments':
739765
return ResultExpectation::ASSERT_SAME;
740766
case 'createIndex':
767+
case 'dropIndex':
741768
return ResultExpectation::ASSERT_MATCHES_DOCUMENT;
742769
case 'distinct':
743770
case 'estimatedDocumentCount':
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
<?php
2+
3+
namespace MongoDB\Tests\SpecTests;
4+
5+
use stdClass;
6+
use function basename;
7+
use function dirname;
8+
use function file_get_contents;
9+
use function glob;
10+
11+
/**
12+
* @see https://github.com/mongodb/specifications/tree/master/source/read-write-concern
13+
*/
14+
class ReadWriteConcernSpecTest extends FunctionalTestCase
15+
{
16+
/** @var array */
17+
private static $incompleteTests = [];
18+
19+
/**
20+
* Assert that the expected and actual command documents match.
21+
*
22+
* @param stdClass $expected Expected command document
23+
* @param stdClass $actual Actual command document
24+
*/
25+
public static function assertCommandMatches(stdClass $expected, stdClass $actual)
26+
{
27+
foreach ($expected as $key => $value) {
28+
if ($value === null) {
29+
static::assertObjectNotHasAttribute($key, $actual);
30+
unset($expected->{$key});
31+
}
32+
}
33+
34+
static::assertDocumentsMatch($expected, $actual);
35+
}
36+
37+
/**
38+
* Execute an individual test case from the specification.
39+
*
40+
* @dataProvider provideTests
41+
* @param stdClass $test Individual "tests[]" document
42+
* @param array $runOn Top-level "runOn" array with server requirements
43+
* @param array $data Top-level "data" array to initialize collection
44+
* @param string $databaseName Name of database under test
45+
* @param string $collectionName Name of collection under test
46+
*/
47+
public function testReadWriteConcern(stdClass $test, array $runOn = null, array $data, $databaseName = null, $collectionName = null)
48+
{
49+
if (isset(self::$incompleteTests[$this->dataDescription()])) {
50+
$this->markTestIncomplete(self::$incompleteTests[$this->dataDescription()]);
51+
}
52+
53+
if (isset($runOn)) {
54+
$this->checkServerRequirements($runOn);
55+
}
56+
57+
if (isset($test->skipReason)) {
58+
$this->markTestSkipped($test->skipReason);
59+
}
60+
61+
$databaseName = $databaseName ?? $this->getDatabaseName();
62+
$collectionName = $collectionName ?? $this->getCollectionName();
63+
64+
$context = Context::fromReadWriteConcern($test, $databaseName, $collectionName);
65+
$this->setContext($context);
66+
67+
$this->dropTestAndOutcomeCollections();
68+
$this->insertDataFixtures($data);
69+
70+
if (isset($test->failPoint)) {
71+
$this->configureFailPoint($test->failPoint);
72+
}
73+
74+
if (isset($test->expectations)) {
75+
$commandExpectations = CommandExpectations::fromReadWriteConcern($test->expectations);
76+
$commandExpectations->startMonitoring();
77+
}
78+
79+
foreach ($test->operations as $operation) {
80+
Operation::fromReadWriteConcern($operation)->assert($this, $context);
81+
}
82+
83+
if (isset($commandExpectations)) {
84+
$commandExpectations->stopMonitoring();
85+
$commandExpectations->assert($this, $context);
86+
}
87+
88+
if (isset($test->outcome->collection->data)) {
89+
$this->assertOutcomeCollectionData($test->outcome->collection->data);
90+
}
91+
}
92+
93+
public function provideTests()
94+
{
95+
$testArgs = [];
96+
97+
foreach (glob(__DIR__ . '/read-write-concern/operation/*.json') as $filename) {
98+
$json = $this->decodeJson(file_get_contents($filename));
99+
$group = basename(dirname($filename)) . '/' . basename($filename, '.json');
100+
$runOn = $json->runOn ?? null;
101+
$data = $json->data ?? [];
102+
$databaseName = $json->database_name ?? null;
103+
$collectionName = $json->collection_name ?? null;
104+
105+
foreach ($json->tests as $test) {
106+
$name = $group . ': ' . $test->description;
107+
$testArgs[$name] = [$test, $runOn, $data, $databaseName, $collectionName];
108+
}
109+
}
110+
111+
return $testArgs;
112+
}
113+
}

tests/SpecTests/ResultExpectation.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,19 @@ public static function fromCrud(stdClass $operation, $defaultAssertionType)
111111
return new self($assertionType, $expectedValue);
112112
}
113113

114+
public static function fromReadWriteConcern(stdClass $operation, $defaultAssertionType)
115+
{
116+
if (property_exists($operation, 'result') && ! self::isErrorResult($operation->result)) {
117+
$assertionType = $operation->result === null ? self::ASSERT_NULL : $defaultAssertionType;
118+
$expectedValue = $operation->result;
119+
} else {
120+
$assertionType = self::ASSERT_NOTHING;
121+
$expectedValue = null;
122+
}
123+
124+
return new self($assertionType, $expectedValue);
125+
}
126+
114127
public static function fromRetryableReads(stdClass $operation, $defaultAssertionType)
115128
{
116129
if (property_exists($operation, 'result') && ! self::isErrorResult($operation->result)) {

0 commit comments

Comments
 (0)