From c520df8acdf448e62794c77873cadb6c66702818 Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Mon, 27 Apr 2015 17:37:49 -0400 Subject: [PATCH 01/19] Split Database and Collection functional tests --- tests/Collection/CollectionFunctionalTest.php | 51 ++++++++++++++++ tests/Collection/FunctionalTestCase.php | 38 ++++++++++++ .../IndexManagementFunctionalTest.php} | 61 +++---------------- .../CollectionManagementFunctionalTest.php} | 28 +-------- tests/Database/DatabaseFunctionalTest.php | 19 ++++++ tests/Database/FunctionalTestCase.php | 22 +++++++ tests/FunctionalTestCase.php | 4 +- tests/TestCase.php | 8 +-- 8 files changed, 146 insertions(+), 85 deletions(-) create mode 100644 tests/Collection/CollectionFunctionalTest.php create mode 100644 tests/Collection/FunctionalTestCase.php rename tests/{CollectionFunctionalTest.php => Collection/IndexManagementFunctionalTest.php} (77%) rename tests/{DatabaseFunctionalTest.php => Database/CollectionManagementFunctionalTest.php} (84%) create mode 100644 tests/Database/DatabaseFunctionalTest.php create mode 100644 tests/Database/FunctionalTestCase.php 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/FunctionalTestCase.php b/tests/Collection/FunctionalTestCase.php new file mode 100644 index 000000000..0c9e8a292 --- /dev/null +++ b/tests/Collection/FunctionalTestCase.php @@ -0,0 +1,38 @@ +collection = new Collection($this->manager, $this->getNamespace()); + $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..98a717db6 100644 --- a/tests/FunctionalTestCase.php +++ b/tests/FunctionalTestCase.php @@ -15,7 +15,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,7 +26,7 @@ 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); diff --git a/tests/TestCase.php b/tests/TestCase.php index 365405d17..d679d40b2 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -11,7 +11,7 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase * * @return string */ - public function getCollectionName() + protected function getCollectionName() { $class = new ReflectionClass($this); @@ -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'; } From c991d3a32bf618d9866a777593111afa8b509581 Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Tue, 28 Apr 2015 17:19:21 -0400 Subject: [PATCH 02/19] PHPLIB-91: Ensure count/distinct filters serialize as BSON objects --- src/Collection.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Collection.php b/src/Collection.php index 2229c8d19..731e90aca 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -239,7 +239,7 @@ 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()); @@ -363,7 +363,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()); From 87cd989906fdc1bd3cac17845a3fff6260bc6429 Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Tue, 17 Mar 2015 19:44:02 -0400 Subject: [PATCH 03/19] PHPLIB-58: Functional tests for CRUD spec read methods --- .../CrudSpec/AggregateFunctionalTest.php | 59 +++++++++++++++++ .../CrudSpec/CountFunctionalTest.php | 38 +++++++++++ .../CrudSpec/DistinctFunctionalTest.php | 30 +++++++++ .../CrudSpec/FindFunctionalTest.php | 64 +++++++++++++++++++ .../CrudSpec/FunctionalTestCase.php | 33 ++++++++++ 5 files changed, 224 insertions(+) create mode 100644 tests/Collection/CrudSpec/AggregateFunctionalTest.php create mode 100644 tests/Collection/CrudSpec/CountFunctionalTest.php create mode 100644 tests/Collection/CrudSpec/DistinctFunctionalTest.php create mode 100644 tests/Collection/CrudSpec/FindFunctionalTest.php create mode 100644 tests/Collection/CrudSpec/FunctionalTestCase.php diff --git a/tests/Collection/CrudSpec/AggregateFunctionalTest.php b/tests/Collection/CrudSpec/AggregateFunctionalTest.php new file mode 100644 index 000000000..9aa4ae7da --- /dev/null +++ b/tests/Collection/CrudSpec/AggregateFunctionalTest.php @@ -0,0 +1,59 @@ +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), + ); + + $this->assertSame($expected, $cursor->toArray()); + } + + public function testAggregateWithOut() + { + $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()); + } +} 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/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/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()); + } +} From 619c27d8a90cfe8583e3d0a32bd4757899172ba5 Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Wed, 29 Apr 2015 14:48:51 -0400 Subject: [PATCH 04/19] PHPLIB-92: Update methods should use "multi" option --- src/Collection.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Collection.php b/src/Collection.php index 731e90aca..3d3254e70 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -1038,7 +1038,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 +1057,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 +1080,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); } From 719fd253dd8b44188881a2858a8b4210b13ae890 Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Wed, 29 Apr 2015 15:34:01 -0400 Subject: [PATCH 05/19] PHPLIB-93: Insert result classes should always track IDs --- src/Collection.php | 13 +++++++++---- src/InsertManyResult.php | 9 +++++---- src/InsertOneResult.php | 12 ++++++------ 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/Collection.php b/src/Collection.php index 3d3254e70..e007ce330 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -961,7 +961,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 +976,8 @@ public function insertMany(array $documents) if ($insertedId !== null) { $insertedIds[$i] = $insertedId; + } else { + $insertedIds[$i] = is_array($document) ? $document['_id'] : $document->_id; } } @@ -989,11 +991,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 +1002,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); } 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() { From 30444c7a6fe6aab33d8cc4f5b298e84e8ae41305 Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Wed, 29 Apr 2015 17:06:59 -0400 Subject: [PATCH 06/19] Restore Collection::_massageFindAndModifyOptions() This was inadvertently removed in 495e46d4ddb3980126635f2397e4301d94f6c262. --- src/Collection.php | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/Collection.php b/src/Collection.php index e007ce330..b283f1714 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -1159,6 +1159,26 @@ protected function _massageAggregateOptions($options) 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; + } + /** * Constructs the Query Wire Protocol field 'flags' based on $options * provided to other helpers From fd3f61738a2eadbf5cd3e7761a922850d3435f46 Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Wed, 29 Apr 2015 17:09:38 -0400 Subject: [PATCH 07/19] Return findAndModify result document as an array --- src/Collection.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Collection.php b/src/Collection.php index b283f1714..79f9b7785 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -497,7 +497,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 +534,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 is_object($doc["value"]) ? (array) $doc["value"] : $doc["value"]; } throw $this->_generateCommandException($doc); @@ -572,7 +572,7 @@ public function findOneAndUpdate(array $filter, array $update, array $options = $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); From 5c64633333864484df535029e5eb27533af60c12 Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Wed, 29 Apr 2015 17:11:51 -0400 Subject: [PATCH 08/19] Clean up after passing Collection functional tests --- tests/Collection/CrudSpec/AggregateFunctionalTest.php | 3 +++ tests/Collection/FunctionalTestCase.php | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/tests/Collection/CrudSpec/AggregateFunctionalTest.php b/tests/Collection/CrudSpec/AggregateFunctionalTest.php index 9aa4ae7da..970b09a66 100644 --- a/tests/Collection/CrudSpec/AggregateFunctionalTest.php +++ b/tests/Collection/CrudSpec/AggregateFunctionalTest.php @@ -55,5 +55,8 @@ public function testAggregateWithOut() ); $this->assertSame($expected, $outputCollection->find()->toArray()); + + // Manually clean up our output collection + $this->dropCollectionIfItExists($outputCollection); } } diff --git a/tests/Collection/FunctionalTestCase.php b/tests/Collection/FunctionalTestCase.php index 0c9e8a292..54f18f2bc 100644 --- a/tests/Collection/FunctionalTestCase.php +++ b/tests/Collection/FunctionalTestCase.php @@ -21,6 +21,15 @@ public function setUp() $this->dropCollectionIfItExists($this->collection); } + public function tearDown() + { + if ($this->hasFailed()) { + return; + } + + $this->dropCollectionIfItExists($this->collection); + } + /** * Drop the collection if it exists. * From 89391d2559320afd8eb6286eeea5d7dabb87ee41 Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Wed, 29 Apr 2015 17:13:45 -0400 Subject: [PATCH 09/19] Hash test case names to avoid hitting namespace limits --- tests/TestCase.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/TestCase.php b/tests/TestCase.php index d679d40b2..6f916e0c1 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -15,7 +15,7 @@ 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())); } /** From 362a27ffd3cb17f13c956e5d6f05ad0455db527b Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Wed, 29 Apr 2015 17:16:06 -0400 Subject: [PATCH 10/19] PHPLIB-58: Functional tests for CRUD spec write methods --- .../CrudSpec/DeleteManyFunctionalTest.php | 48 +++++ .../CrudSpec/DeleteOneFunctionalTest.php | 64 ++++++ .../FindOneAndDeleteFunctionalTest.php | 76 +++++++ .../FindOneAndReplaceFunctionalTest.php | 196 ++++++++++++++++++ .../FindOneAndUpdateFunctionalTest.php | 196 ++++++++++++++++++ .../CrudSpec/InsertManyFunctionalTest.php | 38 ++++ .../CrudSpec/InsertOneFunctionalTest.php | 34 +++ .../CrudSpec/ReplaceOneFunctionalTest.php | 114 ++++++++++ .../CrudSpec/UpdateManyFunctionalTest.php | 93 +++++++++ .../CrudSpec/UpdateOneFunctionalTest.php | 93 +++++++++ 10 files changed, 952 insertions(+) create mode 100644 tests/Collection/CrudSpec/DeleteManyFunctionalTest.php create mode 100644 tests/Collection/CrudSpec/DeleteOneFunctionalTest.php create mode 100644 tests/Collection/CrudSpec/FindOneAndDeleteFunctionalTest.php create mode 100644 tests/Collection/CrudSpec/FindOneAndReplaceFunctionalTest.php create mode 100644 tests/Collection/CrudSpec/FindOneAndUpdateFunctionalTest.php create mode 100644 tests/Collection/CrudSpec/InsertManyFunctionalTest.php create mode 100644 tests/Collection/CrudSpec/InsertOneFunctionalTest.php create mode 100644 tests/Collection/CrudSpec/ReplaceOneFunctionalTest.php create mode 100644 tests/Collection/CrudSpec/UpdateManyFunctionalTest.php create mode 100644 tests/Collection/CrudSpec/UpdateOneFunctionalTest.php 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/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..6ecd70389 --- /dev/null +++ b/tests/Collection/CrudSpec/FindOneAndReplaceFunctionalTest.php @@ -0,0 +1,196 @@ +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); + $replacement = array('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); + $replacement = array('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/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..ef5432757 --- /dev/null +++ b/tests/Collection/CrudSpec/ReplaceOneFunctionalTest.php @@ -0,0 +1,114 @@ +createFixtures(3); + } + + 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->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->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->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->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); + $replacement = array('x' => 1); + $options = array('upsert' => true); + + $result = $this->collection->replaceOne($filter, $replacement, $options); + $this->assertSame(0, $result->getMatchedCount()); + $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..21b227f6d --- /dev/null +++ b/tests/Collection/CrudSpec/UpdateManyFunctionalTest.php @@ -0,0 +1,93 @@ +createFixtures(3); + } + + 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->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->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->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->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..f7242b00b --- /dev/null +++ b/tests/Collection/CrudSpec/UpdateOneFunctionalTest.php @@ -0,0 +1,93 @@ +createFixtures(3); + } + + 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->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->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->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->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()); + } +} From d41c3f2611f9489ea2661982a8f5b9a2011a562c Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Wed, 29 Apr 2015 21:41:31 -0400 Subject: [PATCH 11/19] PHPLIB-96: Fix replacement/upsert test failures for 2.4 --- .../Collection/CrudSpec/FindOneAndReplaceFunctionalTest.php | 6 ++++-- tests/Collection/CrudSpec/ReplaceOneFunctionalTest.php | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/Collection/CrudSpec/FindOneAndReplaceFunctionalTest.php b/tests/Collection/CrudSpec/FindOneAndReplaceFunctionalTest.php index 6ecd70389..26c5cf12c 100644 --- a/tests/Collection/CrudSpec/FindOneAndReplaceFunctionalTest.php +++ b/tests/Collection/CrudSpec/FindOneAndReplaceFunctionalTest.php @@ -128,7 +128,8 @@ public function testFindOneAndReplaceWhenNoDocumentsMatchReturningDocumentBefore public function testFindOneAndReplaceWithUpsertWhenNoDocumentsMatchReturningDocumentBeforeModification() { $filter = array('_id' => 4); - $replacement = array('x' => 44); + // 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), @@ -173,7 +174,8 @@ public function testFindOneAndReplaceWhenNoDocumentsMatchReturningDocumentAfterM public function testFindOneAndReplaceWithUpsertWhenNoDocumentsMatchReturningDocumentAfterModification() { $filter = array('_id' => 4); - $replacement = array('x' => 44); + // 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), diff --git a/tests/Collection/CrudSpec/ReplaceOneFunctionalTest.php b/tests/Collection/CrudSpec/ReplaceOneFunctionalTest.php index ef5432757..0b0376c8b 100644 --- a/tests/Collection/CrudSpec/ReplaceOneFunctionalTest.php +++ b/tests/Collection/CrudSpec/ReplaceOneFunctionalTest.php @@ -94,7 +94,8 @@ public function testReplaceOneWithUpsertWhenNoDocumentsMatchWithAnIdSpecified() public function testReplaceOneWithUpsertWhenNoDocumentsMatchWithoutAnIdSpecified() { $filter = array('_id' => 4); - $replacement = array('x' => 1); + // 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); From fed26a906326ff30135e3083d356d9f16dc62e83 Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Wed, 29 Apr 2015 21:43:50 -0400 Subject: [PATCH 12/19] PHPLIB-97: Cast count() results to integers Older servers may return floats. --- src/Collection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Collection.php b/src/Collection.php index 79f9b7785..da13b12c6 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -244,7 +244,7 @@ public function count(array $filter = array(), array $options = array()) $doc = current($this->_runCommand($this->dbname, $cmd)->toArray()); if ($doc["ok"]) { - return $doc["n"]; + return (integer) $doc["n"]; } throw $this->_generateCommandException($doc); } From 786025d7c99431979caecd917f3443cd2b1a3de4 Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Wed, 29 Apr 2015 21:45:27 -0400 Subject: [PATCH 13/19] PHPLIB-95: Massage findAndModify null results before 3.0 Earlier server versions incorrectly return an empty document instead of null. Thankfully, we can detect this edge case given the command result and input options. --- src/Collection.php | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/src/Collection.php b/src/Collection.php index da13b12c6..13249ac9b 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -534,7 +534,7 @@ public function findOneAndReplace(array $filter, array $replacement, array $opti $doc = current($this->_runCommand($this->dbname, $cmd)->toArray()); if ($doc["ok"]) { - return is_object($doc["value"]) ? (array) $doc["value"] : $doc["value"]; + return $this->_massageFindAndModifyResult($doc, $options); } throw $this->_generateCommandException($doc); @@ -572,7 +572,7 @@ public function findOneAndUpdate(array $filter, array $update, array $options = $doc = current($this->_runCommand($this->dbname, $cmd)->toArray()); if ($doc["ok"]) { - return is_object($doc["value"]) ? (array) $doc["value"] : $doc["value"]; + return $this->_massageFindAndModifyResult($doc, $options); } throw $this->_generateCommandException($doc); @@ -1179,6 +1179,36 @@ final protected function _massageFindAndModifyOptions($options, $update = array( 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 From 3e80df362f549eaa6589f5664b7583563d706612 Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Wed, 29 Apr 2015 19:30:22 -0400 Subject: [PATCH 14/19] PHPLIB-98: Add multiple server versions to Travis CI --- .travis.yml | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 59b908f5e..32f705bc7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,12 +7,27 @@ 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} + - mongod --version + - pecl install -f mongodb-${DRIVER_VERSION} - php --ri mongodb - composer install --dev --no-interaction --prefer-source From 0241edaff4fda441ece117de10a5f17da4578de2 Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Wed, 29 Apr 2015 22:36:42 -0400 Subject: [PATCH 15/19] PHPLIB-86: Fix aggregate() useCursor default and 2.4 compatibility This adds intelligent detection for the useCursor option's default value. Additionally, server 2.4 does not support any of the aggregate command options of newer servers, so we must not specify default values. The array_map() conversion for inline aggregation results is not ideal, but we can revisit that in PHPLIB-100. --- src/Collection.php | 121 ++++++++---------- src/FeatureDetection.php | 1 + .../CrudSpec/AggregateFunctionalTest.php | 3 +- 3 files changed, 59 insertions(+), 66 deletions(-) diff --git a/src/Collection.php b/src/Collection.php index 13249ac9b..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); @@ -578,55 +616,6 @@ public function findOneAndUpdate(array $filter, array $update, array $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. * @@ -1151,8 +1140,10 @@ 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"]); 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/tests/Collection/CrudSpec/AggregateFunctionalTest.php b/tests/Collection/CrudSpec/AggregateFunctionalTest.php index 970b09a66..8bee4313c 100644 --- a/tests/Collection/CrudSpec/AggregateFunctionalTest.php +++ b/tests/Collection/CrudSpec/AggregateFunctionalTest.php @@ -33,7 +33,8 @@ public function testAggregateWithMultipleStages() array('_id' => 3, 'x' => 33), ); - $this->assertSame($expected, $cursor->toArray()); + // Use iterator_to_array() here since aggregate() may return an ArrayIterator + $this->assertSame($expected, iterator_to_array($cursor)); } public function testAggregateWithOut() From 4e22eb2ffe81a94205f98822634359bdc63820de Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Wed, 29 Apr 2015 22:31:50 -0400 Subject: [PATCH 16/19] Skip $out aggregation test for MongoDB 2.4 --- tests/Collection/CrudSpec/AggregateFunctionalTest.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/Collection/CrudSpec/AggregateFunctionalTest.php b/tests/Collection/CrudSpec/AggregateFunctionalTest.php index 8bee4313c..bdacc989b 100644 --- a/tests/Collection/CrudSpec/AggregateFunctionalTest.php +++ b/tests/Collection/CrudSpec/AggregateFunctionalTest.php @@ -3,6 +3,8 @@ namespace MongoDB\Tests\Collection\CrudSpec; use MongoDB\Collection; +use MongoDB\FeatureDetection; +use MongoDB\Driver\ReadPreference; /** * CRUD spec functional tests for aggregate(). @@ -39,6 +41,12 @@ public function testAggregateWithMultipleStages() 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); From 5076ff1635893fa278a0c387f25f53e12688ce23 Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Thu, 30 Apr 2015 11:51:36 -0400 Subject: [PATCH 17/19] Do not check modifiedCount for updates on 2.4 nModified is not provided for legacy write ops, so we should not check for it. Once PHPC-278 is implemented, we can change these assertions to check for null exactly. --- .../CrudSpec/ReplaceOneFunctionalTest.php | 14 +++++++++----- .../CrudSpec/UpdateManyFunctionalTest.php | 12 ++++++++---- .../CrudSpec/UpdateOneFunctionalTest.php | 12 ++++++++---- tests/FunctionalTestCase.php | 16 +++++++++++++++- 4 files changed, 40 insertions(+), 14 deletions(-) diff --git a/tests/Collection/CrudSpec/ReplaceOneFunctionalTest.php b/tests/Collection/CrudSpec/ReplaceOneFunctionalTest.php index 0b0376c8b..40c8f1ef1 100644 --- a/tests/Collection/CrudSpec/ReplaceOneFunctionalTest.php +++ b/tests/Collection/CrudSpec/ReplaceOneFunctionalTest.php @@ -9,11 +9,15 @@ */ class ReplaceOneFunctionalTest extends FunctionalTestCase { + private $omitModifiedCount; + public function setUp() { parent::setUp(); $this->createFixtures(3); + + $this->omitModifiedCount = version_compare($this->getServerVersion(), '2.6.0', '<'); } public function testReplaceOneWhenManyDocumentsMatch() @@ -23,7 +27,7 @@ public function testReplaceOneWhenManyDocumentsMatch() $result = $this->collection->replaceOne($filter, $replacement); $this->assertSame(1, $result->getMatchedCount()); - $this->assertSame(1, $result->getModifiedCount()); + $this->omitModifiedCount or $this->assertSame(1, $result->getModifiedCount()); $expected = array( array('_id' => 1, 'x' => 11), @@ -41,7 +45,7 @@ public function testReplaceOneWhenOneDocumentMatches() $result = $this->collection->replaceOne($filter, $replacement); $this->assertSame(1, $result->getMatchedCount()); - $this->assertSame(1, $result->getModifiedCount()); + $this->omitModifiedCount or $this->assertSame(1, $result->getModifiedCount()); $expected = array( array('_id' => 1, 'x' => 111), @@ -59,7 +63,7 @@ public function testReplaceOneWhenNoDocumentsMatch() $result = $this->collection->replaceOne($filter, $replacement); $this->assertSame(0, $result->getMatchedCount()); - $this->assertSame(0, $result->getModifiedCount()); + $this->omitModifiedCount or $this->assertSame(0, $result->getModifiedCount()); $expected = array( array('_id' => 1, 'x' => 11), @@ -78,7 +82,7 @@ public function testReplaceOneWithUpsertWhenNoDocumentsMatchWithAnIdSpecified() $result = $this->collection->replaceOne($filter, $replacement, $options); $this->assertSame(0, $result->getMatchedCount()); - $this->assertSame(0, $result->getModifiedCount()); + $this->omitModifiedCount or $this->assertSame(0, $result->getModifiedCount()); $this->assertSame(4, $result->getUpsertedId()); $expected = array( @@ -100,7 +104,7 @@ public function testReplaceOneWithUpsertWhenNoDocumentsMatchWithoutAnIdSpecified $result = $this->collection->replaceOne($filter, $replacement, $options); $this->assertSame(0, $result->getMatchedCount()); - $this->assertSame(0, $result->getModifiedCount()); + $this->omitModifiedCount or $this->assertSame(0, $result->getModifiedCount()); $this->assertSame(4, $result->getUpsertedId()); $expected = array( diff --git a/tests/Collection/CrudSpec/UpdateManyFunctionalTest.php b/tests/Collection/CrudSpec/UpdateManyFunctionalTest.php index 21b227f6d..71012e4b9 100644 --- a/tests/Collection/CrudSpec/UpdateManyFunctionalTest.php +++ b/tests/Collection/CrudSpec/UpdateManyFunctionalTest.php @@ -9,11 +9,15 @@ */ class UpdateManyFunctionalTest extends FunctionalTestCase { + private $omitModifiedCount; + public function setUp() { parent::setUp(); $this->createFixtures(3); + + $this->omitModifiedCount = version_compare($this->getServerVersion(), '2.6.0', '<'); } public function testUpdateManyWhenManyDocumentsMatch() @@ -23,7 +27,7 @@ public function testUpdateManyWhenManyDocumentsMatch() $result = $this->collection->updateMany($filter, $update); $this->assertSame(2, $result->getMatchedCount()); - $this->assertSame(2, $result->getModifiedCount()); + $this->omitModifiedCount or $this->assertSame(2, $result->getModifiedCount()); $expected = array( array('_id' => 1, 'x' => 11), @@ -41,7 +45,7 @@ public function testUpdateManyWhenOneDocumentMatches() $result = $this->collection->updateMany($filter, $update); $this->assertSame(1, $result->getMatchedCount()); - $this->assertSame(1, $result->getModifiedCount()); + $this->omitModifiedCount or $this->assertSame(1, $result->getModifiedCount()); $expected = array( array('_id' => 1, 'x' => 12), @@ -59,7 +63,7 @@ public function testUpdateManyWhenNoDocumentsMatch() $result = $this->collection->updateMany($filter, $update); $this->assertSame(0, $result->getMatchedCount()); - $this->assertSame(0, $result->getModifiedCount()); + $this->omitModifiedCount or $this->assertSame(0, $result->getModifiedCount()); $expected = array( array('_id' => 1, 'x' => 11), @@ -78,7 +82,7 @@ public function testUpdateManyWithUpsertWhenNoDocumentsMatch() $result = $this->collection->updateMany($filter, $update, $options); $this->assertSame(0, $result->getMatchedCount()); - $this->assertSame(0, $result->getModifiedCount()); + $this->omitModifiedCount or $this->assertSame(0, $result->getModifiedCount()); $this->assertSame(4, $result->getUpsertedId()); $expected = array( diff --git a/tests/Collection/CrudSpec/UpdateOneFunctionalTest.php b/tests/Collection/CrudSpec/UpdateOneFunctionalTest.php index f7242b00b..3d45ea84a 100644 --- a/tests/Collection/CrudSpec/UpdateOneFunctionalTest.php +++ b/tests/Collection/CrudSpec/UpdateOneFunctionalTest.php @@ -9,11 +9,15 @@ */ class UpdateOneFunctionalTest extends FunctionalTestCase { + private $omitModifiedCount; + public function setUp() { parent::setUp(); $this->createFixtures(3); + + $this->omitModifiedCount = version_compare($this->getServerVersion(), '2.6.0', '<'); } public function testUpdateOneWhenManyDocumentsMatch() @@ -23,7 +27,7 @@ public function testUpdateOneWhenManyDocumentsMatch() $result = $this->collection->updateOne($filter, $update); $this->assertSame(1, $result->getMatchedCount()); - $this->assertSame(1, $result->getModifiedCount()); + $this->omitModifiedCount or $this->assertSame(1, $result->getModifiedCount()); $expected = array( array('_id' => 1, 'x' => 11), @@ -41,7 +45,7 @@ public function testUpdateOneWhenOneDocumentMatches() $result = $this->collection->updateOne($filter, $update); $this->assertSame(1, $result->getMatchedCount()); - $this->assertSame(1, $result->getModifiedCount()); + $this->omitModifiedCount or $this->assertSame(1, $result->getModifiedCount()); $expected = array( array('_id' => 1, 'x' => 12), @@ -59,7 +63,7 @@ public function testUpdateOneWhenNoDocumentsMatch() $result = $this->collection->updateOne($filter, $update); $this->assertSame(0, $result->getMatchedCount()); - $this->assertSame(0, $result->getModifiedCount()); + $this->omitModifiedCount or $this->assertSame(0, $result->getModifiedCount()); $expected = array( array('_id' => 1, 'x' => 11), @@ -78,7 +82,7 @@ public function testUpdateOneWithUpsertWhenNoDocumentsMatch() $result = $this->collection->updateOne($filter, $update, $options); $this->assertSame(0, $result->getMatchedCount()); - $this->assertSame(0, $result->getModifiedCount()); + $this->omitModifiedCount or $this->assertSame(0, $result->getModifiedCount()); $this->assertSame(4, $result->getUpsertedId()); $expected = array( diff --git a/tests/FunctionalTestCase.php b/tests/FunctionalTestCase.php index 98a717db6..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 { @@ -32,4 +33,17 @@ protected function assertCommandSucceeded(Cursor $cursor) $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']; + } } From 9d218a73837e574df36247d72b0b5d647397870f Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Thu, 30 Apr 2015 13:04:11 -0400 Subject: [PATCH 18/19] PHPLIB-98: Ensure mongod service is started for all versions We need to account for differing service names between 2.4 and 2.6+. --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 32f705bc7..fa771335f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,6 +27,8 @@ install: - sudo apt-get install ${SERVER_PACKAGE} before_script: + - 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 From 8e89680d9b322fc9978f481d204fc9d9675c85b9 Mon Sep 17 00:00:00 2001 From: Jeremy Mikola Date: Thu, 30 Apr 2015 14:02:48 -0400 Subject: [PATCH 19/19] Reduce default server selection timeout for tests This should avoid hanging test builds if mongod fails to start. --- phpunit.xml.dist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 @@ - +