public function testParseFromPath() { $file = dirname(dirname(dirname(__DIR__))) . DS . 'data' . DS . 'CsvMigrations' . DS . 'migrations' . DS . 'Foo' . DS . 'migration.csv'; $parser = new MigrationParser(); $result = $parser->parseFromPath($file); $this->assertTrue(is_array($result), "Parser returned a non-array"); $this->assertFalse(empty($result), "Parser returned empty result"); }
/** * Check migration.csv fields * * @param array $modules List of modules to check * @return int Count of errors found */ protected function _checkMigrationFields(array $modules = []) { $errors = []; $warnings = []; $this->out('Checking migration fields:', 2); foreach ($modules as $module => $path) { $moduleErrors = []; $this->out(' - ' . $module . ' ... ', 0); $fields = null; try { $pathFinder = new MigrationPathFinder(); $path = $pathFinder->find($module); $parser = new MigrationParser(); $fields = $parser->parseFromPath($path); } catch (\Exception $e) { // We've already reported this problem in _checkMigrationPresence(); } if ($fields) { $seenFields = []; // Check each field one by one foreach ($fields as $field) { // Field name is required if (empty($field['name'])) { $moduleErrors[] = $module . " migration has a field without a name"; } else { // Check for field duplicates if (in_array($field['name'], $seenFields)) { $moduleErrors[] = $module . " migration specifies field '" . $field['name'] . "' more than once"; } else { $seenFields[] = $field['name']; } // Field type is required if (empty($field['type'])) { $moduleErrors[] = $module . " migration does not specify type for field '" . $field['name'] . "'"; } else { $type = null; $limit = null; // Matches: // * date, time, string, and other simple types // * list(something), related(Others) and other simple limits // * related(Vendor/Plugin.Model) and other complex limits if (preg_match('/^(\\w+?)\\(([\\w\\/\\.]+?)\\)$/', $field['type'], $matches)) { $type = $matches[1]; $limit = $matches[2]; } else { $type = $field['type']; } // Field type must be valid if (!$this->_isValidFieldType($type)) { $moduleErrors[] = $module . " migration specifies invalid type '" . $type . "' for field '" . $field['name'] . "'"; } else { switch ($type) { case 'related': // Only check for simple modules, not the vendor/plugin ones if (preg_match('/^\\w+$/', $limit) && !$this->_isValidModule($limit, array_keys($modules))) { $moduleErrors[] = $module . " migration relates to unknown module '{$limit}' in '" . $field['name'] . "' field"; } // Documents module can be used as `files(Documents)` for a container of the uploaded files, // or as `related(Documents)` as a regular module relationship. It's often easy to overlook // which one was desired. Failing on either one is incorrect, as both are valid. A // warning is needed instead for the `related(Documents)` case instead. // The only known legitimate case is in the Files, which is join table between Documents and FileStorage. if ('Documents' == $limit && 'Files' != $module) { $warnings[] = $module . " migration uses 'related' type for 'Documents' in '" . $field['name'] . "'. Maybe wanted 'files(Documents)'?"; } break; case 'list': case 'money': case 'metric': if (!$this->_isValidList($limit)) { $moduleErrors[] = $module . " migration uses unknown or empty list '{$limit}' in '" . $field['name'] . "' field"; } break; } } } } } // Check for the required fields // TODO: Allow specifying the required fields as the command line argument (for things like trashed) $requiredFields = ['id', 'created', 'modified']; foreach ($requiredFields as $requiredField) { if (!in_array($requiredField, $seenFields)) { $moduleErrors[] = $module . " migration is missing a required field '{$requiredField}'"; } } } $result = empty($moduleErrors) ? '<success>OK</success>' : '<error>FAIL</error>'; $this->out($result); $errors = array_merge($errors, $moduleErrors); } $this->_printCheckStatus($errors, $warnings); return count($errors); }