From 7263a62d763504ae480b8b651c6325137e733fcf Mon Sep 17 00:00:00 2001 From: Andreas Braun Date: Wed, 2 Aug 2023 09:34:08 +0200 Subject: [PATCH] Encode documents before type checks This commit also changes the behaviour to use encode() instead of encodeIfSupported(), requiring documents to be encodable by the given codec in order to be inserted/updated. --- psalm-baseline.xml | 36 +-- src/Collection.php | 4 +- src/Operation/BulkWrite.php | 290 +++++++++--------- src/Operation/FindOneAndReplace.php | 59 ++-- src/Operation/InsertMany.php | 63 ++-- src/Operation/InsertOne.php | 35 ++- src/Operation/ReplaceOne.php | 59 ++-- .../CodecCollectionFunctionalTest.php | 2 +- tests/Operation/BulkWriteTest.php | 26 ++ tests/Operation/InsertManyTest.php | 9 + tests/Operation/InsertOneTest.php | 9 + tests/Operation/ReplaceOneTest.php | 9 + 12 files changed, 326 insertions(+), 275 deletions(-) diff --git a/psalm-baseline.xml b/psalm-baseline.xml index ef694ae8a..56eb17f4d 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -193,6 +193,8 @@ $args[0] $args[0] $args[0] + $args[0] + $args[1] $args[1] $args[1] $args[1] @@ -236,8 +238,6 @@ - $args[0] - $args[1] $args[1] $args[1] $args[1] @@ -250,6 +250,8 @@ $args[2] + $operations[$i][$type][0] + $operations[$i][$type][1] $operations[$i][$type][1] $operations[$i][$type][2] $operations[$i][$type][2] @@ -257,8 +259,6 @@ $args $args - $args[0] - $args[1] $args[2] $args[2] $insertedIds[$i] @@ -270,8 +270,6 @@ - encodeIfSupported - encodeIfSupported isInTransaction @@ -472,10 +470,9 @@ - - assert(is_array($replacement) || is_object($replacement)) - is_array($replacement) - + + $replacement + @@ -484,16 +481,17 @@ - $document $insertedIds[$i] $options[$option] - encodeIfSupported isInTransaction + + $document + @@ -505,10 +503,9 @@ isInTransaction - - assert(is_array($document) || is_object($document)) - is_array($document) - + + $document + @@ -557,10 +554,9 @@ - - assert(is_array($replacement) || is_object($replacement)) - is_array($replacement) - + + $replacement + diff --git a/src/Collection.php b/src/Collection.php index c3d20eec7..427308d0d 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -781,8 +781,8 @@ public function getWriteConcern() * * @see InsertMany::__construct() for supported options * @see https://mongodb.com/docs/manual/reference/command/insert/ - * @param array[]|object[] $documents The documents to insert - * @param array $options Command options + * @param list $documents The documents to insert + * @param array $options Command options * @return InsertManyResult * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) diff --git a/src/Operation/BulkWrite.php b/src/Operation/BulkWrite.php index efb77b931..e42f2fdc2 100644 --- a/src/Operation/BulkWrite.php +++ b/src/Operation/BulkWrite.php @@ -29,12 +29,10 @@ use function array_is_list; use function array_key_exists; -use function assert; use function count; use function current; use function is_array; use function is_bool; -use function is_object; use function key; use function MongoDB\is_document; use function MongoDB\is_first_key_operator; @@ -141,6 +139,139 @@ public function __construct(string $databaseName, string $collectionName, array throw new InvalidArgumentException('$operations is not a list'); } + $options += ['ordered' => true]; + + if (isset($options['bypassDocumentValidation']) && ! is_bool($options['bypassDocumentValidation'])) { + throw InvalidArgumentException::invalidType('"bypassDocumentValidation" option', $options['bypassDocumentValidation'], 'boolean'); + } + + if (isset($options['codec']) && ! $options['codec'] instanceof DocumentCodec) { + throw InvalidArgumentException::invalidType('"codec" option', $options['codec'], DocumentCodec::class); + } + + if (! is_bool($options['ordered'])) { + throw InvalidArgumentException::invalidType('"ordered" option', $options['ordered'], 'boolean'); + } + + if (isset($options['session']) && ! $options['session'] instanceof Session) { + throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class); + } + + if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) { + throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class); + } + + if (isset($options['let']) && ! is_document($options['let'])) { + throw InvalidArgumentException::expectedDocumentType('"let" option', $options['let']); + } + + if (isset($options['bypassDocumentValidation']) && ! $options['bypassDocumentValidation']) { + unset($options['bypassDocumentValidation']); + } + + if (isset($options['writeConcern']) && $options['writeConcern']->isDefault()) { + unset($options['writeConcern']); + } + + $this->databaseName = $databaseName; + $this->collectionName = $collectionName; + $this->operations = $this->validateOperations($operations, $options['codec'] ?? null); + $this->options = $options; + } + + /** + * Execute the operation. + * + * @see Executable::execute() + * @return BulkWriteResult + * @throws UnsupportedException if write concern is used and unsupported + * @throws DriverRuntimeException for other driver errors (e.g. connection errors) + */ + public function execute(Server $server) + { + $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); + if ($inTransaction && isset($this->options['writeConcern'])) { + throw UnsupportedException::writeConcernNotSupportedInTransaction(); + } + + $bulk = new Bulk($this->createBulkWriteOptions()); + $insertedIds = []; + + foreach ($this->operations as $i => $operation) { + $type = key($operation); + $args = current($operation); + + switch ($type) { + case self::DELETE_MANY: + case self::DELETE_ONE: + $bulk->delete($args[0], $args[1]); + break; + + case self::INSERT_ONE: + $insertedIds[$i] = $bulk->insert($args[0]); + break; + + case self::UPDATE_MANY: + case self::UPDATE_ONE: + case self::REPLACE_ONE: + $bulk->update($args[0], $args[1], $args[2]); + break; + } + } + + $writeResult = $server->executeBulkWrite($this->databaseName . '.' . $this->collectionName, $bulk, $this->createExecuteOptions()); + + return new BulkWriteResult($writeResult, $insertedIds); + } + + /** + * Create options for constructing the bulk write. + * + * @see https://php.net/manual/en/mongodb-driver-bulkwrite.construct.php + */ + private function createBulkWriteOptions(): array + { + $options = ['ordered' => $this->options['ordered']]; + + foreach (['bypassDocumentValidation', 'comment'] as $option) { + if (isset($this->options[$option])) { + $options[$option] = $this->options[$option]; + } + } + + if (isset($this->options['let'])) { + $options['let'] = (object) $this->options['let']; + } + + return $options; + } + + /** + * Create options for executing the bulk write. + * + * @see https://php.net/manual/en/mongodb-driver-server.executebulkwrite.php + */ + private function createExecuteOptions(): array + { + $options = []; + + if (isset($this->options['session'])) { + $options['session'] = $this->options['session']; + } + + if (isset($this->options['writeConcern'])) { + $options['writeConcern'] = $this->options['writeConcern']; + } + + return $options; + } + + /** + * @param array[] $operations + * @return array[] + */ + private function validateOperations(array $operations, ?DocumentCodec $codec): array + { foreach ($operations as $i => $operation) { if (! is_array($operation)) { throw InvalidArgumentException::invalidType(sprintf('$operations[%d]', $i), $operation, 'array'); @@ -163,6 +294,10 @@ public function __construct(string $databaseName, string $collectionName, array switch ($type) { case self::INSERT_ONE: + if ($codec) { + $operations[$i][$type][0] = $codec->encode($args[0]); + } + break; case self::DELETE_MANY: @@ -190,6 +325,10 @@ public function __construct(string $databaseName, string $collectionName, array throw new InvalidArgumentException(sprintf('Missing second argument for $operations[%d]["%s"]', $i, $type)); } + if ($codec) { + $operations[$i][$type][1] = $codec->encode($args[1]); + } + if (! is_document($args[1])) { throw InvalidArgumentException::expectedDocumentType(sprintf('$operations[%d]["%s"][1]', $i, $type), $args[1]); } @@ -272,151 +411,6 @@ public function __construct(string $databaseName, string $collectionName, array } } - $options += ['ordered' => true]; - - if (isset($options['bypassDocumentValidation']) && ! is_bool($options['bypassDocumentValidation'])) { - throw InvalidArgumentException::invalidType('"bypassDocumentValidation" option', $options['bypassDocumentValidation'], 'boolean'); - } - - if (isset($options['codec']) && ! $options['codec'] instanceof DocumentCodec) { - throw InvalidArgumentException::invalidType('"codec" option', $options['codec'], DocumentCodec::class); - } - - if (! is_bool($options['ordered'])) { - throw InvalidArgumentException::invalidType('"ordered" option', $options['ordered'], 'boolean'); - } - - if (isset($options['session']) && ! $options['session'] instanceof Session) { - throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class); - } - - if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) { - throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class); - } - - if (isset($options['let']) && ! is_document($options['let'])) { - throw InvalidArgumentException::expectedDocumentType('"let" option', $options['let']); - } - - if (isset($options['bypassDocumentValidation']) && ! $options['bypassDocumentValidation']) { - unset($options['bypassDocumentValidation']); - } - - if (isset($options['writeConcern']) && $options['writeConcern']->isDefault()) { - unset($options['writeConcern']); - } - - $this->databaseName = $databaseName; - $this->collectionName = $collectionName; - $this->operations = $operations; - $this->options = $options; - } - - /** - * Execute the operation. - * - * @see Executable::execute() - * @return BulkWriteResult - * @throws UnsupportedException if write concern is used and unsupported - * @throws DriverRuntimeException for other driver errors (e.g. connection errors) - */ - public function execute(Server $server) - { - $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); - if ($inTransaction && isset($this->options['writeConcern'])) { - throw UnsupportedException::writeConcernNotSupportedInTransaction(); - } - - $bulk = new Bulk($this->createBulkWriteOptions()); - $insertedIds = []; - - foreach ($this->operations as $i => $operation) { - $type = key($operation); - $args = current($operation); - - switch ($type) { - case self::DELETE_MANY: - case self::DELETE_ONE: - $bulk->delete($args[0], $args[1]); - break; - - case self::INSERT_ONE: - if (isset($this->options['codec'])) { - $args[0] = $this->options['codec']->encodeIfSupported($args[0]); - - // Psalm's assert-if-true annotation does not work with unions, so - // assert the type manually instead of using is_document - // See https://github.com/vimeo/psalm/issues/6831 - assert(is_array($args[0]) || is_object($args[0])); - } - - $insertedIds[$i] = $bulk->insert($args[0]); - break; - - case self::REPLACE_ONE: - if (isset($this->options['codec'])) { - $args[1] = $this->options['codec']->encodeIfSupported($args[1]); - - // Psalm's assert-if-true annotation does not work with unions, so - // assert the type manually instead of using is_document - // See https://github.com/vimeo/psalm/issues/6831 - assert(is_array($args[1]) || is_object($args[1])); - } - - // break intentionally missing, as replace is handled - // through update as well - - case self::UPDATE_MANY: - case self::UPDATE_ONE: - $bulk->update($args[0], $args[1], $args[2]); - break; - } - } - - $writeResult = $server->executeBulkWrite($this->databaseName . '.' . $this->collectionName, $bulk, $this->createExecuteOptions()); - - return new BulkWriteResult($writeResult, $insertedIds); - } - - /** - * Create options for constructing the bulk write. - * - * @see https://php.net/manual/en/mongodb-driver-bulkwrite.construct.php - */ - private function createBulkWriteOptions(): array - { - $options = ['ordered' => $this->options['ordered']]; - - foreach (['bypassDocumentValidation', 'comment'] as $option) { - if (isset($this->options[$option])) { - $options[$option] = $this->options[$option]; - } - } - - if (isset($this->options['let'])) { - $options['let'] = (object) $this->options['let']; - } - - return $options; - } - - /** - * Create options for executing the bulk write. - * - * @see https://php.net/manual/en/mongodb-driver-server.executebulkwrite.php - */ - private function createExecuteOptions(): array - { - $options = []; - - if (isset($this->options['session'])) { - $options['session'] = $this->options['session']; - } - - if (isset($this->options['writeConcern'])) { - $options['writeConcern'] = $this->options['writeConcern']; - } - - return $options; + return $operations; } } diff --git a/src/Operation/FindOneAndReplace.php b/src/Operation/FindOneAndReplace.php index e2d3713f8..20412d645 100644 --- a/src/Operation/FindOneAndReplace.php +++ b/src/Operation/FindOneAndReplace.php @@ -24,10 +24,7 @@ use MongoDB\Exception\UnsupportedException; use function array_key_exists; -use function assert; -use function is_array; use function is_integer; -use function is_object; use function MongoDB\is_document; use function MongoDB\is_first_key_operator; use function MongoDB\is_pipeline; @@ -111,23 +108,6 @@ public function __construct(string $databaseName, string $collectionName, $filte throw InvalidArgumentException::expectedDocumentType('$filter', $filter); } - if (! is_document($replacement)) { - throw InvalidArgumentException::expectedDocumentType('$replacement', $replacement); - } - - // Treat empty arrays as replacement documents for BC - if ($replacement === []) { - $replacement = (object) $replacement; - } - - if (is_first_key_operator($replacement)) { - throw new InvalidArgumentException('First key in $replacement is an update operator'); - } - - if (is_pipeline($replacement, true /* allowEmpty */)) { - throw new InvalidArgumentException('$replacement is an update pipeline'); - } - if (isset($options['codec']) && ! $options['codec'] instanceof DocumentCodec) { throw InvalidArgumentException::invalidType('"codec" option', $options['codec'], DocumentCodec::class); } @@ -158,14 +138,7 @@ public function __construct(string $databaseName, string $collectionName, $filte unset($options['projection'], $options['returnDocument']); - if (isset($options['codec'])) { - $replacement = $options['codec']->encodeIfSupported($replacement); - - // Psalm's assert-if-true annotation does not work with unions, so - // assert the type manually instead of using is_document - // See https://github.com/vimeo/psalm/issues/6831 - assert(is_array($replacement) || is_object($replacement)); - } + $replacement = $this->validateReplacement($replacement, $options['codec'] ?? null); $this->findAndModify = new FindAndModify( $databaseName, @@ -197,4 +170,34 @@ public function getCommandDocument() { return $this->findAndModify->getCommandDocument(); } + + /** + * @param array|object $replacement + * @return array|object + */ + private function validateReplacement($replacement, ?DocumentCodec $codec) + { + if (isset($codec)) { + $replacement = $codec->encode($replacement); + } + + if (! is_document($replacement)) { + throw InvalidArgumentException::expectedDocumentType('$replacement', $replacement); + } + + // Treat empty arrays as replacement documents for BC + if ($replacement === []) { + $replacement = (object) $replacement; + } + + if (is_first_key_operator($replacement)) { + throw new InvalidArgumentException('First key in $replacement is an update operator'); + } + + if (is_pipeline($replacement, true /* allowEmpty */)) { + throw new InvalidArgumentException('$replacement is an update pipeline'); + } + + return $replacement; + } } diff --git a/src/Operation/InsertMany.php b/src/Operation/InsertMany.php index 4ab3a02fd..42b3a344f 100644 --- a/src/Operation/InsertMany.php +++ b/src/Operation/InsertMany.php @@ -28,10 +28,7 @@ use MongoDB\InsertManyResult; use function array_is_list; -use function assert; -use function is_array; use function is_bool; -use function is_object; use function MongoDB\is_document; use function sprintf; @@ -76,28 +73,14 @@ class InsertMany implements Executable * * * writeConcern (MongoDB\Driver\WriteConcern): Write concern. * - * @param string $databaseName Database name - * @param string $collectionName Collection name - * @param array[]|object[] $documents List of documents to insert - * @param array $options Command options + * @param string $databaseName Database name + * @param string $collectionName Collection name + * @param list $documents List of documents to insert + * @param array $options Command options * @throws InvalidArgumentException for parameter/option parsing errors */ public function __construct(string $databaseName, string $collectionName, array $documents, array $options = []) { - if (empty($documents)) { - throw new InvalidArgumentException('$documents is empty'); - } - - if (! array_is_list($documents)) { - throw new InvalidArgumentException('$documents is not a list'); - } - - foreach ($documents as $i => $document) { - if (! is_document($document)) { - throw InvalidArgumentException::expectedDocumentType(sprintf('$documents[%d]', $i), $document); - } - } - $options += ['ordered' => true]; if (isset($options['bypassDocumentValidation']) && ! is_bool($options['bypassDocumentValidation'])) { @@ -130,7 +113,7 @@ public function __construct(string $databaseName, string $collectionName, array $this->databaseName = $databaseName; $this->collectionName = $collectionName; - $this->documents = $documents; + $this->documents = $this->validateDocuments($documents, $options['codec'] ?? null); $this->options = $options; } @@ -153,15 +136,6 @@ public function execute(Server $server) $insertedIds = []; foreach ($this->documents as $i => $document) { - if (isset($this->options['codec'])) { - $document = $this->options['codec']->encodeIfSupported($document); - - // Psalm's assert-if-true annotation does not work with unions, so - // assert the type manually instead of using is_document - // See https://github.com/vimeo/psalm/issues/6831 - assert(is_array($document) || is_object($document)); - } - $insertedIds[$i] = $bulk->insert($document); } @@ -207,4 +181,31 @@ private function createExecuteOptions(): array return $options; } + + /** + * @param list $documents + * @return list + */ + private function validateDocuments(array $documents, ?DocumentCodec $codec): array + { + if (empty($documents)) { + throw new InvalidArgumentException('$documents is empty'); + } + + if (! array_is_list($documents)) { + throw new InvalidArgumentException('$documents is not a list'); + } + + foreach ($documents as $i => $document) { + if ($codec) { + $document = $documents[$i] = $codec->encode($document); + } + + if (! is_document($document)) { + throw InvalidArgumentException::expectedDocumentType(sprintf('$documents[%d]', $i), $document); + } + } + + return $documents; + } } diff --git a/src/Operation/InsertOne.php b/src/Operation/InsertOne.php index 72b1c855d..f745664d7 100644 --- a/src/Operation/InsertOne.php +++ b/src/Operation/InsertOne.php @@ -27,10 +27,7 @@ use MongoDB\Exception\UnsupportedException; use MongoDB\InsertOneResult; -use function assert; -use function is_array; use function is_bool; -use function is_object; use function MongoDB\is_document; /** @@ -77,10 +74,6 @@ class InsertOne implements Executable */ public function __construct(string $databaseName, string $collectionName, $document, array $options = []) { - if (! is_document($document)) { - throw InvalidArgumentException::expectedDocumentType('$document', $document); - } - if (isset($options['bypassDocumentValidation']) && ! is_bool($options['bypassDocumentValidation'])) { throw InvalidArgumentException::invalidType('"bypassDocumentValidation" option', $options['bypassDocumentValidation'], 'boolean'); } @@ -105,18 +98,9 @@ public function __construct(string $databaseName, string $collectionName, $docum unset($options['writeConcern']); } - if (isset($options['codec'])) { - $document = $options['codec']->encodeIfSupported($document); - - // Psalm's assert-if-true annotation does not work with unions, so - // assert the type manually instead of using is_document - // See https://github.com/vimeo/psalm/issues/6831 - assert(is_array($document) || is_object($document)); - } - $this->databaseName = $databaseName; $this->collectionName = $collectionName; - $this->document = $document; + $this->document = $this->validateDocument($document, $options['codec'] ?? null); $this->options = $options; } @@ -181,4 +165,21 @@ private function createExecuteOptions(): array return $options; } + + /** + * @param array|object $document + * @return array|object + */ + private function validateDocument($document, ?DocumentCodec $codec) + { + if ($codec) { + $document = $codec->encode($document); + } + + if (! is_document($document)) { + throw InvalidArgumentException::expectedDocumentType('$document', $document); + } + + return $document; + } } diff --git a/src/Operation/ReplaceOne.php b/src/Operation/ReplaceOne.php index c3143b982..92f4d9efd 100644 --- a/src/Operation/ReplaceOne.php +++ b/src/Operation/ReplaceOne.php @@ -24,9 +24,6 @@ use MongoDB\Exception\UnsupportedException; use MongoDB\UpdateResult; -use function assert; -use function is_array; -use function is_object; use function MongoDB\is_document; use function MongoDB\is_first_key_operator; use function MongoDB\is_pipeline; @@ -86,23 +83,6 @@ class ReplaceOne implements Executable */ public function __construct(string $databaseName, string $collectionName, $filter, $replacement, array $options = []) { - if (! is_document($replacement)) { - throw InvalidArgumentException::expectedDocumentType('$replacement', $replacement); - } - - // Treat empty arrays as replacement documents for BC - if ($replacement === []) { - $replacement = (object) $replacement; - } - - if (is_first_key_operator($replacement)) { - throw new InvalidArgumentException('First key in $replacement is an update operator'); - } - - if (is_pipeline($replacement, true /* allowEmpty */)) { - throw new InvalidArgumentException('$replacement is an update pipeline'); - } - if (isset($options['codec']) && ! $options['codec'] instanceof DocumentCodec) { throw InvalidArgumentException::invalidType('"codec" option', $options['codec'], DocumentCodec::class); } @@ -111,20 +91,13 @@ public function __construct(string $databaseName, string $collectionName, $filte if (isset($options['typeMap'])) { throw InvalidArgumentException::cannotCombineCodecAndTypeMap(); } - - $replacement = $options['codec']->encodeIfSupported($replacement); - - // Psalm's assert-if-true annotation does not work with unions, so - // assert the type manually instead of using is_document - // See https://github.com/vimeo/psalm/issues/6831 - assert(is_array($replacement) || is_object($replacement)); } $this->update = new Update( $databaseName, $collectionName, $filter, - $replacement, + $this->validateReplacement($replacement, $options['codec'] ?? null), ['multi' => false] + $options, ); } @@ -141,4 +114,34 @@ public function execute(Server $server) { return $this->update->execute($server); } + + /** + * @param array|object $replacement + * @return array|object + */ + private function validateReplacement($replacement, ?DocumentCodec $codec) + { + if ($codec) { + $replacement = $codec->encode($replacement); + } + + if (! is_document($replacement)) { + throw InvalidArgumentException::expectedDocumentType('$replacement', $replacement); + } + + // Treat empty arrays as replacement documents for BC + if ($replacement === []) { + $replacement = (object) $replacement; + } + + if (is_first_key_operator($replacement)) { + throw new InvalidArgumentException('First key in $replacement is an update operator'); + } + + if (is_pipeline($replacement, true /* allowEmpty */)) { + throw new InvalidArgumentException('$replacement is an update pipeline'); + } + + return $replacement; + } } diff --git a/tests/Collection/CodecCollectionFunctionalTest.php b/tests/Collection/CodecCollectionFunctionalTest.php index 9b4be93e1..47d6d80d4 100644 --- a/tests/Collection/CodecCollectionFunctionalTest.php +++ b/tests/Collection/CodecCollectionFunctionalTest.php @@ -260,7 +260,7 @@ public function testFindOneAndReplaceWithCodecAndTypemap(): void ]; $this->expectExceptionObject(InvalidArgumentException::cannotCombineCodecAndTypeMap()); - $this->collection->findOneAndReplace(['_id' => 1], ['foo' => 'bar'], $options); + $this->collection->findOneAndReplace(['_id' => 1], TestObject::createForFixture(1), $options); } public static function provideFindOptions(): Generator diff --git a/tests/Operation/BulkWriteTest.php b/tests/Operation/BulkWriteTest.php index 8d35d2d74..a307f5b01 100644 --- a/tests/Operation/BulkWriteTest.php +++ b/tests/Operation/BulkWriteTest.php @@ -3,7 +3,9 @@ namespace MongoDB\Tests\Operation; use MongoDB\Exception\InvalidArgumentException; +use MongoDB\Exception\UnsupportedValueException; use MongoDB\Operation\BulkWrite; +use MongoDB\Tests\Fixtures\Codec\TestDocumentCodec; class BulkWriteTest extends TestCase { @@ -63,6 +65,18 @@ public function testInsertOneDocumentArgumentTypeCheck($document): void ]); } + public function testInsertOneWithCodecRejectsInvalidDocuments(): void + { + $this->expectExceptionObject(UnsupportedValueException::invalidEncodableValue([])); + + new BulkWrite( + $this->getDatabaseName(), + $this->getCollectionName(), + [[BulkWrite::INSERT_ONE => [['x' => 1]]]], + ['codec' => new TestDocumentCodec()], + ); + } + public function testDeleteManyFilterArgumentMissing(): void { $this->expectException(InvalidArgumentException::class); @@ -223,6 +237,18 @@ public function provideInvalidBooleanValues() return $this->wrapValuesForDataProvider($this->getInvalidBooleanValues()); } + public function testReplaceOneWithCodecRejectsInvalidDocuments(): void + { + $this->expectExceptionObject(UnsupportedValueException::invalidEncodableValue([])); + + new BulkWrite( + $this->getDatabaseName(), + $this->getCollectionName(), + [[BulkWrite::REPLACE_ONE => [['x' => 1], ['y' => 1]]]], + ['codec' => new TestDocumentCodec()], + ); + } + public function testUpdateManyFilterArgumentMissing(): void { $this->expectException(InvalidArgumentException::class); diff --git a/tests/Operation/InsertManyTest.php b/tests/Operation/InsertManyTest.php index a16710b75..26e62a7eb 100644 --- a/tests/Operation/InsertManyTest.php +++ b/tests/Operation/InsertManyTest.php @@ -3,7 +3,9 @@ namespace MongoDB\Tests\Operation; use MongoDB\Exception\InvalidArgumentException; +use MongoDB\Exception\UnsupportedValueException; use MongoDB\Operation\InsertMany; +use MongoDB\Tests\Fixtures\Codec\TestDocumentCodec; class InsertManyTest extends TestCase { @@ -46,4 +48,11 @@ public function provideInvalidConstructorOptions() 'writeConcern' => $this->getInvalidWriteConcernValues(), ]); } + + public function testCodecRejectsInvalidDocuments(): void + { + $this->expectExceptionObject(UnsupportedValueException::invalidEncodableValue([])); + + new InsertMany($this->getDatabaseName(), $this->getCollectionName(), [[]], ['codec' => new TestDocumentCodec()]); + } } diff --git a/tests/Operation/InsertOneTest.php b/tests/Operation/InsertOneTest.php index 868c0fe47..af0946d17 100644 --- a/tests/Operation/InsertOneTest.php +++ b/tests/Operation/InsertOneTest.php @@ -3,7 +3,9 @@ namespace MongoDB\Tests\Operation; use MongoDB\Exception\InvalidArgumentException; +use MongoDB\Exception\UnsupportedValueException; use MongoDB\Operation\InsertOne; +use MongoDB\Tests\Fixtures\Codec\TestDocumentCodec; class InsertOneTest extends TestCase { @@ -30,4 +32,11 @@ public function provideInvalidConstructorOptions() 'writeConcern' => $this->getInvalidWriteConcernValues(), ]); } + + public function testCodecRejectsInvalidDocuments(): void + { + $this->expectExceptionObject(UnsupportedValueException::invalidEncodableValue([])); + + new InsertOne($this->getDatabaseName(), $this->getCollectionName(), [], ['codec' => new TestDocumentCodec()]); + } } diff --git a/tests/Operation/ReplaceOneTest.php b/tests/Operation/ReplaceOneTest.php index 0de93f271..404cdae78 100644 --- a/tests/Operation/ReplaceOneTest.php +++ b/tests/Operation/ReplaceOneTest.php @@ -3,7 +3,9 @@ namespace MongoDB\Tests\Operation; use MongoDB\Exception\InvalidArgumentException; +use MongoDB\Exception\UnsupportedValueException; use MongoDB\Operation\ReplaceOne; +use MongoDB\Tests\Fixtures\Codec\TestDocumentCodec; class ReplaceOneTest extends TestCase { @@ -62,4 +64,11 @@ public function provideInvalidConstructorOptions() 'codec' => $this->getInvalidDocumentCodecValues(), ]); } + + public function testCodecRejectsInvalidDocuments(): void + { + $this->expectExceptionObject(UnsupportedValueException::invalidEncodableValue([])); + + new ReplaceOne($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], ['y' => 1], ['codec' => new TestDocumentCodec()]); + } }