Skip to content

Commit 53052b6

Browse files
authored
PHPLIB-1144: Add unit tests on Explainable::getCommandDocument() (#1105)
* PHPLIB-1144: Add unit tests on Explainable::getCommandDocument * Add tests on FindOneAnd*::getCommandDocument * Refactor Find::getCommandDocument Remove private function createCommandDocument introduced by c7b2b03 and used only for the explain command. * Add let and comment to explain delete Separate Intentionally omitted option * Aggregate option explain is incompatible with the explain command The 'explain' option is illegal when a explain verbosity is also provided * Let the server throw errors
1 parent 4e849c8 commit 53052b6

13 files changed

+382
-17
lines changed

psalm-baseline.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -471,7 +471,7 @@
471471
<code>$this-&gt;options['writeConcern']</code>
472472
</MixedArgument>
473473
<MixedAssignment occurrences="5">
474-
<code>$cmd['writeConcern']</code>
474+
<code>$cmd['comment']</code>
475475
<code>$deleteOptions['hint']</code>
476476
<code>$options['comment']</code>
477477
<code>$options['session']</code>

src/Operation/Delete.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,17 @@ public function execute(Server $server)
177177
*/
178178
public function getCommandDocument()
179179
{
180-
return ['delete' => $this->collectionName, 'deletes' => [['q' => $this->filter] + $this->createDeleteOptions()]];
180+
$cmd = ['delete' => $this->collectionName, 'deletes' => [['q' => $this->filter] + $this->createDeleteOptions()]];
181+
182+
if (isset($this->options['comment'])) {
183+
$cmd['comment'] = $this->options['comment'];
184+
}
185+
186+
if (isset($this->options['let'])) {
187+
$cmd['let'] = (object) $this->options['let'];
188+
}
189+
190+
return $cmd;
181191
}
182192

183193
/**

src/Operation/Find.php

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -329,21 +329,6 @@ public function execute(Server $server)
329329
* @return array
330330
*/
331331
public function getCommandDocument()
332-
{
333-
$cmd = $this->createCommandDocument();
334-
335-
// Read concern can change the query plan
336-
if (isset($this->options['readConcern'])) {
337-
$cmd['readConcern'] = $this->options['readConcern'];
338-
}
339-
340-
return $cmd;
341-
}
342-
343-
/**
344-
* Construct a command document for Find
345-
*/
346-
private function createCommandDocument(): array
347332
{
348333
$cmd = ['find' => $this->collectionName, 'filter' => (object) $this->filter];
349334

tests/Operation/AggregateTest.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
namespace MongoDB\Tests\Operation;
44

5+
use MongoDB\Driver\ReadConcern;
6+
use MongoDB\Driver\ReadPreference;
7+
use MongoDB\Driver\WriteConcern;
58
use MongoDB\Exception\InvalidArgumentException;
69
use MongoDB\Operation\Aggregate;
710

@@ -104,4 +107,41 @@ private function getInvalidHintValues()
104107
{
105108
return [123, 3.14, true];
106109
}
110+
111+
public function testExplainableCommandDocument(): void
112+
{
113+
$options = [
114+
'allowDiskUse' => true,
115+
'batchSize' => 100,
116+
'bypassDocumentValidation' => true,
117+
'collation' => ['locale' => 'fr'],
118+
'comment' => 'explain me',
119+
'hint' => '_id_',
120+
'let' => ['a' => 1],
121+
'maxTimeMS' => 100,
122+
'readConcern' => new ReadConcern(ReadConcern::LOCAL),
123+
'useCursor' => true,
124+
// Intentionally omitted options
125+
// The "explain" option is illegal
126+
'readPreference' => new ReadPreference(ReadPreference::SECONDARY_PREFERRED),
127+
'typeMap' => ['root' => 'array', 'document' => 'array'],
128+
'writeConcern' => new WriteConcern(0),
129+
];
130+
$operation = new Aggregate($this->getDatabaseName(), $this->getCollectionName(), [['$project' => ['_id' => 0]]], $options);
131+
132+
$expected = [
133+
'aggregate' => $this->getCollectionName(),
134+
'pipeline' => [['$project' => ['_id' => 0]]],
135+
'allowDiskUse' => true,
136+
'bypassDocumentValidation' => true,
137+
'collation' => (object) ['locale' => 'fr'],
138+
'comment' => 'explain me',
139+
'hint' => '_id_',
140+
'maxTimeMS' => 100,
141+
'readConcern' => new ReadConcern(ReadConcern::LOCAL),
142+
'let' => (object) ['a' => 1],
143+
'cursor' => ['batchSize' => 100],
144+
];
145+
$this->assertEquals($expected, $operation->getCommandDocument());
146+
}
107147
}

tests/Operation/CountTest.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace MongoDB\Tests\Operation;
44

5+
use MongoDB\Driver\ReadConcern;
56
use MongoDB\Exception\InvalidArgumentException;
67
use MongoDB\Operation\Count;
78

@@ -64,4 +65,31 @@ private function getInvalidHintValues()
6465
{
6566
return [123, 3.14, true];
6667
}
68+
69+
public function testExplainableCommandDocument(): void
70+
{
71+
$options = [
72+
'hint' => '_id_',
73+
'limit' => 10,
74+
'skip' => 20,
75+
'readConcern' => new ReadConcern(ReadConcern::LOCAL),
76+
'collation' => ['locale' => 'fr'],
77+
'comment' => 'explain me',
78+
'maxTimeMS' => 100,
79+
];
80+
$operation = new Count($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], $options);
81+
82+
$expected = [
83+
'count' => $this->getCollectionName(),
84+
'query' => (object) ['x' => 1],
85+
'collation' => (object) ['locale' => 'fr'],
86+
'hint' => '_id_',
87+
'comment' => 'explain me',
88+
'limit' => 10,
89+
'skip' => 20,
90+
'maxTimeMS' => 100,
91+
'readConcern' => new ReadConcern(ReadConcern::LOCAL),
92+
];
93+
$this->assertEquals($expected, $operation->getCommandDocument());
94+
}
6795
}

