Skip to content

Commit

Permalink
Merge pull request #12 from doesntmattr/feature/replay
Browse files Browse the repository at this point in the history
Add a --replay option to ::execute to avoid mongodb unique index error
  • Loading branch information
caciobanu authored Jan 16, 2017
2 parents 5ebeb21 + cd1d6da commit 4b0a7ff
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ protected function configure()
->addArgument('version', InputArgument::REQUIRED, 'The version to execute.', null)
->addOption('up', null, InputOption::VALUE_NONE, 'Execute the migration up.')
->addOption('down', null, InputOption::VALUE_NONE, 'Execute the migration down.')
->addOption('replay', null, InputOption::VALUE_NONE, 'Replay an \'up\' migration and avoid the duplicate exception.')
->setHelp(<<<'EOT'
The <info>%command.name%</info> command executes a single migration version up or down manually:
Expand All @@ -59,12 +60,13 @@ public function execute(InputInterface $input, OutputInterface $output)
{
$version = $input->getArgument('version');
$direction = $input->getOption('down') ? 'down' : 'up';
$replay = $input->getOption('replay');

$configuration = $this->getMigrationConfiguration($input, $output);
$version = $configuration->getVersion($version);

if (!$input->isInteractive()) {
$version->execute($direction);
$version->execute($direction, $replay);
} else {
$question = new ConfirmationQuestion(
'<question>WARNING! You are about to execute a database migration that could result in data lost. Are you sure you wish to continue? (y/n)</question> ',
Expand All @@ -76,7 +78,7 @@ public function execute(InputInterface $input, OutputInterface $output)
->ask($input, $output, $question);

if ($confirmation === true) {
$version->execute($direction);
$version->execute($direction, $replay);
} else {
$output->writeln('<error>Migration cancelled!</error>');
}
Expand Down
30 changes: 26 additions & 4 deletions src/AntiMattr/MongoDB/Migrations/Version.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use AntiMattr\MongoDB\Migrations\Collection\Statistics;
use AntiMattr\MongoDB\Migrations\Configuration\Configuration;
use AntiMattr\MongoDB\Migrations\Exception\SkipException;
use AntiMattr\MongoDB\Migrations\Exception\AbortException;
use Doctrine\MongoDB\Collection;
use Doctrine\MongoDB\Database;
use Exception;
Expand Down Expand Up @@ -169,11 +170,18 @@ public function analyze(Collection $collection)
* Execute this migration version up or down and and return the SQL.
*
* @param string $direction The direction to execute the migration
* @param bool $replay If the migration is being replayed
*
* @throws \Exception when migration fails
*/
public function execute($direction)
public function execute($direction, $replay = false)
{
if ('down' === $direction && $replay) {
throw new AbortException(
'Cannot run \'down\' and replay it. Use replay with \'up\''
);
}

try {
$start = microtime(true);

Expand All @@ -194,7 +202,7 @@ public function execute($direction)
$this->updateStatisticsAfter();

if ($direction === 'up') {
$this->markMigrated();
$this->markMigrated($replay);
} else {
$this->markNotMigrated();
}
Expand Down Expand Up @@ -276,13 +284,27 @@ public function executeScript(Database $db, $file)
return $result;
}

public function markMigrated()
/**
* markMigrated.
*
* @param bool $replay This is a replayed migration, do an update instead of an insert
*/
public function markMigrated($replay = false)
{
$this->configuration->createMigrationCollection();
$collection = $this->configuration->getCollection();

$document = array('v' => $this->version, 't' => $this->createMongoTimestamp());
$collection->insert($document);

if ($replay) {
$query = array('v' => $this->version);
// If the user asked for a 'replay' of a migration that
// has not been run, it will be inserted anew
$options = array('upsert' => true);
$collection->update($query, $document, $options);
} else {
$collection->insert($document);
}
}

public function markNotMigrated()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public function testExecuteDownWithoutInteraction()

$this->version->expects($this->once())
->method('execute')
->with('down')
->with('down', false)
;

// Run command, run.
Expand Down Expand Up @@ -117,7 +117,52 @@ public function testExecuteUpWithInteraction()

$this->version->expects($this->once())
->method('execute')
->with('up')
->with('up', false)
;

// Run command, run.
$this->command->run(
$input,
$this->output
);
}

public function testExecuteReplayWithoutInteraction()
{
// Variables and Objects
$application = new Application();
$numVersion = '11235713';
$interactive = false;

// Arguments and Options
$input = new ArgvInput(
array(
'application-name',
ExecuteCommand::NAME,
$numVersion,
'--up',
'--replay',
)
);

// Set properties on objects
$this->command->setApplication($application);
$this->command->setMigrationConfiguration($this->config);
$input->setInteractive($interactive);

// Expectations
$this->config->expects($this->once())
->method('getVersion')
->with($numVersion)
->will(
$this->returnValue($this->version)
)
;

$replay = true;
$this->version->expects($this->once())
->method('execute')
->with('up', $replay)
;

// Run command, run.
Expand Down
58 changes: 54 additions & 4 deletions tests/AntiMattr/Tests/MongoDB/Migrations/VersionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ protected function setUp()

public function testConstructor()
{
$this->assertEquals($this->configuration, $this->version->getConfiguration());
$this->assertEquals(Version::STATE_NONE, $this->version->getState());
$this->assertEquals($this->versionName, $this->version->getVersion());
$this->assertSame($this->configuration, $this->version->getConfiguration());
$this->assertSame(Version::STATE_NONE, $this->version->getState());
$this->assertSame($this->versionName, $this->version->getVersion());
$this->assertEquals($this->versionName, (string) $this->version);
$this->assertNotNull($this->version->getMigration());
}
Expand Down Expand Up @@ -122,6 +122,36 @@ public function testMarkMigrated()
$this->version->markMigrated();
}

public function testMarkMigratedWithReplay()
{
$timestamp = $this->buildMock('MongoTimestamp');
$this->version->setTimestamp($timestamp);

$collection = $this->buildMock('Doctrine\MongoDB\Collection');
$this->configuration->expects($this->once())
->method('createMigrationCollection');

$this->configuration->expects($this->once())
->method('getCollection')
->will($this->returnValue($collection));

$query = array(
'v' => $this->versionName,
);

$update = array(
'v' => $this->versionName,
't' => $timestamp,
);

$collection->expects($this->once())
->method('update')
->with($query, $update);

$replay = true;
$this->version->markMigrated($replay);
}

public function testMarkNotMigrated()
{
$timestamp = $this->buildMock('MongoTimestamp');
Expand Down Expand Up @@ -200,12 +230,32 @@ public function testIsMigrated()
$this->version->isMigrated();
}

/**
* @test
*
* testExecuteDownWithReplayThrowsException
*
* @expectedException \AntiMattr\MongoDB\Migrations\Exception\AbortException
*/
public function testExecuteDownWithReplayThrowsException()
{
// These methods will not be called
$this->migration->expects($this->never())->method('down');
$this->configuration->expects($this->never())
->method('createMigrationCollection');
$this->configuration->expects($this->never())
->method('getCollection');

$replay = true;
$this->version->execute('down', $replay);
}

/**
* @dataProvider provideDirection
*/
public function testExecuteThrowsSkipException($direction)
{
$expectedException = $this->buildMock('AntiMattr\MongoDB\Migrations\Exception\SkipException');
$expectedException = new \AntiMattr\MongoDB\Migrations\Exception\SkipException();

$this->migration->expects($this->once())
->method($direction)
Expand Down

0 comments on commit 4b0a7ff

Please sign in to comment.