Commit f24bfcd7 authored by Jeremy Mikola's avatar Jeremy Mikola

Merge pull request #582

parents 21d35954 2e4f0142
......@@ -2,7 +2,11 @@
namespace MongoDB\Tests\Collection;
use MongoDB\BulkWriteResult;
use MongoDB\Collection;
use MongoDB\InsertManyResult;
use MongoDB\Driver\Exception\BulkWriteException;
use MongoDB\Driver\Exception\RuntimeException;
use MongoDB\Operation\FindOneAndReplace;
use IteratorIterator;
use LogicException;
......@@ -39,8 +43,16 @@ class CrudSpecFunctionalTest extends FunctionalTestCase
$expectedData = isset($test['outcome']['collection']['data']) ? $test['outcome']['collection']['data'] : null;
$this->initializeData($initialData, $expectedData);
$result = null;
$exception = null;
try {
$result = $this->executeOperation($test['operation']);
$this->executeOutcome($test['operation'], $test['outcome'], $result);
} catch (RuntimeException $e) {
$exception = $e;
}
$this->executeOutcome($test['operation'], $test['outcome'], $result, $exception);
}
public function provideSpecificationTests()
......@@ -116,10 +128,9 @@ class CrudSpecFunctionalTest extends FunctionalTestCase
);
case 'bulkWrite':
$results = $this->prepareBulkWriteArguments($operation['arguments']);
return $this->collection->bulkWrite(
array_diff_key($results, ['options' => 1]),
$results['options']
array_map([$this, 'prepareBulkWriteRequest'], $operation['arguments']['requests']),
isset($operation['arguments']['options']) ? $operation['arguments']['options'] : []
);
case 'count':
......@@ -174,7 +185,7 @@ class CrudSpecFunctionalTest extends FunctionalTestCase
case 'insertMany':
return $this->collection->insertMany(
$operation['arguments']['documents'],
array_diff_key($operation['arguments'], ['documents' => 1])
isset($operation['arguments']['options']) ? $operation['arguments']['options'] : []
);
case 'insertOne':
......@@ -193,14 +204,24 @@ class CrudSpecFunctionalTest extends FunctionalTestCase
*
* @param array $operation
* @param array $outcome
* @param mixed $actualResult
* @param mixed $result
* @param RuntimeException $exception
* @return mixed
* @throws LogicException if the operation is unsupported
*/
private function executeOutcome(array $operation, array $outcome, $actualResult)
private function executeOutcome(array $operation, array $outcome, $result, RuntimeException $exception = null)
{
$expectedError = array_key_exists('error', $outcome) ? $outcome['error'] : false;
if ($expectedError) {
$this->assertNull($result);
$this->assertNotNull($exception);
$result = $this->extractResultFromException($operation, $outcome, $exception);
}
if (array_key_exists('result', $outcome)) {
$this->executeAssertResult($operation, $outcome['result'], $actualResult);
$this->executeAssertResult($operation, $outcome['result'], $result);
}
if (isset($outcome['collection'])) {
......@@ -212,6 +233,42 @@ class CrudSpecFunctionalTest extends FunctionalTestCase
}
}
/**
* Extracts a result from an exception.
*
* Errors for bulkWrite and insertMany operations may still report a write
* result. This method will attempt to extract such a result so that it can
* be used in executeAssertResult().
*
* If no result can be extracted, null will be returned.
*
* @param array $operation
* @param RuntimeException $exception
* @return mixed
*/
private function extractResultFromException(array $operation, array $outcome, RuntimeException $exception)
{
switch ($operation['name']) {
case 'bulkWrite':
$insertedIds = isset($outcome['result']['insertedIds']) ? $outcome['result']['insertedIds'] : [];
if ($exception instanceof BulkWriteException) {
return new BulkWriteResult($exception->getWriteResult(), $insertedIds);
}
break;
case 'insertMany':
$insertedIds = isset($outcome['result']['insertedIds']) ? $outcome['result']['insertedIds'] : [];
if ($exception instanceof BulkWriteException) {
return new InsertManyResult($exception->getWriteResult(), $insertedIds);
}
break;
}
return null;
}
/**
* Executes the "result" section of an "outcome" block.
*
......@@ -236,6 +293,9 @@ class CrudSpecFunctionalTest extends FunctionalTestCase
break;
case 'bulkWrite':
$this->assertInternalType('array', $expectedResult);
$this->assertInstanceOf('MongoDB\BulkWriteResult', $actualResult);
if (isset($expectedResult['deletedCount'])) {
$this->assertSame($expectedResult['deletedCount'], $actualResult->getDeletedCount());
}
......@@ -290,6 +350,7 @@ class CrudSpecFunctionalTest extends FunctionalTestCase
case 'deleteMany':
case 'deleteOne':
$this->assertInternalType('array', $expectedResult);
$this->assertInstanceOf('MongoDB\DeleteResult', $actualResult);
if (isset($expectedResult['deletedCount'])) {
......@@ -307,6 +368,7 @@ class CrudSpecFunctionalTest extends FunctionalTestCase
break;
case 'insertMany':
$this->assertInternalType('array', $expectedResult);
$this->assertInstanceOf('MongoDB\InsertManyResult', $actualResult);
if (isset($expectedResult['insertedCount'])) {
......@@ -322,6 +384,7 @@ class CrudSpecFunctionalTest extends FunctionalTestCase
break;
case 'insertOne':
$this->assertInternalType('array', $expectedResult);
$this->assertInstanceOf('MongoDB\InsertOneResult', $actualResult);
if (isset($expectedResult['insertedCount'])) {
......@@ -339,6 +402,7 @@ class CrudSpecFunctionalTest extends FunctionalTestCase
case 'replaceOne':
case 'updateMany':
case 'updateOne':
$this->assertInternalType('array', $expectedResult);
$this->assertInstanceOf('MongoDB\UpdateResult', $actualResult);
if (isset($expectedResult['matchedCount'])) {
......@@ -383,36 +447,43 @@ class CrudSpecFunctionalTest extends FunctionalTestCase
}
}
private function prepareBulkWriteArguments($arguments)
/**
* Prepares a request element for a bulkWrite operation.
*
* @param array $request
* @return array
*/
private function prepareBulkWriteRequest(array $request)
{
$operations = [];
$operations['options'] = $arguments['options'];
foreach ($arguments['requests'] as $request) {
$innerArray = [];
switch ($request['name']) {
case 'deleteMany':
case 'deleteOne':
$options = array_diff_key($request['arguments'], ['filter' => 1]);
$innerArray = [$request['arguments']['filter'], $options];
break;
return [ $request['name'] => [
$request['arguments']['filter'],
array_diff_key($request['arguments'], ['filter' => 1]),
]];
case 'insertOne':
$innerArray = [$request['arguments']['document']];
break;
return [ 'insertOne' => [ $request['arguments']['document'] ]];
case 'replaceOne':
$options = array_diff_key($request['arguments'], ['filter' => 1, 'replacement' => 1]);
$innerArray = [$request['arguments']['filter'], $request['arguments']['replacement'], $options];
break;
return [ 'replaceOne' => [
$request['arguments']['filter'],
$request['arguments']['replacement'],
array_diff_key($request['arguments'], ['filter' => 1, 'replacement' => 1]),
]];
case 'updateMany':
case 'updateOne':
$options = array_diff_key($request['arguments'], ['filter' => 1, 'update' => 1]);
$innerArray = [$request['arguments']['filter'], $request['arguments']['update'], $options];
break;
return [ $request['name'] => [
$request['arguments']['filter'],
$request['arguments']['update'],
array_diff_key($request['arguments'], ['filter' => 1, 'update' => 1]),
]];
default:
throw new LogicException('Unsupported bulk write request: ' . $request['name']);
}
$operations[] = [$request['name'] => $innerArray];
}
return $operations;
}
/**
......@@ -421,7 +492,7 @@ class CrudSpecFunctionalTest extends FunctionalTestCase
* @param array $arguments
* @return array
*/
private function prepareFindAndModifyArguments($arguments)
private function prepareFindAndModifyArguments(array $arguments)
{
if (isset($arguments['returnDocument'])) {
$arguments['returnDocument'] = ('after' === strtolower($arguments['returnDocument']))
......
{
"data": [],
"tests": [
{
"description": "Estimated document count with empty collection",
"operation": {
"name": "estimatedDocumentCount",
"arguments": {}
},
"outcome": {
"result": 0
}
},
{
"description": "Count documents with empty collection",
"operation": {
"name": "countDocuments",
"arguments": {
"filter": {}
}
},
"outcome": {
"result": 0
}
},
{
"description": "Deprecated count with empty collection",
"operation": {
"name": "count",
"arguments": {
"filter": {}
}
},
"outcome": {
"result": 0
}
}
]
}
{
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": "ping"
},
{
"_id": 3,
"x": "pINg"
},
{
"_id": 4,
"x": "pong"
},
{
"_id": 5,
"x": "pONg"
}
],
"minServerVersion": "3.4",
"tests": [
{
"description": "BulkWrite with delete operations and collation",
"operation": {
"name": "bulkWrite",
"arguments": {
"requests": [
{
"name": "deleteOne",
"arguments": {
"filter": {
"x": "PING"
},
"collation": {
"locale": "en_US",
"strength": 2
}
}
},
{
"name": "deleteOne",
"arguments": {
"filter": {
"x": "PING"
},
"collation": {
"locale": "en_US",
"strength": 2
}
}
},
{
"name": "deleteMany",
"arguments": {
"filter": {
"x": "PONG"
},
"collation": {
"locale": "en_US",
"strength": 2
}
}
}
],
"options": {
"ordered": true
}
}
},
"outcome": {
"result": {
"deletedCount": 4,
"insertedCount": 0,
"insertedIds": {},
"matchedCount": 0,
"modifiedCount": 0,
"upsertedCount": 0,
"upsertedIds": {}
},
"collection": {
"data": [
{
"_id": 1,
"x": 11
}
]
}
}
},
{
"description": "BulkWrite with update operations and collation",
"operation": {
"name": "bulkWrite",
"arguments": {
"requests": [
{
"name": "updateMany",
"arguments": {
"filter": {
"x": "ping"
},
"update": {
"$set": {
"x": "PONG"
}
},
"collation": {
"locale": "en_US",
"strength": 3
}
}
},
{
"name": "updateOne",
"arguments": {
"filter": {
"x": "ping"
},
"update": {
"$set": {
"x": "PONG"
}
},
"collation": {
"locale": "en_US",
"strength": 2
}
}
},
{
"name": "replaceOne",
"arguments": {
"filter": {
"x": "ping"
},
"replacement": {
"_id": 6,
"x": "ping"
},
"upsert": true,
"collation": {
"locale": "en_US",
"strength": 3
}
}
},
{
"name": "updateMany",
"arguments": {
"filter": {
"x": "pong"
},
"update": {
"$set": {
"x": "PONG"
}
},
"collation": {
"locale": "en_US",
"strength": 2
}
}
}
],
"options": {
"ordered": true
}
}
},
"outcome": {
"result": {
"deletedCount": 0,
"insertedCount": 0,
"insertedIds": {},
"matchedCount": 6,
"modifiedCount": 4,
"upsertedCount": 1,
"upsertedIds": {
"2": 6
}
},
"collection": {
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": "PONG"
},
{
"_id": 3,
"x": "PONG"
},
{
"_id": 4,
"x": "PONG"
},
{
"_id": 5,
"x": "PONG"
},
{
"_id": 6,
"x": "ping"
}
]
}
}
}
]
}
{
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 22
}
],
"minServerVersion": "2.6",
"tests": [
{
"description": "BulkWrite with deleteOne operations",
"operation": {
"name": "bulkWrite",
"arguments": {
"requests": [
{
"name": "deleteOne",
"arguments": {
"filter": {
"_id": 3
}
}
},
{
"name": "deleteOne",
"arguments": {
"filter": {
"_id": 2
}
}
}
],
"options": {
"ordered": true
}
}
},
"outcome": {
"result": {
"deletedCount": 1,
"insertedCount": 0,
"insertedIds": {},
"matchedCount": 0,
"modifiedCount": 0,
"upsertedCount": 0,
"upsertedIds": {}
},
"collection": {
"data": [
{
"_id": 1,
"x": 11
}
]
}
}
},
{
"description": "BulkWrite with deleteMany operations",
"operation": {
"name": "bulkWrite",
"arguments": {
"requests": [
{
"name": "deleteMany",
"arguments": {
"filter": {
"x": {
"$lt": 11
}
}
}
},
{
"name": "deleteMany",
"arguments": {
"filter": {
"x": {
"$lte": 22
}
}
}
}
],
"options": {
"ordered": true
}
}
},
"outcome": {
"result": {
"deletedCount": 2,
"insertedCount": 0,
"insertedIds": {},
"matchedCount": 0,
"modifiedCount": 0,
"upsertedCount": 0,
"upsertedIds": {}
},
"collection": {
"data": []
}
}
},
{
"description": "BulkWrite with insertOne operations",
"operation": {
"name": "bulkWrite",
"arguments": {
"requests": [
{
"name": "insertOne",
"arguments": {
"document": {
"_id": 3,
"x": 33
}
}
},
{
"name": "insertOne",
"arguments": {
"document": {
"_id": 4,
"x": 44
}
}
}
],
"options": {
"ordered": true
}
}
},
"outcome": {
"result": {
"deletedCount": 0,
"insertedCount": 2,
"insertedIds": {
"0": 3,
"1": 4
},
"matchedCount": 0,
"modifiedCount": 0,
"upsertedCount": 0,
"upsertedIds": {}
},
"collection": {
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 22
},
{
"_id": 3,
"x": 33
},
{
"_id": 4,
"x": 44
}
]
}
}
},
{
"description": "BulkWrite with replaceOne operations",
"operation": {
"name": "bulkWrite",
"arguments": {
"requests": [
{
"name": "replaceOne",
"arguments": {
"filter": {
"_id": 3
},
"replacement": {
"x": 33
}
}
},
{
"name": "replaceOne",
"arguments": {
"filter": {
"_id": 1
},
"replacement": {
"_id": 1,
"x": 11
}
}
},
{
"name": "replaceOne",
"arguments": {
"filter": {
"_id": 1
},
"replacement": {
"x": 12
}
}
},
{
"name": "replaceOne",
"arguments": {
"filter": {
"_id": 3
},
"replacement": {
"x": 33
},
"upsert": true
}
}
],
"options": {
"ordered": true
}
}
},
"outcome": {
"result": {
"deletedCount": 0,
"insertedCount": 0,
"insertedIds": {},
"matchedCount": 2,
"modifiedCount": 1,
"upsertedCount": 1,
"upsertedIds": {
"3": 3
}
},
"collection": {
"data": [
{
"_id": 1,
"x": 12
},
{
"_id": 2,
"x": 22
},
{
"_id": 3,
"x": 33
}
]
}
}
},
{
"description": "BulkWrite with updateOne operations",
"operation": {
"name": "bulkWrite",
"arguments": {
"requests": [
{
"name": "updateOne",
"arguments": {
"filter": {
"_id": 0
},
"update": {
"$set": {
"x": 0
}
}
}
},
{
"name": "updateOne",
"arguments": {
"filter": {
"_id": 1
},
"update": {
"$set": {
"x": 11
}
}
}
},
{
"name": "updateOne",
"arguments": {
"filter": {
"_id": 2
},
"update": {
"$inc": {
"x": 1
}
}
}
},
{
"name": "updateOne",
"arguments": {
"filter": {
"_id": 3
},
"update": {
"$set": {
"x": 33
}
},
"upsert": true
}
}
],
"options": {
"ordered": true
}
}
},
"outcome": {
"result": {
"deletedCount": 0,
"insertedCount": 0,
"insertedIds": {},
"matchedCount": 2,
"modifiedCount": 1,
"upsertedCount": 1,
"upsertedIds": {
"3": 3
}
},
"collection": {
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 23
},
{
"_id": 3,
"x": 33
}
]
}
}
},
{
"description": "BulkWrite with updateMany operations",
"operation": {
"name": "bulkWrite",
"arguments": {
"requests": [
{
"name": "updateMany",
"arguments": {
"filter": {
"x": {
"$lt": 11
}
},
"update": {
"$set": {
"x": 0
}
}
}
},
{
"name": "updateMany",
"arguments": {
"filter": {
"x": {
"$lte": 22
}
},
"update": {
"$unset": {
"y": 1
}
}
}
},
{
"name": "updateMany",
"arguments": {
"filter": {
"x": {
"$lte": 22
}
},
"update": {
"$inc": {
"x": 1
}
}
}
},
{
"name": "updateMany",
"arguments": {
"filter": {
"_id": 3
},
"update": {
"$set": {
"x": 33
}
},
"upsert": true
}
}
],
"options": {
"ordered": true
}
}
},
"outcome": {
"result": {
"deletedCount": 0,
"insertedCount": 0,
"insertedIds": {},
"matchedCount": 4,
"modifiedCount": 2,
"upsertedCount": 1,
"upsertedIds": {
"3": 3
}
},
"collection": {
"data": [
{
"_id": 1,
"x": 12
},
{
"_id": 2,
"x": 23
},
{
"_id": 3,
"x": 33
}
]
}
}
},
{
"description": "BulkWrite with mixed ordered operations",
"operation": {
"name": "bulkWrite",
"arguments": {
"requests": [
{
"name": "insertOne",
"arguments": {
"document": {
"_id": 3,
"x": 33
}
}
},
{
"name": "updateOne",
"arguments": {
"filter": {
"_id": 2
},
"update": {
"$inc": {
"x": 1
}
}
}
},
{
"name": "updateMany",
"arguments": {
"filter": {
"_id": {
"$gt": 1
}
},
"update": {
"$inc": {
"x": 1
}
}
}
},
{
"name": "insertOne",
"arguments": {
"document": {
"_id": 4,
"x": 44
}
}
},
{
"name": "deleteMany",
"arguments": {
"filter": {
"x": {
"$nin": [
24,
34
]
}
}
}
},
{
"name": "replaceOne",
"arguments": {
"filter": {
"_id": 4
},
"replacement": {
"_id": 4,
"x": 44
},
"upsert": true
}
}
],
"options": {
"ordered": true
}
}
},
"outcome": {
"result": {
"deletedCount": 2,
"insertedCount": 2,
"insertedIds": {
"0": 3,
"3": 4
},
"matchedCount": 3,
"modifiedCount": 3,
"upsertedCount": 1,
"upsertedIds": {
"5": 4
}
},
"collection": {
"data": [
{
"_id": 2,
"x": 24
},
{
"_id": 3,
"x": 34
},
{
"_id": 4,
"x": 44
}
]
}
}
},
{
"description": "BulkWrite with mixed unordered operations",
"operation": {
"name": "bulkWrite",
"arguments": {
"requests": [
{
"name": "replaceOne",
"arguments": {
"filter": {
"_id": 3
},
"replacement": {
"_id": 3,
"x": 33
},
"upsert": true
}
},
{
"name": "deleteOne",
"arguments": {
"filter": {
"_id": 1
}
}
},
{
"name": "updateOne",
"arguments": {
"filter": {
"_id": 2
},
"update": {
"$inc": {
"x": 1
}
}
}
}
],
"options": {
"ordered": false
}
}
},
"outcome": {
"result": {
"deletedCount": 1,
"insertedCount": 0,
"insertedIds": {},
"matchedCount": 1,
"modifiedCount": 1,
"upsertedCount": 1,
"upsertedIds": {
"0": 3
}
},
"collection": {
"data": [
{
"_id": 2,
"x": 23
},
{
"_id": 3,
"x": 33
}
]
}
}
},
{
"description": "BulkWrite continue-on-error behavior with unordered (preexisting duplicate key)",
"operation": {
"name": "bulkWrite",
"arguments": {
"requests": [
{
"name": "insertOne",
"arguments": {
"document": {
"_id": 2,
"x": 22
}
}
},
{
"name": "insertOne",
"arguments": {
"document": {
"_id": 3,
"x": 33
}
}
},
{
"name": "insertOne",
"arguments": {
"document": {
"_id": 4,
"x": 44
}
}
}
],
"options": {
"ordered": false
}
}
},
"outcome": {
"error": true,
"result": {
"deletedCount": 0,
"insertedCount": 2,
"matchedCount": 0,
"modifiedCount": 0,
"upsertedCount": 0,
"upsertedIds": {}
},
"collection": {
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 22
},
{
"_id": 3,
"x": 33
},
{
"_id": 4,
"x": 44
}
]
}
}
},
{
"description": "BulkWrite continue-on-error behavior with unordered (duplicate key in requests)",
"operation": {
"name": "bulkWrite",
"arguments": {
"requests": [
{
"name": "insertOne",
"arguments": {
"document": {
"_id": 3,
"x": 33
}
}
},
{
"name": "insertOne",
"arguments": {
"document": {
"_id": 3,
"x": 33
}
}
},
{
"name": "insertOne",
"arguments": {
"document": {
"_id": 4,
"x": 44
}
}
}
],
"options": {
"ordered": false
}
}
},
"outcome": {
"error": true,
"result": {
"deletedCount": 0,
"insertedCount": 2,
"matchedCount": 0,
"modifiedCount": 0,
"upsertedCount": 0,
"upsertedIds": {}
},
"collection": {
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 22
},
{
"_id": 3,
"x": 33
},
{
"_id": 4,
"x": 44
}
]
}
}
}
]
}
......@@ -20,7 +20,10 @@
"_id": 3,
"x": 33
}
]
],
"options": {
"ordered": true
}
}
},
"outcome": {
......@@ -47,6 +50,110 @@
]
}
}
},
{
"description": "InsertMany continue-on-error behavior with unordered (preexisting duplicate key)",
"operation": {
"name": "insertMany",
"arguments": {
"documents": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 22
},
{
"_id": 3,
"x": 33
}
],
"options": {
"ordered": false
}
}
},
"outcome": {
"error": true,
"result": {
"deletedCount": 0,
"insertedCount": 2,
"matchedCount": 0,
"modifiedCount": 0,
"upsertedCount": 0,
"upsertedIds": {}
},
"collection": {
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 22
},
{
"_id": 3,
"x": 33
}
]
}
}
},
{
"description": "InsertMany continue-on-error behavior with unordered (duplicate key in requests)",
"operation": {
"name": "insertMany",
"arguments": {
"documents": [
{
"_id": 2,
"x": 22
},
{
"_id": 2,
"x": 22
},
{
"_id": 3,
"x": 33
}
],
"options": {
"ordered": false
}
}
},
"outcome": {
"error": true,
"result": {
"deletedCount": 0,
"insertedCount": 2,
"matchedCount": 0,
"modifiedCount": 0,
"upsertedCount": 0,
"upsertedIds": {}
},
"collection": {
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 22
},
{
"_id": 3,
"x": 33
}
]
}
}
}
]
}
......@@ -98,7 +98,8 @@
"outcome": {
"result": {
"matchedCount": 2,
"modifiedCount": 1
"modifiedCount": 1,
"upsertedCount": 0
},
"collection": {
"data": [
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment