Skip to content

PHPLIB-1144: Add unit tests on Explainable::getCommandDocument() #1105

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jun 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion psalm-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,7 @@
<code>$this-&gt;options['writeConcern']</code>
</MixedArgument>
<MixedAssignment occurrences="5">
<code>$cmd['writeConcern']</code>
<code>$cmd['comment']</code>
<code>$deleteOptions['hint']</code>
<code>$options['comment']</code>
<code>$options['session']</code>
Expand Down
12 changes: 11 additions & 1 deletion src/Operation/Delete.php
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,17 @@ public function execute(Server $server)
*/
public function getCommandDocument()
{
return ['delete' => $this->collectionName, 'deletes' => [['q' => $this->filter] + $this->createDeleteOptions()]];
$cmd = ['delete' => $this->collectionName, 'deletes' => [['q' => $this->filter] + $this->createDeleteOptions()]];

if (isset($this->options['comment'])) {
$cmd['comment'] = $this->options['comment'];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note Psalm's MixedAssignment error. You may need an annotation to note that comment is actually mixed (any BSON type is supported).

Alternatively, you could just add this to the baseline file. We already have a number suppressions for MixedAssignment on $cmd['comment'].

}

if (isset($this->options['let'])) {
$cmd['let'] = (object) $this->options['let'];
}

return $cmd;
}

/**
Expand Down
15 changes: 0 additions & 15 deletions src/Operation/Find.php
Original file line number Diff line number Diff line change
Expand Up @@ -329,21 +329,6 @@ public function execute(Server $server)
* @return array
*/
public function getCommandDocument()
{
$cmd = $this->createCommandDocument();

// Read concern can change the query plan
if (isset($this->options['readConcern'])) {
$cmd['readConcern'] = $this->options['readConcern'];
}

return $cmd;
}

/**
* Construct a command document for Find
*/
private function createCommandDocument(): array
{
$cmd = ['find' => $this->collectionName, 'filter' => (object) $this->filter];

Expand Down
40 changes: 40 additions & 0 deletions tests/Operation/AggregateTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

namespace MongoDB\Tests\Operation;

use MongoDB\Driver\ReadConcern;
use MongoDB\Driver\ReadPreference;
use MongoDB\Driver\WriteConcern;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Operation\Aggregate;

Expand Down Expand Up @@ -104,4 +107,41 @@ private function getInvalidHintValues()
{
return [123, 3.14, true];
}

public function testExplainableCommandDocument(): void
{
$options = [
'allowDiskUse' => true,
'batchSize' => 100,
'bypassDocumentValidation' => true,
'collation' => ['locale' => 'fr'],
'comment' => 'explain me',
'hint' => '_id_',
'let' => ['a' => 1],
'maxTimeMS' => 100,
'readConcern' => new ReadConcern(ReadConcern::LOCAL),
'useCursor' => true,
// Intentionally omitted options
// The "explain" option is illegal
'readPreference' => new ReadPreference(ReadPreference::SECONDARY_PREFERRED),
'typeMap' => ['root' => 'array', 'document' => 'array'],
'writeConcern' => new WriteConcern(0),
];
$operation = new Aggregate($this->getDatabaseName(), $this->getCollectionName(), [['$project' => ['_id' => 0]]], $options);

$expected = [
'aggregate' => $this->getCollectionName(),
'pipeline' => [['$project' => ['_id' => 0]]],
'allowDiskUse' => true,
'bypassDocumentValidation' => true,
'collation' => (object) ['locale' => 'fr'],
'comment' => 'explain me',
'hint' => '_id_',
'maxTimeMS' => 100,
'readConcern' => new ReadConcern(ReadConcern::LOCAL),
'let' => (object) ['a' => 1],
'cursor' => ['batchSize' => 100],
];
$this->assertEquals($expected, $operation->getCommandDocument());
}
}
28 changes: 28 additions & 0 deletions tests/Operation/CountTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace MongoDB\Tests\Operation;

use MongoDB\Driver\ReadConcern;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Operation\Count;

Expand Down Expand Up @@ -64,4 +65,31 @@ private function getInvalidHintValues()
{
return [123, 3.14, true];
}