tests/Operation/DeleteTest.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
namespace MongoDB\Tests\Operation;
99

10+
use MongoDB\Driver\WriteConcern;
1011
use MongoDB\Exception\InvalidArgumentException;
1112
use MongoDB\Operation\Delete;
1213
use TypeError;
@@ -65,4 +66,32 @@ public function provideInvalidConstructorOptions()
6566

6667
return $options;
6768
}
69+
70+
public function testExplainableCommandDocument(): void
71+
{
72+
$options = [
73+
'collation' => ['locale' => 'fr'],
74+
'hint' => '_id_',
75+
'let' => ['a' => 1],
76+
'comment' => 'explain me',
77+
// Intentionally omitted options
78+
'writeConcern' => new WriteConcern(0),
79+
];
80+
$operation = new Delete($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], 0, $options);
81+
82+
$expected = [
83+
'delete' => $this->getCollectionName(),
84+
'deletes' => [
85+
[
86+
'q' => ['x' => 1],
87+
'limit' => 0,
88+
'collation' => (object) ['locale' => 'fr'],
89+
'hint' => '_id_',
90+
],
91+
],
92+
'comment' => 'explain me',
93+
'let' => (object) ['a' => 1],
94+
];
95+
$this->assertEquals($expected, $operation->getCommandDocument());
96+
}
6897
}

tests/Operation/DistinctTest.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace MongoDB\Tests\Operation;
44

5+
use MongoDB\Driver\ReadConcern;
6+
use MongoDB\Driver\ReadPreference;
57
use MongoDB\Exception\InvalidArgumentException;
68
use MongoDB\Operation\Distinct;
79

@@ -51,4 +53,29 @@ public function provideInvalidConstructorOptions()
5153

5254
return $options;
5355
}
56+
57+
public function testExplainableCommandDocument(): void
58+
{
59+
$options = [
60+
'collation' => ['locale' => 'fr'],
61+
'maxTimeMS' => 100,
62+
'readConcern' => new ReadConcern(ReadConcern::LOCAL),
63+
'comment' => 'explain me',
64+
// Intentionally omitted options
65+
'readPreference' => new ReadPreference(ReadPreference::SECONDARY_PREFERRED),
66+
'typeMap' => ['root' => 'array'],
67+
];
68+
$operation = new Distinct($this->getDatabaseName(), $this->getCollectionName(), 'f', ['x' => 1], $options);
69+
70+
$expected = [
71+
'distinct' => $this->getCollectionName(),
72+
'key' => 'f',
73+
'query' => (object) ['x' => 1],
74+
'collation' => (object) ['locale' => 'fr'],
75+
'comment' => 'explain me',
76+
'maxTimeMS' => 100,
77+
'readConcern' => new ReadConcern(ReadConcern::LOCAL),
78+
];
79+
$this->assertEquals($expected, $operation->getCommandDocument());
80+
}
5481
}

tests/Operation/FindAndModifyTest.php

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace MongoDB\Tests\Operation;
44

5+
use MongoDB\Driver\WriteConcern;
56
use MongoDB\Exception\InvalidArgumentException;
67
use MongoDB\Operation\FindAndModify;
78

