Skip to content

Commit b183116

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

10 files changed

+1167
-6
lines changed

tests/SpecTests/CommandExpectations.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,21 @@ 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+
/* Retryable read spec tests don't include extra commands, e.g. the
113+
* killCursors command issued when a change stream is garbage collected.
114+
* We ignore any extra events for that reason. \*/
115+
$o->ignoreExtraEvents = true;
116+
117+
return $o;
118+
}
119+
105120
public static function fromRetryableReads(array $expectedEvents)
106121
{
107122
$o = new self($expectedEvents);

tests/SpecTests/Context.php

Lines changed: 25 additions & 6 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);
@@ -312,13 +327,17 @@ public function prepareOptions(array $options)
312327
throw new LogicException('Unsupported writeConcern args: ' . implode(',', array_keys($diff)));
313328
}
314329

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

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

324343
return $options;

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: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,16 @@ public static function fromCrud(stdClass $operation)
184184
return $o;
185185
}
186186

187+
public static function fromReadWriteConcern(stdClass $operation)
188+
{
189+
$o = new self($operation);
190+
191+
$o->errorExpectation = ErrorExpectation::fromReadWriteConcern($operation);
192+
$o->resultExpectation = ResultExpectation::fromReadWriteConcern($operation, $o->getResultAssertionType());
193+
194+
return $o;
195+
}
196+
187197
public static function fromRetryableReads(stdClass $operation)
188198
{
189199
$o = new self($operation);
@@ -367,6 +377,11 @@ private function executeForCollection(Collection $collection, Context $context)
367377
$args['keys'],
368378
array_diff_key($args, ['keys' => 1])
369379
);
380+
case 'dropIndex':
381+
return $collection->dropIndex(
382+
$args['name'],
383+
array_diff_key($args, ['name' => 1])
384+
);
370385
case 'count':
371386
case 'countDocuments':
372387
case 'find':
@@ -738,6 +753,7 @@ private function getResultAssertionTypeForCollection()
738753
case 'countDocuments':
739754
return ResultExpectation::ASSERT_SAME;
740755
case 'createIndex':
756+
case 'dropIndex':
741757
return ResultExpectation::ASSERT_MATCHES_DOCUMENT;
742758
case 'distinct':
743759
case 'estimatedDocumentCount':
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
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+
* Transactions spec tests.
13+
*
14+
* @see https://github.com/mongodb/specifications/tree/master/source/read-write-concern
15+
*/
16+
class ReadWriteConcernSpecTest extends FunctionalTestCase
17+
{
18+
/** @var array */
19+
private static $incompleteTests = [];
20+
21+
/**
22+
* Assert that the expected and actual command documents match.
23+
*
24+
* Note: this method may modify the $expected object.
25+
*
26+
* @param stdClass $expected Expected command document
27+
* @param stdClass $actual Actual command document
28+
*/
29+
public static function assertCommandMatches(stdClass $expected, stdClass $actual)
30+
{
31+
foreach ($expected as $key => $value) {
32+
if ($value === null) {
33+
static::assertObjectNotHasAttribute($key, $actual);
34+
unset($expected->{$key});
35+
}
36+
}
37+
38+
static::assertDocumentsMatch($expected, $actual);
39+
}
40+
41+
/**
42+
* Execute an individual test case from the specification.
43+
*
44+
* @dataProvider provideTests
45+
* @param stdClass $test Individual "tests[]" document
46+
* @param array $runOn Top-level "runOn" array with server requirements
47+
* @param array $data Top-level "data" array to initialize collection
48+
* @param string $databaseName Name of database under test
49+
* @param string $collectionName Name of collection under test
50+
*/
51+
public function testReadWriteConcern(stdClass $test, array $runOn = null, array $data, $databaseName = null, $collectionName = null)
52+
{
53+
if (isset(self::$incompleteTests[$this->dataDescription()])) {
54+
$this->markTestIncomplete(self::$incompleteTests[$this->dataDescription()]);
55+
}
56+
57+
if (isset($test->skipReason)) {
58+
$this->markTestSkipped($test->skipReason);
59+
}
60+
61+
if (isset($runOn)) {
62+
$this->checkServerRequirements($runOn);
63+
}
64+
65+
if (isset($test->skipReason)) {
66+
$this->markTestSkipped($test->skipReason);
67+
}
68+
69+
$databaseName = $databaseName ?? $this->getDatabaseName();
70+
$collectionName = $collectionName ?? $this->getCollectionName();
71+
72+
$context = Context::fromReadWriteConcern($test, $databaseName, $collectionName);
73+
$this->setContext($context);
74+
75+
$this->dropTestAndOutcomeCollections();
76+
$this->insertDataFixtures($data);
77+
78+
if (isset($test->failPoint)) {
79+
$this->configureFailPoint($test->failPoint);
80+
}
81+
82+
if (isset($test->expectations)) {
83+
$commandExpectations = CommandExpectations::fromReadWriteConcern($test->expectations);
84+
$commandExpectations->startMonitoring();
85+
}
86+
87+
foreach ($test->operations as $operation) {
88+
Operation::fromReadWriteConcern($operation)->assert($this, $context);
89+
}
90+
91+
if (isset($commandExpectations)) {
92+
$commandExpectations->stopMonitoring();
93+
$commandExpectations->assert($this, $context);
94+
}
95+
96+
if (isset($test->outcome->collection->data)) {
97+
$this->assertOutcomeCollectionData($test->outcome->collection->data);
98+
}
99+
}
100+
101+
public function provideTests()
102+
{
103+
$testArgs = [];
104+
105+
foreach (glob(__DIR__ . '/read-write-concern/operation/*.json') as $filename) {
106+
$json = $this->decodeJson(file_get_contents($filename));
107+
$group = basename(dirname($filename)) . '/' . basename($filename, '.json');
108+
$runOn = $json->runOn ?? null;
109+
$data = $json->data ?? [];
110+
$databaseName = $json->database_name ?? null;
111+
$collectionName = $json->collection_name ?? null;
112+
113+
foreach ($json->tests as $test) {
114+
$name = $group . ': ' . $test->description;
115+
$testArgs[$name] = [$test, $runOn, $data, $databaseName, $collectionName];
116+
}
117+
}
118+
119+
return $testArgs;
120+
}
121+
}

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)