diff --git a/src/App.php b/src/App.php index a327149..bddff05 100644 --- a/src/App.php +++ b/src/App.php @@ -1,4 +1,5 @@ databaseConfigurationCollection = $databaseConfigurationCollection; + } + /** * Load common DB credentials file * @@ -46,44 +60,26 @@ public function loadCredentialsFile($filename) $this['db.credentialsFile'] = $filename; } - /** - * Load migration specification for a single database - * - * @param string $filename Path to specification file - * - * @return void - * @throws \RuntimeException If config is invalid - */ - public function loadDatabaseConfigFile($filename) - { - $config = json_decode(file_get_contents($filename)); - $name = $config->name; - if (!$name) { - throw new \RuntimeException("Migration file '$filename' has no name field"); - } - - $this->configuredMigrationNames[] = $name; - $this["db.migrations.$name"] = $config; - } - /** * Perform migrations from a named set * * @param string $name Migration name as specified in a .db.json migration file * - * @return void * @throws \RuntimeException If config is invalid */ - public function performMigrationSet($name) + public function performMigrationSet($name): void { - $migrator = new Migrator($name); - $migrator->setLogger($this->getLogger()); - $migrator->setDatabaseConnectionFactory($this); + $configuration = $this->databaseConfigurationCollection->get($name); + + $sourceConnection = $this->createSourceConnectionByDbName($configuration->getSourceDbName()); + $destConnection = $this->createDestConnectionByDbName($configuration->getDestinationDbName()); - $migrationConfigProcessor = new MigrationConfigProcessor(); - $migrationConfigProcessor->configureMigratorFromConfig($migrator, $this["db.migrations.$name"]); + $migrator = new Migrator($sourceConnection, $destConnection, $this->getLogger()); - $migrator->execute(); + $tableCollection = TablesList::fromConfig($configuration); + $viewCollection = ViewsList::fromConfig($configuration); + + $migrator->execute($name, $tableCollection, $viewCollection); } /** @@ -117,14 +113,14 @@ public function createDestConnectionByDbName($name) /** * Create connection object for DB name / direction. Other credentials (host, password etc) must already be known * - * @param string $name Database name + * @param string $name Database name * @param string $direction Determines whether 'source' or 'dest' credentials used, must be one of those values * * @return Connection * @throws \UnexpectedValueException If configuration is invalid * @throws \Doctrine\DBAL\DBALException If connection cannot be made */ - protected function createConnectionByDbName($name, $direction = self::CONNECTION_SOURCE) + protected function createConnectionByDbName($name, $direction): Connection { if (!isset($this["db.connections.$name"])) { $dbcredentials = isset($this["db.credentials"]->$direction) ? @@ -211,6 +207,6 @@ protected function getLogger() */ public function getConfiguredMigrationNames() { - return $this->configuredMigrationNames; + return $this->databaseConfigurationCollection->listConfigurations(); } } diff --git a/src/Configuration/MigrationConfiguration.php b/src/Configuration/MigrationConfiguration.php new file mode 100644 index 0000000..726ace0 --- /dev/null +++ b/src/Configuration/MigrationConfiguration.php @@ -0,0 +1,53 @@ +config = $config; + } + + public static function fromJson(string $configJson): self + { + $config = \json_decode($configJson); + + if (null === $config) { + throw new \RuntimeException("Migration JSON config was not valid"); + } + + if (!isset($config->name)) { + throw new \RuntimeException("Migration file has no name field"); + } + + return new self($config); + } + + public function getName(): string + { + return $this->config->name; + } + + public function getTables(): array + { + return (array)$this->config->tables; + } + + public function getViews(): array + { + return $this->config->views ?? []; + } + + public function getSourceDbName(): string + { + return $this->config->sourceDb; + } + + public function getDestinationDbName(): string + { + return $this->config->destDb; + } +} diff --git a/src/Configuration/MigrationConfigurationCollection.php b/src/Configuration/MigrationConfigurationCollection.php new file mode 100644 index 0000000..7763542 --- /dev/null +++ b/src/Configuration/MigrationConfigurationCollection.php @@ -0,0 +1,51 @@ +configs = $configs; + } + + public static function fromFilePaths(array $filepaths): self + { + $configs = []; + + foreach ($filepaths as $migrationFilePath) { + try { + $migrationFiles = [$migrationFilePath]; + + if (is_dir($migrationFilePath)) { + $migrationFiles = glob(rtrim($migrationFilePath, '/') . '/*.json'); + } + + foreach ($migrationFiles as $file) { + $config = MigrationConfiguration::fromJson(file_get_contents($file)); + + $configs[$config->getName()] = $config; + } + } catch (\RuntimeException $e) { + print("Migration file '$migrationFilePath' is invalid " . $e->getMessage()); + } + } + + return new self($configs); + } + + public function get(string $configName): MigrationConfiguration + { + return $this->configs[$configName]; + } + + public function listConfigurations(): array + { + return \array_keys($this->configs); + } +} diff --git a/src/ConsoleRunner.php b/src/ConsoleRunner.php index 92be1f7..d73f285 100644 --- a/src/ConsoleRunner.php +++ b/src/ConsoleRunner.php @@ -5,6 +5,7 @@ use Monolog\Logger; use Psr\Log\LoggerAwareInterface; use Psr\Log\LoggerInterface; +use Quidco\DbSampler\Configuration\MigrationConfigurationCollection; /** * Console Runner for Quidco\DbSampler\App @@ -88,7 +89,10 @@ public function run() exit($exitCode); } - $app = new App(); + $app = new App( + MigrationConfigurationCollection::fromFilePaths($this->migrationFilePaths) + ); + $app->setLogger($this->getLogger()); try { @@ -97,20 +101,6 @@ public function run() print("Credentials file '{$this->credentialsFilePath}' is invalid " . $e->getMessage()); } - foreach ($this->migrationFilePaths as $migrationFilePath) { - try { - if (is_dir($migrationFilePath)) { - $migrationFiles = glob(rtrim($migrationFilePath, '/') . '/*.json'); - } else { - $migrationFiles = [$migrationFilePath]; - } - foreach ($migrationFiles as $file) { - $app->loadDatabaseConfigFile($file); - } - } catch (\RuntimeException $e) { - print("Migration file '$migrationFilePath' is invalid " . $e->getMessage()); - } - } if (!$this->databases) { $this->databases = $app->getConfiguredMigrationNames(); diff --git a/src/DatabaseSchema/TablesList.php b/src/DatabaseSchema/TablesList.php new file mode 100644 index 0000000..82ff6ff --- /dev/null +++ b/src/DatabaseSchema/TablesList.php @@ -0,0 +1,73 @@ +referenceStore = new ReferenceStore(); + $this->rawTables = $rawTables; + } + + public static function fromConfig(MigrationConfiguration $configuration): self + { + return new self((array)$configuration->getTables()); + } + + public function getTables(): array + { + // @todo we probably shouldn't be building the sampler in this getter. find another place to do it! + if ([] === $this->tables) { + foreach ($this->rawTables as $table => $migrationSpec) { + $this->tables[$table] = $this->buildTableSampler($migrationSpec); + } + } + + return $this->tables; + } + + /** + * Build a SamplerInterface object from configuration + * + * @throws \UnexpectedValueException If bad object created - should be impossible + * @throws \RuntimeException On invalid specification + */ + private function buildTableSampler(\stdClass $migrationSpec): SamplerInterface + { + $sampler = null; + + // @todo: $migrationSpec should be an object with a getSampler() method + $samplerType = strtolower($migrationSpec->sampler); + if (array_key_exists($samplerType, SamplerMap::MAP)) { + $samplerClass = SamplerMap::MAP[$samplerType]; + $sampler = new $samplerClass; + if (!$sampler instanceof SamplerInterface) { + throw new \UnexpectedValueException('Invalid sampler created'); + } + /** @var SamplerInterface $sampler */ + $sampler->loadConfig($migrationSpec); + $sampler->setReferenceStore($this->referenceStore); + } else { + throw new \RuntimeException("Unrecognised sampler type '$samplerType' required"); + } + + return $sampler; + } +} diff --git a/src/DatabaseSchema/ViewsList.php b/src/DatabaseSchema/ViewsList.php new file mode 100644 index 0000000..0ab0e20 --- /dev/null +++ b/src/DatabaseSchema/ViewsList.php @@ -0,0 +1,25 @@ +views = $views; + } + + public static function fromConfig(MigrationConfiguration $configuration): self + { + return new self($configuration->getViews()); + } + + public function getViews(): array + { + return $this->views; + } +} diff --git a/src/MigrationConfigProcessor.php b/src/MigrationConfigProcessor.php deleted file mode 100644 index 629d118..0000000 --- a/src/MigrationConfigProcessor.php +++ /dev/null @@ -1,107 +0,0 @@ - CopyAll::class, - 'empty' => CopyEmpty::class, - 'matched' => Matched::class, - 'newestbyid' => NewestById::class, - 'cleanall' => CleanAll::class, - 'cleanmatched' => CleanMatched::class, - ]; - - - /** - * @var ReferenceStore - */ - protected $referenceStore; - - /** - * Create a MigrationConfigProcessor with empty ReferenceStore - */ - public function __construct() - { - $this->referenceStore = new ReferenceStore(); - } - - /** - * Apply the settings in the config file to the provided migrator - * - * @param Migrator $migrator Migrator to configure - * @param \stdClass $configuration Configuration to apply - * - * @return void - * @throws \RuntimeException If any configuration fails - */ - public function configureMigratorFromConfig(Migrator $migrator, \stdClass $configuration) - { - if (!strcasecmp(trim($configuration->sourceDb), trim($configuration->destDb))) { - throw new \RuntimeException("Source and dest DBs must not match for '{$configuration->name}'"); - } - - $migrator->setSourceDb($configuration->sourceDb); - $migrator->setDestinationDb($configuration->destDb); - $tables = (array)$configuration->tables; - $migrator->clearTableMigrations(); - foreach ($tables as $table => $migrationSpec) { - $migrator->addTableSampler($table, $this->buildTableSampler($migrationSpec)); - } - - if (isset($configuration->views)) { - $views = (array)$configuration->views; - foreach ($views as $view) { - $migrator->addViewToMigrate($view); - } - } - } - - /** - * Build a SamplerInterface object from configuration - * - * @param \stdClass $migrationSpec Config - * - * @return SamplerInterface - * @throws \UnexpectedValueException If bad object created - should be impossible - * @throws \RuntimeException On invalid specification - */ - private function buildTableSampler($migrationSpec) - { - $sampler = null; - - $samplerType = strtolower($migrationSpec->sampler); - if (array_key_exists($samplerType, $this->samplerMap)) { - $samplerClass = $this->samplerMap[$samplerType]; - $sampler = new $samplerClass; - if (!$sampler instanceof SamplerInterface) { - throw new \UnexpectedValueException('Invalid sampler created'); - } - /** @var SamplerInterface $sampler */ - $sampler->loadConfig($migrationSpec); - $sampler->setReferenceStore($this->referenceStore); - } else { - throw new \RuntimeException("Unrecognised sampler type '$samplerType' required"); - } - - return $sampler; - } -} diff --git a/src/Migrator.php b/src/Migrator.php index c175e31..375a7c2 100644 --- a/src/Migrator.php +++ b/src/Migrator.php @@ -1,226 +1,80 @@ migrationSetName = $migrationSetName; - } - - /** - * @return string - */ - public function getSourceDb() - { - return $this->sourceDb; - } + private $logger; /** - * Set source DB name - * - * @param string $sourceDb Source DB name - * - * @return Migrator + * @var Connection */ - public function setSourceDb($sourceDb) - { - $this->sourceDb = $sourceDb; - - return $this; - } + private $sourceConnection; /** - * @return string + * @var Connection */ - public function getDestinationDb() - { - return $this->destinationDb; - } + private $destConnection; - /** - * Set destination DB name - * - * @param string $destinationDb Destination DB name - * - * @return Migrator - */ - public function setDestinationDb($destinationDb) - { - $this->destinationDb = $destinationDb; - - return $this; - } - - /** - * @return DatabaseConnectionFactoryInterface - */ - public function getDatabaseConnectionFactory() - { - return $this->databaseConnectionFactory; - } - /** - * Set Database Connection Factory - * - * @param DatabaseConnectionFactoryInterface $databaseConnectionFactory Database Connection Factory - * - * @return Migrator - */ - public function setDatabaseConnectionFactory(DatabaseConnectionFactoryInterface $databaseConnectionFactory) - { - $this->databaseConnectionFactory = $databaseConnectionFactory; - - return $this; + public function __construct( + Connection $sourceConnection, + Connection $destConnection, + LoggerInterface $logger + ) { + $this->sourceConnection = $sourceConnection; + $this->destConnection = $destConnection; + $this->logger = $logger; } /** * Perform the configured migrations * - * @return void - * - * @throws \Exception Rethrows any exceptions after loggin + * @throws \Exception Rethrows any exceptions after logging */ - public function execute() + public function execute(string $setName, TablesList $tableCollection, ViewsList $viewCollection): void { - $sourceConnection = $this->databaseConnectionFactory->createSourceConnectionByDbName($this->sourceDb); - $destConnection = $this->databaseConnectionFactory->createDestConnectionByDbName($this->destinationDb); - - $setName = $this->migrationSetName; - - foreach ($this->tableMigrations as $table => $sampler) { + foreach ($tableCollection->getTables() as $table => $sampler) { try { - $this->ensureEmptyTargetTable($table, $sourceConnection, $destConnection); + $this->ensureEmptyTargetTable($table, $this->sourceConnection, $this->destConnection); $sampler->setTableName($table); - $sampler->setSourceConnection($sourceConnection); - $sampler->setDestConnection($destConnection); + $sampler->setSourceConnection($this->sourceConnection); + $sampler->setDestConnection($this->destConnection); $rows = $sampler->execute(); - $this->getLogger()->info("$setName: migrated '$table' with '" . $sampler->getName() . "': $rows rows"); + $this->logger->info("$setName: migrated '$table' with '" . $sampler->getName() . "': $rows rows"); } catch (\Exception $e) { - $this->getLogger()->error( + $this->logger->error( "$setName: failed to migrate '$table' with '" . $sampler->getName() . "': " . $e->getMessage() ); throw $e; } } - foreach ($this->viewsToMigrate as $view) { - $this->migrateView($view, $sourceConnection, $destConnection); + foreach ($viewCollection->getViews() as $view) { + $this->migrateView($view, $setName); } - $this->migrateTableTriggers($sourceConnection, $destConnection); + $this->migrateTableTriggers($setName, $tableCollection); } - /** - * Reset the list of table samplers to be empty - * - * @return void - */ - public function clearTableMigrations() - { - $this->tableMigrations = []; - } - - /** - * Reset the list of view migrations to be empty - * - * @return void - */ - public function clearViewMigrations() - { - $this->viewsToMigrate = []; - } - - /** - * Add a SamplerInterface object to handle a named table - * - * @param string $table Table name - * @param SamplerInterface $sampler Sampler class - * - * @return void - */ - public function addTableSampler($table, SamplerInterface $sampler) - { - //TODO these might need to be in order - $this->tableMigrations[$table] = $sampler; - } - - /** - * Add view to be migrated, by name - * - * @param string $view Name of view to add - * - * @return void - */ - public function addViewToMigrate($view) - { - $this->viewsToMigrate[] = $view; - } /** * Ensure that the specified table is present in the destination DB as an empty copy of the source * - * @param string $table Table name + * @param string $table Table name * @param Connection $sourceConnection Originating DB connection - * @param Connection $destConnection Target DB connection + * @param Connection $destConnection Target DB connection * * @return void * @throws \RuntimeException If DB type not supported @@ -249,27 +103,24 @@ private function ensureEmptyTargetTable($table, Connection $sourceConnection, Co /** * Ensure that all table triggers from source are recreated in the destination * - * @param Connection $sourceConnection Originating DB connection - * @param Connection $destConnection Target DB connection - * * @return void * @throws \RuntimeException If DB type not supported * @throws \Doctrine\DBAL\DBALException If target trigger cannot be recreated */ - private function migrateTableTriggers(Connection $sourceConnection, Connection $destConnection) + private function migrateTableTriggers(string $setName, TablesList $tableCollection): void { - foreach ($this->tableMigrations as $table => $sampler) { + foreach ($tableCollection->getTables() as $table => $sampler) { try { - $triggerSql = $this->generateTableTriggerSql($table, $sourceConnection); + $triggerSql = $this->generateTableTriggerSql($table, $this->sourceConnection); foreach ($triggerSql as $sql) { - $destConnection->exec($sql); + $this->destConnection->exec($sql); } if (count($triggerSql)) { - $this->getLogger()->info("$this->migrationSetName: Migrated " . count($triggerSql) . " trigger(s) on $table"); + $this->logger->info("$setName: Migrated " . count($triggerSql) . " trigger(s) on $table"); } } catch (\Exception $e) { - $this->getLogger()->error( - "$this->migrationSetName: failed to migrate '$table' with '" . $sampler->getName() . "': " . $e->getMessage() + $this->logger->error( + "$setName: failed to migrate '$table' triggers with '" . $sampler->getName() . "': " . $e->getMessage() ); throw $e; } @@ -279,7 +130,7 @@ private function migrateTableTriggers(Connection $sourceConnection, Connection $ /** * Regenerate the SQL to create any triggers from the table * - * @param string $table Table name + * @param string $table Table name * @param Connection $dbConnection Originating DB connection * * @return array @@ -294,7 +145,7 @@ private function generateTableTriggerSql($table, Connection $dbConnection) if ($triggers && count($triggers) > 0) { foreach ($triggers as $trigger) { $triggerSql[] = 'CREATE TRIGGER ' . $trigger['Trigger'] . ' ' . $trigger['Timing'] . ' ' . $trigger['Event'] . - ' ON ' . $dbConnection->quoteIdentifier($trigger['Table']) . ' FOR EACH ROW ' .PHP_EOL . $trigger['Statement'] . '; '; + ' ON ' . $dbConnection->quoteIdentifier($trigger['Table']) . ' FOR EACH ROW ' . PHP_EOL . $trigger['Statement'] . '; '; } } } elseif ($driverName === 'pdo_sqlite') { @@ -312,43 +163,20 @@ private function generateTableTriggerSql($table, Connection $dbConnection) return $triggerSql; } - - /** - * Sets a logger instance on the object. - * - * @param LoggerInterface $logger Logger instance - * - * @return void - */ - public function setLogger(LoggerInterface $logger) - { - $this->logger = $logger; - } - - /** - * Safely obtain either a configured or null logger - * - * @return LoggerInterface - */ - protected function getLogger() - { - return $this->logger ?: new NullLogger(); - } - /** * Migrate a view from source to dest DB * - * @param string $view Name of view to migrate - * @param Connection $sourceConnection Source connection - * @param Connection $destConnection Destination connection - * - * @return void + * @param string $view Name of view to migrate + * @param string $setName Name of migration set being executed * * @throws \Doctrine\DBAL\DBALException If view cannot be read * @throws \RuntimeException For DB types where this is unsupported */ - protected function migrateView($view, Connection $sourceConnection, Connection $destConnection) + protected function migrateView(string $view, string $setName): void { + $sourceConnection = $this->sourceConnection; + $destConnection = $this->destConnection; + $destConnection->exec('DROP VIEW IF EXISTS ' . $sourceConnection->quoteIdentifier($view)); $driverName = $sourceConnection->getDriver()->getName(); @@ -375,7 +203,6 @@ protected function migrateView($view, Connection $sourceConnection, Connection $ } $destConnection->exec($createSql); - $setName = $this->migrationSetName; $this->logger->info("$setName: migrated view '$view'"); } } diff --git a/src/SamplerMap.php b/src/SamplerMap.php new file mode 100644 index 0000000..f350d4c --- /dev/null +++ b/src/SamplerMap.php @@ -0,0 +1,22 @@ + CopyAll::class, + 'empty' => CopyEmpty::class, + 'matched' => Matched::class, + 'newestbyid' => NewestById::class, + 'cleanall' => CleanAll::class, + 'cleanmatched' => CleanMatched::class, + ]; +} diff --git a/tests/AppSetupTest.php b/tests/AppSetupTest.php index 0de13f0..5d8d28e 100644 --- a/tests/AppSetupTest.php +++ b/tests/AppSetupTest.php @@ -3,34 +3,25 @@ namespace Quidco\DbSampler\Tests; use Quidco\DbSampler\App; +use Quidco\DbSampler\Configuration\MigrationConfigurationCollection; /** * Class AppSetupTest */ class AppSetupTest extends SqliteBasedTestCase { - - - /** - * Run at least one test to confirm that tests are working! - * - * @return void - */ - public function testTrivial() - { - $this->assertTrue(true); - } - /** * Run the example migration * * @return void */ - public function testSampleMigration() + public function testSampleMigration(): void { - $app = new App(); + $config = MigrationConfigurationCollection::fromFilePaths([$this->fixturesDir . '/small_sqlite_migration.json']); + + $app = new App($config); $this->assertInstanceOf(App::class, $app); - $app->loadDatabaseConfigFile($this->fixturesDir . '/small_sqlite_migration.json'); + $this->assertSame(['small-sqlite-test'], $app->getConfiguredMigrationNames()); $app->loadCredentialsFile($this->fixturesDir . '/sqlite-credentials.json'); $app->performMigrationSet('small-sqlite-test'); @@ -46,13 +37,14 @@ public function testSampleMigration() * Check that sqlite credential files handle missing directory field correctly * * @return void + * @expectedException \RuntimeException */ - public function testSqliteCredentialMissingDirectoryHandling() + public function testSqliteCredentialMissingDirectoryHandling(): void { - $app = new App(); + $config = MigrationConfigurationCollection::fromFilePaths([$this->fixturesDir . '/small_sqlite_migration.json']); + + $app = new App($config); $app->loadCredentialsFile($this->fixturesDir . '/sqlite-credentials-no-dir.json'); - $app->loadDatabaseConfigFile($this->fixturesDir . '/small_sqlite_migration.json'); - $this->expectException(\RuntimeException::class); $app->createDestConnectionByDbName('small-sqlite-source'); // directory tested at connection time now } @@ -61,11 +53,12 @@ public function testSqliteCredentialMissingDirectoryHandling() * * @return void */ - public function testSqliteCredentialRelativeDirectoryHandling() + public function testSqliteCredentialRelativeDirectoryHandling(): void { - $app = new App(); + $config = MigrationConfigurationCollection::fromFilePaths([$this->fixturesDir . '/small_sqlite_migration.json']); + + $app = new App($config); $app->loadCredentialsFile($this->fixturesDir . '/sqlite-credentials-relative-dir.json'); - $app->loadDatabaseConfigFile($this->fixturesDir . '/small_sqlite_migration.json'); $app->createDestConnectionByDbName('small-sqlite-source'); // resolution of dirs now happens later @@ -79,11 +72,13 @@ public function testSqliteCredentialRelativeDirectoryHandling() * * @return void */ - public function testSqliteCredentialSourceDestDirectoryHandling() + public function testSqliteCredentialSourceDestDirectoryHandling(): void { - $app = new App(); + $config = MigrationConfigurationCollection::fromFilePaths([$this->fixturesDir . '/small_sqlite_migration.json']); + + $app = new App($config); $app->loadCredentialsFile($this->fixturesDir . '/sqlite-credentials-source-dest.json'); - $app->loadDatabaseConfigFile($this->fixturesDir . '/small_sqlite_migration.json'); + $destConn = $app->createDestConnectionByDbName('small-sqlite-source'); $this->assertInstanceOf(\Doctrine\DBAL\Connection::class, $destConn); } diff --git a/tests/Configuration/MigrationConfigurationCollectionTest.php b/tests/Configuration/MigrationConfigurationCollectionTest.php new file mode 100644 index 0000000..2e1bbf4 --- /dev/null +++ b/tests/Configuration/MigrationConfigurationCollectionTest.php @@ -0,0 +1,102 @@ +expectException(\RuntimeException::class); + $this->expectExceptionMessage('Migration JSON config was not valid'); + MigrationConfiguration::fromJson('hello!'); + } + + public function testANameFieldMustBeSupplied(): void + { + $this->expectException(\RuntimeException::class); + $this->expectExceptionMessage('Migration file has no name field'); + MigrationConfiguration::fromJson('{}'); + } + + public function testTheNameIsReturned(): void + { + $migrationSetName = 'test-migration'; + + $config = MigrationConfiguration::fromJson(\json_encode([ + 'name' => $migrationSetName, + ])); + + $this->assertSame('test-migration', $config->getName()); + } + + public function testTheDatabaseNamesAreReturned(): void + { + $sourceDb = 'source-db'; + $destinationDb = 'destination-db'; + + $config = MigrationConfiguration::fromJson(\json_encode([ + 'name' => 'test-migration', + 'sourceDb' => $sourceDb, + 'destDb' => $destinationDb, + ])); + + $this->assertSame($sourceDb, $config->getSourceDbName()); + $this->assertSame($destinationDb, $config->getDestinationDbName()); + } + + public function testTheTableConfigIsReturned(): void + { + $fruits = [ + 'sampler' => 'matched', + 'constraints' => [ + 'name' => [ + 'apple', + 'pear' + ] + ], + 'remember' => [ + 'id' => 'fruit_ids' + ] + ]; + + $vegetables = [ + 'sampler' => 'NewestById', + 'idField' => 'id', + 'quantity' => 2 + ]; + + $config = MigrationConfiguration::fromJson(\json_encode([ + 'name' => 'test-migration', + 'tables' => [ + 'fruits' => $fruits, + 'vegetables' => $vegetables, + ] + ])); + + $this->assertEquals( + [ + 'fruits' => $fruits, + 'vegetables' => $vegetables + ], + \json_decode(\json_encode($config->getTables()), true) + ); + } + + public function testTheViewConfigIsReturned(): void + { + $config = MigrationConfiguration::fromJson(\json_encode([ + 'name' => 'test-migration', + 'views' => [ + 'basket_contents' + ] + ])); + + $this->assertEquals( + ['basket_contents'], + $config->getViews() + ); + } +} diff --git a/tests/DatabaseSchema/TablesListTest.php b/tests/DatabaseSchema/TablesListTest.php new file mode 100644 index 0000000..9eaea0d --- /dev/null +++ b/tests/DatabaseSchema/TablesListTest.php @@ -0,0 +1,67 @@ + 'invalidsampler', + ]; + + $config = MigrationConfiguration::fromJson(\json_encode([ + 'name' => 'test-migration', + 'tables' => [ + 'fruits' => $fruits + ] + ])); + + $tablesList = TablesList::fromConfig($config); + + $this->expectException(\RuntimeException::class); + $this->expectExceptionMessage('Unrecognised sampler type \'invalidsampler\' required'); + $tablesList->getTables(); + } + + public function testItCreatesTheCorrectSamplerConfig(): void + { + $fruits = [ + 'sampler' => 'matched', + 'constraints' => [ + 'name' => [ + 'apple', + 'pear' + ] + ], + 'remember' => [ + 'id' => 'fruit_ids' + ] + ]; + + $vegetables = [ + 'sampler' => 'NewestById', + 'idField' => 'id', + 'quantity' => 2 + ]; + + $config = MigrationConfiguration::fromJson(\json_encode([ + 'name' => 'test-migration', + 'tables' => [ + 'fruits' => $fruits, + 'vegetables' => $vegetables, + ] + ])); + + $tablesList = TablesList::fromConfig($config); + + $this->assertInstanceOf(Matched::class, $tablesList->getTables()['fruits']); + $this->assertInstanceOf(NewestById::class, $tablesList->getTables()['vegetables']); + } +} diff --git a/tests/DatabaseSchema/ViewsListTest.php b/tests/DatabaseSchema/ViewsListTest.php new file mode 100644 index 0000000..245c581 --- /dev/null +++ b/tests/DatabaseSchema/ViewsListTest.php @@ -0,0 +1,45 @@ + 'test-migration', + 'views' => $views + ])); + + $viewsList = ViewsList::fromConfig($config); + + $this->assertSame($views, $viewsList->getViews()); + } + + public function testViewsAreOptional(): void + { + $fruits = [ + 'sampler' => 'matched', + ]; + + $config = MigrationConfiguration::fromJson(\json_encode([ + 'name' => 'test-migration', + 'tables' => [ + 'fruits' => $fruits, + ] + ])); + + $viewsList = ViewsList::fromConfig($config); + + $this->assertSame([], $viewsList->getViews()); + } +} diff --git a/tests/FieldCleanerProviderTest.php b/tests/FieldCleanerProviderTest.php index 29f5b9e..04af95d 100644 --- a/tests/FieldCleanerProviderTest.php +++ b/tests/FieldCleanerProviderTest.php @@ -17,7 +17,7 @@ public function setUp(): void $this->fieldCleaner = new FieldCleanerProvider(); } - public function randomDigitsProvider() + public function randomDigitsProvider(): array { return [ [5], @@ -28,10 +28,8 @@ public function randomDigitsProvider() /** * @dataProvider randomDigitsProvider - * - * @param $numDigits */ - public function testRandomDigits($numDigits) + public function testRandomDigits(int $numDigits): void { $randomDigitCleaner = $this->fieldCleaner->getCleanerByDescription('randomdigits:' . $numDigits); @@ -42,7 +40,7 @@ public function testRandomDigits($numDigits) $this->assertEquals($numDigits, strlen($cleaned)); } - public function testRandomDigitsDefault() + public function testRandomDigitsDefault(): void { $randomDigitCleaner = $this->fieldCleaner->getCleanerByDescription('randomdigits'); diff --git a/tests/ReferenceStoreTest.php b/tests/ReferenceStoreTest.php index 88d2d85..e88542e 100644 --- a/tests/ReferenceStoreTest.php +++ b/tests/ReferenceStoreTest.php @@ -7,7 +7,7 @@ class ReferenceStoreTest extends TestCase { - public function testBasicFunctions() + public function testBasicFunctions(): void { $store = new ReferenceStore(); $primes = [1, 3, 5, 7]; diff --git a/tests/SamplerTest.php b/tests/SamplerTest.php index e7a5a23..35e8384 100644 --- a/tests/SamplerTest.php +++ b/tests/SamplerTest.php @@ -9,13 +9,13 @@ class SamplerTest extends SqliteBasedTestCase { - public function testEmptySampler() + public function testEmptySampler(): void { $sampler = new CopyEmpty(); $this->assertSame([], $sampler->getRows()); } - public function testCopyAllSampler() + public function testCopyAllSampler(): void { $sampler = new CopyAll(); $sampler->setTableName('fruits'); @@ -23,7 +23,7 @@ public function testCopyAllSampler() $this->assertCount(4, $sampler->getRows()); } - public function testCopyAllWithReferenceStore() + public function testCopyAllWithReferenceStore(): void { $sampler = new CopyAll(); $sampler->setTableName('fruits'); @@ -46,7 +46,7 @@ private function generateMatched() return $sampler; } - public function testMatchedWithWhereClause() + public function testMatchedWithWhereClause(): void { $sampler = $this->generateMatched(); $config = [ @@ -59,7 +59,7 @@ public function testMatchedWithWhereClause() $this->assertCount(2, $sampler->getRows()); } - public function testMatchedWhereNoConstraints() + public function testMatchedWhereNoConstraints(): void { $sampler = $this->generateMatched(); $config = [ @@ -72,7 +72,7 @@ public function testMatchedWhereNoConstraints() /** */ - public function testMatchedNoConfigThrows() + public function testMatchedNoConfigThrows(): void { $sampler = $this->generateMatched(); $this->expectException(\RuntimeException::class);