public function testExplainableCommandDocument(): void
{
$options = [
'hint' => '_id_',
'limit' => 10,
'skip' => 20,
'readConcern' => new ReadConcern(ReadConcern::LOCAL),
'collation' => ['locale' => 'fr'],
'comment' => 'explain me',
'maxTimeMS' => 100,
];
$operation = new Count($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], $options);

$expected = [
'count' => $this->getCollectionName(),
'query' => (object) ['x' => 1],
'collation' => (object) ['locale' => 'fr'],
'hint' => '_id_',
'comment' => 'explain me',
'limit' => 10,
'skip' => 20,
'maxTimeMS' => 100,
'readConcern' => new ReadConcern(ReadConcern::LOCAL),
];
$this->assertEquals($expected, $operation->getCommandDocument());
}
}
29 changes: 29 additions & 0 deletions tests/Operation/DeleteTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

namespace MongoDB\Tests\Operation;

use MongoDB\Driver\WriteConcern;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Operation\Delete;
use TypeError;
Expand Down Expand Up @@ -65,4 +66,32 @@ public function provideInvalidConstructorOptions()

return $options;
}

public function testExplainableCommandDocument(): void
{
$options = [
'collation' => ['locale' => 'fr'],
'hint' => '_id_',
'let' => ['a' => 1],
'comment' => 'explain me',
// Intentionally omitted options
'writeConcern' => new WriteConcern(0),
];
$operation = new Delete($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], 0, $options);

$expected = [
'delete' => $this->getCollectionName(),
'deletes' => [
[
'q' => ['x' => 1],
'limit' => 0,
'collation' => (object) ['locale' => 'fr'],
'hint' => '_id_',
],
],
'comment' => 'explain me',
'let' => (object) ['a' => 1],
];
$this->assertEquals($expected, $operation->getCommandDocument());
}
}
27 changes: 27 additions & 0 deletions tests/Operation/DistinctTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace MongoDB\Tests\Operation;

use MongoDB\Driver\ReadConcern;
use MongoDB\Driver\ReadPreference;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Operation\Distinct;

Expand Down Expand Up @@ -51,4 +53,29 @@ public function provideInvalidConstructorOptions()

return $options;
}

public function testExplainableCommandDocument(): void
{
$options = [
'collation' => ['locale' => 'fr'],
'maxTimeMS' => 100,
'readConcern' => new ReadConcern(ReadConcern::LOCAL),
'comment' => 'explain me',
// Intentionally omitted options
'readPreference' => new ReadPreference(ReadPreference::SECONDARY_PREFERRED),
'typeMap' => ['root' => 'array'],
];
$operation = new Distinct($this->getDatabaseName(), $this->getCollectionName(), 'f', ['x' => 1], $options);

$expected = [
'distinct' => $this->getCollectionName(),
'key' => 'f',
'query' => (object) ['x' => 1],
'collation' => (object) ['locale' => 'fr'],
'comment' => 'explain me',
'maxTimeMS' => 100,
'readConcern' => new ReadConcern(ReadConcern::LOCAL),
];
$this->assertEquals($expected, $operation->getCommandDocument());
}
}
43 changes: 43 additions & 0 deletions tests/Operation/FindAndModifyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace MongoDB\Tests\Operation;

use MongoDB\Driver\WriteConcern;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Operation\FindAndModify;

Expand Down Expand Up @@ -83,4 +84,46 @@ public function testConstructorUpdateAndRemoveOptionsAreMutuallyExclusive(): voi
$this->expectExceptionMessage('The "remove" option must be true or an "update" document must be specified, but not both');
new FindAndModify($this->getDatabaseName(), $this->getCollectionName(), ['remove' => true, 'update' => []]);
}