@@ -83,4 +84,46 @@ public function testConstructorUpdateAndRemoveOptionsAreMutuallyExclusive(): voi
8384
$this->expectExceptionMessage('The "remove" option must be true or an "update" document must be specified, but not both');
8485
new FindAndModify($this->getDatabaseName(), $this->getCollectionName(), ['remove' => true, 'update' => []]);
8586
}
87+
88+
public function testExplainableCommandDocument(): void
89+
{
90+
$options = [
91+
'arrayFilters' => [['x' => 1]],
92+
'bypassDocumentValidation' => true,
93+
'collation' => ['locale' => 'fr'],
94+
'comment' => 'explain me',
95+
'fields' => ['_id' => 0],
96+
'hint' => '_id_',
97+
'maxTimeMS' => 100,
98+
'new' => true,
99+
'query' => ['y' => 2],
100+
'sort' => ['x' => 1],
101+
'update' => ['$set' => ['x' => 2]],
102+
'upsert' => true,
103+
'let' => ['a' => 3],
104+
// Intentionally omitted options
105+
'remove' => false, // When "update" is set
106+
'typeMap' => ['root' => 'array'],
107+
'writeConcern' => new WriteConcern(0),
108+
];
109+
$operation = new FindAndModify($this->getDatabaseName(), $this->getCollectionName(), $options);
110+
111+
$expected = [
112+
'findAndModify' => $this->getCollectionName(),
113+
'new' => true,
114+
'upsert' => true,
115+
'collation' => (object) ['locale' => 'fr'],
116+
'fields' => (object) ['_id' => 0],
117+
'let' => (object) ['a' => 3],
118+
'query' => (object) ['y' => 2],
119+
'sort' => (object) ['x' => 1],
120+
'update' => (object) ['$set' => ['x' => 2]],
121+
'arrayFilters' => [['x' => 1]],
122+
'bypassDocumentValidation' => true,
123+
'comment' => 'explain me',
124+
'hint' => '_id_',
125+
'maxTimeMS' => 100,
126+
];
127+
$this->assertEquals($expected, $operation->getCommandDocument());
128+
}
86129
}

tests/Operation/FindOneAndDeleteTest.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace MongoDB\Tests\Operation;
44

5+
use MongoDB\Driver\WriteConcern;
56
use MongoDB\Exception\InvalidArgumentException;
67
use MongoDB\Operation\FindOneAndDelete;
78

@@ -31,4 +32,35 @@ public function provideInvalidConstructorOptions()
3132

3233
return $options;
3334
}
35+
36+
public function testExplainableCommandDocument(): void
37+
{
38+
$options = [
39+
'collation' => ['locale' => 'fr'],
40+
'comment' => 'explain me',
41+
'hint' => '_id_',
42+
'maxTimeMS' => 100,
43+
'sort' => ['x' => 1],
44+
'let' => ['a' => 3],
45+
// Intentionally omitted options
46+
'projection' => ['_id' => 0],
47+
'typeMap' => ['root' => 'array'],
48+
'writeConcern' => new WriteConcern(WriteConcern::MAJORITY),
49+
];
50+
$operation = new FindOneAndDelete($this->getDatabaseName(), $this->getCollectionName(), ['y' => 2], $options);
51+
52+
$expected = [
53+
'findAndModify' => $this->getCollectionName(),
54+
'collation' => (object) ['locale' => 'fr'],
55+
'fields' => (object) ['_id' => 0],
56+
'let' => (object) ['a' => 3],
57+
'query' => (object) ['y' => 2],
58+
'sort' => (object) ['x' => 1],
59+
'comment' => 'explain me',
60+
'hint' => '_id_',
61+
'maxTimeMS' => 100,
62+
'remove' => true,
63+
];
64+
$this->assertEquals($expected, $operation->getCommandDocument());
65+
}
3466
}

tests/Operation/FindOneAndReplaceTest.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace MongoDB\Tests\Operation;
44

5+
use MongoDB\Driver\WriteConcern;
56
use MongoDB\Exception\InvalidArgumentException;
67
use MongoDB\Operation\FindOneAndReplace;
78

@@ -82,4 +83,40 @@ public function provideInvalidConstructorReturnDocumentOptions()
8283
{
8384
return $this->wrapValuesForDataProvider([-1, 0, 3]);
8485
}
86+
87+
public function testExplainableCommandDocument(): void
88+
{
89+
$options = [
90+
'bypassDocumentValidation' => true,
91+
'collation' => ['locale' => 'fr'],
92+
'comment' => 'explain me',
93+
'fields' => ['_id' => 0],
94+
'hint' => '_id_',
95+
'maxTimeMS' => 100,
96+
'projection' => ['_id' => 0],
97+
'sort' => ['x' => 1],
98+
'let' => ['a' => 3],
99+
// Intentionally omitted options
100+
'returnDocument' => FindOneAndReplace::RETURN_DOCUMENT_AFTER,
101+
'typeMap' => ['root' => 'array'],
102+
'writeConcern' => new WriteConcern(WriteConcern::MAJORITY),
103+
];
104+
$operation = new FindOneAndReplace($this->getDatabaseName(), $this->getCollectionName(), ['y' => 2], ['y' => 3], $options);
105+
106+
$expected = [
107+
'findAndModify' => $this->getCollectionName(),
108+
'new' => true,
109+
'collation' => (object) ['locale' => 'fr'],
110+
'fields' => (object) ['_id' => 0],
111+
'let' => (object) ['a' => 3],
112+
'query' => (object) ['y' => 2],
113+
'sort' => (object) ['x' => 1],
114+
'update' => (object) ['y' => 3],
115+
'bypassDocumentValidation' => true,
116+
'comment' => 'explain me',
117+
'hint' => '_id_',
118+
'maxTimeMS' => 100,
119+
];
120+
$this->assertEquals($expected, $operation->getCommandDocument());
121+
}
85122
}

0 commit comments

Comments
 (0)