Commit 35e6181a authored by Jeremy Mikola's avatar Jeremy Mikola

Merge ChangeStreamsProseTest into WatchFunctionalTest

parent 6afb08f4
...@@ -11,15 +11,31 @@ use MongoDB\Driver\Server; ...@@ -11,15 +11,31 @@ use MongoDB\Driver\Server;
use MongoDB\Driver\WriteConcern; use MongoDB\Driver\WriteConcern;
use MongoDB\Driver\Exception\CommandException; use MongoDB\Driver\Exception\CommandException;
use MongoDB\Operation\CreateCollection; use MongoDB\Operation\CreateCollection;
use MongoDB\Operation\DatabaseCommand;
use MongoDB\Operation\DropCollection; use MongoDB\Operation\DropCollection;
use InvalidArgumentException;
use stdClass; use stdClass;
use UnexpectedValueException; use UnexpectedValueException;
abstract class FunctionalTestCase extends TestCase abstract class FunctionalTestCase extends TestCase
{ {
protected $manager;
private $configuredFailPoints = [];
public function setUp() public function setUp()
{ {
parent::setUp();
$this->manager = new Manager(static::getUri()); $this->manager = new Manager(static::getUri());
$this->configuredFailPoints = [];
}
public function tearDown()
{
$this->disableFailPoints();
parent::tearDown();
} }
protected function assertCollectionCount($namespace, $count) protected function assertCollectionCount($namespace, $count)
...@@ -49,6 +65,39 @@ abstract class FunctionalTestCase extends TestCase ...@@ -49,6 +65,39 @@ abstract class FunctionalTestCase extends TestCase
$this->assertEquals((string) $expectedObjectId, (string) $actualObjectId); $this->assertEquals((string) $expectedObjectId, (string) $actualObjectId);
} }
/**
* Configure a fail point for the test.
*
* The fail point will automatically be disabled during tearDown() to avoid
* affecting a subsequent test.
*
* @param array|stdClass $command configureFailPoint command document
* @throws InvalidArgumentException if $command is not a configureFailPoint command
*/
protected function configureFailPoint($command)
{
if (is_array($command)) {
$command = (object) $command;
}
if ( ! $command instanceof stdClass) {
throw new InvalidArgumentException('$command is not an array or stdClass instance');
}
if (key($command) !== 'configureFailPoint') {
throw new InvalidArgumentException('$command is not a configureFailPoint command');
}
$operation = new DatabaseCommand('admin', $command);
$cursor = $operation->execute($this->getPrimaryServer());
$result = $cursor->toArray()[0];
$this->assertCommandSucceeded($result);
// Record the fail point so it can be disabled during tearDown()
$this->configuredFailPoints[] = $command->configureFailPoint;
}
/** /**
* Creates the test collection with the specified options. * Creates the test collection with the specified options.
* *
...@@ -260,4 +309,24 @@ abstract class FunctionalTestCase extends TestCase ...@@ -260,4 +309,24 @@ abstract class FunctionalTestCase extends TestCase
$this->markTestSkipped('Transactions require WiredTiger storage engine'); $this->markTestSkipped('Transactions require WiredTiger storage engine');
} }
} }
/**
* Disables any fail points that were configured earlier in the test.
*
* This tracks fail points set via configureFailPoint() and should be called
* during tearDown().
*/
private function disableFailPoints()
{
if (empty($this->configuredFailPoints)) {
return;
}
$server = $this->getPrimaryServer();
foreach ($this->configuredFailPoints as $failPoint) {
$operation = new DatabaseCommand('admin', ['configureFailPoint' => $failPoint, 'mode' => 'off']);
$operation->execute($server);
}
}
} }
...@@ -9,6 +9,7 @@ use MongoDB\Driver\ReadPreference; ...@@ -9,6 +9,7 @@ use MongoDB\Driver\ReadPreference;
use MongoDB\Driver\Server; use MongoDB\Driver\Server;
use MongoDB\Driver\WriteConcern; use MongoDB\Driver\WriteConcern;
use MongoDB\Driver\Exception\LogicException; use MongoDB\Driver\Exception\LogicException;
use MongoDB\Driver\Exception\ServerException;
use MongoDB\Exception\ResumeTokenException; use MongoDB\Exception\ResumeTokenException;
use MongoDB\Operation\CreateCollection; use MongoDB\Operation\CreateCollection;
use MongoDB\Operation\DatabaseCommand; use MongoDB\Operation\DatabaseCommand;
...@@ -580,6 +581,45 @@ class WatchFunctionalTest extends FunctionalTestCase ...@@ -580,6 +581,45 @@ class WatchFunctionalTest extends FunctionalTestCase
$this->assertFalse($cursor->isDead()); $this->assertFalse($cursor->isDead());
} }
/**
* Prose test: "ChangeStream will not attempt to resume after encountering
* error code 11601 (Interrupted), 136 (CappedPositionLost), or 237
* (CursorKilled) while executing a getMore command."
*
* @dataProvider provideNonResumableErrorCodes
*/
public function testNonResumableErrorCodes($errorCode)
{
if (version_compare($this->getServerVersion(), '4.0.0', '<')) {
$this->markTestSkipped('failCommand is not supported');
}
$this->configureFailPoint([
'configureFailPoint' => 'failCommand',
'mode' => ['times' => 1],
'data' => ['failCommands' => ['getMore'], 'errorCode' => $errorCode],
]);
$this->insertDocument(['x' => 1]);
$operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), []);
$changeStream = $operation->execute($this->getPrimaryServer());
$changeStream->rewind();
$this->expectException(ServerException::class);
$this->expectExceptionCode($errorCode);
$changeStream->next();
}
public function provideNonResumableErrorCodes()
{
return [
[136], // CappedPositionLost
[237], // CursorKilled
[11601], // Interrupted
];
}
public function testNextResumeTokenNotFound() public function testNextResumeTokenNotFound()
{ {
if (version_compare($this->getServerVersion(), '4.1.8', '>=')) { if (version_compare($this->getServerVersion(), '4.1.8', '>=')) {
......
<?php
namespace MongoDB\Tests\SpecTests;
use MongoDB\Collection;
use MongoDB\Driver\Exception\ServerException;
/**
* Change Streams spec prose tests.
*
* @see https://github.com/mongodb/specifications/tree/master/source/change-streams
*/
class ChangeStreamsProseTest extends FunctionalTestCase
{
private $collection;
public function setUp()
{
parent::setUp();
$this->skipIfChangeStreamIsNotSupported();
$this->collection = new Collection($this->manager, $this->getDatabaseName(), $this->getCollectionName());
$this->dropCollection();
}
public function tearDown()
{
if (!$this->hasFailed()) {
$this->dropCollection();
}
parent::tearDown();
}
/**
* ChangeStream will not attempt to resume after encountering error code
* 11601 (Interrupted), 136 (CappedPositionLost), or 237 (CursorKilled)
* while executing a getMore command.
*
* @dataProvider provideNonResumableErrorCodes
*/
public function testProseTest5($errorCode)
{
if (version_compare($this->getServerVersion(), '4.0.0', '<')) {
$this->markTestSkipped('failCommand is not supported');
}
$this->configureFailPoint([
'configureFailPoint' => 'failCommand',
'mode' => ['times' => 1],
'data' => ['failCommands' => ['getMore'], 'errorCode' => $errorCode],
]);
$this->createCollection();
$changeStream = $this->collection->watch();
$changeStream->rewind();
$this->expectException(ServerException::class);
$this->expectExceptionCode($errorCode);
$changeStream->next();
}
public function provideNonResumableErrorCodes()
{
return [
[136], // CappedPositionLost
[237], // CursorKilled
[11601], // Interrupted
];
}
}
...@@ -4,7 +4,6 @@ namespace MongoDB\Tests\SpecTests; ...@@ -4,7 +4,6 @@ namespace MongoDB\Tests\SpecTests;
use MongoDB\Client; use MongoDB\Client;
use MongoDB\Collection; use MongoDB\Collection;
use MongoDB\Database;
use MongoDB\Driver\Server; use MongoDB\Driver\Server;
use MongoDB\Driver\WriteConcern; use MongoDB\Driver\WriteConcern;
use MongoDB\Driver\Exception\BulkWriteException; use MongoDB\Driver\Exception\BulkWriteException;
...@@ -14,7 +13,6 @@ use MongoDB\Tests\FunctionalTestCase as BaseFunctionalTestCase; ...@@ -14,7 +13,6 @@ use MongoDB\Tests\FunctionalTestCase as BaseFunctionalTestCase;
use MongoDB\Tests\TestCase; use MongoDB\Tests\TestCase;
use PHPUnit\Framework\SkippedTest; use PHPUnit\Framework\SkippedTest;
use ArrayIterator; use ArrayIterator;
use InvalidArgumentException;
use IteratorIterator; use IteratorIterator;
use LogicException; use LogicException;
use MultipleIterator; use MultipleIterator;
...@@ -32,21 +30,18 @@ class FunctionalTestCase extends BaseFunctionalTestCase ...@@ -32,21 +30,18 @@ class FunctionalTestCase extends BaseFunctionalTestCase
const TOPOLOGY_REPLICASET = 'replicaset'; const TOPOLOGY_REPLICASET = 'replicaset';
const TOPOLOGY_SHARDED = 'sharded'; const TOPOLOGY_SHARDED = 'sharded';
private $configuredFailPoints = [];
private $context; private $context;
public function setUp() public function setUp()
{ {
parent::setUp(); parent::setUp();
$this->configuredFailPoints = [];
$this->context = null; $this->context = null;
} }
public function tearDown() public function tearDown()
{ {
$this->context = null; $this->context = null;
$this->disableFailPoints();
parent::tearDown(); parent::tearDown();
} }
...@@ -141,39 +136,6 @@ class FunctionalTestCase extends BaseFunctionalTestCase ...@@ -141,39 +136,6 @@ class FunctionalTestCase extends BaseFunctionalTestCase
$this->markTestSkipped(sprintf('Server version "%s" and topology "%s" do not meet test requirements: %s', $serverVersion, $topology, json_encode($runOn))); $this->markTestSkipped(sprintf('Server version "%s" and topology "%s" do not meet test requirements: %s', $serverVersion, $topology, json_encode($runOn)));
} }
/**
* Configure a fail point for the test.
*
* The fail point will automatically be disabled during tearDown() to avoid
* affecting a subsequent test.
*
* @param array|stdClass $command configureFailPoint command document
* @throws InvalidArgumentException if $command is not a configureFailPoint command
*/
protected function configureFailPoint($command)
{
if (is_array($command)) {
$command = (object) $command;
}
if ( ! $command instanceof stdClass) {
throw new InvalidArgumentException('$command is not an array or stdClass instance');
}
if (key($command) !== 'configureFailPoint') {
throw new InvalidArgumentException('$command is not a configureFailPoint command');
}
$database = new Database($this->manager, 'admin');
$cursor = $database->command($command);
$result = $cursor->toArray()[0];
$this->assertCommandSucceeded($result);
// Record the fail point so it can be disabled during tearDown()
$this->configuredFailPoints[] = $command->configureFailPoint;
}
/** /**
* Decode a JSON spec test. * Decode a JSON spec test.
* *
...@@ -255,21 +217,6 @@ class FunctionalTestCase extends BaseFunctionalTestCase ...@@ -255,21 +217,6 @@ class FunctionalTestCase extends BaseFunctionalTestCase
return new Collection($this->manager, $context->databaseName, $context->outcomeCollectionName); return new Collection($this->manager, $context->databaseName, $context->outcomeCollectionName);
} }
/**
* Disables any fail points that were configured earlier in the test.
*
* This tracks fail points set via configureFailPoint() and should be called
* during tearDown().
*/
private function disableFailPoints()
{
$database = new Database($this->manager, 'admin');
foreach ($this->configuredFailPoints as $failPoint) {
$database->command(['configureFailPoint' => $failPoint, 'mode' => 'off']);
}
}
/** /**
* Return the corresponding topology constants for the current topology. * Return the corresponding topology constants for the current topology.
* *
......
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