public function testExplainableCommandDocument(): void
{
$options = [
'arrayFilters' => [['x' => 1]],
'bypassDocumentValidation' => true,
'collation' => ['locale' => 'fr'],
'comment' => 'explain me',
'fields' => ['_id' => 0],
'hint' => '_id_',
'maxTimeMS' => 100,
'new' => true,
'query' => ['y' => 2],
'sort' => ['x' => 1],
'update' => ['$set' => ['x' => 2]],
'upsert' => true,
'let' => ['a' => 3],
// Intentionally omitted options
'remove' => false, // When "update" is set
'typeMap' => ['root' => 'array'],
'writeConcern' => new WriteConcern(0),
];
$operation = new FindAndModify($this->getDatabaseName(), $this->getCollectionName(), $options);

$expected = [
'findAndModify' => $this->getCollectionName(),
'new' => true,
'upsert' => true,
'collation' => (object) ['locale' => 'fr'],
'fields' => (object) ['_id' => 0],
'let' => (object) ['a' => 3],
'query' => (object) ['y' => 2],
'sort' => (object) ['x' => 1],
'update' => (object) ['$set' => ['x' => 2]],
'arrayFilters' => [['x' => 1]],
'bypassDocumentValidation' => true,
'comment' => 'explain me',
'hint' => '_id_',
'maxTimeMS' => 100,
];
$this->assertEquals($expected, $operation->getCommandDocument());
}
}
32 changes: 32 additions & 0 deletions tests/Operation/FindOneAndDeleteTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace MongoDB\Tests\Operation;

use MongoDB\Driver\WriteConcern;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Operation\FindOneAndDelete;

Expand Down Expand Up @@ -31,4 +32,35 @@ public function provideInvalidConstructorOptions()

return $options;
}

public function testExplainableCommandDocument(): void
{
$options = [
'collation' => ['locale' => 'fr'],
'comment' => 'explain me',
'hint' => '_id_',
'maxTimeMS' => 100,
'sort' => ['x' => 1],
'let' => ['a' => 3],
// Intentionally omitted options
'projection' => ['_id' => 0],
'typeMap' => ['root' => 'array'],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest adding a comment before any ignored options.

'writeConcern' => new WriteConcern(WriteConcern::MAJORITY),
];
$operation = new FindOneAndDelete($this->getDatabaseName(), $this->getCollectionName(), ['y' => 2], $options);

$expected = [
'findAndModify' => $this->getCollectionName(),
'collation' => (object) ['locale' => 'fr'],
'fields' => (object) ['_id' => 0],
'let' => (object) ['a' => 3],
'query' => (object) ['y' => 2],
'sort' => (object) ['x' => 1],
'comment' => 'explain me',
'hint' => '_id_',
'maxTimeMS' => 100,
'remove' => true,
];
$this->assertEquals($expected, $operation->getCommandDocument());
}
}
37 changes: 37 additions & 0 deletions tests/Operation/FindOneAndReplaceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace MongoDB\Tests\Operation;

use MongoDB\Driver\WriteConcern;
use MongoDB\Exception\InvalidArgumentException;
use MongoDB\Operation\FindOneAndReplace;

Expand Down Expand Up @@ -82,4 +83,40 @@ public function provideInvalidConstructorReturnDocumentOptions()
{
return $this->wrapValuesForDataProvider([-1, 0, 3]);
}

public function testExplainableCommandDocument(): void
{
$options = [
'bypassDocumentValidation' => true,
'collation' => ['locale' => 'fr'],
'comment' => 'explain me',
'fields' => ['_id' => 0],
'hint' => '_id_',
'maxTimeMS' => 100,
'projection' => ['_id' => 0],
'sort' => ['x' => 1],
'let' => ['a' => 3],
// Intentionally omitted options
'returnDocument' => FindOneAndReplace::RETURN_DOCUMENT_AFTER,
'typeMap' => ['root' => 'array'],
'writeConcern' => new WriteConcern(WriteConcern::MAJORITY),
];
$operation = new FindOneAndReplace($this->getDatabaseName(), $this->getCollectionName(), ['y' => 2], ['y' => 3], $options);

$expected = [
'findAndModify' => $this->getCollectionName(),
'new' => true,
'collation' => (object) ['locale' => 'fr'],
'fields' => (object) ['_id' => 0],
'let' => (object) ['a' => 3],
'query' => (object) ['y' => 2],
'sort' => (object) ['x' => 1],
'update' => (object) ['y' => 3],
'bypassDocumentValidation' => true,
'comment' => 'explain me',
'hint' => '_id_',
'maxTimeMS' => 100,
];
$this->assertEquals($expected, $operation->getCommandDocument());
}
}
Loading