diff --git a/docs/commands.rst b/docs/commands.rst index 0c51422c5..8dea85b83 100644 --- a/docs/commands.rst +++ b/docs/commands.rst @@ -127,6 +127,18 @@ Specifying 0 as the target version will revert all migrations. $ phinx rollback -e development -t 0 +To rollback all migrations to a specific date then use the ``--date`` +parameter or ``-d`` for short. + +.. code-block:: bash + + $ phinx rollback -e development -d 2012 + $ phinx rollback -e development -d 201201 + $ phinx rollback -e development -d 20120103 + $ phinx rollback -e development -d 2012010312 + $ phinx rollback -e development -d 201201031205 + $ phinx rollback -e development -d 20120103120530 + If a breakpoint is set, blocking further rollbacks, you can override the breakpoint using the ``--force`` parameter or ``-f`` for short. @@ -134,6 +146,13 @@ breakpoint using the ``--force`` parameter or ``-f`` for short. $ phinx rollback -e development -t 0 -f +.. note:: + + When rolling back, Phinx orders the executed migrations using + the order specified in the ``version_order`` option of your + ``phinx.yml`` file. + Please see the :doc:`Configuration ` chapter for more information. + The Status Command ------------------ diff --git a/docs/configuration.rst b/docs/configuration.rst index d6dd160a1..8f2ef3657 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -300,3 +300,12 @@ The aliased classes will still be required to implement the ``Phinx\Migration\Cr aliases: permission: \Namespace\Migrations\PermissionMigrationTemplateGenerator view: \Namespace\Migrations\ViewMigrationTemplateGenerator + +Version Order +------ + +When rolling back or printing the status of migrations, Phinx orders the executed migrations according to the +``version_order`` option, which can have the following values: + +* ``creation`` (the default): migrations are ordered by their creation time, which is also part of their filename. +* ``execution``: migrations are ordered by their execution time, also known as start time. \ No newline at end of file diff --git a/phinx.yml b/phinx.yml index 17481b97c..5a7ba85df 100644 --- a/phinx.yml +++ b/phinx.yml @@ -31,3 +31,5 @@ environments: pass: '' port: 3306 charset: utf8 + +version_order: creation \ No newline at end of file diff --git a/src/Phinx/Config/Config.php b/src/Phinx/Config/Config.php index c0e6de357..4a052b21f 100644 --- a/src/Phinx/Config/Config.php +++ b/src/Phinx/Config/Config.php @@ -38,6 +38,16 @@ */ class Config implements ConfigInterface { + /** + * The value that identifies a version order by creation time. + */ + const VERSION_ORDER_CREATION_TIME = 'creation'; + + /** + * The value that identifies a version order by execution time. + */ + const VERSION_ORDER_EXECUTION_TIME = 'execution'; + /** * @var array */ @@ -297,6 +307,34 @@ public function getTemplateClass() return $this->values['templates']['class']; } + /** + * Get the version order. + * + * @return string + */ + public function getVersionOrder() + { + if (!isset($this->values['version_order'])) { + return self::VERSION_ORDER_CREATION_TIME; + } + + return $this->values['version_order']; + } + + /** + * Is version order creation time? + * + * @return boolean + */ + public function isVersionOrderCreationTime() + { + $versionOrder = $this->getVersionOrder(); + + return $versionOrder == self::VERSION_ORDER_CREATION_TIME; + } + + + /** * Replace tokens in the specified array. * diff --git a/src/Phinx/Config/ConfigInterface.php b/src/Phinx/Config/ConfigInterface.php index 8cef08626..2fa1f4896 100644 --- a/src/Phinx/Config/ConfigInterface.php +++ b/src/Phinx/Config/ConfigInterface.php @@ -116,6 +116,20 @@ public function getTemplateFile(); */ public function getTemplateClass(); + /** + * Get the version order. + * + * @return string + */ + public function getVersionOrder(); + + /** + * Is version order creation time? + * + * @return boolean + */ + public function isVersionOrderCreationTime(); + /** * Gets the base class name for migrations. * diff --git a/src/Phinx/Console/Command/Rollback.php b/src/Phinx/Console/Command/Rollback.php index 5a3d513e6..495720672 100644 --- a/src/Phinx/Console/Command/Rollback.php +++ b/src/Phinx/Console/Command/Rollback.php @@ -31,6 +31,7 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; +use Phinx\Config\Config; class Rollback extends AbstractCommand { @@ -61,6 +62,10 @@ protected function configure() If you have a breakpoint set, then you can rollback to target 0 and the rollbacks will stop at the breakpoint. phinx rollback -e development -t 0 +The version_order configuration option is used to determine the order of the migrations when rolling back. +This can be used to allow the rolling back of the last executed migration instead of the last created one, or combined +with the -d|--date option to rollback to a certain date using the migration start times to order them. + EOT ); } @@ -81,14 +86,16 @@ protected function execute(InputInterface $input, OutputInterface $output) $date = $input->getOption('date'); $force = !!$input->getOption('force'); + $config = $this->getConfig(); + if (null === $environment) { - $environment = $this->getConfig()->getDefaultEnvironment(); + $environment = $config->getDefaultEnvironment(); $output->writeln('warning no environment specified, defaulting to: ' . $environment); } else { $output->writeln('using environment ' . $environment); } - $envOptions = $this->getConfig()->getEnvironment($environment); + $envOptions = $config->getEnvironment($environment); if (isset($envOptions['adapter'])) { $output->writeln('using adapter ' . $envOptions['adapter']); } @@ -100,17 +107,61 @@ protected function execute(InputInterface $input, OutputInterface $output) if (isset($envOptions['name'])) { $output->writeln('using database ' . $envOptions['name']); } + + $versionOrder = $this->getConfig()->getVersionOrder(); + $output->writeln('ordering by ' . $versionOrder . " time"); // rollback the specified environment - $start = microtime(true); - if (null !== $date) { - $this->getManager()->rollbackToDateTime($environment, new \DateTime($date), $force); + if (null === $date) { + $targetMustMatchVersion = true; + $target = $version; } else { - $this->getManager()->rollback($environment, $version, $force); + $targetMustMatchVersion = false; + $target = $this->getTargetFromDate($date); } + + $start = microtime(true); + $this->getManager()->rollback($environment, $target, $force, $targetMustMatchVersion); $end = microtime(true); $output->writeln(''); $output->writeln('All Done. Took ' . sprintf('%.4fs', $end - $start) . ''); } + + /** + * Get Target from Date + * + * @param string $date The date to convert to a target. + * @return string The target + */ + public function getTargetFromDate($date) + { + if (!preg_match('/^\d{4,14}$/', $date)) { + throw new \InvalidArgumentException('Invalid date. Format is YYYY[MM[DD[HH[II[SS]]]]].'); + } + + // what we need to append to the date according to the possible date string lengths + $dateStrlenToAppend = array( + 14 => '', + 12 => '00', + 10 => '0000', + 8 => '000000', + 6 => '01000000', + 4 => '0101000000', + ); + + if (!isset($dateStrlenToAppend[strlen($date)])) { + throw new \InvalidArgumentException('Invalid date. Format is YYYY[MM[DD[HH[II[SS]]]]].'); + } + + $target = $date . $dateStrlenToAppend[strlen($date)]; + + $dateTime = \DateTime::createFromFormat('YmdHis', $target); + + if ($dateTime === false) { + throw new \InvalidArgumentException('Invalid date. Format is YYYY[MM[DD[HH[II[SS]]]]].'); + } + + return $dateTime->format('YmdHis'); + } } diff --git a/src/Phinx/Console/Command/Status.php b/src/Phinx/Console/Command/Status.php index 904f5536e..3d53ecc36 100644 --- a/src/Phinx/Console/Command/Status.php +++ b/src/Phinx/Console/Command/Status.php @@ -52,6 +52,8 @@ protected function configure() phinx status -e development phinx status -e development -f json + +The version_order configuration option is used to determine the order of the status migrations. EOT ); } @@ -80,6 +82,8 @@ protected function execute(InputInterface $input, OutputInterface $output) $output->writeln('using format ' . $format); } + $output->writeln('ordering by ' . $this->getConfig()->getVersionOrder() . " time"); + // print the status return $this->getManager()->printStatus($environment, $format); } diff --git a/src/Phinx/Db/Adapter/AdapterInterface.php b/src/Phinx/Db/Adapter/AdapterInterface.php index 9d45b885b..1201e0b5b 100644 --- a/src/Phinx/Db/Adapter/AdapterInterface.php +++ b/src/Phinx/Db/Adapter/AdapterInterface.php @@ -81,8 +81,9 @@ interface AdapterInterface public function getVersions(); /** - * Get all migration log entries, indexed by version number. - * + * Get all migration log entries, indexed by version creation time and sorted ascendingly by the configuration's + * version order option + * * @return array */ public function getVersionLog(); diff --git a/src/Phinx/Db/Adapter/PdoAdapter.php b/src/Phinx/Db/Adapter/PdoAdapter.php index 6cabf8d92..d3d5fd326 100644 --- a/src/Phinx/Db/Adapter/PdoAdapter.php +++ b/src/Phinx/Db/Adapter/PdoAdapter.php @@ -408,8 +408,20 @@ public function getVersions() public function getVersionLog() { $result = array(); - $rows = $this->fetchAll(sprintf('SELECT * FROM %s ORDER BY version ASC', $this->getSchemaTableName())); - foreach ($rows as $version) { + + switch ($this->options['version_order']) { + case \Phinx\Config\Config::VERSION_ORDER_CREATION_TIME: + $orderBy = 'version ASC'; + break; + case \Phinx\Config\Config::VERSION_ORDER_EXECUTION_TIME: + $orderBy = 'start_time ASC, version ASC'; + break; + default: + throw new \RuntimeException('Invalid version_order configuration option'); + } + + $rows = $this->fetchAll(sprintf('SELECT * FROM %s ORDER BY %s', $this->getSchemaTableName(), $orderBy)); + foreach($rows as $version) { $result[$version['version']] = $version; } diff --git a/src/Phinx/Migration/Manager.php b/src/Phinx/Migration/Manager.php index bc1db17f2..7dc833e1a 100644 --- a/src/Phinx/Migration/Manager.php +++ b/src/Phinx/Migration/Manager.php @@ -105,22 +105,86 @@ public function printStatus($environment, $format = null) $migrations = array(); $hasDownMigration = false; $hasMissingMigration = false; - if (count($this->getMigrations())) { + $migrations = $this->getMigrations(); + if (count($migrations)) { // TODO - rewrite using Symfony Table Helper as we already have this library // included and it will fix formatting issues (e.g drawing the lines) $output->writeln(''); - $output->writeln(' Status Migration ID Started Finished Migration Name '); + + switch ($this->getConfig()->getVersionOrder()) { + case \Phinx\Config\Config::VERSION_ORDER_CREATION_TIME: + $migrationIdAndStartedHeader = "[Migration ID] Started "; + break; + case \Phinx\Config\Config::VERSION_ORDER_EXECUTION_TIME: + $migrationIdAndStartedHeader = "Migration ID [Started ]"; + break; + default: + throw new \RuntimeException('Invalid version_order configuration option'); + } + + $output->writeln(" Status $migrationIdAndStartedHeader Finished Migration Name "); $output->writeln('----------------------------------------------------------------------------------'); $env = $this->getEnvironment($environment); $versions = $env->getVersionLog(); + $maxNameLength = $versions ? max(array_map(function($version) { return strlen($version['migration_name']); }, $versions)) : 0; - foreach ($this->getMigrations() as $migration) { + $missingVersions = array_diff_key($versions, $migrations); + + $hasMissingMigration = !empty($missingVersions); + + // get the migrations sorted in the same way as the versions + $sortedMigrations = array(); + + foreach ($versions as $versionCreationTime => $version) { + if (isset($migrations[$versionCreationTime])) { + array_push($sortedMigrations, $migrations[$versionCreationTime]); + unset($migrations[$versionCreationTime]); + } + } + + if (empty($sortedMigrations) && !empty($missingVersions)) { + // this means we have no up migrations, so we write all the missing versions already so they show up + // before any possible down migration + foreach ($missingVersions as $missingVersionCreationTime => $missingVersion) { + $this->printMissingVersion($missingVersion, $maxNameLength); + + unset($missingVersions[$missingVersionCreationTime]); + } + } + + // any migration left in the migrations (ie. not unset when sorting the migrations by the version order) is + // a migration that is down, so we add them to the end of the sorted migrations list + if (!empty($migrations)) { + $sortedMigrations = array_merge($sortedMigrations, $migrations); + } + + foreach ($sortedMigrations as $migration) { $version = array_key_exists($migration->getVersion(), $versions) ? $versions[$migration->getVersion()] : false; if ($version) { + // check if there are missing versions before this version + foreach ($missingVersions as $missingVersionCreationTime => $missingVersion) { + if ($this->getConfig()->isVersionOrderCreationTime()) { + if ($missingVersion['version'] > $version['version']) { + break; + } + } else { + if ($missingVersion['start_time'] > $version['start_time']) { + break; + } elseif ($missingVersion['start_time'] == $version['start_time'] && + $missingVersion['version'] > $version['version']) { + break; + } + } + + $this->printMissingVersion($missingVersion, $maxNameLength); + + unset($missingVersions[$missingVersionCreationTime]); + } + $status = ' up '; } else { $hasDownMigration = true; @@ -141,18 +205,11 @@ public function printStatus($environment, $format = null) unset($versions[$migration->getVersion()]); } - if (count($versions)) { - $hasMissingMigration = true; - foreach ($versions as $missing => $version) { - $output->writeln(sprintf( - ' up %14.0f %19s %19s %s ** MISSING **', - $missing, $version['start_time'], $version['end_time'], str_pad($version['migration_name'], $maxNameLength, ' ') - )); + // and finally add any possibly-remaining missing migrations + foreach ($missingVersions as $missingVersionCreationTime => $missingVersion) { + $this->printMissingVersion($missingVersion, $maxNameLength); - if ($version && $version['breakpoint']){ - $output->writeln(' BREAKPOINT SET'); - } - } + unset($missingVersions[$missingVersionCreationTime]); } } else { // there are no migrations @@ -186,6 +243,24 @@ public function printStatus($environment, $format = null) } } + /** + * Print Missing Version + * + * @param array $version The missing version to print (in the format returned by Environment.getVersionLog). + * @param integer $maxNameLength The maximum migration name length. + */ + private function printMissingVersion($version, $maxNameLength) + { + $this->getOutput()->writeln(sprintf( + ' up %14.0f %19s %19s %s ** MISSING **', + $version['version'], $version['start_time'], $version['end_time'], str_pad($version['migration_name'], $maxNameLength, ' ') + )); + + if ($version && $version['breakpoint']){ + $this->getOutput()->writeln(' BREAKPOINT SET'); + } + } + /** * Migrate to the version of the database on a given date. * @@ -210,42 +285,6 @@ public function migrateToDateTime($environment, \DateTime $dateTime) } } - /** - * Roll back to the version of the database on a given date. - * - * @param string $environment Environment - * @param \DateTime $dateTime Date to roll back to - * @param bool $force - * - * @return void - */ - public function rollbackToDateTime($environment, \DateTime $dateTime, $force = false) - { - $env = $this->getEnvironment($environment); - $versions = $env->getVersions(); - $dateString = $dateTime->format('YmdHis'); - sort($versions); - - $earlierVersion = null; - $availableMigrations = array_filter($versions, function($version) use($dateString, &$earlierVersion) { - if ($version <= $dateString) { - $earlierVersion = $version; - } - return $version >= $dateString; - }); - - if (count($availableMigrations) > 0) { - if (is_null($earlierVersion)) { - $this->getOutput()->writeln('Rolling back all migrations'); - $migration = 0; - } else { - $this->getOutput()->writeln('Rolling back to version ' . $earlierVersion); - $migration = $earlierVersion; - } - $this->rollback($environment, $migration, $force); - } - } - /** * Migrate an environment to the specified version. * @@ -368,61 +407,93 @@ public function executeSeed($name, SeedInterface $seed) * Rollback an environment to the specified version. * * @param string $environment Environment - * @param int $version + * @param int $target * @param bool $force + * @param bool $targetMustMatchVersion * @return void */ - public function rollback($environment, $version = null, $force = false) + public function rollback($environment, $target = null, $force = false, $targetMustMatchVersion = true) { + // note that the migrations are indexed by name (aka creation time) in ascending order $migrations = $this->getMigrations(); - $versionLog = $this->getEnvironment($environment)->getVersionLog(); - $versions = array_keys($versionLog); - ksort($migrations); - sort($versions); + // note that the version log are also indexed by name with the proper ascending order according to the version order + $executedVersions = $this->getEnvironment($environment)->getVersionLog(); + + if ($target === "0") { + $target = 0; + } + + // get a list of migrations sorted in the opposite way of the executed versions + $sortedMigrations = array(); + + foreach ($executedVersions as $versionCreationTime => &$executedVersion) { + // if we have a date (ie. the target must not match a version) and we are sorting by execution time, we + // convert the version start time so we can compare directly with the target date + if (!$this->getConfig()->isVersionOrderCreationTime() && !$targetMustMatchVersion) { + $dateTime = \DateTime::createFromFormat('Y-m-d H:i:s', $executedVersion['start_time']); + $executedVersion['start_time'] = $dateTime->format('YmdHis'); + } + + if (isset($migrations[$versionCreationTime])) { + array_unshift($sortedMigrations, $migrations[$versionCreationTime]); + } else { + // this means the version is missing so we unset it so that we don't consider it when rolling back + // migrations (or choosing the last up version as target) + unset($executedVersions[$versionCreationTime]); + } + } // Check we have at least 1 migration to revert - if (empty($versions) || $version == end($versions)) { + $executedVersionCreationTimes = array_keys($executedVersions); + if (empty($executedVersionCreationTimes) || $target == end($executedVersionCreationTimes)) { $this->getOutput()->writeln('No migrations to rollback'); return; } - // If no target version was supplied, revert the last migration - if (null === $version) { + // If no target was supplied, revert the last migration + if (null === $target) { // Get the migration before the last run migration - $prev = count($versions) - 2; - $version = $prev < 0 ? 0 : $versions[$prev]; - } else { - // Get the first migration number - $first = $versions[0]; - - // If the target version is before the first migration, revert all migrations - if ($version < $first) { - $version = 0; - } + $prev = count($executedVersionCreationTimes) - 2; + $target = $prev >= 0 ? $executedVersionCreationTimes[$prev] : $executedVersionCreationTimes[0]; } - // Check the target version exists - if (0 !== $version && !isset($migrations[$version])) { - $this->getOutput()->writeln("Target version ($version) not found"); + // If the target must match a version, check the target version exists + if ($targetMustMatchVersion && 0 !== $target && !isset($migrations[$target])) { + $this->getOutput()->writeln("Target version ($target) not found"); return; } - // Revert the migration(s) - krsort($migrations); - foreach ($migrations as $migration) { - if ($migration->getVersion() <= $version) { + // Rollback all versions until we find the wanted rollback target + $rollbacked = false; + + foreach ($sortedMigrations as $migration) { + if ($targetMustMatchVersion && $migration->getVersion() == $target) { break; } - if (in_array($migration->getVersion(), $versions)) { - if (isset($versionLog[$migration->getVersion()]) && 0 != $versionLog[$migration->getVersion()]['breakpoint'] && !$force){ + if (in_array($migration->getVersion(), $executedVersionCreationTimes)) { + $executedVersion = $executedVersions[$migration->getVersion()]; + + if (!$targetMustMatchVersion) { + if (($this->getConfig()->isVersionOrderCreationTime() && $executedVersion['version'] <= $target) || + (!$this->getConfig()->isVersionOrderCreationTime() && $executedVersion['start_time'] <= $target)) { + break; + } + } + + if (0 != $executedVersion['breakpoint'] && !$force){ $this->getOutput()->writeln('Breakpoint reached. Further rollbacks inhibited.'); break; } $this->executeMigration($environment, $migration, MigrationInterface::DOWN); + $rollbacked = true; } } + + if (!$rollbacked) { + $this->getOutput()->writeln('No migrations to rollback'); + } } /** @@ -487,7 +558,10 @@ public function getEnvironment($name) } // create an environment instance and cache it - $environment = new Environment($name, $this->getConfig()->getEnvironment($name)); + $envOptions = $this->getConfig()->getEnvironment($name); + $envOptions['version_order'] = $this->getConfig()->getVersionOrder(); + + $environment = new Environment($name, $envOptions); $this->environments[$name] = $environment; $environment->setInput($this->getInput()); $environment->setOutput($this->getOutput()); @@ -552,7 +626,8 @@ public function setMigrations(array $migrations) } /** - * Gets an array of the database migrations. + * Gets an array of the database migrations, indexed by migration name (aka creation time) and sorted in ascending + * order * * @throws \InvalidArgumentException * @return AbstractMigration[] diff --git a/src/Phinx/Migration/Manager/Environment.php b/src/Phinx/Migration/Manager/Environment.php index 58bee2081..e2986cc7e 100644 --- a/src/Phinx/Migration/Manager/Environment.php +++ b/src/Phinx/Migration/Manager/Environment.php @@ -259,7 +259,8 @@ public function getVersions() } /** - * Get all migration log entries, indexed by version number. + * Get all migration log entries, indexed by version creation time and sorted ascendingly by the configuration's + * version_order option * * @return array */ diff --git a/tests/Phinx/Config/ConfigTest.php b/tests/Phinx/Config/ConfigTest.php index abf56fe8c..e0de25943 100644 --- a/tests/Phinx/Config/ConfigTest.php +++ b/tests/Phinx/Config/ConfigTest.php @@ -18,6 +18,8 @@ class ConfigTest extends AbstractConfigTest public function testConstructEmptyArguments() { $config = new Config(array()); + // this option is set to its default value when not being passed in the constructor, so we can ignore it + unset($config['version_order']); $this->assertAttributeEmpty('values', $config); $this->assertAttributeEquals(null, 'configFilePath', $config); $this->assertNull($config->getConfigFilePath()); @@ -235,4 +237,51 @@ public function testGetMigrationBaseClassNameNoNamespaceNoDrop() $config = new Config(array('migration_base_class' => 'BaseMigration')); $this->assertEquals('BaseMigration', $config->getMigrationBaseClassName(false)); } + + /** + * @covers \Phinx\Config\Config::getVersionOrder + */ + public function testGetVersionOrder() + { + $config = new \Phinx\Config\Config(array()); + $config['version_order'] = \Phinx\Config\Config::VERSION_ORDER_EXECUTION_TIME; + $this->assertEquals(\Phinx\Config\Config::VERSION_ORDER_EXECUTION_TIME, $config->getVersionOrder()); + } + + /** + * @covers \Phinx\Config\Config::isVersionOrderCreationTime + * @dataProvider isVersionOrderCreationTimeDataProvider + */ + public function testIsVersionOrderCreationTime($versionOrder, $expected) + { + // get config stub + $configStub = $this->getMockBuilder('\Phinx\Config\Config') + ->setMethods(array('getVersionOrder')) + ->setConstructorArgs(array(array())) + ->getMock(); + + $configStub->expects($this->once()) + ->method('getVersionOrder') + ->will($this->returnValue($versionOrder)); + + $this->assertEquals($expected, $configStub->isVersionOrderCreationTime()); + } + + /** + * @covers \Phinx\Config\Config::isVersionOrderCreationTime + */ + public function isVersionOrderCreationTimeDataProvider() + { + return [ + 'With Creation Time Version Order' => + [ + \Phinx\Config\Config::VERSION_ORDER_CREATION_TIME, true + ], + 'With Execution Time Version Order' => + [ + \Phinx\Config\Config::VERSION_ORDER_EXECUTION_TIME, false + ], + ]; + } + } diff --git a/tests/Phinx/Console/Command/RollbackTest.php b/tests/Phinx/Console/Command/RollbackTest.php index 997ae9a5d..623395eae 100644 --- a/tests/Phinx/Console/Command/RollbackTest.php +++ b/tests/Phinx/Console/Command/RollbackTest.php @@ -31,6 +31,11 @@ class RollbackTest extends \PHPUnit_Framework_TestCase */ protected $output; + /** + * Default Test Environment + */ + const DEFAULT_TEST_ENVIRONMENT = 'development'; + protected function setUp() { $this->config = new Config(array( @@ -69,7 +74,8 @@ public function testExecute() ->setConstructorArgs([$this->config, $this->input, $this->output]) ->getMock(); $managerStub->expects($this->once()) - ->method('rollback'); + ->method('rollback') + ->with(self::DEFAULT_TEST_ENVIRONMENT, null, false, true); $command->setConfig($this->config); $command->setManager($managerStub); @@ -77,7 +83,12 @@ public function testExecute() $commandTester = new CommandTester($command); $commandTester->execute(array('command' => $command->getName()), array('decorated' => false)); - $this->assertRegExp('/no environment specified/', $commandTester->getDisplay()); + $display = $commandTester->getDisplay(); + + $this->assertRegExp('/no environment specified/', $display); + + // note that the default order is by creation time + $this->assertRegExp('/ordering by creation time/', $display); } public function testExecuteWithEnvironmentOption() @@ -94,7 +105,8 @@ public function testExecuteWithEnvironmentOption() ->setConstructorArgs([$this->config, $this->input, $this->output]) ->getMock(); $managerStub->expects($this->once()) - ->method('rollback'); + ->method('rollback') + ->with('fakeenv', null, false, true); $command->setConfig($this->config); $command->setManager($managerStub); @@ -118,7 +130,8 @@ public function testDatabaseNameSpecified() ->setConstructorArgs([$this->config, $this->input, $this->output]) ->getMock(); $managerStub->expects($this->once()) - ->method('rollback'); + ->method('rollback') + ->with(self::DEFAULT_TEST_ENVIRONMENT, null, false); $command->setConfig($this->config); $command->setManager($managerStub); @@ -127,4 +140,147 @@ public function testDatabaseNameSpecified() $commandTester->execute(array('command' => $command->getName()), array('decorated' => false)); $this->assertRegExp('/using database development/', $commandTester->getDisplay()); } + + public function testStartTimeVersionOrder() + { + $application = new \Phinx\Console\PhinxApplication('testing'); + $application->add(new Rollback()); + + // setup dependencies + $this->config['version_order'] = \Phinx\Config\Config::VERSION_ORDER_EXECUTION_TIME; + + $command = $application->find('rollback'); + + // mock the manager class + $managerStub = $this->getMockBuilder('\Phinx\Migration\Manager') + ->setConstructorArgs(array($this->config, $this->input, $this->output)) + ->getMock(); + + $managerStub->expects($this->once()) + ->method('rollback') + ->with(self::DEFAULT_TEST_ENVIRONMENT, null, false, true); + + $command->setConfig($this->config); + $command->setManager($managerStub); + + $commandTester = new CommandTester($command); + $commandTester->execute(array('command' => $command->getName()), array('decorated' => false)); + $this->assertRegExp('/ordering by execution time/', $commandTester->getDisplay()); + } + + public function testWithDate() + { + $application = new \Phinx\Console\PhinxApplication('testing'); + + $date = '20160101'; + $target = '20160101000000'; + $rollbackStub = $this->getMockBuilder('\Phinx\Console\Command\Rollback') + ->setMethods(array('getTargetFromDate')) + ->getMock(); + + $rollbackStub->expects($this->once()) + ->method('getTargetFromDate') + ->with($date) + ->will($this->returnValue($target)); + + $application->add($rollbackStub); + + // setup dependencies + $command = $application->find('rollback'); + + // mock the manager class + $managerStub = $this->getMockBuilder('\Phinx\Migration\Manager') + ->setConstructorArgs(array($this->config, $this->input, $this->output)) + ->getMock(); + $managerStub->expects($this->once()) + ->method('rollback') + ->with(self::DEFAULT_TEST_ENVIRONMENT, $target, false, false); + + $command->setConfig($this->config); + $command->setManager($managerStub); + + $commandTester = new CommandTester($command); + $commandTester->execute(array('command' => $command->getName(), '-d' => $date), array('decorated' => false)); + } + + /** + * @dataProvider getTargetFromDataProvider + */ + public function testGetTargetFromDate($date, $expectedTarget) + { + $rollbackCommand = new Rollback(); + $this->assertEquals($expectedTarget, $rollbackCommand->getTargetFromDate($date)); + } + + public function getTargetFromDataProvider() + { + return [ + 'Date with only year' => [ + '2015', '20150101000000' + ], + 'Date with year and month' => [ + '201409', '20140901000000' + ], + 'Date with year, month and day' => [ + '20130517', '20130517000000' + ], + 'Date with year, month, day and hour' => [ + '2013051406', '20130514060000' + ], + 'Date with year, month, day, hour and minutes' => [ + '201305140647', '20130514064700' + ], + 'Date with year, month, day, hour, minutes and seconds' => [ + '20130514064726', '20130514064726' + ] + ]; + } + + + /** + * @dataProvider getTargetFromDateThrowsExceptionDataProvider + * @expectedException InvalidArgumentException + * @expectedExceptionMessage Invalid date. Format is YYYY[MM[DD[HH[II[SS]]]]]. + */ + public function testGetTargetFromDateThrowsException($invalidDate) + { + $rollbackCommand = new Rollback(); + $rollbackCommand->getTargetFromDate($invalidDate); + } + + public function getTargetFromDateThrowsExceptionDataProvider() + { + return [ + ['20'], + ['2015060522354698'], + ['invalid'] + ]; + } + + public function testStarTimeVersionOrderWithDate() + { + $application = new \Phinx\Console\PhinxApplication('testing'); + $application->add(new Rollback()); + + // setup dependencies + $this->config['version_order'] = \Phinx\Config\Config::VERSION_ORDER_EXECUTION_TIME; + + $command = $application->find('rollback'); + + // mock the manager class + $targetDate = '20150101'; + $managerStub = $this->getMockBuilder('\Phinx\Migration\Manager') + ->setConstructorArgs(array($this->config, $this->input, $this->output)) + ->getMock(); + $managerStub->expects($this->once()) + ->method('rollback') + ->with(self::DEFAULT_TEST_ENVIRONMENT, '20150101000000', false, false); + + $command->setConfig($this->config); + $command->setManager($managerStub); + + $commandTester = new CommandTester($command); + $commandTester->execute(array('command' => $command->getName(), '-d' => $targetDate), array('decorated' => false)); + $this->assertRegExp('/ordering by execution time/', $commandTester->getDisplay()); + } } diff --git a/tests/Phinx/Console/Command/StatusTest.php b/tests/Phinx/Console/Command/StatusTest.php index e2fdd66fe..9d4059a2f 100644 --- a/tests/Phinx/Console/Command/StatusTest.php +++ b/tests/Phinx/Console/Command/StatusTest.php @@ -31,6 +31,11 @@ class StatusTest extends \PHPUnit_Framework_TestCase */ protected $output; + /** + * Default Test Environment + */ + const DEFAULT_TEST_ENVIRONMENT = 'development'; + protected function setUp() { $this->config = new Config(array( @@ -70,6 +75,7 @@ public function testExecute() ->getMock(); $managerStub->expects($this->once()) ->method('printStatus') + ->with(self::DEFAULT_TEST_ENVIRONMENT, null) ->will($this->returnValue(0)); $command->setConfig($this->config); @@ -79,7 +85,12 @@ public function testExecute() $return = $commandTester->execute(array('command' => $command->getName()), array('decorated' => false)); $this->assertEquals(0, $return); - $this->assertRegExp('/no environment specified/', $commandTester->getDisplay()); + + $display = $commandTester->getDisplay(); + $this->assertRegExp('/no environment specified/', $display); + + // note that the default order is by creation time + $this->assertRegExp('/ordering by creation time/', $display); } public function testExecuteWithEnvironmentOption() @@ -97,6 +108,7 @@ public function testExecuteWithEnvironmentOption() ->getMock(); $managerStub->expects($this->once()) ->method('printStatus') + ->with('fakeenv', null) ->will($this->returnValue(0)); $command->setConfig($this->config); @@ -123,6 +135,7 @@ public function testFormatSpecified() ->getMock(); $managerStub->expects($this->once()) ->method('printStatus') + ->with(self::DEFAULT_TEST_ENVIRONMENT, 'json') ->will($this->returnValue(0)); $command->setConfig($this->config); @@ -133,4 +146,37 @@ public function testFormatSpecified() $this->assertEquals(0, $return); $this->assertRegExp('/using format json/', $commandTester->getDisplay()); } + + public function testExecuteVersionOrderByExecutionTime() + { + $application = new PhinxApplication('testing'); + $application->add(new Status()); + + /** @var Status $command */ + $command = $application->find('status'); + + // mock the manager class + /** @var Manager|PHPUnit_Framework_MockObject_MockObject $managerStub */ + $managerStub = $this->getMockBuilder('\Phinx\Migration\Manager') + ->setConstructorArgs(array($this->config, $this->input, $this->output)) + ->getMock(); + $managerStub->expects($this->once()) + ->method('printStatus') + ->with(self::DEFAULT_TEST_ENVIRONMENT, null) + ->will($this->returnValue(0)); + + $this->config['version_order'] = \Phinx\Config\Config::VERSION_ORDER_EXECUTION_TIME; + + $command->setConfig($this->config); + $command->setManager($managerStub); + + $commandTester = new CommandTester($command); + $return = $commandTester->execute(array('command' => $command->getName()), array('decorated' => false)); + + $this->assertEquals(0, $return); + + $display = $commandTester->getDisplay(); + $this->assertRegExp('/no environment specified/', $display); + $this->assertRegExp('/ordering by execution time/', $display); + } } diff --git a/tests/Phinx/Db/Adapter/PdoAdapterTest.php b/tests/Phinx/Db/Adapter/PdoAdapterTest.php index d899d4e36..012c62539 100644 --- a/tests/Phinx/Db/Adapter/PdoAdapterTest.php +++ b/tests/Phinx/Db/Adapter/PdoAdapterTest.php @@ -30,4 +30,73 @@ public function testSchemaTableName() $this->adapter->setSchemaTableName('schema_table_test'); $this->assertEquals('schema_table_test', $this->adapter->getSchemaTableName()); } + + /** + * @dataProvider getVersionLogDataProvider + */ + public function testGetVersionLog($versionOrder, $expectedOrderBy) + { + $adapter = $this->getMockForAbstractClass('\Phinx\Db\Adapter\PdoAdapter', + array(array('version_order' => $versionOrder)), '', true, true, true, + array('fetchAll', 'getSchemaTableName')); + + $schemaTableName = 'log'; + $adapter->expects($this->once()) + ->method('getSchemaTableName') + ->will($this->returnValue($schemaTableName)); + + $mockRows = array ( + array( + 'version' => '20120508120534', + 'key' => 'value' + ), + array( + 'version' => '20130508120534', + 'key' => 'value' + ), + ); + + $adapter->expects($this->once()) + ->method('fetchAll') + ->with("SELECT * FROM $schemaTableName ORDER BY $expectedOrderBy") + ->will($this->returnValue($mockRows)); + + // we expect the mock rows but indexed by version creation time + $expected = array( + '20120508120534' => array( + 'version' => '20120508120534', + 'key' => 'value' + ), + '20130508120534' => array( + 'version' => '20130508120534', + 'key' => 'value' + ), + ); + + $this->assertEquals($expected, $adapter->getVersionLog()); + } + + public function getVersionLogDataProvider() + { + return array( + 'With Creation Time Version Order' => array( + \Phinx\Config\Config::VERSION_ORDER_CREATION_TIME, 'version ASC' + ), + 'With Execution Time Version Order' => array( + \Phinx\Config\Config::VERSION_ORDER_EXECUTION_TIME, 'start_time ASC, version ASC' + ), + ); + } + + /** + * @expectedException RuntimeException + * @expectedExceptionMessage Invalid version_order configuration option + */ + public function testGetVersionLogInvalidVersionOrderKO() + { + $adapter = $this->getMockForAbstractClass('\Phinx\Db\Adapter\PdoAdapter', + array(array('version_order' => 'invalid'))); + + $adapter->getVersionLog(); + } } diff --git a/tests/Phinx/Migration/ManagerTest.php b/tests/Phinx/Migration/ManagerTest.php index 63be3cb03..1c68b9c13 100644 --- a/tests/Phinx/Migration/ManagerTest.php +++ b/tests/Phinx/Migration/ManagerTest.php @@ -216,8 +216,63 @@ public function testPrintStatusMethodWithMissingMigrations() rewind($this->manager->getOutput()->getStream()); $outputStr = stream_get_contents($this->manager->getOutput()->getStream()); - $this->assertRegExp('/up 20120103083300 2012-01-11 23:53:36 2012-01-11 23:53:37 *\*\* MISSING \*\*/', $outputStr); - $this->assertRegExp('/up 20120815145812 2012-01-16 18:35:40 2012-01-16 18:35:41 Example *\*\* MISSING \*\*/', $outputStr); + + // note that the order is important: missing migrations should appear before down migrations + $this->assertRegExp('/\s*up 20120103083300 2012-01-11 23:53:36 2012-01-11 23:53:37 *\*\* MISSING \*\*'.PHP_EOL. + '\s*up 20120815145812 2012-01-16 18:35:40 2012-01-16 18:35:41 Example *\*\* MISSING \*\*'.PHP_EOL. + '\s*down 20120111235330 TestMigration'.PHP_EOL. + '\s*down 20120116183504 TestMigration2/', $outputStr); + } + + public function testPrintStatusMethodWithMissingLastMigration() + { + // stub environment + $envStub = $this->getMockBuilder('\Phinx\Migration\Manager\Environment') + ->setConstructorArgs(array('mockenv', array())) + ->getMock(); + $envStub->expects($this->once()) + ->method('getVersionLog') + ->will($this->returnValue( + array ( + '20120111235330' => + array ( + 'version' => '20120111235330', + 'start_time' => '2012-01-16 18:35:40', + 'end_time' => '2012-01-16 18:35:41', + 'migration_name' => '', + 'breakpoint' => 0 + ), + '20120116183504' => + array ( + 'version' => '20120116183504', + 'start_time' => '2012-01-16 18:35:40', + 'end_time' => '2012-01-16 18:35:41', + 'migration_name' => '', + 'breakpoint' => '0', + ), + '20120120145114' => + array ( + 'version' => '20120120145114', + 'start_time' => '2012-01-20 14:51:14', + 'end_time' => '2012-01-20 14:51:14', + 'migration_name' => 'Example', + 'breakpoint' => '0', + ), + ) + )); + + $this->manager->setEnvironments(array('mockenv' => $envStub)); + $this->manager->getOutput()->setDecorated(false); + $return = $this->manager->printStatus('mockenv'); + $this->assertEquals(Manager::EXIT_STATUS_MISSING, $return); + + rewind($this->manager->getOutput()->getStream()); + $outputStr = stream_get_contents($this->manager->getOutput()->getStream()); + + // note that the order is important: missing migrations should appear before down migrations + $this->assertRegExp('/\s*up 20120111235330 2012-01-16 18:35:40 2012-01-16 18:35:41 TestMigration'.PHP_EOL. + '\s*up 20120116183504 2012-01-16 18:35:40 2012-01-16 18:35:41 TestMigration2'.PHP_EOL. + '\s*up 20120120145114 2012-01-20 14:51:14 2012-01-20 14:51:14 Example *\*\* MISSING \*\*/', $outputStr); } public function testPrintStatusMethodWithMissingMigrationsAndBreakpointSet() @@ -288,6 +343,134 @@ public function testPrintStatusMethodWithDownMigrations() $this->assertRegExp('/up 20120111235330 2012-01-16 18:35:40 2012-01-16 18:35:41 TestMigration/', $outputStr); $this->assertRegExp('/down 20120116183504 TestMigration2/', $outputStr); } + + public function testPrintStatusMethodWithMissingAndDownMigrations() + { + // stub environment + $envStub = $this->getMockBuilder('\Phinx\Migration\Manager\Environment') + ->setConstructorArgs(array('mockenv', array())) + ->getMock(); + $envStub->expects($this->once()) + ->method('getVersionLog') + ->will($this->returnValue(array( + '20120111235330' => + array ( + 'version' => '20120111235330', + 'start_time' => '2012-01-16 18:35:40', + 'end_time' => '2012-01-16 18:35:41', + 'migration_name' => '', + 'breakpoint' => 0 + ), + '20120103083300' => + array ( + 'version' => '20120103083300', + 'start_time' => '2012-01-11 23:53:36', + 'end_time' => '2012-01-11 23:53:37', + 'migration_name' => '', + 'breakpoint' => 0, + ), + '20120815145812' => + array ( + 'version' => '20120815145812', + 'start_time' => '2012-01-16 18:35:40', + 'end_time' => '2012-01-16 18:35:41', + 'migration_name' => 'Example', + 'breakpoint' => 0, + )))); + + $this->manager->setEnvironments(array('mockenv' => $envStub)); + $this->manager->getOutput()->setDecorated(false); + $return = $this->manager->printStatus('mockenv'); + $this->assertEquals(Manager::EXIT_STATUS_MISSING, $return); + + rewind($this->manager->getOutput()->getStream()); + $outputStr = stream_get_contents($this->manager->getOutput()->getStream()); + + // note that the order is important: missing migrations should appear before down migrations (and in the right + // place with regard to other up non-missing migrations) + $this->assertRegExp('/\s*up 20120103083300 2012-01-11 23:53:36 2012-01-11 23:53:37 *\*\* MISSING \*\*'.PHP_EOL. + '\s*up 20120111235330 2012-01-16 18:35:40 2012-01-16 18:35:41 TestMigration'.PHP_EOL. + '\s*down 20120116183504 TestMigration2/', $outputStr); + } + + /** + * Test that ensures the status header is correctly printed with regards to the version order + * + * @dataProvider statusVersionOrderProvider + * + * @param array $config + * @param string $expectedStatusHeader + */ + public function testPrintStatusMethodVersionOrderHeader($config, $expectedStatusHeader) + { + // stub environment + $envStub = $this->getMockBuilder('\Phinx\Migration\Manager\Environment') + ->setConstructorArgs(array('mockenv', array())) + ->getMock(); + $envStub->expects($this->once()) + ->method('getVersionLog') + ->will($this->returnValue(array())); + + $output = new RawBufferedOutput(); + $this->manager = new Manager($config, $this->input, $output); + + $this->manager->setEnvironments(array('mockenv' => $envStub)); + $return = $this->manager->printStatus('mockenv'); + $this->assertEquals(Manager::EXIT_STATUS_DOWN, $return); + + $outputStr = $this->manager->getOutput()->fetch(); + $this->assertContains($expectedStatusHeader, $outputStr); + } + + public function statusVersionOrderProvider() + { + // create the necessary configuration objects + $configArray = $this->getConfigArray(); + + $configWithNoVersionOrder = new Config($configArray); + + $configArray['version_order'] = Config::VERSION_ORDER_CREATION_TIME; + $configWithCreationVersionOrder = new Config($configArray); + + $configArray['version_order'] = Config::VERSION_ORDER_EXECUTION_TIME; + $configWithExecutionVersionOrder = new Config($configArray); + + return [ + 'With the default version order' => [ + $configWithNoVersionOrder, + ' Status [Migration ID] Started Finished Migration Name ' + ], + 'With the creation version order' => [ + $configWithCreationVersionOrder, + ' Status [Migration ID] Started Finished Migration Name ' + ], + 'With the execution version order' => [ + $configWithExecutionVersionOrder, + ' Status Migration ID [Started ] Finished Migration Name ' + ] + ]; + } + + /** + * @expectedException RuntimeException + * @expectedExceptionMessage Invalid version_order configuration option + */ + public function testPrintStatusInvalidVersionOrderKO() + { + // stub environment + $envStub = $this->getMockBuilder('\Phinx\Migration\Manager\Environment') + ->setConstructorArgs(array('mockenv', array())) + ->getMock(); + + $configArray = $this->getConfigArray(); + $configArray['version_order'] ='invalid'; + $config = new Config($configArray); + + $this->manager = new Manager($config, $this->input, $this->output); + + $this->manager->setEnvironments(array('mockenv' => $envStub)); + $this->manager->printStatus('mockenv'); + } public function testGetMigrationsWithDuplicateMigrationVersions() { @@ -375,11 +558,12 @@ public function testMigrationsByDate(array $availableMigrations, $dateString, $e } /** - * Test that migrating by date chooses the correct migration to point to. + * Test that rollbacking to version chooses the correct + * migration to point to. * - * @dataProvider rollbackDateDataProvider + * @dataProvider rollbackToVersionDataProvider */ - public function testRollbacksByDate(array $availableRollbacks, $dateString, $expectedRollback, $message) + public function testRollbackToVersion($availableRollbacks, $version, $expectedOutput) { // stub environment $envStub = $this->getMockBuilder('\Phinx\Migration\Manager\Environment') @@ -388,22 +572,144 @@ public function testRollbacksByDate(array $availableRollbacks, $dateString, $exp $envStub->expects($this->any()) ->method('getVersionLog') ->will($this->returnValue($availableRollbacks)); + + $this->manager->setEnvironments(array('mockenv' => $envStub)); + $this->manager->rollback('mockenv', $version); + rewind($this->manager->getOutput()->getStream()); + $output = stream_get_contents($this->manager->getOutput()->getStream()); + if (is_null($expectedOutput)) { + $this->assertEquals("No migrations to rollback".PHP_EOL, $output); + } else { + if (is_string($expectedOutput)) { + $expectedOutput = [$expectedOutput]; + } + + foreach ($expectedOutput as $expectedLine) { + $this->assertContains($expectedLine, $output); + } + } + } + + /** + * Test that rollbacking to date chooses the correct + * migration to point to. + * + * @dataProvider rollbackToDateDataProvider + */ + public function testRollbackToDate($availableRollbacks, $version, $expectedOutput) + { + // stub environment + $envStub = $this->getMockBuilder('\Phinx\Migration\Manager\Environment') + ->setConstructorArgs(array('mockenv', array())) + ->getMock(); $envStub->expects($this->any()) - ->method('getVersions') - ->will($this->returnValue(array_keys($availableRollbacks))); + ->method('getVersionLog') + ->will($this->returnValue($availableRollbacks)); $this->manager->setEnvironments(array('mockenv' => $envStub)); - $this->manager->rollbackToDateTime('mockenv', new \DateTime($dateString)); + $this->manager->rollback('mockenv', $version, false, false); rewind($this->manager->getOutput()->getStream()); $output = stream_get_contents($this->manager->getOutput()->getStream()); - if (is_null($expectedRollback)) { - $this->assertEmpty($output, $message); + if (is_null($expectedOutput)) { + $this->assertEquals("No migrations to rollback".PHP_EOL, $output); } else { - $this->assertRegExp($expectedRollback, $output, $message); + if (is_string($expectedOutput)) { + $expectedOutput = [$expectedOutput]; + } + + foreach ($expectedOutput as $expectedLine) { + $this->assertContains($expectedLine, $output); + } } } + + /** + * Test that rollbacking to version by execution time chooses the correct + * migration to point to. + * + * @dataProvider rollbackToVersionByExecutionTimeDataProvider + */ + public function testRollbackToVersionByExecutionTime($availableRollbacks, $version, $expectedOutput) + { + // stub environment + $envStub = $this->getMockBuilder('\Phinx\Migration\Manager\Environment') + ->setConstructorArgs(array('mockenv', array())) + ->getMock(); + $envStub->expects($this->any()) + ->method('getVersionLog') + ->will($this->returnValue($availableRollbacks)); + + // get a manager with a config whose version order is set to execution time + $configArray = $this->getConfigArray(); + $configArray['version_order'] = \Phinx\Config\Config::VERSION_ORDER_EXECUTION_TIME; + $config = new Config($configArray); + $this->input = new ArrayInput([]); + $this->output = new StreamOutput(fopen('php://memory', 'a', false)); + $this->output->setDecorated(false); + + $this->manager = new Manager($config, $this->input, $this->output); + $this->manager->setEnvironments(array('mockenv' => $envStub)); + $this->manager->rollback('mockenv', $version); + rewind($this->manager->getOutput()->getStream()); + $output = stream_get_contents($this->manager->getOutput()->getStream()); - public function testRollbackWithSingleMigrationDoesNotFail() + if (is_null($expectedOutput)) { + $this->assertEmpty($output); + } else { + if (is_string($expectedOutput)) { + $expectedOutput = [$expectedOutput]; + } + + foreach ($expectedOutput as $expectedLine) { + $this->assertContains($expectedLine, $output); + } + } + } + + /** + * Test that rollbacking to date by execution time chooses the correct + * migration to point to. + * + * @dataProvider rollbackToDateByExecutionTimeDataProvider + */ + public function testRollbackToDateByExecutionTime($availableRollbacks, $date, $expectedOutput) + { + // stub environment + $envStub = $this->getMockBuilder('\Phinx\Migration\Manager\Environment') + ->setConstructorArgs(array('mockenv', array())) + ->getMock(); + $envStub->expects($this->any()) + ->method('getVersionLog') + ->will($this->returnValue($availableRollbacks)); + + // get a manager with a config whose version order is set to execution time + $configArray = $this->getConfigArray(); + $configArray['version_order'] = \Phinx\Config\Config::VERSION_ORDER_EXECUTION_TIME; + $config = new Config($configArray); + $this->input = new ArrayInput([]); + $this->output = new StreamOutput(fopen('php://memory', 'a', false)); + $this->output->setDecorated(false); + + $this->manager = new Manager($config, $this->input, $this->output); + $this->manager->setEnvironments(array('mockenv' => $envStub)); + $this->manager->rollback('mockenv', $date, false, false); + rewind($this->manager->getOutput()->getStream()); + $output = stream_get_contents($this->manager->getOutput()->getStream()); + + if (is_null($expectedOutput)) { + $this->assertEquals("No migrations to rollback".PHP_EOL, $output); + } else { + if (is_string($expectedOutput)) { + $expectedOutput = [$expectedOutput]; + } + + foreach ($expectedOutput as $expectedLine) { + $this->assertContains($expectedLine, $output); + } + } + } + + public function testRollbackToVersionWithSingleMigrationDoesNotFail() { // stub environment $envStub = $this->getMockBuilder('\Phinx\Migration\Manager\Environment') @@ -425,7 +731,7 @@ public function testRollbackWithSingleMigrationDoesNotFail() $this->assertNotContains('Undefined offset: -1', $output); } - public function testRollbackWithTwoMigrationsDoesNotRollbackBothMigrations() + public function testRollbackToVersionWithTwoMigrationsDoesNotRollbackBothMigrations() { // stub environment $envStub = $this->getMockBuilder('\Phinx\Migration\Manager\Environment') @@ -459,6 +765,46 @@ public function testRollbackWithTwoMigrationsDoesNotRollbackBothMigrations() $this->assertNotContains('== 20120111235330 TestMigration: reverting', $output); } + /** + * Test that rollbacking last migration + * + * @dataProvider rollbackLastDataProvider + */ + public function testRollbackLast($availableRolbacks, $versionOrder, $expectedOutput) + { + // stub environment + $envStub = $this->getMockBuilder('\Phinx\Migration\Manager\Environment') + ->setConstructorArgs(array('mockenv', array())) + ->getMock(); + $envStub->expects($this->any()) + ->method('getVersionLog') + ->will($this->returnValue($availableRolbacks)); + + // get a manager with a config whose version order is set to execution time + $configArray = $this->getConfigArray(); + $configArray['version_order'] = $versionOrder; + $config = new Config($configArray); + $this->input = new ArrayInput([]); + $this->output = new StreamOutput(fopen('php://memory', 'a', false)); + $this->output->setDecorated(false); + $this->manager = new Manager($config, $this->input, $this->output); + $this->manager->setEnvironments(array('mockenv' => $envStub)); + $this->manager->rollback('mockenv', null); + rewind($this->manager->getOutput()->getStream()); + $output = stream_get_contents($this->manager->getOutput()->getStream()); + if (is_null($expectedOutput)) { + $this->assertEquals("No migrations to rollback".PHP_EOL, $output); + } else { + if (is_string($expectedOutput)) { + $expectedOutput = [$expectedOutput]; + } + + foreach ($expectedOutput as $expectedLine) { + $this->assertContains($expectedLine, $output); + } + } + } + /** * Migration lists, dates, and expected migrations to point to. * @@ -475,208 +821,1253 @@ public function migrateDateDataProvider() } /** - * Migration lists, dates, and expected migrations to point to. + * Migration lists, dates, and expected migration version to rollback to. * * @return array */ - public function rollbackDateDataProvider() + public function rollbackToDateDataProvider() { return [ // No breakpoints set - [ + 'Rollback to date which is later than all migrations - no breakpoints set' => [ - '20120111235330' => ['version' => '20120111235330', 'migration' => '', 'breakpoint' => 0], - '20120116183504' => ['version' => '20120116183504', 'migration' => '', 'breakpoint' => 0], + [ + '20120111235330' => ['version' => '20120111235330', 'migration_name' => '', 'breakpoint' => 0], + '20120116183504' => ['version' => '20120116183504', 'migration_name' => '', 'breakpoint' => 0], + ], + '20130118000000', + null ], - '20130118', - null, - 'Failed to rollback 0 migrations when rollback to date is later than all migrations - no breakpoints set', - ], - [ + 'Rollback to date of the most recent migration - no breakpoints set' => [ - '20120111235330' => ['version' => '20120111235330', 'migration' => '', 'breakpoint' => 0], - '20120116183504' => ['version' => '20120116183504', 'migration' => '', 'breakpoint' => 0], + [ + '20120111235330' => ['version' => '20120111235330', 'migration_name' => '', 'breakpoint' => 0], + '20120116183504' => ['version' => '20120116183504', 'migration_name' => '', 'breakpoint' => 0], + ], + '20120116183504', + null ], - '20120116183504', - '`No migrations to rollback`', - 'Failed to rollback 0 migrations when rollback to date is the most recent migration - no breakpoints set', - ], - [ + 'Rollback to date between 2 migrations - no breakpoints set' => [ - '20120111235330' => ['version' => '20120111235330', 'migration' => '', 'breakpoint' => 0], - '20120116183504' => ['version' => '20120116183504', 'migration' => '', 'breakpoint' => 0], + [ + '20120111235330' => ['version' => '20120111235330', 'migration_name' => '', 'breakpoint' => 0], + '20120116183504' => ['version' => '20120116183504', 'migration_name' => '', 'breakpoint' => 0], + ], + '20120115', + '== 20120116183504 TestMigration2: reverted' ], - '20120115', - '`20120116183504`', - 'Failed to rollback 1 migration when rollback date is between 2 migrations - no breakpoints set', - ], - [ + 'Rollback to date of the oldest migration - no breakpoints set' => [ - '20120111235330' => ['version' => '20120111235330', 'migration' => '', 'breakpoint' => 0], - '20120116183504' => ['version' => '20120116183504', 'migration' => '', 'breakpoint' => 0], + [ + '20120111235330' => ['version' => '20120111235330', 'migration_name' => '', 'breakpoint' => 0], + '20120116183504' => ['version' => '20120116183504', 'migration_name' => '', 'breakpoint' => 0], + ], + '20120111235330', + '== 20120116183504 TestMigration2: reverted' ], - '20120111235330', - '`20120116183504`', - 'Failed to rollback 1 migration when rollback datetime is the one of the migrations - no breakpoints set', - ], - [ + 'Rollback to date before all the migrations - no breakpoints set' => [ - '20120111235330' => ['version' => '20120111235330', 'migration' => '', 'breakpoint' => 0], - '20120116183504' => ['version' => '20120116183504', 'migration' => '', 'breakpoint' => 0], + [ + '20120111235330' => ['version' => '20120111235330', 'migration_name' => '', 'breakpoint' => 0], + '20120116183504' => ['version' => '20120116183504', 'migration_name' => '', 'breakpoint' => 0], + ], + '20110115', + ['== 20120116183504 TestMigration2: reverted', '== 20120111235330 TestMigration: reverted'] ], - '20110115', - '`20120111235330`', - 'Failed to rollback all the migrations when the rollback date is before all the migrations - no breakpoints set', - ], // Breakpoint set on first migration - [ + 'Rollback to date which is later than all migrations - breakpoint set on first migration' => [ - '20120111235330' => ['version' => '20120111235330', 'migration' => '', 'breakpoint' => 1], - '20120116183504' => ['version' => '20120116183504', 'migration' => '', 'breakpoint' => 0], + [ + '20120111235330' => ['version' => '20120111235330', 'migration_name' => '', 'breakpoint' => 1], + '20120116183504' => ['version' => '20120116183504', 'migration_name' => '', 'breakpoint' => 0], + ], + '20130118000000', + null ], - '20130118', - null, - 'Failed to rollback 0 migrations when rollback to date is later than all migrations - breakpoint set on first migration', - ], - [ + 'Rollback to date of the most recent migration - breakpoint set on first migration' => [ - '20120111235330' => ['version' => '20120111235330', 'migration' => '', 'breakpoint' => 1], - '20120116183504' => ['version' => '20120116183504', 'migration' => '', 'breakpoint' => 0], + [ + '20120111235330' => ['version' => '20120111235330', 'migration_name' => '', 'breakpoint' => 1], + '20120116183504' => ['version' => '20120116183504', 'migration_name' => '', 'breakpoint' => 0], + ], + '20120116183504', + null ], - '20120116183504', - '`No migrations to rollback`', - 'Failed to rollback 0 migrations when rollback to date is the most recent migration - breakpoint set on first migration', - ], - [ + 'Rollback to date between 2 migrations - breakpoint set on first migration' => [ - '20120111235330' => ['version' => '20120111235330', 'migration' => '', 'breakpoint' => 1], - '20120116183504' => ['version' => '20120116183504', 'migration' => '', 'breakpoint' => 0], + [ + '20120111235330' => ['version' => '20120111235330', 'migration_name' => '', 'breakpoint' => 1], + '20120116183504' => ['version' => '20120116183504', 'migration_name' => '', 'breakpoint' => 0], + ], + '20120115', + '== 20120116183504 TestMigration2: reverted' ], - '20120115', - '`20120116183504`', - 'Failed to rollback 1 migration when rollback date is between 2 migrations - breakpoint set on first migration', - ], - [ + 'Rollback to date of the oldest migration - breakpoint set on first migration' => [ - '20120111235330' => ['version' => '20120111235330', 'migration' => '', 'breakpoint' => 1], - '20120116183504' => ['version' => '20120116183504', 'migration' => '', 'breakpoint' => 0], + [ + '20120111235330' => ['version' => '20120111235330', 'migration_name' => '', 'breakpoint' => 1], + '20120116183504' => ['version' => '20120116183504', 'migration_name' => '', 'breakpoint' => 0], + ], + '20120111235330', + '== 20120116183504 TestMigration2: reverted' ], - '20120111235330', - '`20120116183504`', - 'Failed to rollback 1 migration when rollback datetime is the one of the migrations - breakpoint set on first migration', - ], - [ + 'Rollback to date before all the migrations - breakpoint set on first migration' => [ - '20120111235330' => ['version' => '20120111235330', 'migration' => '', 'breakpoint' => 1], - '20120116183504' => ['version' => '20120116183504', 'migration' => '', 'breakpoint' => 0], + [ + '20120111235330' => ['version' => '20120111235330', 'migration_name' => '', 'breakpoint' => 1], + '20120116183504' => ['version' => '20120116183504', 'migration_name' => '', 'breakpoint' => 0], + ], + '20110115', + 'Breakpoint reached. Further rollbacks inhibited.' ], - '20110115', - '`(?!.*20120111235330.*)20120116183504.*Breakpoint reached.*`s', - 'Failed to rollback 1 migration when the rollback date is before all the migrations and breakpoint set on first migration', - ], // Breakpoint set on last migration - [ + 'Rollback to date which is later than all migrations - breakpoint set on last migration' => [ - '20120111235330' => ['version' => '20120111235330', 'migration' => '', 'breakpoint' => 0], - '20120116183504' => ['version' => '20120116183504', 'migration' => '', 'breakpoint' => 1], + [ + '20120111235330' => ['version' => '20120111235330', 'migration_name' => '', 'breakpoint' => 0], + '20120116183504' => ['version' => '20120116183504', 'migration_name' => '', 'breakpoint' => 1], + ], + '20130118000000', + null ], - '20130118', - null, - 'Failed to rollback 0 migrations when rollback to date is later than all migrations - breakpoint set on last migration', - ], - [ + 'Rollback to date of the most recent migration - breakpoint set on last migration' => [ - '20120111235330' => ['version' => '20120111235330', 'migration' => '', 'breakpoint' => 0], - '20120116183504' => ['version' => '20120116183504', 'migration' => '', 'breakpoint' => 1], + [ + '20120111235330' => ['version' => '20120111235330', 'migration_name' => '', 'breakpoint' => 0], + '20120116183504' => ['version' => '20120116183504', 'migration_name' => '', 'breakpoint' => 1], + ], + '20120116183504', + null ], - '20120116183504', - '`No migrations to rollback`', - 'Failed to rollback 0 migrations when rollback to date is the most recent migration - breakpoint set on last migration', - ], - [ + 'Rollback to date between 2 migrations - breakpoint set on last migration' => [ - '20120111235330' => ['version' => '20120111235330', 'migration' => '', 'breakpoint' => 0], - '20120116183504' => ['version' => '20120116183504', 'migration' => '', 'breakpoint' => 1], + [ + '20120111235330' => ['version' => '20120111235330', 'migration_name' => '', 'breakpoint' => 0], + '20120116183504' => ['version' => '20120116183504', 'migration_name' => '', 'breakpoint' => 1], + ], + '20120115000000', + 'Breakpoint reached. Further rollbacks inhibited.' ], - '20120115', - '`(?!.*20120116183504.*).*Breakpoint reached.*`s', - 'Failed to rollback 0 migrations when rollback date is between 2 migrations and breakpoint set on last migration', - ], - [ + 'Rollback to date of the oldest migration - breakpoint set on last migration' => [ - '20120111235330' => ['version' => '20120111235330', 'migration' => '', 'breakpoint' => 0], - '20120116183504' => ['version' => '20120116183504', 'migration' => '', 'breakpoint' => 1], + [ + '20120111235330' => ['version' => '20120111235330', 'migration_name' => '', 'breakpoint' => 0], + '20120116183504' => ['version' => '20120116183504', 'migration_name' => '', 'breakpoint' => 1], + ], + '20120111235330', + 'Breakpoint reached. Further rollbacks inhibited.' ], - '20120111235330', - '`(?!.*20120116183504.*).*Breakpoint reached.*`s', - 'Failed to rollback 0 migrations when rollback datetime is the one of the migrations and breakpoint set on last migration', - ], - [ + 'Rollback to date before all the migrations - breakpoint set on last migration' => [ - '20120111235330' => ['version' => '20120111235330', 'migration' => '', 'breakpoint' => 0], - '20120116183504' => ['version' => '20120116183504', 'migration' => '', 'breakpoint' => 1], + [ + '20120111235330' => ['version' => '20120111235330', 'migration_name' => '', 'breakpoint' => 0], + '20120116183504' => ['version' => '20120116183504', 'migration_name' => '', 'breakpoint' => 1], + ], + '20110115000000', + 'Breakpoint reached. Further rollbacks inhibited.' ], - '20110115', - '`(?!.*20120116183504.*).*Breakpoint reached.*`s', - 'Failed to rollback 0 migrations when the rollback date is before all the migrations and breakpoint set on last migration', - ], + + // Breakpoint set on all migrations + + 'Rollback to date which is later than all migrations - breakpoint set on all migrations' => + [ + [ + '20120111235330' => ['version' => '20120111235330', 'migration_name' => '', 'breakpoint' => 1], + '20120116183504' => ['version' => '20120116183504', 'migration_name' => '', 'breakpoint' => 1], + ], + '20130118000000', + null + ], + 'Rollback to date of the most recent migration - breakpoint set on all migrations' => + [ + [ + '20120111235330' => ['version' => '20120111235330', 'migration_name' => '', 'breakpoint' => 1], + '20120116183504' => ['version' => '20120116183504', 'migration_name' => '', 'breakpoint' => 1], + ], + '20120116183504', + null + ], + 'Rollback to date between 2 migrations - breakpoint set on all migrations' => + [ + [ + '20120111235330' => ['version' => '20120111235330', 'migration_name' => '', 'breakpoint' => 1], + '20120116183504' => ['version' => '20120116183504', 'migration_name' => '', 'breakpoint' => 1], + ], + '20120115000000', + 'Breakpoint reached. Further rollbacks inhibited.' + ], + 'Rollback to date of the oldest migration - breakpoint set on all migrations' => + [ + [ + '20120111235330' => ['version' => '20120111235330', 'migration_name' => '', 'breakpoint' => 1], + '20120116183504' => ['version' => '20120116183504', 'migration_name' => '', 'breakpoint' => 1], + ], + '20120111235330', + 'Breakpoint reached. Further rollbacks inhibited.' + ], + 'Rollback to date before all the migrations - breakpoint set on all migrations' => + [ + [ + '20120111235330' => ['version' => '20120111235330', 'migration_name' => '', 'breakpoint' => 1], + '20120116183504' => ['version' => '20120116183504', 'migration_name' => '', 'breakpoint' => 1], + ], + '20110115000000', + 'Breakpoint reached. Further rollbacks inhibited.' + ], + ]; + } + + /** + * Migration lists, dates, and expected migration version to rollback to. + * + * @return array + */ + public function rollbackToDateByExecutionTimeDataProvider() + { + return array( + + // No breakpoints set + + 'Rollback to date later than all migration start times when they were created in a different order than they were executed - no breakpoints set' => + array( + array( + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'breakpoint' => 0), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-20 23:53:30', 'breakpoint' => 0), + ), + '20131212000000', + null + ), + 'Rollback to date earlier than all migration start times when they were created in a different order than they were executed - no breakpoints set' => + array( + array( + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'breakpoint' => 0), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-20 23:53:30', 'breakpoint' => 0), + ), + '20111212000000', + ['== 20120111235330 TestMigration: reverted', '== 20120116183504 TestMigration2: reverted'] + ), + 'Rollback to start time of first created version which was the last to be executed - no breakpoints set' => + array( + array( + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'breakpoint' => 0), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-20 23:53:30', 'breakpoint' => 0), + ), + '20120120235330', + null + ), + 'Rollback to start time of second created version which was the first to be executed - no breakpoints set' => + array( + array( + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'breakpoint' => 0), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-20 23:53:30', 'breakpoint' => 0), + ), + '20120117183504', + '== 20120111235330 TestMigration: reverted' + ), + 'Rollback to date between the 2 migrations when they were created in a different order than they were executed - no breakpoints set' => + array( + array( + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'breakpoint' => 0), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-20 23:53:30', 'breakpoint' => 0), + ), + '20120118000000', + '== 20120111235330 TestMigration: reverted' + ), + 'Rollback the last executed migration when the migrations were created in a different order than they were executed - no breakpoints set' => + array( + array( + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'breakpoint' => 0), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-20 23:53:30', 'breakpoint' => 0), + ), + null, + '== 20120111235330 TestMigration: reverted' + ), + + + // Breakpoint set on first/last created/executed migration + + 'Rollback to date later than all migration start times when they were created in a different order than they were executed - breakpoints set on first created (and last executed) migration' => + array( + array( + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'breakpoint' => 0), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-20 23:53:30', 'breakpoint' => 1), + ), + '20131212000000', + null + ), + 'Rollback to date later than all migration start times when they were created in a different order than they were executed - breakpoints set on first executed (and last created) migration' => + array( + array( + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'breakpoint' => 1), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-20 23:53:30', 'breakpoint' => 0), + ), + '20131212000000', + null + ), + 'Rollback to date earlier than all migration start times when they were created in a different order than they were executed - breakpoints set on first created (and last executed) migration' => + array( + array( + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'breakpoint' => 0), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-20 23:53:30', 'breakpoint' => 1), + ), + '20111212000000', + 'Breakpoint reached. Further rollbacks inhibited.' + ), + 'Rollback to date earlier than all migration start times when they were created in a different order than they were executed - breakpoints set on first executed (and last created) migration' => + array( + array( + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'breakpoint' => 1), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-20 23:53:30', 'breakpoint' => 0), + ), + '20111212000000', + ['== 20120111235330 TestMigration: reverted', 'Breakpoint reached. Further rollbacks inhibited.'] + ), + 'Rollback to start time of first created version which was the last to be executed - breakpoints set on first created (and last executed) migration' => + array( + array( + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'breakpoint' => 0), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-20 23:53:30', 'breakpoint' => 1), + ), + '20120120235330', + null + ), + 'Rollback to start time of first created version which was the last to be executed - breakpoints set on first executed (and last created) migration' => + array( + array( + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'breakpoint' => 1), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-20 23:53:30', 'breakpoint' => 0), + ), + '20120120235330', + null + ), + 'Rollback to start time of second created version which was the first to be executed - breakpoints set on first created (and last executed) migration' => + array( + array( + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'breakpoint' => 0), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-20 23:53:30', 'breakpoint' => 1), + ), + '20120117183504', + 'Breakpoint reached. Further rollbacks inhibited.' + ), + 'Rollback to start time of second created version which was the first to be executed - breakpoints set on first executed (and last created) migration' => + array( + array( + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'breakpoint' => 1), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-20 23:53:30', 'breakpoint' => 0), + ), + '20120117183504', + '== 20120111235330 TestMigration: reverted' + ), + 'Rollback to date between the 2 migrations when they were created in a different order than they were executed - breakpoints set on first created (and last executed) migration' => + array( + array( + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'breakpoint' => 0), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-20 23:53:30', 'breakpoint' => 1), + ), + '20120118000000', + 'Breakpoint reached. Further rollbacks inhibited.' + ), + 'Rollback to date between the 2 migrations when they were created in a different order than they were executed - breakpoints set on first executed (and last created) migration' => + array( + array( + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'breakpoint' => 1), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-20 23:53:30', 'breakpoint' => 0), + ), + '20120118000000', + '== 20120111235330 TestMigration: reverted' + ), + 'Rollback the last executed migration when the migrations were created in a different order than they were executed - breakpoints set on first created (and last executed) migration' => + array( + array( + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'breakpoint' => 0), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-20 23:53:30', 'breakpoint' => 1), + ), + null, + 'Breakpoint reached. Further rollbacks inhibited.' + ), + 'Rollback the last executed migration when the migrations were created in a different order than they were executed - breakpoints set on first executed (and last created) migration' => + array( + array( + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'breakpoint' => 1), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-20 23:53:30', 'breakpoint' => 0), + ), + null, + '== 20120111235330 TestMigration: reverted' + ), // Breakpoint set on all migration - [ + 'Rollback to date later than all migration start times when they were created in a different order than they were executed - breakpoints set on all migrations' => + array( + array( + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'breakpoint' => 1), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-20 23:53:30', 'breakpoint' => 1), + ), + '20131212000000', + null + ), + 'Rollback to date earlier than all migration start times when they were created in a different order than they were executed - breakpoints set on all migrations' => + array( + array( + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'breakpoint' => 1), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-20 23:53:30', 'breakpoint' => 1), + ), + '20111212000000', + 'Breakpoint reached. Further rollbacks inhibited.' + ), + 'Rollback to start time of first created version which was the last to be executed - breakpoints set on all migrations' => + array( + array( + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'breakpoint' => 1), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-20 23:53:30', 'breakpoint' => 1), + ), + '20120120235330', + null + ), + 'Rollback to start time of second created version which was the first to be executed - breakpoints set on all migrations' => + array( + array( + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'breakpoint' => 1), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-20 23:53:30', 'breakpoint' => 1), + ), + '20120117183504', + 'Breakpoint reached. Further rollbacks inhibited.' + ), + 'Rollback to date between the 2 migrations when they were created in a different order than they were executed - breakpoints set on all migrations' => + array( + array( + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'breakpoint' => 1), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-20 23:53:30', 'breakpoint' => 1), + ), + '20120118000000', + 'Breakpoint reached. Further rollbacks inhibited.' + ), + 'Rollback the last executed migration when the migrations were created in a different order than they were executed - breakpoints set on all migrations' => + array( + array( + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'breakpoint' => 1), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-20 23:53:30', 'breakpoint' => 1), + ), + null, + 'Breakpoint reached. Further rollbacks inhibited.' + ), + ); + } + + /** + * Migration lists, dates, and expected output. + * + * @return array + */ + public function rollbackToVersionDataProvider() + { + return [ + + // No breakpoints set + + 'Rollback to one of the versions - no breakpoints set' => [ - '20120111235330' => ['version' => '20120111235330', 'migration' => '', 'breakpoint' => 1], - '20120116183504' => ['version' => '20120116183504', 'migration' => '', 'breakpoint' => 1], + [ + '20120111235330' => ['version' => '20120111235330', 'migration_name' => '', 'breakpoint' => 0], + '20120116183504' => ['version' => '20120116183504', 'migration_name' => '', 'breakpoint' => 0], + ], + '20120111235330', + '== 20120116183504 TestMigration2: reverted' ], - '20130118', - null, - 'Failed to rollback 0 migrations when rollback to date is later than all migrations - breakpoint set on all migrations', - ], - [ + 'Rollback to the latest version - no breakpoints set' => [ - '20120111235330' => ['version' => '20120111235330', 'migration' => '', 'breakpoint' => 1], - '20120116183504' => ['version' => '20120116183504', 'migration' => '', 'breakpoint' => 1], + [ + '20120111235330' => ['version' => '20120111235330', 'migration_name' => '', 'breakpoint' => 0], + '20120116183504' => ['version' => '20120116183504', 'migration_name' => '', 'breakpoint' => 0], + ], + '20120116183504', + null ], - '20120116183504', - '`No migrations to rollback`', - 'Failed to rollback 0 migrations when rollback to date is the most recent migration - breakpoint set on all migrations', - ], - [ + 'Rollback all versions (ie. rollback to version 0) - no breakpoints set' => [ - '20120111235330' => ['version' => '20120111235330', 'migration' => '', 'breakpoint' => 1], - '20120116183504' => ['version' => '20120116183504', 'migration' => '', 'breakpoint' => 1], + [ + '20120111235330' => ['version' => '20120111235330', 'migration_name' => '', 'breakpoint' => 0], + '20120116183504' => ['version' => '20120116183504', 'migration_name' => '', 'breakpoint' => 0], + ], + '0', + ['== 20120111235330 TestMigration: reverted', '== 20120116183504 TestMigration2: reverted'] ], - '20120115', - '`(?!.*20120116183504.*).*Breakpoint reached.*`s', - 'Failed to rollback 0 migrations when rollback date is between 2 migrations and breakpoint set on all migrations', - ], - [ + 'Rollback last version - no breakpoints set' => [ - '20120111235330' => ['version' => '20120111235330', 'migration' => '', 'breakpoint' => 1], - '20120116183504' => ['version' => '20120116183504', 'migration' => '', 'breakpoint' => 1], + [ + '20120111235330' => ['version' => '20120111235330', 'migration_name' => '', 'breakpoint' => 0], + '20120116183504' => ['version' => '20120116183504', 'migration_name' => '', 'breakpoint' => 0], + ], + null, + '== 20120116183504 TestMigration2: reverted', ], - '20120111235330', - '`(?!.*20120116183504.*).*Breakpoint reached.*`s', - 'Failed to rollback 0 migrations when rollback datetime is the one of the migrations and breakpoint set on all migrations', - ], - [ + 'Rollback to non-existing version - no breakpoints set' => [ - '20120111235330' => ['version' => '20120111235330', 'migration' => '', 'breakpoint' => 1], - '20120116183504' => ['version' => '20120116183504', 'migration' => '', 'breakpoint' => 1], + [ + '20120111235330' => ['version' => '20120111235330', 'migration_name' => '', 'breakpoint' => 0], + '20120116183504' => ['version' => '20120116183504', 'migration_name' => '', 'breakpoint' => 0], + ], + '20121225000000', + 'Target version (20121225000000) not found', + ], + 'Rollback to missing version - no breakpoints set' => + [ + [ + '20111225000000' => ['version' => '20111225000000', 'migration_name' => '', 'breakpoint' => 0], + '20120111235330' => ['version' => '20120111235330', 'migration_name' => '', 'breakpoint' => 0], + '20120116183504' => ['version' => '20120116183504', 'migration_name' => '', 'breakpoint' => 0], + ], + '20111225000000', + 'Target version (20111225000000) not found', + ], + + // Breakpoint set on first migration + + 'Rollback to one of the versions - breakpoint set on first migration' => + [ + [ + '20120111235330' => ['version' => '20120111235330', 'migration_name' => '', 'breakpoint' => 1], + '20120116183504' => ['version' => '20120116183504', 'migration_name' => '', 'breakpoint' => 0], + ], + '20120111235330', + '== 20120116183504 TestMigration2: reverted' + ], + 'Rollback to the latest version - breakpoint set on first migration' => + [ + [ + '20120111235330' => ['version' => '20120111235330', 'migration_name' => '', 'breakpoint' => 1], + '20120116183504' => ['version' => '20120116183504', 'migration_name' => '', 'breakpoint' => 0], + ], + '20120116183504', + null + ], + 'Rollback all versions (ie. rollback to version 0) - breakpoint set on first migration' => + [ + [ + '20120111235330' => ['version' => '20120111235330', 'migration_name' => '', 'breakpoint' => 1], + '20120116183504' => ['version' => '20120116183504', 'migration_name' => '', 'breakpoint' => 0], + ], + '0', + '== 20120116183504 TestMigration2: reverted' + ], + 'Rollback last version - breakpoint set on first migration' => + [ + [ + '20120111235330' => ['version' => '20120111235330', 'migration_name' => '', 'breakpoint' => 1], + '20120116183504' => ['version' => '20120116183504', 'migration_name' => '', 'breakpoint' => 0], + ], + null, + '== 20120116183504 TestMigration2: reverted', + ], + 'Rollback to non-existing version - breakpoint set on first migration' => + [ + [ + '20120111235330' => ['version' => '20120111235330', 'migration_name' => '', 'breakpoint' => 1], + '20120116183504' => ['version' => '20120116183504', 'migration_name' => '', 'breakpoint' => 0], + ], + '20121225000000', + 'Target version (20121225000000) not found', + ], + 'Rollback to missing version - breakpoint set on first migration' => + [ + [ + '20111225000000' => ['version' => '20111225000000', 'migration_name' => '', 'breakpoint' => 0], + '20120111235330' => ['version' => '20120111235330', 'migration_name' => '', 'breakpoint' => 1], + '20120116183504' => ['version' => '20120116183504', 'migration_name' => '', 'breakpoint' => 0], + ], + '20111225000000', + 'Target version (20111225000000) not found', + ], + + // Breakpoint set on last migration + + 'Rollback to one of the versions - breakpoint set on last migration' => + [ + [ + '20120111235330' => ['version' => '20120111235330', 'migration_name' => '', 'breakpoint' => 0], + '20120116183504' => ['version' => '20120116183504', 'migration_name' => '', 'breakpoint' => 1], + ], + '20120111235330', + 'Breakpoint reached. Further rollbacks inhibited.' + ], + 'Rollback to the latest version - breakpoint set on last migration' => + [ + [ + '20120111235330' => ['version' => '20120111235330', 'migration_name' => '', 'breakpoint' => 0], + '20120116183504' => ['version' => '20120116183504', 'migration_name' => '', 'breakpoint' => 1], + ], + '20120116183504', + null + ], + 'Rollback all versions (ie. rollback to version 0) - breakpoint set on last migration' => + [ + [ + '20120111235330' => ['version' => '20120111235330', 'migration_name' => '', 'breakpoint' => 0], + '20120116183504' => ['version' => '20120116183504', 'migration_name' => '', 'breakpoint' => 1], + ], + '0', + 'Breakpoint reached. Further rollbacks inhibited.' + ], + 'Rollback last version - breakpoint set on last migration' => + [ + [ + '20120111235330' => ['version' => '20120111235330', 'migration_name' => '', 'breakpoint' => 0], + '20120116183504' => ['version' => '20120116183504', 'migration_name' => '', 'breakpoint' => 1], + ], + null, + 'Breakpoint reached. Further rollbacks inhibited.' + ], + 'Rollback to non-existing version - breakpoint set on last migration' => + [ + [ + '20120111235330' => ['version' => '20120111235330', 'migration_name' => '', 'breakpoint' => 0], + '20120116183504' => ['version' => '20120116183504', 'migration_name' => '', 'breakpoint' => 1], + ], + '20121225000000', + 'Target version (20121225000000) not found', + ], + 'Rollback to missing version - breakpoint set on last migration' => + [ + [ + '20111225000000' => ['version' => '20111225000000', 'migration_name' => '', 'breakpoint' => 0], + '20120111235330' => ['version' => '20120111235330', 'migration_name' => '', 'breakpoint' => 0], + '20120116183504' => ['version' => '20120116183504', 'migration_name' => '', 'breakpoint' => 1], + ], + '20111225000000', + 'Target version (20111225000000) not found', + ], + + // Breakpoint set on all migrations + + 'Rollback to one of the versions - breakpoint set on last migration' => + [ + [ + '20120111235330' => ['version' => '20120111235330', 'migration_name' => '', 'breakpoint' => 1], + '20120116183504' => ['version' => '20120116183504', 'migration_name' => '', 'breakpoint' => 1], + ], + '20120111235330', + 'Breakpoint reached. Further rollbacks inhibited.' + ], + 'Rollback to the latest version - breakpoint set on last migration' => + [ + [ + '20120111235330' => ['version' => '20120111235330', 'migration_name' => '', 'breakpoint' => 1], + '20120116183504' => ['version' => '20120116183504', 'migration_name' => '', 'breakpoint' => 1], + ], + '20120116183504', + null + ], + 'Rollback all versions (ie. rollback to version 0) - breakpoint set on last migration' => + [ + [ + '20120111235330' => ['version' => '20120111235330', 'migration_name' => '', 'breakpoint' => 1], + '20120116183504' => ['version' => '20120116183504', 'migration_name' => '', 'breakpoint' => 1], + ], + '0', + 'Breakpoint reached. Further rollbacks inhibited.' + ], + 'Rollback last version - breakpoint set on last migration' => + [ + [ + '20120111235330' => ['version' => '20120111235330', 'migration_name' => '', 'breakpoint' => 1], + '20120116183504' => ['version' => '20120116183504', 'migration_name' => '', 'breakpoint' => 1], + ], + null, + 'Breakpoint reached. Further rollbacks inhibited.' + ], + 'Rollback to non-existing version - breakpoint set on last migration' => + [ + [ + '20120111235330' => ['version' => '20120111235330', 'migration_name' => '', 'breakpoint' => 1], + '20120116183504' => ['version' => '20120116183504', 'migration_name' => '', 'breakpoint' => 1], + ], + '20121225000000', + 'Target version (20121225000000) not found', + ], + 'Rollback to missing version - breakpoint set on last migration' => + [ + [ + '20111225000000' => ['version' => '20111225000000', 'migration_name' => '', 'breakpoint' => 1], + '20120111235330' => ['version' => '20120111235330', 'migration_name' => '', 'breakpoint' => 1], + '20120116183504' => ['version' => '20120116183504', 'migration_name' => '', 'breakpoint' => 1], + ], + '20111225000000', + 'Target version (20111225000000) not found', + ] + ]; + } + + public function rollbackToVersionByExecutionTimeDataProvider() + { + return [ + + // No breakpoints set + + 'Rollback to first created version with was also the first to be executed - no breakpoints set' => + [ + [ + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-12 23:53:30', 'end_time' => '2012-01-12 23:53:30', 'breakpoint' => 0), + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'end_time' => '2012-01-17 18:35:04', 'breakpoint' => 0), + ], + '20120111235330', + '== 20120116183504 TestMigration2: reverted' + ], + 'Rollback to last created version which was also the last to be executed - no breakpoints set' => + [ + [ + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-12 23:53:30', 'end_time' => '2012-01-12 23:53:30', 'breakpoint' => 0), + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'end_time' => '2012-01-17 18:35:04', 'breakpoint' => 0), + ], + '20120116183504', + 'No migrations to rollback' + ], + 'Rollback all versions (ie. rollback to version 0) - no breakpoints set' => + [ + [ + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-12 23:53:30', 'end_time' => '2012-01-12 23:53:30', 'breakpoint' => 0), + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'end_time' => '2012-01-17 18:35:04', 'breakpoint' => 0), + ], + '0', + ['== 20120111235330 TestMigration: reverted', '== 20120116183504 TestMigration2: reverted'] + ], + 'Rollback to second created version which was the first to be executed - no breakpoints set' => + [ + [ + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-10 18:35:04', 'end_time' => '2012-01-10 18:35:04', 'breakpoint' => 0), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-12 23:53:30', 'end_time' => '2012-01-12 23:53:30', 'breakpoint' => 0), + ], + '20120116183504', + '== 20120111235330 TestMigration: reverted' + ], + 'Rollback to first created version which was the second to be executed - no breakpoints set' => + [ + [ + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'end_time' => '2012-01-17 18:35:04', 'breakpoint' => 0), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-20 23:53:30', 'end_time' => '2012-01-20 23:53:30', 'breakpoint' => 0), + ], + '20120111235330', + 'No migrations to rollback' + ], + 'Rollback last executed version which was also the last created version - no breakpoints set' => + [ + [ + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-12 23:53:30', 'end_time' => '2012-01-12 23:53:30', 'breakpoint' => 0), + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'end_time' => '2012-01-17 18:35:04', 'breakpoint' => 0), + ], + null, + '== 20120116183504 TestMigration2: reverted' + ], + 'Rollback last executed version which was the first created version - no breakpoints set' => + [ + [ + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'end_time' => '2012-01-17 18:35:04', 'breakpoint' => 0), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-20 23:53:30', 'end_time' => '2012-01-20 23:53:30', 'breakpoint' => 0), + ], + null, + '== 20120111235330 TestMigration: reverted' + ], + 'Rollback to non-existing version - no breakpoints set' => + [ + [ + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'end_time' => '2012-01-17 18:35:04', 'breakpoint' => 0), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-20 23:53:30', 'end_time' => '2012-01-20 23:53:30', 'breakpoint' => 0), + ], + '20121225000000', + 'Target version (20121225000000) not found', + ], + 'Rollback to missing version - no breakpoints set' => + [ + [ + '20111225000000' => array('version' => '20111225000000', 'start_time' => '2011-12-25 00:00:00', 'end_time' => '2011-12-25 00:00:00', 'breakpoint' => 0), + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'end_time' => '2012-01-17 18:35:04', 'breakpoint' => 0), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-20 23:53:30', 'end_time' => '2012-01-20 23:53:30', 'breakpoint' => 0), + ], + '20121225000000', + 'Target version (20121225000000) not found', + ], + + // Breakpoint set on first migration + + 'Rollback to first created version with was also the first to be executed - breakpoint set on first (executed and created) migration' => + [ + [ + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-12 23:53:30', 'end_time' => '2012-01-12 23:53:30', 'breakpoint' => 1), + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'end_time' => '2012-01-17 18:35:04', 'breakpoint' => 0), + ], + '20120111235330', + '== 20120116183504 TestMigration2: reverted' + ], + 'Rollback to last created version which was also the last to be executed - breakpoint set on first (executed and created) migration' => + [ + [ + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-12 23:53:30', 'end_time' => '2012-01-12 23:53:30', 'breakpoint' => 1), + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'end_time' => '2012-01-17 18:35:04', 'breakpoint' => 0), + ], + '20120116183504', + 'No migrations to rollback' + ], + 'Rollback all versions (ie. rollback to version 0) - breakpoint set on first (executed and created) migration' => + [ + [ + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-12 23:53:30', 'end_time' => '2012-01-12 23:53:30', 'breakpoint' => 1), + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'end_time' => '2012-01-17 18:35:04', 'breakpoint' => 0), + ], + '0', + ['== 20120116183504 TestMigration2: reverted', 'Breakpoint reached. Further rollbacks inhibited.'] + ], + 'Rollback to second created version which was the first to be executed - breakpoint set on first executed migration' => + [ + [ + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-10 18:35:04', 'end_time' => '2012-01-10 18:35:04', 'breakpoint' => 1), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-12 23:53:30', 'end_time' => '2012-01-12 23:53:30', 'breakpoint' => 0), + ], + '20120116183504', + '== 20120111235330 TestMigration: reverted' + ], + 'Rollback to second created version which was the first to be executed - breakpoint set on first created migration' => + [ + [ + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-10 18:35:04', 'end_time' => '2012-01-10 18:35:04', 'breakpoint' => 0), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-12 23:53:30', 'end_time' => '2012-01-12 23:53:30', 'breakpoint' => 1), + ], + '20120116183504', + 'Breakpoint reached. Further rollbacks inhibited.' + ], + 'Rollback to first created version which was the second to be executed - breakpoint set on first executed migration' => + [ + [ + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'end_time' => '2012-01-17 18:35:04', 'breakpoint' => 1), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-20 23:53:30', 'end_time' => '2012-01-20 23:53:30', 'breakpoint' => 0), + ], + '20120111235330', + 'No migrations to rollback' + ], + 'Rollback to first created version which was the second to be executed - breakpoint set on first created migration' => + [ + [ + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'end_time' => '2012-01-17 18:35:04', 'breakpoint' => 0), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-20 23:53:30', 'end_time' => '2012-01-20 23:53:30', 'breakpoint' => 1), + ], + '20120111235330', + 'No migrations to rollback' + ], + 'Rollback last executed version which was also the last created version - breakpoint set on first (executed and created) migration' => + [ + [ + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-12 23:53:30', 'end_time' => '2012-01-12 23:53:30', 'breakpoint' => 1), + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'end_time' => '2012-01-17 18:35:04', 'breakpoint' => 0), + ], + null, + '== 20120116183504 TestMigration2: reverted' + ], + 'Rollback last executed version which was the first created version - breakpoint set on first executed migration' => + [ + [ + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'end_time' => '2012-01-17 18:35:04', 'breakpoint' => 1), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-20 23:53:30', 'end_time' => '2012-01-20 23:53:30', 'breakpoint' => 0), + ], + null, + '== 20120111235330 TestMigration: reverted' + ], + 'Rollback last executed version which was the first created version - breakpoint set on first created migration' => + [ + [ + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'end_time' => '2012-01-17 18:35:04', 'breakpoint' => 0), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-20 23:53:30', 'end_time' => '2012-01-20 23:53:30', 'breakpoint' => 1), + ], + null, + 'Breakpoint reached. Further rollbacks inhibited.' + ], + 'Rollback to non-existing version - breakpoint set on first executed migration' => + [ + [ + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'end_time' => '2012-01-17 18:35:04', 'breakpoint' => 1), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-20 23:53:30', 'end_time' => '2012-01-20 23:53:30', 'breakpoint' => 0), + ], + '20121225000000', + 'Target version (20121225000000) not found', + ], + 'Rollback to missing version - breakpoint set on first executed migration' => + [ + [ + '20111225000000' => array('version' => '20111225000000', 'start_time' => '2011-12-25 00:00:00', 'end_time' => '2011-12-25 00:00:00', 'breakpoint' => 1), + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'end_time' => '2012-01-17 18:35:04', 'breakpoint' => 0), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-20 23:53:30', 'end_time' => '2012-01-20 23:53:30', 'breakpoint' => 0), + ], + '20121225000000', + 'Target version (20121225000000) not found', + ], + + // Breakpoint set on last migration + + 'Rollback to first created version with was also the first to be executed - breakpoint set on last (executed and created) migration' => + [ + [ + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-12 23:53:30', 'end_time' => '2012-01-12 23:53:30', 'breakpoint' => 0), + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'end_time' => '2012-01-17 18:35:04', 'breakpoint' => 1), + ], + '20120111235330', + 'Breakpoint reached. Further rollbacks inhibited.' + ], + 'Rollback to last created version which was also the last to be executed - breakpoint set on last (executed and created) migration' => + [ + [ + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-12 23:53:30', 'end_time' => '2012-01-12 23:53:30', 'breakpoint' => 0), + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'end_time' => '2012-01-17 18:35:04', 'breakpoint' => 1), + ], + '20120116183504', + 'No migrations to rollback' + ], + 'Rollback all versions (ie. rollback to version 0) - breakpoint set on last (executed and created) migration' => + [ + [ + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-12 23:53:30', 'end_time' => '2012-01-12 23:53:30', 'breakpoint' => 0), + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'end_time' => '2012-01-17 18:35:04', 'breakpoint' => 1), + ], + '0', + ['Breakpoint reached. Further rollbacks inhibited.'] + ], + 'Rollback to second created version which was the first to be executed - breakpoint set on last executed migration' => + [ + [ + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-10 18:35:04', 'end_time' => '2012-01-10 18:35:04', 'breakpoint' => 0), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-12 23:53:30', 'end_time' => '2012-01-12 23:53:30', 'breakpoint' => 1), + ], + '20120116183504', + 'Breakpoint reached. Further rollbacks inhibited.' + ], + 'Rollback to second created version which was the first to be executed - breakpoint set on last created migration' => + [ + [ + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-10 18:35:04', 'end_time' => '2012-01-10 18:35:04', 'breakpoint' => 1), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-12 23:53:30', 'end_time' => '2012-01-12 23:53:30', 'breakpoint' => 0), + ], + '20120116183504', + '== 20120111235330 TestMigration: reverted' + ], + 'Rollback to first created version which was the second to be executed - breakpoint set on last executed migration' => + [ + [ + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'end_time' => '2012-01-17 18:35:04', 'breakpoint' => 0), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-20 23:53:30', 'end_time' => '2012-01-20 23:53:30', 'breakpoint' => 1), + ], + '20120111235330', + 'No migrations to rollback' + ], + 'Rollback to first created version which was the second to be executed - breakpoint set on last created migration' => + [ + [ + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'end_time' => '2012-01-17 18:35:04', 'breakpoint' => 1), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-20 23:53:30', 'end_time' => '2012-01-20 23:53:30', 'breakpoint' => 0), + ], + '20120111235330', + 'No migrations to rollback' + ], + 'Rollback last executed version which was also the last created version - breakpoint set on last (executed and created) migration' => + [ + [ + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-12 23:53:30', 'end_time' => '2012-01-12 23:53:30', 'breakpoint' => 0), + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'end_time' => '2012-01-17 18:35:04', 'breakpoint' => 1), + ], + null, + 'Breakpoint reached. Further rollbacks inhibited.' + ], + 'Rollback last executed version which was the first created version - breakpoint set on last executed migration' => + [ + [ + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'end_time' => '2012-01-17 18:35:04', 'breakpoint' => 0), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-20 23:53:30', 'end_time' => '2012-01-20 23:53:30', 'breakpoint' => 1), + ], + null, + 'Breakpoint reached. Further rollbacks inhibited.' + ], + 'Rollback last executed version which was the first created version - breakpoint set on last created migration' => + [ + [ + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'end_time' => '2012-01-17 18:35:04', 'breakpoint' => 1), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-20 23:53:30', 'end_time' => '2012-01-20 23:53:30', 'breakpoint' => 0), + ], + null, + '== 20120111235330 TestMigration: reverted' + ], + 'Rollback to non-existing version - breakpoint set on last executed migration' => + [ + [ + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'end_time' => '2012-01-17 18:35:04', 'breakpoint' => 0), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-20 23:53:30', 'end_time' => '2012-01-20 23:53:30', 'breakpoint' => 1), + ], + '20121225000000', + 'Target version (20121225000000) not found', + ], + 'Rollback to missing version - breakpoint set on last executed migration' => + [ + [ + '20111225000000' => array('version' => '20111225000000', 'start_time' => '2011-12-25 00:00:00', 'end_time' => '2011-12-25 00:00:00', 'breakpoint' => 0), + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'end_time' => '2012-01-17 18:35:04', 'breakpoint' => 0), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-20 23:53:30', 'end_time' => '2012-01-20 23:53:30', 'breakpoint' => 1), + ], + '20121225000000', + 'Target version (20121225000000) not found', + ], + + // Breakpoint set on all migrations + + 'Rollback to first created version with was also the first to be executed - breakpoint set on all migrations' => + [ + [ + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-12 23:53:30', 'end_time' => '2012-01-12 23:53:30', 'breakpoint' => 1), + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'end_time' => '2012-01-17 18:35:04', 'breakpoint' => 1), + ], + '20120111235330', + 'Breakpoint reached. Further rollbacks inhibited.' + ], + 'Rollback to last created version which was also the last to be executed - breakpoint set on all migrations' => + [ + [ + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-12 23:53:30', 'end_time' => '2012-01-12 23:53:30', 'breakpoint' => 1), + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'end_time' => '2012-01-17 18:35:04', 'breakpoint' => 1), + ], + '20120116183504', + 'No migrations to rollback' + ], + 'Rollback all versions (ie. rollback to version 0) - breakpoint set on all migrations' => + [ + [ + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-12 23:53:30', 'end_time' => '2012-01-12 23:53:30', 'breakpoint' => 1), + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'end_time' => '2012-01-17 18:35:04', 'breakpoint' => 1), + ], + '0', + ['Breakpoint reached. Further rollbacks inhibited.'] + ], + 'Rollback to second created version which was the first to be executed - breakpoint set on all migrations' => + [ + [ + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-10 18:35:04', 'end_time' => '2012-01-10 18:35:04', 'breakpoint' => 1), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-12 23:53:30', 'end_time' => '2012-01-12 23:53:30', 'breakpoint' => 1), + ], + '20120116183504', + 'Breakpoint reached. Further rollbacks inhibited.' + ], + 'Rollback to first created version which was the second to be executed - breakpoint set on all migrations' => + [ + [ + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'end_time' => '2012-01-17 18:35:04', 'breakpoint' => 1), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-20 23:53:30', 'end_time' => '2012-01-20 23:53:30', 'breakpoint' => 1), + ], + '20120111235330', + 'No migrations to rollback' + ], + 'Rollback last executed version which was also the last created version - breakpoint set on all migrations' => + [ + [ + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-12 23:53:30', 'end_time' => '2012-01-12 23:53:30', 'breakpoint' => 1), + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'end_time' => '2012-01-17 18:35:04', 'breakpoint' => 1), + ], + null, + 'Breakpoint reached. Further rollbacks inhibited.' + ], + 'Rollback last executed version which was the first created version - breakpoint set on all migrations' => + [ + [ + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'end_time' => '2012-01-17 18:35:04', 'breakpoint' => 1), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-20 23:53:30', 'end_time' => '2012-01-20 23:53:30', 'breakpoint' => 1), + ], + null, + 'Breakpoint reached. Further rollbacks inhibited.' + ], + 'Rollback to non-existing version - breakpoint set on all migrations' => + [ + [ + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'end_time' => '2012-01-17 18:35:04', 'breakpoint' => 1), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-20 23:53:30', 'end_time' => '2012-01-20 23:53:30', 'breakpoint' => 1), + ], + '20121225000000', + 'Target version (20121225000000) not found', + ], + 'Rollback to missing version - breakpoint set on all migrations' => + [ + [ + '20111225000000' => array('version' => '20111225000000', 'start_time' => '2011-12-25 00:00:00', 'end_time' => '2011-12-25 00:00:00', 'breakpoint' => 1), + '20120116183504' => array('version' => '20120116183504', 'start_time' => '2012-01-17 18:35:04', 'end_time' => '2012-01-17 18:35:04', 'breakpoint' => 1), + '20120111235330' => array('version' => '20120111235330', 'start_time' => '2012-01-20 23:53:30', 'end_time' => '2012-01-20 23:53:30', 'breakpoint' => 1), + ], + '20121225000000', + 'Target version (20121225000000) not found', ], - '20110115', - '`(?!.*20120116183504.*).*Breakpoint reached.*`s', - 'Failed to rollback 0 migrations when the rollback date is before all the migrations and breakpoint set on all migrations', - ], ]; } + /** + * Migration lists, version order configuration and expected output. + * + * @return array + */ + public function rollbackLastDataProvider() + { + return [ + + // No breakpoints set + + 'Rollback to last migration with creation time version ordering - no breakpoints set' => + [ + [ + '20120111235330' => ['version' => '20120111235330', 'start_time' => '2012-01-12 23:53:30', 'breakpoint' => 0], + '20120116183504' => ['version' => '20120116183504', 'start_time' => '2012-01-16 18:35:04', 'breakpoint' => 0], + ], + \Phinx\Config\Config::VERSION_ORDER_CREATION_TIME, + '== 20120116183504 TestMigration2: reverted' + ], + + 'Rollback to last migration with execution time version ordering - no breakpoints set' => + [ + [ + '20120116183504' => ['version' => '20120116183504', 'start_time' => '2012-01-10 18:35:04', 'breakpoint' => 0], + '20120111235330' => ['version' => '20120111235330', 'start_time' => '2012-01-12 23:53:30', 'breakpoint' => 0], + ], + \Phinx\Config\Config::VERSION_ORDER_EXECUTION_TIME, + '== 20120111235330 TestMigration: reverted' + ], + + 'Rollback to last migration with missing last migration and creation time version ordering - no breakpoints set' => + [ + [ + '20120111235330' => ['version' => '20120111235330', 'start_time' => '2012-01-12 23:53:30', 'breakpoint' => 0], + '20120116183504' => ['version' => '20120116183504', 'start_time' => '2012-01-16 18:35:04', 'breakpoint' => 0], + '20130101225232' => ['version' => '20130101225232', 'start_time' => '2013-01-01 22:52:32', 'breakpoint' => 0], + ], + \Phinx\Config\Config::VERSION_ORDER_CREATION_TIME, + '== 20120116183504 TestMigration2: reverted' + ], + + 'Rollback to last migration with missing last migration and execution time version ordering - no breakpoints set' => + [ + [ + '20120116183504' => ['version' => '20120116183504', 'start_time' => '2012-01-10 18:35:04', 'breakpoint' => 0], + '20120111235330' => ['version' => '20120111235330', 'start_time' => '2012-01-12 23:53:30', 'breakpoint' => 0], + '20130101225232' => ['version' => '20130101225232', 'start_time' => '2013-01-01 22:52:32', 'breakpoint' => 0], + ], + \Phinx\Config\Config::VERSION_ORDER_EXECUTION_TIME, + '== 20120111235330 TestMigration: reverted' + ], + + // Breakpoint set on last migration + + 'Rollback to last migration with creation time version ordering - breakpoint set on last created migration' => + [ + [ + '20120111235330' => ['version' => '20120111235330', 'start_time' => '2012-01-12 23:53:30', 'breakpoint' => 0], + '20120116183504' => ['version' => '20120116183504', 'start_time' => '2012-01-16 18:35:04', 'breakpoint' => 1], + ], + \Phinx\Config\Config::VERSION_ORDER_CREATION_TIME, + 'Breakpoint reached. Further rollbacks inhibited.' + ], + + 'Rollback to last migration with creation time version ordering - breakpoint set on last executed migration' => + [ + [ + '20120111235330' => ['version' => '20120111235330', 'start_time' => '2012-01-12 23:53:30', 'breakpoint' => 1], + '20120116183504' => ['version' => '20120116183504', 'start_time' => '2012-01-16 18:35:04', 'breakpoint' => 0], + ], + \Phinx\Config\Config::VERSION_ORDER_CREATION_TIME, + '== 20120116183504 TestMigration2: reverted' + ], + + 'Rollback to last migration with missing last migration and creation time version ordering - breakpoint set on last non-missing created migration' => + [ + [ + '20120111235330' => ['version' => '20120111235330', 'start_time' => '2012-01-12 23:53:30', 'breakpoint' => 0], + '20120116183504' => ['version' => '20120116183504', 'start_time' => '2012-01-16 18:35:04', 'breakpoint' => 1], + '20130101225232' => ['version' => '20130101225232', 'start_time' => '2013-01-01 22:52:32', 'breakpoint' => 0], + ], + \Phinx\Config\Config::VERSION_ORDER_CREATION_TIME, + 'Breakpoint reached. Further rollbacks inhibited.' + ], + + 'Rollback to last migration with missing last migration and execution time version ordering - breakpoint set on last non-missing executed migration' => + [ + [ + '20120116183504' => ['version' => '20120116183504', 'start_time' => '2012-01-10 18:35:04', 'breakpoint' => 0], + '20120111235330' => ['version' => '20120111235330', 'start_time' => '2012-01-12 23:53:30', 'breakpoint' => 1], + '20130101225232' => ['version' => '20130101225232', 'start_time' => '2013-01-01 22:52:32', 'breakpoint' => 0], + ], + \Phinx\Config\Config::VERSION_ORDER_EXECUTION_TIME, + 'Breakpoint reached. Further rollbacks inhibited.' + ], + + 'Rollback to last migration with missing last migration and creation time version ordering - breakpoint set on missing migration' => + [ + [ + '20120111235330' => ['version' => '20120111235330', 'start_time' => '2012-01-12 23:53:30', 'breakpoint' => 0], + '20120116183504' => ['version' => '20120116183504', 'start_time' => '2012-01-16 18:35:04', 'breakpoint' => 0], + '20130101225232' => ['version' => '20130101225232', 'start_time' => '2013-01-01 22:52:32', 'breakpoint' => 1], + ], + \Phinx\Config\Config::VERSION_ORDER_CREATION_TIME, + '== 20120116183504 TestMigration2: reverted' + ], + + 'Rollback to last migration with missing last migration and execution time version ordering - breakpoint set on missing migration' => + [ + [ + '20120116183504' => ['version' => '20120116183504', 'start_time' => '2012-01-10 18:35:04', 'breakpoint' => 0], + '20120111235330' => ['version' => '20120111235330', 'start_time' => '2012-01-12 23:53:30', 'breakpoint' => 0], + '20130101225232' => ['version' => '20130101225232', 'start_time' => '2013-01-01 22:52:32', 'breakpoint' => 1], + ], + \Phinx\Config\Config::VERSION_ORDER_EXECUTION_TIME, + '== 20120111235330 TestMigration: reverted' + ], + + // Breakpoint set on all migrations + + 'Rollback to last migration with creation time version ordering - breakpoint set on all migrations' => + [ + [ + '20120111235330' => ['version' => '20120111235330', 'start_time' => '2012-01-12 23:53:30', 'breakpoint' => 1], + '20120116183504' => ['version' => '20120116183504', 'start_time' => '2012-01-16 18:35:04', 'breakpoint' => 1], + ], + \Phinx\Config\Config::VERSION_ORDER_CREATION_TIME, + 'Breakpoint reached. Further rollbacks inhibited.' + ], + + 'Rollback to last migration with creation time version ordering - breakpoint set on all migrations' => + [ + [ + '20120111235330' => ['version' => '20120111235330', 'start_time' => '2012-01-12 23:53:30', 'breakpoint' => 1], + '20120116183504' => ['version' => '20120116183504', 'start_time' => '2012-01-16 18:35:04', 'breakpoint' => 1], + ], + \Phinx\Config\Config::VERSION_ORDER_CREATION_TIME, + 'Breakpoint reached. Further rollbacks inhibited.' + ], + + 'Rollback to last migration with missing last migration and creation time version ordering - breakpoint set on all migrations' => + [ + [ + '20120111235330' => ['version' => '20120111235330', 'start_time' => '2012-01-12 23:53:30', 'breakpoint' => 1], + '20120116183504' => ['version' => '20120116183504', 'start_time' => '2012-01-16 18:35:04', 'breakpoint' => 1], + '20130101225232' => ['version' => '20130101225232', 'start_time' => '2013-01-01 22:52:32', 'breakpoint' => 1], + ], + \Phinx\Config\Config::VERSION_ORDER_CREATION_TIME, + 'Breakpoint reached. Further rollbacks inhibited.' + ], + + 'Rollback to last migration with missing last migration and execution time version ordering - breakpoint set on all migrations' => + [ + [ + '20120116183504' => ['version' => '20120116183504', 'start_time' => '2012-01-10 18:35:04', 'breakpoint' => 1], + '20120111235330' => ['version' => '20120111235330', 'start_time' => '2012-01-12 23:53:30', 'breakpoint' => 1], + '20130101225232' => ['version' => '20130101225232', 'start_time' => '2013-01-01 22:52:32', 'breakpoint' => 1], + ], + \Phinx\Config\Config::VERSION_ORDER_EXECUTION_TIME, + 'Breakpoint reached. Further rollbacks inhibited.' + ], + ]; + } + public function testExecuteSeedWorksAsExpected() { // stub environment @@ -890,3 +2281,15 @@ public function testBreakpointWithInvalidVersion() $this->assertContains('is not a valid version', $output); } } + +/** + * RawBufferedOutput is a specialized BufferedOutput that outputs raw "writeln" calls (ie. it doesn't replace the + * tags like message. + */ +class RawBufferedOutput extends \Symfony\Component\Console\Output\BufferedOutput +{ + public function writeln($messages, $options = self::OUTPUT_RAW) + { + $this->write($messages, true, $options); + } +}