diff --git a/.travis.yml b/.travis.yml index 59b908f5e..fa771335f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,12 +7,29 @@ php: - 5.6 env: - - MONGODB_VERSION=alpha + global: + - KEY_SERVER="hkp://keyserver.ubuntu.com:80" + - MONGO_REPO_URI="http://repo.mongodb.com/apt/ubuntu" + - MONGO_REPO_TYPE="precise/mongodb-enterprise/" + - SOURCES_LOC="/etc/apt/sources.list.d/mongodb.list" + matrix: + - DRIVER_VERSION=alpha SERVER_VERSION=2.4 + - DRIVER_VERSION=alpha SERVER_VERSION=2.6 + - DRIVER_VERSION=alpha SERVER_VERSION=3.0 -services: mongodb +before_install: + - sudo apt-key adv --keyserver ${KEY_SERVER} --recv 7F0CEB10 + - echo "deb ${MONGO_REPO_URI} ${MONGO_REPO_TYPE}${SERVER_VERSION} multiverse" | sudo tee ${SOURCES_LOC} + - sudo apt-get update -qq + +install: + - if dpkg --compare-versions ${SERVER_VERSION} le "2.4"; then export SERVER_PACKAGE=mongodb-10gen-enterprise; else export SERVER_PACKAGE=mongodb-enterprise; fi + - sudo apt-get install ${SERVER_PACKAGE} before_script: - - mongo --eval 'tojson(db.runCommand({buildInfo:1}))' - - pecl install -f mongodb-${MONGODB_VERSION} + - if dpkg --compare-versions ${SERVER_VERSION} le "2.4"; then export SERVER_SERVICE=mongodb; else export SERVER_SERVICE=mongod; fi + - if ! nc -z localhost 27017; then sudo service ${SERVER_SERVICE} start; fi + - mongod --version + - pecl install -f mongodb-${DRIVER_VERSION} - php --ri mongodb - composer install --dev --no-interaction --prefer-source diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 4f324887a..1ec848b37 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -16,7 +16,7 @@ - + diff --git a/src/Collection.php b/src/Collection.php index 2229c8d19..0c3d1f036 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -71,13 +71,13 @@ public function __construct(Manager $manager, $namespace, WriteConcern $writeCon /** * Runs an aggregation framework pipeline - * NOTE: The return value of this method depends on your MongoDB server version - * and possibly options. - * MongoDB 2.6 (and later) will return a Cursor by default - * MongoDB pre 2.6 will return an ArrayIterator + * + * Note: this method's return value depends on the MongoDB server version + * and the "useCursor" option. If "useCursor" is true, a Cursor will be + * returned; otherwise, an ArrayIterator is returned, which wraps the + * "result" array from the command response document. * * @see http://docs.mongodb.org/manual/reference/command/aggregate/ - * @see Collection::getAggregateOptions() for supported $options * * @param array $pipeline The pipeline to execute * @param array $options Additional options @@ -85,23 +85,61 @@ public function __construct(Manager $manager, $namespace, WriteConcern $writeCon */ public function aggregate(array $pipeline, array $options = array()) { - $options = array_merge($this->getAggregateOptions(), $options); - $options = $this->_massageAggregateOptions($options); - $cmd = array( - "aggregate" => $this->collname, - "pipeline" => $pipeline, - ) + $options; + $readPreference = new ReadPreference(ReadPreference::RP_PRIMARY); + $server = $this->manager->selectServer($readPreference); + + if (FeatureDetection::isSupported($server, FeatureDetection::API_AGGREGATE_CURSOR)) { + $options = array_merge( + array( + /** + * Enables writing to temporary files. When set to true, aggregation stages + * can write data to the _tmp subdirectory in the dbPath directory. The + * default is false. + * + * @see http://docs.mongodb.org/manual/reference/command/aggregate/ + */ + 'allowDiskUse' => false, + /** + * The number of documents to return per batch. + * + * @see http://docs.mongodb.org/manual/reference/command/aggregate/ + */ + 'batchSize' => 0, + /** + * The maximum amount of time to allow the query to run. + * + * @see http://docs.mongodb.org/manual/reference/command/aggregate/ + */ + 'maxTimeMS' => 0, + /** + * Indicates if the results should be provided as a cursor. + * + * @see http://docs.mongodb.org/manual/reference/command/aggregate/ + */ + 'useCursor' => true, + ), + $options + ); + } - $cursor = $this->_runCommand($this->dbname, $cmd); + $options = $this->_massageAggregateOptions($options); + $command = new Command(array( + 'aggregate' => $this->collname, + 'pipeline' => $pipeline, + ) + $options); + $cursor = $server->executeCommand($this->dbname, $command); - if (isset($cmd["cursor"]) && $cmd["cursor"]) { + if ( ! empty($options["cursor"])) { return $cursor; } $doc = current($cursor->toArray()); if ($doc["ok"]) { - return new \ArrayIterator($doc["result"]); + return new \ArrayIterator(array_map( + function (\stdClass $document) { return (array) $document; }, + $doc["result"] + )); } throw $this->_generateCommandException($doc); @@ -239,12 +277,12 @@ public function count(array $filter = array(), array $options = array()) { $cmd = array( "count" => $this->collname, - "query" => $filter, + "query" => (object) $filter, ) + $options; $doc = current($this->_runCommand($this->dbname, $cmd)->toArray()); if ($doc["ok"]) { - return $doc["n"]; + return (integer) $doc["n"]; } throw $this->_generateCommandException($doc); } @@ -363,7 +401,7 @@ public function distinct($fieldName, array $filter = array(), array $options = a $cmd = array( "distinct" => $this->collname, "key" => $fieldName, - "query" => $filter, + "query" => (object) $filter, ) + $options; $doc = current($this->_runCommand($this->dbname, $cmd)->toArray()); @@ -497,7 +535,7 @@ public function findOneAndDelete(array $filter, array $options = array()) $doc = current($this->_runCommand($this->dbname, $cmd)->toArray()); if ($doc["ok"]) { - return $doc["value"]; + return is_object($doc["value"]) ? (array) $doc["value"] : $doc["value"]; } throw $this->_generateCommandException($doc); @@ -534,7 +572,7 @@ public function findOneAndReplace(array $filter, array $replacement, array $opti $doc = current($this->_runCommand($this->dbname, $cmd)->toArray()); if ($doc["ok"]) { - return $doc["value"]; + return $this->_massageFindAndModifyResult($doc, $options); } throw $this->_generateCommandException($doc); @@ -572,61 +610,12 @@ public function findOneAndUpdate(array $filter, array $update, array $options = $doc = current($this->_runCommand($this->dbname, $cmd)->toArray()); if ($doc["ok"]) { - return $doc["value"]; + return $this->_massageFindAndModifyResult($doc, $options); } throw $this->_generateCommandException($doc); } - /** - * Retrieves all aggregate options with their default values. - * - * @return array of Collection::aggregate() options - */ - public function getAggregateOptions() - { - $opts = array( - /** - * Enables writing to temporary files. When set to true, aggregation stages - * can write data to the _tmp subdirectory in the dbPath directory. The - * default is false. - * - * @see http://docs.mongodb.org/manual/reference/command/aggregate/ - */ - "allowDiskUse" => false, - - /** - * The number of documents to return per batch. - * - * @see http://docs.mongodb.org/manual/reference/command/aggregate/ - */ - "batchSize" => 0, - - /** - * The maximum amount of time to allow the query to run. - * - * @see http://docs.mongodb.org/manual/reference/command/aggregate/ - */ - "maxTimeMS" => 0, - - /** - * Indicates if the results should be provided as a cursor. - * - * The default for this value depends on the version of the server. - * - Servers >= 2.6 will use a default of true. - * - Servers < 2.6 will use a default of false. - * - * As with any other property, this value can be changed. - * - * @see http://docs.mongodb.org/manual/reference/command/aggregate/ - */ - "useCursor" => true, - ); - - /* FIXME: Add a version check for useCursor */ - return $opts; - } - /** * Retrieves all Bulk Write options with their default values. * @@ -961,7 +950,7 @@ public function getWriteOptions() * * @see http://docs.mongodb.org/manual/reference/command/insert/ * - * @param array $documents The documents to insert + * @param array[]|object[] $documents The documents to insert * @return InsertManyResult */ public function insertMany(array $documents) @@ -976,6 +965,8 @@ public function insertMany(array $documents) if ($insertedId !== null) { $insertedIds[$i] = $insertedId; + } else { + $insertedIds[$i] = is_array($document) ? $document['_id'] : $document->_id; } } @@ -989,11 +980,10 @@ public function insertMany(array $documents) * * @see http://docs.mongodb.org/manual/reference/command/insert/ * - * @param array $document The document to insert - * @param array $options Additional options + * @param array|object $document The document to insert * @return InsertOneResult */ - public function insertOne(array $document) + public function insertOne($document) { $options = array_merge($this->getWriteOptions()); @@ -1001,6 +991,10 @@ public function insertOne(array $document) $id = $bulk->insert($document); $wr = $this->manager->executeBulkWrite($this->ns, $bulk, $this->wc); + if ($id === null) { + $id = is_array($document) ? $document['_id'] : $document->_id; + } + return new InsertOneResult($wr, $id); } @@ -1038,7 +1032,7 @@ public function replaceOne(array $filter, array $update, array $options = array( if (isset($firstKey[0]) && $firstKey[0] == '$') { throw new InvalidArgumentException("First key in \$update must NOT be a \$operator"); } - $wr = $this->_update($filter, $update, $options); + $wr = $this->_update($filter, $update, $options + array("multi" => false)); return new UpdateResult($wr); } @@ -1057,7 +1051,7 @@ public function replaceOne(array $filter, array $update, array $options = array( */ public function updateMany(array $filter, $update, array $options = array()) { - $wr = $this->_update($filter, $update, $options + array("limit" => 0)); + $wr = $this->_update($filter, $update, $options + array("multi" => true)); return new UpdateResult($wr); } @@ -1080,7 +1074,7 @@ public function updateOne(array $filter, array $update, array $options = array() if (!isset($firstKey[0]) || $firstKey[0] != '$') { throw new InvalidArgumentException("First key in \$update must be a \$operator"); } - $wr = $this->_update($filter, $update, $options); + $wr = $this->_update($filter, $update, $options + array("multi" => false)); return new UpdateResult($wr); } @@ -1146,14 +1140,66 @@ final protected function _generateCommandException($doc) */ protected function _massageAggregateOptions($options) { - if ($options["useCursor"]) { - $options["cursor"] = array("batchSize" => $options["batchSize"]); + if ( ! empty($options["useCursor"])) { + $options["cursor"] = isset($options["batchSize"]) + ? array("batchSize" => (integer) $options["batchSize"]) + : new stdClass; } unset($options["useCursor"], $options["batchSize"]); return $options; } + /** + * Internal helper for massaging findandmodify options + * @internal + */ + final protected function _massageFindAndModifyOptions($options, $update = array()) + { + $ret = array( + "sort" => $options["sort"], + "new" => isset($options["returnDocument"]) ? $options["returnDocument"] == self::FIND_ONE_AND_RETURN_AFTER : false, + "fields" => $options["projection"], + "upsert" => isset($options["upsert"]) ? $options["upsert"] : false, + ); + if ($update) { + $ret["update"] = $update; + } else { + $ret["remove"] = true; + } + return $ret; + } + + /** + * Internal helper for massaging the findAndModify result. + * + * @internal + * @param array $result + * @param array $options + * @return array|null + */ + final protected function _massageFindAndModifyResult(array $result, array $options) + { + if ($result['value'] === null) { + return null; + } + + /* Prior to 3.0, findAndModify returns an empty document instead of null + * when an upsert is performed and the pre-modified document was + * requested. + */ + if ($options['upsert'] && ! $options['new'] && + isset($result['lastErrorObject']->updatedExisting) && + ! $result['lastErrorObject']->updatedExisting) { + + return null; + } + + return is_object($result["value"]) + ? (array) $result['value'] + : $result['value']; + } + /** * Constructs the Query Wire Protocol field 'flags' based on $options * provided to other helpers diff --git a/src/FeatureDetection.php b/src/FeatureDetection.php index 51b6783de..418bef75d 100644 --- a/src/FeatureDetection.php +++ b/src/FeatureDetection.php @@ -14,6 +14,7 @@ class FeatureDetection const API_LISTCOLLECTIONS_CMD = 3; const API_LISTINDEXES_CMD = 3; const API_CREATEINDEXES_CMD = 2; + const API_AGGREGATE_CURSOR = 2; /** * Return whether the server supports a particular feature. diff --git a/src/InsertManyResult.php b/src/InsertManyResult.php index 4480bc8f8..e7401b541 100644 --- a/src/InsertManyResult.php +++ b/src/InsertManyResult.php @@ -16,7 +16,7 @@ class InsertManyResult * Constructor. * * @param WriteResult $writeResult - * @param array $insertedIds + * @param mixed[] $insertedIds */ public function __construct(WriteResult $writeResult, array $insertedIds = array()) { @@ -41,10 +41,11 @@ public function getInsertedCount() * Return the map of inserted IDs that were generated by the driver. * * The index of each ID in the map corresponds to the document's position - * in bulk operation. If an inserted document already had an ID (e.g. it was - * generated by the application), it will not be present in this map. + * in bulk operation. If the document already an ID prior to insertion (i.e. + * the driver did not need to generate an ID), this will contain its "_id". + * Any driver-generated ID will be an MongoDB\Driver\ObjectID instance. * - * @return array + * @return mixed[] */ public function getInsertedIds() { diff --git a/src/InsertOneResult.php b/src/InsertOneResult.php index efdd92066..fc377e999 100644 --- a/src/InsertOneResult.php +++ b/src/InsertOneResult.php @@ -2,7 +2,6 @@ namespace MongoDB; -use BSON\ObjectId; use MongoDB\Driver\WriteResult; /** @@ -17,9 +16,9 @@ class InsertOneResult * Constructor. * * @param WriteResult $writeResult - * @param ObjectId $insertedId + * @param mixed $insertedId */ - public function __construct(WriteResult $writeResult, ObjectId $insertedId = null) + public function __construct(WriteResult $writeResult, $insertedId) { $this->writeResult = $writeResult; $this->insertedId = $insertedId; @@ -41,10 +40,11 @@ public function getInsertedCount() /** * Return the inserted ID that was generated by the driver. * - * If the inserted document already had an ID (e.g. it was generated by the - * application), this will be null. + * If the document already an ID prior to insertion (i.e. the driver did not + * need to generate an ID), this will contain its "_id". Any + * driver-generated ID will be an MongoDB\Driver\ObjectID instance. * - * @return ObjectId|null + * @return mixed */ public function getInsertedId() { diff --git a/tests/Collection/CollectionFunctionalTest.php b/tests/Collection/CollectionFunctionalTest.php new file mode 100644 index 000000000..bad4bd58f --- /dev/null +++ b/tests/Collection/CollectionFunctionalTest.php @@ -0,0 +1,51 @@ +collection->insertOne(array('x' => 1)); + $this->assertEquals(1, $writeResult->getInsertedCount()); + + $commandResult = $this->collection->drop(); + $this->assertCommandSucceeded($commandResult); + $this->assertCollectionCount($this->getNamespace(), 0); + } + + function testInsertAndRetrieve() + { + $generator = new FixtureGenerator(); + + for ($i = 0; $i < 10; $i++) { + $user = $generator->createUser(); + $result = $this->collection->insertOne($user); + $this->assertInstanceOf('MongoDB\InsertOneResult', $result); + $this->assertInstanceOf('BSON\ObjectId', $result->getInsertedId()); + $this->assertEquals(24, strlen($result->getInsertedId())); + + $user["_id"] = $result->getInsertedId(); + $document = $this->collection->findOne(array("_id" => $result->getInsertedId())); + $this->assertEquals($document, $user, "The inserted and returned objects are the same"); + } + + $this->assertEquals(10, $i); + + $query = array("firstName" => "Ransom"); + $count = $this->collection->count($query); + $this->assertEquals(1, $count); + $cursor = $this->collection->find($query); + $this->assertInstanceOf('MongoDB\Driver\Cursor', $cursor); + + foreach($cursor as $n => $person) { + $this->assertInternalType("array", $person); + } + $this->assertEquals(0, $n); + } +} diff --git a/tests/Collection/CrudSpec/AggregateFunctionalTest.php b/tests/Collection/CrudSpec/AggregateFunctionalTest.php new file mode 100644 index 000000000..bdacc989b --- /dev/null +++ b/tests/Collection/CrudSpec/AggregateFunctionalTest.php @@ -0,0 +1,71 @@ +createFixtures(3); + } + + public function testAggregateWithMultipleStages() + { + $cursor = $this->collection->aggregate( + array( + array('$sort' => array('x' => 1)), + array('$match' => array('_id' => array('$gt' => 1))), + ), + array('batchSize' => 2) + ); + + $expected = array( + array('_id' => 2, 'x' => 22), + array('_id' => 3, 'x' => 33), + ); + + // Use iterator_to_array() here since aggregate() may return an ArrayIterator + $this->assertSame($expected, iterator_to_array($cursor)); + } + + public function testAggregateWithOut() + { + $server = $this->manager->selectServer(new ReadPreference(ReadPreference::RP_PRIMARY)); + + if ( ! FeatureDetection::isSupported($server, FeatureDetection::API_AGGREGATE_CURSOR)) { + $this->markTestSkipped('$out aggregation pipeline operator is not supported'); + } + + $outputCollection = new Collection($this->manager, $this->getNamespace() . '_output'); + $this->dropCollectionIfItExists($outputCollection); + + $this->collection->aggregate( + array( + array('$sort' => array('x' => 1)), + array('$match' => array('_id' => array('$gt' => 1))), + array('$out' => $outputCollection->getCollectionName()), + ) + ); + + $expected = array( + array('_id' => 2, 'x' => 22), + array('_id' => 3, 'x' => 33), + ); + + $this->assertSame($expected, $outputCollection->find()->toArray()); + + // Manually clean up our output collection + $this->dropCollectionIfItExists($outputCollection); + } +} diff --git a/tests/Collection/CrudSpec/CountFunctionalTest.php b/tests/Collection/CrudSpec/CountFunctionalTest.php new file mode 100644 index 000000000..46bb26e82 --- /dev/null +++ b/tests/Collection/CrudSpec/CountFunctionalTest.php @@ -0,0 +1,38 @@ +createFixtures(3); + } + + public function testCountWithoutFilter() + { + $this->assertSame(3, $this->collection->count()); + } + + public function testCountWithFilter() + { + $filter = array('_id' => array('$gt' => 1)); + + $this->assertSame(2, $this->collection->count($filter)); + } + + public function testCountWithSkipAndLimit() + { + $filter = array(); + $options = array('skip' => 1, 'limit' => 3); + + $this->assertSame(2, $this->collection->count($filter, $options)); + } +} diff --git a/tests/Collection/CrudSpec/DeleteManyFunctionalTest.php b/tests/Collection/CrudSpec/DeleteManyFunctionalTest.php new file mode 100644 index 000000000..adfe5df5c --- /dev/null +++ b/tests/Collection/CrudSpec/DeleteManyFunctionalTest.php @@ -0,0 +1,48 @@ +createFixtures(3); + } + + public function testDeleteManyWhenManyDocumentsMatch() + { + $filter = array('_id' => array('$gt' => 1)); + + $result = $this->collection->deleteMany($filter); + $this->assertSame(2, $result->getDeletedCount()); + + $expected = array( + array('_id' => 1, 'x' => 11), + ); + + $this->assertSame($expected, $this->collection->find()->toArray()); + } + + public function testDeleteManyWhenNoDocumentsMatch() + { + $filter = array('_id' => 4); + + $result = $this->collection->deleteMany($filter); + $this->assertSame(0, $result->getDeletedCount()); + + $expected = array( + array('_id' => 1, 'x' => 11), + array('_id' => 2, 'x' => 22), + array('_id' => 3, 'x' => 33), + ); + + $this->assertSame($expected, $this->collection->find()->toArray()); + } +} diff --git a/tests/Collection/CrudSpec/DeleteOneFunctionalTest.php b/tests/Collection/CrudSpec/DeleteOneFunctionalTest.php new file mode 100644 index 000000000..e89c4b708 --- /dev/null +++ b/tests/Collection/CrudSpec/DeleteOneFunctionalTest.php @@ -0,0 +1,64 @@ +createFixtures(3); + } + + public function testDeleteOneWhenManyDocumentsMatch() + { + $filter = array('_id' => array('$gt' => 1)); + + $result = $this->collection->deleteOne($filter); + $this->assertSame(1, $result->getDeletedCount()); + + $expected = array( + array('_id' => 1, 'x' => 11), + array('_id' => 3, 'x' => 33), + ); + + $this->assertSame($expected, $this->collection->find()->toArray()); + } + + public function testDeleteOneWhenOneDocumentMatches() + { + $filter = array('_id' => 2); + + $result = $this->collection->deleteOne($filter); + $this->assertSame(1, $result->getDeletedCount()); + + $expected = array( + array('_id' => 1, 'x' => 11), + array('_id' => 3, 'x' => 33), + ); + + $this->assertSame($expected, $this->collection->find()->toArray()); + } + + public function testDeleteOneWhenNoDocumentsMatch() + { + $filter = array('_id' => 4); + + $result = $this->collection->deleteOne($filter); + $this->assertSame(0, $result->getDeletedCount()); + + $expected = array( + array('_id' => 1, 'x' => 11), + array('_id' => 2, 'x' => 22), + array('_id' => 3, 'x' => 33), + ); + + $this->assertSame($expected, $this->collection->find()->toArray()); + } +} diff --git a/tests/Collection/CrudSpec/DistinctFunctionalTest.php b/tests/Collection/CrudSpec/DistinctFunctionalTest.php new file mode 100644 index 000000000..2d33ea4bd --- /dev/null +++ b/tests/Collection/CrudSpec/DistinctFunctionalTest.php @@ -0,0 +1,30 @@ +createFixtures(3); + } + + public function testDistinctWithoutFilter() + { + $this->assertSame(array(11, 22, 33), $this->collection->distinct('x')); + } + + public function testDistinctWithFilter() + { + $filter = array('_id' => array('$gt' => 1)); + + $this->assertSame(array(22, 33), $this->collection->distinct('x', $filter)); + } +} diff --git a/tests/Collection/CrudSpec/FindFunctionalTest.php b/tests/Collection/CrudSpec/FindFunctionalTest.php new file mode 100644 index 000000000..c82da7d64 --- /dev/null +++ b/tests/Collection/CrudSpec/FindFunctionalTest.php @@ -0,0 +1,64 @@ +createFixtures(5); + } + + public function testFindWithFilter() + { + $filter = array('_id' => 1); + + $expected = array( + array('_id' => 1, 'x' => 11), + ); + + $this->assertSame($expected, $this->collection->find($filter)->toArray()); + } + + public function testFindWithFilterSortSkipAndLimit() + { + $filter = array('_id' => array('$gt' => 2)); + $options = array( + 'sort' => array('_id' => 1), + 'skip' => 2, + 'limit' => 2, + ); + + $expected = array( + array('_id' => 5, 'x' => 55), + ); + + $this->assertSame($expected, $this->collection->find($filter, $options)->toArray()); + } + + public function testFindWithLimitSortAndBatchSize() + { + $filter = array(); + $options = array( + 'sort' => array('_id' => 1), + 'limit' => 4, + 'batchSize' => 2, + ); + + $expected = array( + array('_id' => 1, 'x' => 11), + array('_id' => 2, 'x' => 22), + array('_id' => 3, 'x' => 33), + array('_id' => 4, 'x' => 44), + ); + + $this->assertSame($expected, $this->collection->find($filter, $options)->toArray()); + } +} diff --git a/tests/Collection/CrudSpec/FindOneAndDeleteFunctionalTest.php b/tests/Collection/CrudSpec/FindOneAndDeleteFunctionalTest.php new file mode 100644 index 000000000..3d5aa9b0a --- /dev/null +++ b/tests/Collection/CrudSpec/FindOneAndDeleteFunctionalTest.php @@ -0,0 +1,76 @@ +createFixtures(3); + } + + public function testFindOneAndDeleteWhenManyDocumentsMatch() + { + $filter = array('_id' => array('$gt' => 1)); + $options = array( + 'projection' => array('x' => 1, '_id' => 0), + 'sort' => array('x' => 1), + ); + + $document = $this->collection->findOneAndDelete($filter, $options); + $this->assertSame(array('x' => 22), $document); + + $expected = array( + array('_id' => 1, 'x' => 11), + array('_id' => 3, 'x' => 33), + ); + + $this->assertSame($expected, $this->collection->find()->toArray()); + } + + public function testFindOneAndDeleteWhenOneDocumentMatches() + { + $filter = array('_id' => 2); + $options = array( + 'projection' => array('x' => 1, '_id' => 0), + 'sort' => array('x' => 1), + ); + + $document = $this->collection->findOneAndDelete($filter, $options); + $this->assertSame(array('x' => 22), $document); + + $expected = array( + array('_id' => 1, 'x' => 11), + array('_id' => 3, 'x' => 33), + ); + + $this->assertSame($expected, $this->collection->find()->toArray()); + } + + public function testFindOneAndDeleteWhenNoDocumentsMatch() + { + $filter = array('_id' => 4); + $options = array( + 'projection' => array('x' => 1, '_id' => 0), + 'sort' => array('x' => 1), + ); + + $document = $this->collection->findOneAndDelete($filter, $options); + $this->assertNull($document); + + $expected = array( + array('_id' => 1, 'x' => 11), + array('_id' => 2, 'x' => 22), + array('_id' => 3, 'x' => 33), + ); + + $this->assertSame($expected, $this->collection->find()->toArray()); + } +} diff --git a/tests/Collection/CrudSpec/FindOneAndReplaceFunctionalTest.php b/tests/Collection/CrudSpec/FindOneAndReplaceFunctionalTest.php new file mode 100644 index 000000000..26c5cf12c --- /dev/null +++ b/tests/Collection/CrudSpec/FindOneAndReplaceFunctionalTest.php @@ -0,0 +1,198 @@ +createFixtures(3); + } + + public function testFindOneAndReplaceWhenManyDocumentsMatchReturningDocumentBeforeModification() + { + $filter = array('_id' => array('$gt' => 1)); + $replacement = array('x' => 32); + $options = array( + 'projection' => array('x' => 1, '_id' => 0), + 'sort' => array('x' => 1), + ); + + $document = $this->collection->findOneAndReplace($filter, $replacement, $options); + $this->assertSame(array('x' => 22), $document); + + $expected = array( + array('_id' => 1, 'x' => 11), + array('_id' => 2, 'x' => 32), + array('_id' => 3, 'x' => 33), + ); + + $this->assertSame($expected, $this->collection->find()->toArray()); + } + + public function testFindOneAndReplaceWhenManyDocumentsMatchReturningDocumentAfterModification() + { + $filter = array('_id' => array('$gt' => 1)); + $replacement = array('x' => 32); + $options = array( + 'projection' => array('x' => 1, '_id' => 0), + 'sort' => array('x' => 1), + 'returnDocument' => Collection::FIND_ONE_AND_RETURN_AFTER, + ); + + $document = $this->collection->findOneAndReplace($filter, $replacement, $options); + $this->assertSame(array('x' => 32), $document); + + $expected = array( + array('_id' => 1, 'x' => 11), + array('_id' => 2, 'x' => 32), + array('_id' => 3, 'x' => 33), + ); + + $this->assertSame($expected, $this->collection->find()->toArray()); + } + + public function testFindOneAndReplaceWhenOneDocumentMatchesReturningDocumentBeforeModification() + { + $filter = array('_id' => 2); + $replacement = array('x' => 32); + $options = array( + 'projection' => array('x' => 1, '_id' => 0), + 'sort' => array('x' => 1), + ); + + $document = $this->collection->findOneAndReplace($filter, $replacement, $options); + $this->assertSame(array('x' => 22), $document); + + $expected = array( + array('_id' => 1, 'x' => 11), + array('_id' => 2, 'x' => 32), + array('_id' => 3, 'x' => 33), + ); + + $this->assertSame($expected, $this->collection->find()->toArray()); + } + + public function testFindOneAndReplaceWhenOneDocumentMatchesReturningDocumentAfterModification() + { + $filter = array('_id' => 2); + $replacement = array('x' => 32); + $options = array( + 'projection' => array('x' => 1, '_id' => 0), + 'sort' => array('x' => 1), + 'returnDocument' => Collection::FIND_ONE_AND_RETURN_AFTER, + ); + + $document = $this->collection->findOneAndReplace($filter, $replacement, $options); + $this->assertSame(array('x' => 32), $document); + + $expected = array( + array('_id' => 1, 'x' => 11), + array('_id' => 2, 'x' => 32), + array('_id' => 3, 'x' => 33), + ); + + $this->assertSame($expected, $this->collection->find()->toArray()); + } + + public function testFindOneAndReplaceWhenNoDocumentsMatchReturningDocumentBeforeModification() + { + $filter = array('_id' => 4); + $replacement = array('x' => 44); + $options = array( + 'projection' => array('x' => 1, '_id' => 0), + 'sort' => array('x' => 1), + ); + + $document = $this->collection->findOneAndReplace($filter, $replacement, $options); + $this->assertNull($document); + + $expected = array( + array('_id' => 1, 'x' => 11), + array('_id' => 2, 'x' => 22), + array('_id' => 3, 'x' => 33), + ); + + $this->assertSame($expected, $this->collection->find()->toArray()); + } + + public function testFindOneAndReplaceWithUpsertWhenNoDocumentsMatchReturningDocumentBeforeModification() + { + $filter = array('_id' => 4); + // Server 2.4 and earlier requires any custom ID to also be in the replacement document + $replacement = array('_id' => 4, 'x' => 44); + $options = array( + 'projection' => array('x' => 1, '_id' => 0), + 'sort' => array('x' => 1), + 'upsert' => true, + ); + + $document = $this->collection->findOneAndReplace($filter, $replacement, $options); + $this->assertNull($document); + + $expected = array( + array('_id' => 1, 'x' => 11), + array('_id' => 2, 'x' => 22), + array('_id' => 3, 'x' => 33), + array('_id' => 4, 'x' => 44), + ); + + $this->assertSame($expected, $this->collection->find()->toArray()); + } + + public function testFindOneAndReplaceWhenNoDocumentsMatchReturningDocumentAfterModification() + { + $filter = array('_id' => 4); + $replacement = array('x' => 44); + $options = array( + 'projection' => array('x' => 1, '_id' => 0), + 'sort' => array('x' => 1), + 'returnDocument' => Collection::FIND_ONE_AND_RETURN_AFTER, + ); + + $document = $this->collection->findOneAndReplace($filter, $replacement, $options); + $this->assertNull($document); + + $expected = array( + array('_id' => 1, 'x' => 11), + array('_id' => 2, 'x' => 22), + array('_id' => 3, 'x' => 33), + ); + + $this->assertSame($expected, $this->collection->find()->toArray()); + } + + public function testFindOneAndReplaceWithUpsertWhenNoDocumentsMatchReturningDocumentAfterModification() + { + $filter = array('_id' => 4); + // Server 2.4 and earlier requires any custom ID to also be in the replacement document + $replacement = array('_id' => 4, 'x' => 44); + $options = array( + 'projection' => array('x' => 1, '_id' => 0), + 'sort' => array('x' => 1), + 'returnDocument' => Collection::FIND_ONE_AND_RETURN_AFTER, + 'upsert' => true, + ); + + $document = $this->collection->findOneAndReplace($filter, $replacement, $options); + $this->assertSame(array('x' => 44), $document); + + $expected = array( + array('_id' => 1, 'x' => 11), + array('_id' => 2, 'x' => 22), + array('_id' => 3, 'x' => 33), + array('_id' => 4, 'x' => 44), + ); + + $this->assertSame($expected, $this->collection->find()->toArray()); + } +} diff --git a/tests/Collection/CrudSpec/FindOneAndUpdateFunctionalTest.php b/tests/Collection/CrudSpec/FindOneAndUpdateFunctionalTest.php new file mode 100644 index 000000000..10a017da0 --- /dev/null +++ b/tests/Collection/CrudSpec/FindOneAndUpdateFunctionalTest.php @@ -0,0 +1,196 @@ +createFixtures(3); + } + + public function testFindOneAndUpdateWhenManyDocumentsMatchReturningDocumentBeforeModification() + { + $filter = array('_id' => array('$gt' => 1)); + $update = array('$inc' => array('x' => 1)); + $options = array( + 'projection' => array('x' => 1, '_id' => 0), + 'sort' => array('x' => 1), + ); + + $document = $this->collection->findOneAndUpdate($filter, $update, $options); + $this->assertSame(array('x' => 22), $document); + + $expected = array( + array('_id' => 1, 'x' => 11), + array('_id' => 2, 'x' => 23), + array('_id' => 3, 'x' => 33), + ); + + $this->assertSame($expected, $this->collection->find()->toArray()); + } + + public function testFindOneAndUpdateWhenManyDocumentsMatchReturningDocumentAfterModification() + { + $filter = array('_id' => array('$gt' => 1)); + $update = array('$inc' => array('x' => 1)); + $options = array( + 'projection' => array('x' => 1, '_id' => 0), + 'sort' => array('x' => 1), + 'returnDocument' => Collection::FIND_ONE_AND_RETURN_AFTER, + ); + + $document = $this->collection->findOneAndUpdate($filter, $update, $options); + $this->assertSame(array('x' => 23), $document); + + $expected = array( + array('_id' => 1, 'x' => 11), + array('_id' => 2, 'x' => 23), + array('_id' => 3, 'x' => 33), + ); + + $this->assertSame($expected, $this->collection->find()->toArray()); + } + + public function testFindOneAndUpdateWhenOneDocumentMatchesReturningDocumentBeforeModification() + { + $filter = array('_id' => 2); + $update = array('$inc' => array('x' => 1)); + $options = array( + 'projection' => array('x' => 1, '_id' => 0), + 'sort' => array('x' => 1), + ); + + $document = $this->collection->findOneAndUpdate($filter, $update, $options); + $this->assertSame(array('x' => 22), $document); + + $expected = array( + array('_id' => 1, 'x' => 11), + array('_id' => 2, 'x' => 23), + array('_id' => 3, 'x' => 33), + ); + + $this->assertSame($expected, $this->collection->find()->toArray()); + } + + public function testFindOneAndUpdateWhenOneDocumentMatchesReturningDocumentAfterModification() + { + $filter = array('_id' => 2); + $update = array('$inc' => array('x' => 1)); + $options = array( + 'projection' => array('x' => 1, '_id' => 0), + 'sort' => array('x' => 1), + 'returnDocument' => Collection::FIND_ONE_AND_RETURN_AFTER, + ); + + $document = $this->collection->findOneAndUpdate($filter, $update, $options); + $this->assertSame(array('x' => 23), $document); + + $expected = array( + array('_id' => 1, 'x' => 11), + array('_id' => 2, 'x' => 23), + array('_id' => 3, 'x' => 33), + ); + + $this->assertSame($expected, $this->collection->find()->toArray()); + } + + public function testFindOneAndUpdateWhenNoDocumentsMatchReturningDocumentBeforeModification() + { + $filter = array('_id' => 4); + $update = array('$inc' => array('x' => 1)); + $options = array( + 'projection' => array('x' => 1, '_id' => 0), + 'sort' => array('x' => 1), + ); + + $document = $this->collection->findOneAndUpdate($filter, $update, $options); + $this->assertNull($document); + + $expected = array( + array('_id' => 1, 'x' => 11), + array('_id' => 2, 'x' => 22), + array('_id' => 3, 'x' => 33), + ); + + $this->assertSame($expected, $this->collection->find()->toArray()); + } + + public function testFindOneAndUpdateWithUpsertWhenNoDocumentsMatchReturningDocumentBeforeModification() + { + $filter = array('_id' => 4); + $update = array('$inc' => array('x' => 1)); + $options = array( + 'projection' => array('x' => 1, '_id' => 0), + 'sort' => array('x' => 1), + 'upsert' => true, + ); + + $document = $this->collection->findOneAndUpdate($filter, $update, $options); + $this->assertNull($document); + + $expected = array( + array('_id' => 1, 'x' => 11), + array('_id' => 2, 'x' => 22), + array('_id' => 3, 'x' => 33), + array('_id' => 4, 'x' => 1), + ); + + $this->assertSame($expected, $this->collection->find()->toArray()); + } + + public function testFindOneAndUpdateWhenNoDocumentsMatchReturningDocumentAfterModification() + { + $filter = array('_id' => 4); + $update = array('$inc' => array('x' => 1)); + $options = array( + 'projection' => array('x' => 1, '_id' => 0), + 'sort' => array('x' => 1), + 'returnDocument' => Collection::FIND_ONE_AND_RETURN_AFTER, + ); + + $document = $this->collection->findOneAndUpdate($filter, $update, $options); + $this->assertNull($document); + + $expected = array( + array('_id' => 1, 'x' => 11), + array('_id' => 2, 'x' => 22), + array('_id' => 3, 'x' => 33), + ); + + $this->assertSame($expected, $this->collection->find()->toArray()); + } + + public function testFindOneAndUpdateWithUpsertWhenNoDocumentsMatchReturningDocumentAfterModification() + { + $filter = array('_id' => 4); + $update = array('$inc' => array('x' => 1)); + $options = array( + 'projection' => array('x' => 1, '_id' => 0), + 'sort' => array('x' => 1), + 'returnDocument' => Collection::FIND_ONE_AND_RETURN_AFTER, + 'upsert' => true, + ); + + $document = $this->collection->findOneAndUpdate($filter, $update, $options); + $this->assertSame(array('x' => 1), $document); + + $expected = array( + array('_id' => 1, 'x' => 11), + array('_id' => 2, 'x' => 22), + array('_id' => 3, 'x' => 33), + array('_id' => 4, 'x' => 1), + ); + + $this->assertSame($expected, $this->collection->find()->toArray()); + } +} diff --git a/tests/Collection/CrudSpec/FunctionalTestCase.php b/tests/Collection/CrudSpec/FunctionalTestCase.php new file mode 100644 index 000000000..38693f1cd --- /dev/null +++ b/tests/Collection/CrudSpec/FunctionalTestCase.php @@ -0,0 +1,33 @@ +insert(array( + '_id' => $i, + 'x' => (integer) ($i . $i), + )); + } + + $result = $this->manager->executeBulkWrite($this->getNamespace(), $bulkWrite); + + $this->assertEquals($n, $result->getInsertedCount()); + } +} diff --git a/tests/Collection/CrudSpec/InsertManyFunctionalTest.php b/tests/Collection/CrudSpec/InsertManyFunctionalTest.php new file mode 100644 index 000000000..a5b550dc6 --- /dev/null +++ b/tests/Collection/CrudSpec/InsertManyFunctionalTest.php @@ -0,0 +1,38 @@ +createFixtures(1); + } + + public function testInsertManyWithNonexistentDocuments() + { + $documents = array( + array('_id' => 2, 'x' => 22), + array('_id' => 3, 'x' => 33), + ); + + $result = $this->collection->insertMany($documents); + $this->assertSame(2, $result->getInsertedCount()); + $this->assertSame(array(2, 3), $result->getInsertedIds()); + + $expected = array( + array('_id' => 1, 'x' => 11), + array('_id' => 2, 'x' => 22), + array('_id' => 3, 'x' => 33), + ); + + $this->assertSame($expected, $this->collection->find()->toArray()); + } +} diff --git a/tests/Collection/CrudSpec/InsertOneFunctionalTest.php b/tests/Collection/CrudSpec/InsertOneFunctionalTest.php new file mode 100644 index 000000000..9b198a371 --- /dev/null +++ b/tests/Collection/CrudSpec/InsertOneFunctionalTest.php @@ -0,0 +1,34 @@ +createFixtures(1); + } + + public function testInsertOneWithANonexistentDocument() + { + $document = array('_id' => 2, 'x' => 22); + + $result = $this->collection->insertOne($document); + $this->assertSame(1, $result->getInsertedCount()); + $this->assertSame(2, $result->getInsertedId()); + + $expected = array( + array('_id' => 1, 'x' => 11), + array('_id' => 2, 'x' => 22), + ); + + $this->assertSame($expected, $this->collection->find()->toArray()); + } +} diff --git a/tests/Collection/CrudSpec/ReplaceOneFunctionalTest.php b/tests/Collection/CrudSpec/ReplaceOneFunctionalTest.php new file mode 100644 index 000000000..40c8f1ef1 --- /dev/null +++ b/tests/Collection/CrudSpec/ReplaceOneFunctionalTest.php @@ -0,0 +1,119 @@ +createFixtures(3); + + $this->omitModifiedCount = version_compare($this->getServerVersion(), '2.6.0', '<'); + } + + public function testReplaceOneWhenManyDocumentsMatch() + { + $filter = array('_id' => array('$gt' => 1)); + $replacement = array('x' => 111); + + $result = $this->collection->replaceOne($filter, $replacement); + $this->assertSame(1, $result->getMatchedCount()); + $this->omitModifiedCount or $this->assertSame(1, $result->getModifiedCount()); + + $expected = array( + array('_id' => 1, 'x' => 11), + array('_id' => 2, 'x' => 111), + array('_id' => 3, 'x' => 33), + ); + + $this->assertSame($expected, $this->collection->find()->toArray()); + } + + public function testReplaceOneWhenOneDocumentMatches() + { + $filter = array('_id' => 1); + $replacement = array('x' => 111); + + $result = $this->collection->replaceOne($filter, $replacement); + $this->assertSame(1, $result->getMatchedCount()); + $this->omitModifiedCount or $this->assertSame(1, $result->getModifiedCount()); + + $expected = array( + array('_id' => 1, 'x' => 111), + array('_id' => 2, 'x' => 22), + array('_id' => 3, 'x' => 33), + ); + + $this->assertSame($expected, $this->collection->find()->toArray()); + } + + public function testReplaceOneWhenNoDocumentsMatch() + { + $filter = array('_id' => 4); + $replacement = array('x' => 111); + + $result = $this->collection->replaceOne($filter, $replacement); + $this->assertSame(0, $result->getMatchedCount()); + $this->omitModifiedCount or $this->assertSame(0, $result->getModifiedCount()); + + $expected = array( + array('_id' => 1, 'x' => 11), + array('_id' => 2, 'x' => 22), + array('_id' => 3, 'x' => 33), + ); + + $this->assertSame($expected, $this->collection->find()->toArray()); + } + + public function testReplaceOneWithUpsertWhenNoDocumentsMatchWithAnIdSpecified() + { + $filter = array('_id' => 4); + $replacement = array('_id' => 4, 'x' => 1); + $options = array('upsert' => true); + + $result = $this->collection->replaceOne($filter, $replacement, $options); + $this->assertSame(0, $result->getMatchedCount()); + $this->omitModifiedCount or $this->assertSame(0, $result->getModifiedCount()); + $this->assertSame(4, $result->getUpsertedId()); + + $expected = array( + array('_id' => 1, 'x' => 11), + array('_id' => 2, 'x' => 22), + array('_id' => 3, 'x' => 33), + array('_id' => 4, 'x' => 1), + ); + + $this->assertSame($expected, $this->collection->find()->toArray()); + } + + public function testReplaceOneWithUpsertWhenNoDocumentsMatchWithoutAnIdSpecified() + { + $filter = array('_id' => 4); + // Server 2.4 and earlier requires any custom ID to also be in the replacement document + $replacement = array('_id' => 4, 'x' => 1); + $options = array('upsert' => true); + + $result = $this->collection->replaceOne($filter, $replacement, $options); + $this->assertSame(0, $result->getMatchedCount()); + $this->omitModifiedCount or $this->assertSame(0, $result->getModifiedCount()); + $this->assertSame(4, $result->getUpsertedId()); + + $expected = array( + array('_id' => 1, 'x' => 11), + array('_id' => 2, 'x' => 22), + array('_id' => 3, 'x' => 33), + array('_id' => 4, 'x' => 1), + ); + + $this->assertSame($expected, $this->collection->find()->toArray()); + } +} diff --git a/tests/Collection/CrudSpec/UpdateManyFunctionalTest.php b/tests/Collection/CrudSpec/UpdateManyFunctionalTest.php new file mode 100644 index 000000000..71012e4b9 --- /dev/null +++ b/tests/Collection/CrudSpec/UpdateManyFunctionalTest.php @@ -0,0 +1,97 @@ +createFixtures(3); + + $this->omitModifiedCount = version_compare($this->getServerVersion(), '2.6.0', '<'); + } + + public function testUpdateManyWhenManyDocumentsMatch() + { + $filter = array('_id' => array('$gt' => 1)); + $update = array('$inc' => array('x' => 1)); + + $result = $this->collection->updateMany($filter, $update); + $this->assertSame(2, $result->getMatchedCount()); + $this->omitModifiedCount or $this->assertSame(2, $result->getModifiedCount()); + + $expected = array( + array('_id' => 1, 'x' => 11), + array('_id' => 2, 'x' => 23), + array('_id' => 3, 'x' => 34), + ); + + $this->assertSame($expected, $this->collection->find()->toArray()); + } + + public function testUpdateManyWhenOneDocumentMatches() + { + $filter = array('_id' => 1); + $update = array('$inc' => array('x' => 1)); + + $result = $this->collection->updateMany($filter, $update); + $this->assertSame(1, $result->getMatchedCount()); + $this->omitModifiedCount or $this->assertSame(1, $result->getModifiedCount()); + + $expected = array( + array('_id' => 1, 'x' => 12), + array('_id' => 2, 'x' => 22), + array('_id' => 3, 'x' => 33), + ); + + $this->assertSame($expected, $this->collection->find()->toArray()); + } + + public function testUpdateManyWhenNoDocumentsMatch() + { + $filter = array('_id' => 4); + $update = array('$inc' => array('x' => 1)); + + $result = $this->collection->updateMany($filter, $update); + $this->assertSame(0, $result->getMatchedCount()); + $this->omitModifiedCount or $this->assertSame(0, $result->getModifiedCount()); + + $expected = array( + array('_id' => 1, 'x' => 11), + array('_id' => 2, 'x' => 22), + array('_id' => 3, 'x' => 33), + ); + + $this->assertSame($expected, $this->collection->find()->toArray()); + } + + public function testUpdateManyWithUpsertWhenNoDocumentsMatch() + { + $filter = array('_id' => 4); + $update = array('$inc' => array('x' => 1)); + $options = array('upsert' => true); + + $result = $this->collection->updateMany($filter, $update, $options); + $this->assertSame(0, $result->getMatchedCount()); + $this->omitModifiedCount or $this->assertSame(0, $result->getModifiedCount()); + $this->assertSame(4, $result->getUpsertedId()); + + $expected = array( + array('_id' => 1, 'x' => 11), + array('_id' => 2, 'x' => 22), + array('_id' => 3, 'x' => 33), + array('_id' => 4, 'x' => 1), + ); + + $this->assertSame($expected, $this->collection->find()->toArray()); + } +} diff --git a/tests/Collection/CrudSpec/UpdateOneFunctionalTest.php b/tests/Collection/CrudSpec/UpdateOneFunctionalTest.php new file mode 100644 index 000000000..3d45ea84a --- /dev/null +++ b/tests/Collection/CrudSpec/UpdateOneFunctionalTest.php @@ -0,0 +1,97 @@ +createFixtures(3); + + $this->omitModifiedCount = version_compare($this->getServerVersion(), '2.6.0', '<'); + } + + public function testUpdateOneWhenManyDocumentsMatch() + { + $filter = array('_id' => array('$gt' => 1)); + $update = array('$inc' => array('x' => 1)); + + $result = $this->collection->updateOne($filter, $update); + $this->assertSame(1, $result->getMatchedCount()); + $this->omitModifiedCount or $this->assertSame(1, $result->getModifiedCount()); + + $expected = array( + array('_id' => 1, 'x' => 11), + array('_id' => 2, 'x' => 23), + array('_id' => 3, 'x' => 33), + ); + + $this->assertSame($expected, $this->collection->find()->toArray()); + } + + public function testUpdateOneWhenOneDocumentMatches() + { + $filter = array('_id' => 1); + $update = array('$inc' => array('x' => 1)); + + $result = $this->collection->updateOne($filter, $update); + $this->assertSame(1, $result->getMatchedCount()); + $this->omitModifiedCount or $this->assertSame(1, $result->getModifiedCount()); + + $expected = array( + array('_id' => 1, 'x' => 12), + array('_id' => 2, 'x' => 22), + array('_id' => 3, 'x' => 33), + ); + + $this->assertSame($expected, $this->collection->find()->toArray()); + } + + public function testUpdateOneWhenNoDocumentsMatch() + { + $filter = array('_id' => 4); + $update = array('$inc' => array('x' => 1)); + + $result = $this->collection->updateOne($filter, $update); + $this->assertSame(0, $result->getMatchedCount()); + $this->omitModifiedCount or $this->assertSame(0, $result->getModifiedCount()); + + $expected = array( + array('_id' => 1, 'x' => 11), + array('_id' => 2, 'x' => 22), + array('_id' => 3, 'x' => 33), + ); + + $this->assertSame($expected, $this->collection->find()->toArray()); + } + + public function testUpdateOneWithUpsertWhenNoDocumentsMatch() + { + $filter = array('_id' => 4); + $update = array('$inc' => array('x' => 1)); + $options = array('upsert' => true); + + $result = $this->collection->updateOne($filter, $update, $options); + $this->assertSame(0, $result->getMatchedCount()); + $this->omitModifiedCount or $this->assertSame(0, $result->getModifiedCount()); + $this->assertSame(4, $result->getUpsertedId()); + + $expected = array( + array('_id' => 1, 'x' => 11), + array('_id' => 2, 'x' => 22), + array('_id' => 3, 'x' => 33), + array('_id' => 4, 'x' => 1), + ); + + $this->assertSame($expected, $this->collection->find()->toArray()); + } +} diff --git a/tests/Collection/FunctionalTestCase.php b/tests/Collection/FunctionalTestCase.php new file mode 100644 index 000000000..54f18f2bc --- /dev/null +++ b/tests/Collection/FunctionalTestCase.php @@ -0,0 +1,47 @@ +collection = new Collection($this->manager, $this->getNamespace()); + $this->dropCollectionIfItExists($this->collection); + } + + public function tearDown() + { + if ($this->hasFailed()) { + return; + } + + $this->dropCollectionIfItExists($this->collection); + } + + /** + * Drop the collection if it exists. + * + * @param Collection $collection + */ + protected function dropCollectionIfItExists(Collection $collection) + { + $database = new Database($this->manager, $collection->getDatabaseName()); + $collections = $database->listCollections(array('filter' => array('name' => $collection->getCollectionName()))); + + if (iterator_count($collections) > 0) { + $this->assertCommandSucceeded($collection->drop()); + } + } +} diff --git a/tests/CollectionFunctionalTest.php b/tests/Collection/IndexManagementFunctionalTest.php similarity index 77% rename from tests/CollectionFunctionalTest.php rename to tests/Collection/IndexManagementFunctionalTest.php index 7c3bd0300..d91f62918 100644 --- a/tests/CollectionFunctionalTest.php +++ b/tests/Collection/IndexManagementFunctionalTest.php @@ -1,64 +1,17 @@ collection = new Collection($this->manager, $this->getNamespace()); - $this->collection->deleteMany(array()); - } - - public function testDrop() - { - $writeResult = $this->collection->insertOne(array('x' => 1)); - $this->assertEquals(1, $writeResult->getInsertedCount()); - - $commandResult = $this->collection->drop(); - $this->assertCommandSucceeded($commandResult); - $this->assertCollectionCount($this->getNamespace(), 0); - } - - function testInsertAndRetrieve() - { - $generator = new FixtureGenerator(); - - for ($i = 0; $i < 10; $i++) { - $user = $generator->createUser(); - $result = $this->collection->insertOne($user); - $this->assertInstanceOf('MongoDB\InsertOneResult', $result); - $this->assertInstanceOf('BSON\ObjectId', $result->getInsertedId()); - $this->assertEquals(24, strlen($result->getInsertedId())); - - $user["_id"] = $result->getInsertedId(); - $document = $this->collection->findOne(array("_id" => $result->getInsertedId())); - $this->assertEquals($document, $user, "The inserted and returned objects are the same"); - } - - $this->assertEquals(10, $i); - - $query = array("firstName" => "Ransom"); - $count = $this->collection->count($query); - $this->assertEquals(1, $count); - $cursor = $this->collection->find($query); - $this->assertInstanceOf('MongoDB\Driver\Cursor', $cursor); - - foreach($cursor as $n => $person) { - $this->assertInternalType("array", $person); - } - $this->assertEquals(0, $n); - } - public function testCreateIndex() { $that = $this; diff --git a/tests/DatabaseFunctionalTest.php b/tests/Database/CollectionManagementFunctionalTest.php similarity index 84% rename from tests/DatabaseFunctionalTest.php rename to tests/Database/CollectionManagementFunctionalTest.php index b49ccf6dc..a54d5cfd8 100644 --- a/tests/DatabaseFunctionalTest.php +++ b/tests/Database/CollectionManagementFunctionalTest.php @@ -1,27 +1,15 @@ database = new Database($this->manager, $this->getDatabaseName()); - $this->database->drop(); - } - public function testCreateCollection() { $that = $this; @@ -49,16 +37,6 @@ public function testCreateCollection() }); } - public function testDrop() - { - $writeResult = $this->manager->executeInsert($this->getNamespace(), array('x' => 1)); - $this->assertEquals(1, $writeResult->getInsertedCount()); - - $commandResult = $this->database->drop(); - $this->assertCommandSucceeded($commandResult); - $this->assertCollectionCount($this->getNamespace(), 0); - } - public function testDropCollection() { $writeResult = $this->manager->executeInsert($this->getNamespace(), array('x' => 1)); diff --git a/tests/Database/DatabaseFunctionalTest.php b/tests/Database/DatabaseFunctionalTest.php new file mode 100644 index 000000000..430866495 --- /dev/null +++ b/tests/Database/DatabaseFunctionalTest.php @@ -0,0 +1,19 @@ +manager->executeInsert($this->getNamespace(), array('x' => 1)); + $this->assertEquals(1, $writeResult->getInsertedCount()); + + $commandResult = $this->database->drop(); + $this->assertCommandSucceeded($commandResult); + $this->assertCollectionCount($this->getNamespace(), 0); + } +} diff --git a/tests/Database/FunctionalTestCase.php b/tests/Database/FunctionalTestCase.php new file mode 100644 index 000000000..2b84bbbd1 --- /dev/null +++ b/tests/Database/FunctionalTestCase.php @@ -0,0 +1,22 @@ +database = new Database($this->manager, $this->getDatabaseName()); + $this->database->drop(); + } +} diff --git a/tests/FunctionalTestCase.php b/tests/FunctionalTestCase.php index e64090f65..cdf522e5c 100644 --- a/tests/FunctionalTestCase.php +++ b/tests/FunctionalTestCase.php @@ -3,8 +3,9 @@ namespace MongoDB\Tests; use MongoDB\Driver\Command; -use MongoDB\Driver\Manager; use MongoDB\Driver\Cursor; +use MongoDB\Driver\Manager; +use MongoDB\Driver\ReadPreference; abstract class FunctionalTestCase extends TestCase { @@ -15,7 +16,7 @@ public function setUp() $this->manager = new Manager($this->getUri()); } - public function assertCollectionCount($namespace, $count) + protected function assertCollectionCount($namespace, $count) { list($databaseName, $collectionName) = explode('.', $namespace, 2); @@ -26,10 +27,23 @@ public function assertCollectionCount($namespace, $count) $this->assertEquals($count, $document['n']); } - public function assertCommandSucceeded(Cursor $cursor) + protected function assertCommandSucceeded(Cursor $cursor) { $document = current($cursor->toArray()); $this->assertArrayHasKey('ok', $document); $this->assertEquals(1, $document['ok']); } + + protected function getServerVersion(ReadPreference $readPreference = null) + { + $cursor = $this->manager->executeCommand( + $this->getDatabaseName(), + new Command(array('buildInfo' => 1)), + $readPreference ?: new ReadPreference(ReadPreference::RP_PRIMARY) + ); + + $document = current($cursor->toArray()); + + return $document['version']; + } } diff --git a/tests/TestCase.php b/tests/TestCase.php index 365405d17..6f916e0c1 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -11,11 +11,11 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase * * @return string */ - public function getCollectionName() + protected function getCollectionName() { $class = new ReflectionClass($this); - return sprintf('%s.%s', $class->getShortName(), $this->getName(false)); + return sprintf('%s.%s', $class->getShortName(), hash('crc32b', $this->getName())); } /** @@ -23,7 +23,7 @@ public function getCollectionName() * * @return string */ - public function getDatabaseName() + protected function getDatabaseName() { return getenv('MONGODB_DATABASE') ?: 'phplib_test'; } @@ -33,7 +33,7 @@ public function getDatabaseName() * * @return string */ - public function getNamespace() + protected function getNamespace() { return sprintf('%s.%s', $this->getDatabaseName(), $this->getCollectionName()); } @@ -43,7 +43,7 @@ public function getNamespace() * * @return string */ - public function getUri() + protected function getUri() { return getenv('MONGODB_URI') ?: 'mongodb://127.0.0.1:27017'; }