first commit
This commit is contained in:
+6
@@ -0,0 +1,6 @@
|
||||
name: 'Migrate entity test'
|
||||
type: module
|
||||
description: 'Support module for entity destination test.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_entity_test\Entity;
|
||||
|
||||
use Drupal\Core\Entity\ContentEntityBase;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Field\BaseFieldDefinition;
|
||||
|
||||
/**
|
||||
* Defines a content entity type that has a string ID.
|
||||
*
|
||||
* @ContentEntityType(
|
||||
* id = "migrate_string_id_entity_test",
|
||||
* label = @Translation("String id entity test"),
|
||||
* base_table = "migrate_entity_test_string_id",
|
||||
* entity_keys = {
|
||||
* "id" = "id",
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
class StringIdEntityTest extends ContentEntityBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
|
||||
return [
|
||||
'id' => BaseFieldDefinition::create('integer')
|
||||
->setSetting('size', 'big')
|
||||
->setLabel('ID'),
|
||||
'version' => BaseFieldDefinition::create('string')
|
||||
->setLabel('Version'),
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
name: 'Migrate events test'
|
||||
type: module
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_events_test\Plugin\migrate\destination;
|
||||
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\migrate\Plugin\migrate\destination\DestinationBase;
|
||||
use Drupal\migrate\Row;
|
||||
|
||||
/**
|
||||
* @MigrateDestination(
|
||||
* id = "dummy",
|
||||
* requirements_met = true
|
||||
* )
|
||||
*/
|
||||
class DummyDestination extends DestinationBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIds() {
|
||||
$ids['value']['type'] = 'string';
|
||||
return $ids;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fields(MigrationInterface $migration = NULL) {
|
||||
return ['value' => 'Dummy value'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function import(Row $row, array $old_destination_id_values = []) {
|
||||
return ['value' => $row->getDestinationProperty('value')];
|
||||
}
|
||||
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
name: 'Migration external translated test'
|
||||
type: module
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- drupal:node
|
||||
- drupal:migrate
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
id: external_translated_test_node
|
||||
label: External translated content
|
||||
source:
|
||||
plugin: migrate_external_translated_test
|
||||
default_lang: true
|
||||
constants:
|
||||
type: external_test
|
||||
process:
|
||||
type: constants/type
|
||||
title: title
|
||||
langcode:
|
||||
plugin: static_map
|
||||
source: lang
|
||||
map:
|
||||
English: en
|
||||
French: fr
|
||||
Spanish: es
|
||||
destination:
|
||||
plugin: entity:node
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
id: external_translated_test_node_translation
|
||||
label: External translated content translations
|
||||
source:
|
||||
plugin: migrate_external_translated_test
|
||||
default_lang: false
|
||||
constants:
|
||||
type: external_test
|
||||
process:
|
||||
nid:
|
||||
plugin: migration_lookup
|
||||
source: name
|
||||
migration: external_translated_test_node
|
||||
type: constants/type
|
||||
title: title
|
||||
langcode:
|
||||
plugin: static_map
|
||||
source: lang
|
||||
map:
|
||||
English: en
|
||||
French: fr
|
||||
Spanish: es
|
||||
destination:
|
||||
plugin: entity:node
|
||||
translations: true
|
||||
migration_dependencies:
|
||||
required:
|
||||
- external_translated_test_node
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_external_translated_test\Plugin\migrate\source;
|
||||
|
||||
use Drupal\migrate\Plugin\migrate\source\SourcePluginBase;
|
||||
|
||||
/**
|
||||
* A simple migrate source for our tests.
|
||||
*
|
||||
* @MigrateSource(
|
||||
* id = "migrate_external_translated_test",
|
||||
* source_module = "migrate_external_translated_test"
|
||||
* )
|
||||
*/
|
||||
class MigrateExternalTranslatedTestSource extends SourcePluginBase {
|
||||
|
||||
/**
|
||||
* The data to import.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $import = [
|
||||
['name' => 'cat', 'title' => 'Cat', 'lang' => 'English'],
|
||||
['name' => 'cat', 'title' => 'Chat', 'lang' => 'French'],
|
||||
['name' => 'cat', 'title' => 'Gato', 'lang' => 'Spanish'],
|
||||
['name' => 'dog', 'title' => 'Dog', 'lang' => 'English'],
|
||||
['name' => 'dog', 'title' => 'Chien', 'lang' => 'French'],
|
||||
['name' => 'monkey', 'title' => 'Monkey', 'lang' => 'English'],
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fields() {
|
||||
return [
|
||||
'name' => $this->t('Unique name'),
|
||||
'title' => $this->t('Title'),
|
||||
'lang' => $this->t('Language'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __toString() {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIds() {
|
||||
$ids['name']['type'] = 'string';
|
||||
if (!$this->configuration['default_lang']) {
|
||||
$ids['lang']['type'] = 'string';
|
||||
}
|
||||
return $ids;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function initializeIterator() {
|
||||
$data = [];
|
||||
|
||||
// Keep the rows with the right languages.
|
||||
$want_default = $this->configuration['default_lang'];
|
||||
foreach ($this->import as $row) {
|
||||
$is_english = $row['lang'] == 'English';
|
||||
if ($want_default == $is_english) {
|
||||
$data[] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
return new \ArrayIterator($data);
|
||||
}
|
||||
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
langcode: en
|
||||
status: true
|
||||
name: High Water import node
|
||||
type: high_water_import_node
|
||||
description: ''
|
||||
help: ''
|
||||
new_revision: false
|
||||
preview_mode: 1
|
||||
display_submitted: true
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
type: module
|
||||
name: Migration High Water Test
|
||||
description: 'Provides test fixtures for testing high water marks.'
|
||||
package: Testing
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- drupal:migrate
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
id: high_water_test
|
||||
label: High water test.
|
||||
source:
|
||||
plugin: high_water_test
|
||||
high_water_property:
|
||||
name: changed
|
||||
destination:
|
||||
plugin: entity:node
|
||||
migration_tags:
|
||||
test: test
|
||||
process:
|
||||
changed: changed
|
||||
title: title
|
||||
type:
|
||||
plugin: default_value
|
||||
default_value: high_water_import_node
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_high_water_test\Plugin\migrate\source;
|
||||
|
||||
use Drupal\migrate\Plugin\migrate\source\SqlBase;
|
||||
|
||||
/**
|
||||
* Source plugin for migration high water tests.
|
||||
*
|
||||
* @MigrateSource(
|
||||
* id = "high_water_test"
|
||||
* )
|
||||
*/
|
||||
class HighWaterTest extends SqlBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function query() {
|
||||
$field_names = array_keys($this->fields());
|
||||
$query = $this
|
||||
->select('high_water_node', 'm')
|
||||
->fields('m', $field_names);
|
||||
foreach ($field_names as $field_name) {
|
||||
$query->groupBy($field_name);
|
||||
}
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fields() {
|
||||
$fields = [
|
||||
'id' => $this->t('Id'),
|
||||
'title' => $this->t('Title'),
|
||||
'changed' => $this->t('Changed'),
|
||||
];
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIds() {
|
||||
return [
|
||||
'id' => [
|
||||
'type' => 'integer',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
name: 'Migration Lookup Test'
|
||||
type: module
|
||||
description: 'Provides test migrations to test migration lookup service.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- drupal:migrate
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
id: sample_lookup_migration
|
||||
label: Sample Lookup Migration
|
||||
source:
|
||||
plugin: embedded_data
|
||||
data_rows:
|
||||
- id: 17
|
||||
nid: 1
|
||||
title: Node 1
|
||||
- id: 25
|
||||
nid: 2
|
||||
title: Node 2
|
||||
ids:
|
||||
id:
|
||||
type: integer
|
||||
process:
|
||||
nid: nid
|
||||
title: title
|
||||
destination:
|
||||
default_bundle: node_lookup
|
||||
plugin: entity:node
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
id: sample_lookup_migration_2
|
||||
label: Sample Lookup Migration
|
||||
source:
|
||||
plugin: embedded_data
|
||||
data_rows:
|
||||
- id: 27
|
||||
nid: 3
|
||||
title: Node 3
|
||||
- id: 35
|
||||
nid: 4
|
||||
title: Node 4
|
||||
ids:
|
||||
id:
|
||||
type: integer
|
||||
process:
|
||||
nid: nid
|
||||
title: title
|
||||
destination:
|
||||
default_bundle: node_lookup
|
||||
plugin: entity:node
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
id: sample_lookup_migration_multiple_source_ids
|
||||
label: "Sample Lookup Migration With Multiple Source Ids."
|
||||
source:
|
||||
plugin: embedded_data
|
||||
data_rows:
|
||||
- id: 17
|
||||
version_id: 17
|
||||
nid: 1
|
||||
title: "Node 1"
|
||||
- id: 25
|
||||
version_id: 25
|
||||
nid: 2
|
||||
title: "Node 2"
|
||||
- id: 25
|
||||
version_id: 26
|
||||
nid: 3
|
||||
title: "Node 3"
|
||||
ids:
|
||||
id:
|
||||
type: integer
|
||||
version_id:
|
||||
type: integer
|
||||
process:
|
||||
nid: nid
|
||||
title: title
|
||||
destination:
|
||||
default_bundle: node_lookup
|
||||
plugin: entity:node
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
id: sample_lookup_migration_string_ids
|
||||
label: Sample Lookup Migration with string ids
|
||||
source:
|
||||
plugin: embedded_data
|
||||
data_rows:
|
||||
- id: node1
|
||||
nid: 10
|
||||
title: Node 10
|
||||
- id: node2
|
||||
nid: 11
|
||||
title: Node 11
|
||||
ids:
|
||||
id:
|
||||
type: string
|
||||
process:
|
||||
nid: nid
|
||||
title: title
|
||||
destination:
|
||||
default_bundle: node_lookup
|
||||
plugin: entity:node
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
name: 'Migrate No Migrate Drupal Test'
|
||||
type: module
|
||||
description: Provides fixture for testing without migrate_drupal.
|
||||
package: Testing
|
||||
version: VERSION
|
||||
dependencies:
|
||||
- drupal:migrate
|
||||
- drupal:node
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
migrate_no_migrate_drupal_test.execute:
|
||||
path: '/migrate_no_migrate_drupal_test/execute'
|
||||
defaults:
|
||||
_controller: '\Drupal\migrate_no_migrate_drupal_test\Controller\ExecuteMigration::execute'
|
||||
_title: 'Execute'
|
||||
requirements:
|
||||
_access: 'TRUE'
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
id: node_migration_no_migrate_drupal
|
||||
label: Node Migration No Migrate Drupal
|
||||
source:
|
||||
plugin: embedded_data
|
||||
data_rows:
|
||||
-
|
||||
id: 1
|
||||
nid: 1
|
||||
title: Node 1
|
||||
-
|
||||
id: 2
|
||||
nid: 2
|
||||
title: Node 2
|
||||
ids:
|
||||
id:
|
||||
type: integer
|
||||
process:
|
||||
nid: nid
|
||||
title: title
|
||||
destination:
|
||||
default_bundle: no_migrate_drupal
|
||||
plugin: entity:node
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_no_migrate_drupal_test\Controller;
|
||||
|
||||
use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
|
||||
use Drupal\Core\Controller\ControllerBase;
|
||||
use Drupal\migrate\MigrateExecutable;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
|
||||
/**
|
||||
* Custom controller to execute the test migrations.
|
||||
*
|
||||
* This controller class is required for the proper functional testing of
|
||||
* migration dependencies. Otherwise, the migration directly executed from the
|
||||
* functional test would use the functional test's class map and autoloader. The
|
||||
* functional test has all the classes available to it but the controller
|
||||
* does not.
|
||||
*/
|
||||
class ExecuteMigration extends ControllerBase {
|
||||
|
||||
/**
|
||||
* Run the node_migration_no_migrate_drupal test migration.
|
||||
*
|
||||
* @return array
|
||||
* A renderable array.
|
||||
*/
|
||||
public function execute() {
|
||||
$migration_plugin_manager = \Drupal::service('plugin.manager.migration');
|
||||
$definitions = $migration_plugin_manager->getDefinitions();
|
||||
if ($definitions['node_migration_no_migrate_drupal']['label'] !== 'Node Migration No Migrate Drupal') {
|
||||
throw new InvalidPluginDefinitionException('node_migration_no_migrate_drupal');
|
||||
}
|
||||
$migrations = $migration_plugin_manager->createInstances('');
|
||||
$result = (new MigrateExecutable($migrations['node_migration_no_migrate_drupal']))->import();
|
||||
if ($result !== MigrationInterface::RESULT_COMPLETED) {
|
||||
throw new \RuntimeException('Migration failed');
|
||||
}
|
||||
|
||||
return [
|
||||
'#type' => 'markup',
|
||||
'#markup' => 'Migration was successful.',
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
name: 'Migrate module prepareRow tests'
|
||||
type: module
|
||||
description: 'Support module for source plugin prepareRow testing.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Test module for testing the migration source plugin prepareRow() exception
|
||||
* handling.
|
||||
*/
|
||||
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\migrate\MigrateSkipRowException;
|
||||
use Drupal\migrate\Plugin\MigrateSourceInterface;
|
||||
use Drupal\migrate\Row;
|
||||
|
||||
/**
|
||||
* Implements hook_migrate_prepare_row().
|
||||
*/
|
||||
function migrate_prepare_row_test_migrate_prepare_row(Row $row, MigrateSourceInterface $source, MigrationInterface $migration) {
|
||||
// Test both options for save_to_map.
|
||||
$data = $row->getSourceProperty('data');
|
||||
if ($data == 'skip_and_record') {
|
||||
// Record mapping but don't record a message.
|
||||
throw new MigrateSkipRowException('', TRUE);
|
||||
}
|
||||
elseif ($data == 'skip_and_dont_record') {
|
||||
// Don't record mapping but record a message.
|
||||
throw new MigrateSkipRowException('skip_and_dont_record message', FALSE);
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_prepare_row_test\Plugin\migrate\process;
|
||||
|
||||
use Drupal\migrate\MigrateExecutableInterface;
|
||||
use Drupal\migrate\MigrateSkipRowException;
|
||||
use Drupal\migrate\ProcessPluginBase;
|
||||
use Drupal\migrate\Row;
|
||||
|
||||
/**
|
||||
* Provides a testing process plugin that skips rows.
|
||||
*
|
||||
* @MigrateProcessPlugin(
|
||||
* id = "test_skip_row_process"
|
||||
* )
|
||||
*/
|
||||
class TestSkipRowProcess extends ProcessPluginBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
|
||||
// Test both options for save_to_map.
|
||||
$data = $row->getSourceProperty('data');
|
||||
if ($data == 'skip_and_record (use plugin)') {
|
||||
throw new MigrateSkipRowException('', TRUE);
|
||||
}
|
||||
elseif ($data == 'skip_and_dont_record (use plugin)') {
|
||||
throw new MigrateSkipRowException('', FALSE);
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
type: module
|
||||
name: Migrate query batch Source test
|
||||
description: 'Provides a database table and records for SQL import with batch testing.'
|
||||
package: Testing
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- drupal:migrate
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_query_batch_test\Plugin\migrate\source;
|
||||
|
||||
use Drupal\migrate\Plugin\migrate\source\SqlBase;
|
||||
|
||||
/**
|
||||
* Source plugin for migration high water tests.
|
||||
*
|
||||
* @MigrateSource(
|
||||
* id = "query_batch_test"
|
||||
* )
|
||||
*/
|
||||
class QueryBatchTest extends SqlBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function query() {
|
||||
return ($this->select('query_batch_test', 'q')->fields('q'));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fields() {
|
||||
$fields = [
|
||||
'id' => $this->t('Id'),
|
||||
'data' => $this->t('data'),
|
||||
];
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIds() {
|
||||
return [
|
||||
'id' => [
|
||||
'type' => 'integer',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
name: 'Migration Stub Test'
|
||||
type: module
|
||||
description: 'Provides test migrations to test migration stub service.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- drupal:migrate
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
id: sample_stubbing_migration
|
||||
label: "Sample Stubbing Migration"
|
||||
source:
|
||||
plugin: embedded_data
|
||||
data_rows:
|
||||
- id: 17
|
||||
title: "Sample 1"
|
||||
bodyvalue: "This is the body for ID 17"
|
||||
bodyformat: "plain_text"
|
||||
- id: 25
|
||||
title: "Sample 2"
|
||||
bodyvalue: "This is the body for ID 25"
|
||||
bodyformat: "plain_text"
|
||||
ids:
|
||||
id:
|
||||
type: integer
|
||||
process:
|
||||
title: title
|
||||
body/0/value: bodyvalue
|
||||
body/0/format: bodyformat
|
||||
destination:
|
||||
default_bundle: node_stub
|
||||
plugin: entity:node
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
id: sample_stubbing_migration_with_multiple_source_ids
|
||||
label: "Sample stubbing migration with multiple source ids."
|
||||
source:
|
||||
plugin: embedded_data
|
||||
data_rows:
|
||||
- id: 17
|
||||
version_id: 17
|
||||
title: "Sample 1"
|
||||
bodyvalue: "This is the body for ID 17"
|
||||
bodyformat: "plain_text"
|
||||
- id: 25
|
||||
version_id: 25
|
||||
title: "Sample 2"
|
||||
bodyvalue: "This is the body for ID 25"
|
||||
bodyformat: "plain_text"
|
||||
ids:
|
||||
id:
|
||||
type: integer
|
||||
version_id:
|
||||
type: integer
|
||||
process:
|
||||
title: title
|
||||
body/0/value: bodyvalue
|
||||
body/0/format: bodyformat
|
||||
destination:
|
||||
default_bundle: node_stub
|
||||
plugin: entity:node
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies: { }
|
||||
name: Track changes import term
|
||||
vid: track_changes_import_term
|
||||
description: ''
|
||||
weight: 0
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
type: module
|
||||
name: Migration Track Changes Test
|
||||
description: 'Provides test fixtures for testing track changes marks.'
|
||||
package: Testing
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- drupal:migrate
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
id: track_changes_test
|
||||
label: Track changes test.
|
||||
source:
|
||||
plugin: track_changes_test
|
||||
track_changes: true
|
||||
destination:
|
||||
plugin: entity:taxonomy_term
|
||||
migration_tags:
|
||||
test: test
|
||||
process:
|
||||
name: name
|
||||
vid:
|
||||
plugin: default_value
|
||||
default_value: track_changes_import_term
|
||||
'description/value': description
|
||||
'description/format':
|
||||
plugin: default_value
|
||||
default_value: 'basic_html'
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\migrate_track_changes_test\Plugin\migrate\source;
|
||||
|
||||
use Drupal\migrate\Plugin\migrate\source\SqlBase;
|
||||
|
||||
/**
|
||||
* Source plugin for migration track changes tests.
|
||||
*
|
||||
* @MigrateSource(
|
||||
* id = "track_changes_test"
|
||||
* )
|
||||
*/
|
||||
class TrackChangesTest extends SqlBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function query() {
|
||||
$field_names = array_keys($this->fields());
|
||||
$query = $this
|
||||
->select('track_changes_term', 't')
|
||||
->fields('t', $field_names);
|
||||
foreach ($field_names as $field_name) {
|
||||
$query->groupBy($field_name);
|
||||
}
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fields() {
|
||||
$fields = [
|
||||
'tid' => $this->t('Term id'),
|
||||
'name' => $this->t('Name'),
|
||||
'description' => $this->t('Description'),
|
||||
];
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIds() {
|
||||
return [
|
||||
'tid' => [
|
||||
'type' => 'integer',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
name: 'Migration directory test'
|
||||
type: module
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- drupal:migrate
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
id: migration_templates_test
|
||||
label: Migration templates test
|
||||
source:
|
||||
plugin: embedded_data
|
||||
process:
|
||||
id: id
|
||||
destination:
|
||||
plugin: null
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Functional;
|
||||
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\Tests\node\Traits\ContentTypeCreationTrait;
|
||||
|
||||
/**
|
||||
* Execute migration.
|
||||
*
|
||||
* This is intentionally a Functional test instead of a Kernel test because
|
||||
* Kernel tests have proven to not catch all edge cases that are encountered
|
||||
* via a Functional test.
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class MigrateNoMigrateDrupalTest extends BrowserTestBase {
|
||||
use ContentTypeCreationTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = [
|
||||
'migrate',
|
||||
'migrate_no_migrate_drupal_test',
|
||||
'node',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
// Ensures that code from the migrate_drupal module can not be autoloaded
|
||||
// while testing on DrupalCI.
|
||||
$this->writeSettings(['settings' => ['deployment_identifier' => (object) ['value' => 'force-new-apcu-key', 'required' => TRUE]]]);
|
||||
$this->createContentType(['type' => 'no_migrate_drupal']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests execution of a migration.
|
||||
*/
|
||||
public function testExecutionNoMigrateDrupal() {
|
||||
$this->drupalGet('/migrate_no_migrate_drupal_test/execute');
|
||||
$this->assertSession()->pageTextContains('Migration was successful.');
|
||||
$node_1 = Node::load(1);
|
||||
$node_2 = Node::load(2);
|
||||
$this->assertEquals('Node 1', $node_1->label());
|
||||
$this->assertEquals('Node 2', $node_2->label());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Functional\process;
|
||||
|
||||
use Drupal\migrate\MigrateExecutable;
|
||||
use Drupal\migrate\Plugin\MigrateIdMapInterface;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
/**
|
||||
* Tests the 'download' process plugin.
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class DownloadFunctionalTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['migrate', 'file'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* Tests that an exception is thrown bu migration continues with the next row.
|
||||
*/
|
||||
public function testExceptionThrow() {
|
||||
$invalid_url = "{$this->baseUrl}/not-existent-404";
|
||||
$valid_url = "{$this->baseUrl}/core/misc/favicon.ico";
|
||||
|
||||
$definition = [
|
||||
'source' => [
|
||||
'plugin' => 'embedded_data',
|
||||
'data_rows' => [
|
||||
['url' => $invalid_url, 'uri' => 'public://first.txt'],
|
||||
['url' => $valid_url, 'uri' => 'public://second.ico'],
|
||||
],
|
||||
'ids' => [
|
||||
'url' => ['type' => 'string'],
|
||||
],
|
||||
],
|
||||
'process' => [
|
||||
'uri' => [
|
||||
'plugin' => 'download',
|
||||
'source' => ['url', 'uri'],
|
||||
],
|
||||
],
|
||||
'destination' => [
|
||||
'plugin' => 'entity:file',
|
||||
],
|
||||
];
|
||||
|
||||
$migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition);
|
||||
|
||||
$executable = new MigrateExecutable($migration);
|
||||
$result = $executable->import();
|
||||
|
||||
// Check that the migration has completed.
|
||||
$this->assertEquals($result, MigrationInterface::RESULT_COMPLETED);
|
||||
|
||||
/** @var \Drupal\migrate\Plugin\MigrateIdMapInterface $id_map_plugin */
|
||||
$id_map_plugin = $migration->getIdMap();
|
||||
|
||||
// Check that the first row was marked as failed in the id map table.
|
||||
$map_row = $id_map_plugin->getRowBySource(['url' => $invalid_url]);
|
||||
$this->assertEquals(MigrateIdMapInterface::STATUS_FAILED, $map_row['source_row_status']);
|
||||
$this->assertNull($map_row['destid1']);
|
||||
|
||||
// Check that a message with the thrown exception has been logged.
|
||||
$messages = $id_map_plugin->getMessages(['url' => $invalid_url])->fetchAll();
|
||||
$this->assertCount(1, $messages);
|
||||
$message = reset($messages);
|
||||
$this->assertEquals("Client error: `GET $invalid_url` resulted in a `404 Not Found` response ($invalid_url)", $message->message);
|
||||
$this->assertEquals(MigrationInterface::MESSAGE_ERROR, $message->level);
|
||||
|
||||
// Check that the second row was migrated successfully.
|
||||
$map_row = $id_map_plugin->getRowBySource(['url' => $valid_url]);
|
||||
$this->assertEquals(MigrateIdMapInterface::STATUS_IMPORTED, $map_row['source_row_status']);
|
||||
$this->assertEquals(1, $map_row['destid1']);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel;
|
||||
|
||||
/**
|
||||
* Tests the high water handling.
|
||||
*
|
||||
* @covers \Drupal\migrate_high_water_test\Plugin\migrate\source\HighWaterTest
|
||||
* @group migrate
|
||||
*/
|
||||
class HighWaterNotJoinableTest extends MigrateSqlSourceTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'migrate',
|
||||
'migrate_drupal',
|
||||
'migrate_high_water_test',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function providerSource() {
|
||||
$tests = [];
|
||||
|
||||
// Test high water when the map is not joinable.
|
||||
// The source data.
|
||||
$tests[0]['source_data']['high_water_node'] = [
|
||||
[
|
||||
'id' => 1,
|
||||
'title' => 'Item 1',
|
||||
'changed' => 1,
|
||||
],
|
||||
[
|
||||
'id' => 2,
|
||||
'title' => 'Item 2',
|
||||
'changed' => 2,
|
||||
],
|
||||
[
|
||||
'id' => 3,
|
||||
'title' => 'Item 3',
|
||||
'changed' => 3,
|
||||
],
|
||||
];
|
||||
|
||||
// The expected results.
|
||||
$tests[0]['expected_data'] = [
|
||||
[
|
||||
'id' => 2,
|
||||
'title' => 'Item 2',
|
||||
'changed' => 2,
|
||||
],
|
||||
[
|
||||
'id' => 3,
|
||||
'title' => 'Item 3',
|
||||
'changed' => 3,
|
||||
],
|
||||
];
|
||||
|
||||
// The expected count is the count returned by the query before the query
|
||||
// is modified by SqlBase::initializeIterator().
|
||||
$tests[0]['expected_count'] = 3;
|
||||
$tests[0]['configuration'] = [
|
||||
'high_water_property' => [
|
||||
'name' => 'changed',
|
||||
],
|
||||
];
|
||||
$tests[0]['high_water'] = $tests[0]['source_data']['high_water_node'][0]['changed'];
|
||||
|
||||
// Test high water initialized to NULL.
|
||||
$tests[1]['source_data'] = $tests[0]['source_data'];
|
||||
$tests[1]['expected_data'] = [
|
||||
[
|
||||
'id' => 1,
|
||||
'title' => 'Item 1',
|
||||
'changed' => 1,
|
||||
],
|
||||
[
|
||||
'id' => 2,
|
||||
'title' => 'Item 2',
|
||||
'changed' => 2,
|
||||
],
|
||||
[
|
||||
'id' => 3,
|
||||
'title' => 'Item 3',
|
||||
'changed' => 3,
|
||||
],
|
||||
];
|
||||
$tests[1]['expected_count'] = $tests[0]['expected_count'];
|
||||
$tests[1]['configuration'] = $tests[0]['configuration'];
|
||||
$tests[1]['high_water'] = NULL;
|
||||
|
||||
// Test high water initialized to an empty string.
|
||||
$tests[2]['source_data'] = $tests[0]['source_data'];
|
||||
$tests[2]['expected_data'] = [
|
||||
[
|
||||
'id' => 1,
|
||||
'title' => 'Item 1',
|
||||
'changed' => 1,
|
||||
],
|
||||
[
|
||||
'id' => 2,
|
||||
'title' => 'Item 2',
|
||||
'changed' => 2,
|
||||
],
|
||||
[
|
||||
'id' => 3,
|
||||
'title' => 'Item 3',
|
||||
'changed' => 3,
|
||||
],
|
||||
];
|
||||
$tests[2]['expected_count'] = $tests[0]['expected_count'];
|
||||
$tests[2]['configuration'] = $tests[0]['configuration'];
|
||||
$tests[2]['high_water'] = '';
|
||||
|
||||
return $tests;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,301 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel;
|
||||
|
||||
/**
|
||||
* Tests migration high water property.
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class HighWaterTest extends MigrateTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'system',
|
||||
'user',
|
||||
'node',
|
||||
'migrate',
|
||||
'migrate_high_water_test',
|
||||
'field',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
// Create source test table.
|
||||
$this->sourceDatabase->schema()->createTable('high_water_node', [
|
||||
'fields' => [
|
||||
'id' => [
|
||||
'description' => 'Serial',
|
||||
'type' => 'serial',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
],
|
||||
'changed' => [
|
||||
'description' => 'Highwater',
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
],
|
||||
'title' => [
|
||||
'description' => 'Title',
|
||||
'type' => 'varchar',
|
||||
'length' => 128,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
],
|
||||
],
|
||||
'primary key' => [
|
||||
'id',
|
||||
],
|
||||
'description' => 'Contains nodes to import',
|
||||
]);
|
||||
|
||||
// Add 3 items to source table.
|
||||
$this->sourceDatabase->insert('high_water_node')
|
||||
->fields([
|
||||
'title',
|
||||
'changed',
|
||||
])
|
||||
->values([
|
||||
'title' => 'Item 1',
|
||||
'changed' => 1,
|
||||
])
|
||||
->values([
|
||||
'title' => 'Item 2',
|
||||
'changed' => 2,
|
||||
])
|
||||
->values([
|
||||
'title' => 'Item 3',
|
||||
'changed' => 3,
|
||||
])
|
||||
->execute();
|
||||
|
||||
$this->installEntitySchema('node');
|
||||
$this->installEntitySchema('user');
|
||||
$this->installSchema('node', 'node_access');
|
||||
|
||||
$this->executeMigration('high_water_test');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests high water property of SqlBase.
|
||||
*/
|
||||
public function testHighWater() {
|
||||
// Assert all of the nodes have been imported.
|
||||
$this->assertNodeExists('Item 1');
|
||||
$this->assertNodeExists('Item 2');
|
||||
$this->assertNodeExists('Item 3');
|
||||
|
||||
// Update Item 1 setting its high_water_property to value that is below
|
||||
// current high water mark.
|
||||
$this->sourceDatabase->update('high_water_node')
|
||||
->fields([
|
||||
'title' => 'Item 1 updated',
|
||||
'changed' => 2,
|
||||
])
|
||||
->condition('title', 'Item 1')
|
||||
->execute();
|
||||
|
||||
// Update Item 2 setting its high_water_property to value equal to
|
||||
// current high water mark.
|
||||
$this->sourceDatabase->update('high_water_node')
|
||||
->fields([
|
||||
'title' => 'Item 2 updated',
|
||||
'changed' => 3,
|
||||
])
|
||||
->condition('title', 'Item 2')
|
||||
->execute();
|
||||
|
||||
// Update Item 3 setting its high_water_property to value that is above
|
||||
// current high water mark.
|
||||
$this->sourceDatabase->update('high_water_node')
|
||||
->fields([
|
||||
'title' => 'Item 3 updated',
|
||||
'changed' => 4,
|
||||
])
|
||||
->condition('title', 'Item 3')
|
||||
->execute();
|
||||
|
||||
// Execute migration again.
|
||||
$this->executeMigration('high_water_test');
|
||||
|
||||
// Item with lower highwater should not be updated.
|
||||
$this->assertNodeExists('Item 1');
|
||||
$this->assertNodeDoesNotExist('Item 1 updated');
|
||||
|
||||
// Item with equal highwater should not be updated.
|
||||
$this->assertNodeExists('Item 2');
|
||||
$this->assertNodeDoesNotExist('Item 2 updated');
|
||||
|
||||
// Item with greater highwater should be updated.
|
||||
$this->assertNodeExists('Item 3 updated');
|
||||
$this->assertNodeDoesNotExist('Item 3');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the high water value can be 0.
|
||||
*/
|
||||
public function testZeroHighwater() {
|
||||
// Assert all of the nodes have been imported.
|
||||
$this->assertNodeExists('Item 1');
|
||||
$this->assertNodeExists('Item 2');
|
||||
$this->assertNodeExists('Item 3');
|
||||
$migration = $this->container->get('plugin.manager.migration')->CreateInstance('high_water_test', []);
|
||||
$source = $migration->getSourcePlugin();
|
||||
$source->rewind();
|
||||
$count = 0;
|
||||
while ($source->valid()) {
|
||||
$count++;
|
||||
$source->next();
|
||||
}
|
||||
|
||||
// Expect no rows as everything is below the high water mark.
|
||||
$this->assertSame(0, $count);
|
||||
|
||||
// Test resetting the high water mark to 0.
|
||||
$this->container->get('keyvalue')->get('migrate:high_water')->set('high_water_test', 0);
|
||||
$migration = $this->container->get('plugin.manager.migration')->CreateInstance('high_water_test', []);
|
||||
$source = $migration->getSourcePlugin();
|
||||
$source->rewind();
|
||||
$count = 0;
|
||||
while ($source->valid()) {
|
||||
$count++;
|
||||
$source->next();
|
||||
}
|
||||
$this->assertSame(3, $count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that deleting the high water value causes all rows to be reimported.
|
||||
*/
|
||||
public function testNullHighwater() {
|
||||
// Assert all of the nodes have been imported.
|
||||
$this->assertNodeExists('Item 1');
|
||||
$this->assertNodeExists('Item 2');
|
||||
$this->assertNodeExists('Item 3');
|
||||
$migration = $this->container->get('plugin.manager.migration')->CreateInstance('high_water_test', []);
|
||||
$source = $migration->getSourcePlugin();
|
||||
$source->rewind();
|
||||
$count = 0;
|
||||
while ($source->valid()) {
|
||||
$count++;
|
||||
$source->next();
|
||||
}
|
||||
|
||||
// Expect no rows as everything is below the high water mark.
|
||||
$this->assertSame(0, $count);
|
||||
|
||||
// Test resetting the high water mark.
|
||||
$this->container->get('keyvalue')->get('migrate:high_water')->delete('high_water_test');
|
||||
$migration = $this->container->get('plugin.manager.migration')->CreateInstance('high_water_test', []);
|
||||
$source = $migration->getSourcePlugin();
|
||||
$source->rewind();
|
||||
$count = 0;
|
||||
while ($source->valid()) {
|
||||
$count++;
|
||||
$source->next();
|
||||
}
|
||||
$this->assertSame(3, $count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests high water property of SqlBase when rows marked for update.
|
||||
*/
|
||||
public function testHighWaterUpdate() {
|
||||
// Assert all of the nodes have been imported.
|
||||
$this->assertNodeExists('Item 1');
|
||||
$this->assertNodeExists('Item 2');
|
||||
$this->assertNodeExists('Item 3');
|
||||
|
||||
// Update Item 1 setting its high_water_property to value that is below
|
||||
// current high water mark.
|
||||
$this->sourceDatabase->update('high_water_node')
|
||||
->fields([
|
||||
'title' => 'Item 1 updated',
|
||||
'changed' => 2,
|
||||
])
|
||||
->condition('title', 'Item 1')
|
||||
->execute();
|
||||
|
||||
// Update Item 2 setting its high_water_property to value equal to
|
||||
// current high water mark.
|
||||
$this->sourceDatabase->update('high_water_node')
|
||||
->fields([
|
||||
'title' => 'Item 2 updated',
|
||||
'changed' => 3,
|
||||
])
|
||||
->condition('title', 'Item 2')
|
||||
->execute();
|
||||
|
||||
// Update Item 3 setting its high_water_property to value that is above
|
||||
// current high water mark.
|
||||
$this->sourceDatabase->update('high_water_node')
|
||||
->fields([
|
||||
'title' => 'Item 3 updated',
|
||||
'changed' => 4,
|
||||
])
|
||||
->condition('title', 'Item 3')
|
||||
->execute();
|
||||
|
||||
// Set all rows as needing an update.
|
||||
$id_map = $this->getMigration('high_water_test')->getIdMap();
|
||||
$id_map->prepareUpdate();
|
||||
|
||||
$this->executeMigration('high_water_test');
|
||||
|
||||
// Item with lower highwater should be updated.
|
||||
$this->assertNodeExists('Item 1 updated');
|
||||
$this->assertNodeDoesNotExist('Item 1');
|
||||
|
||||
// Item with equal highwater should be updated.
|
||||
$this->assertNodeExists('Item 2 updated');
|
||||
$this->assertNodeDoesNotExist('Item 2');
|
||||
|
||||
// Item with greater highwater should be updated.
|
||||
$this->assertNodeExists('Item 3 updated');
|
||||
$this->assertNodeDoesNotExist('Item 3');
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that node with given title exists.
|
||||
*
|
||||
* @param string $title
|
||||
* Title of the node.
|
||||
*/
|
||||
protected function assertNodeExists($title) {
|
||||
self::assertTrue($this->nodeExists($title));
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that node with given title does not exist.
|
||||
*
|
||||
* @param string $title
|
||||
* Title of the node.
|
||||
*/
|
||||
protected function assertNodeDoesNotExist($title) {
|
||||
self::assertFalse($this->nodeExists($title));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if node with given title exists.
|
||||
*
|
||||
* @param string $title
|
||||
* Title of the node.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function nodeExists($title) {
|
||||
$query = \Drupal::entityQuery('node');
|
||||
$result = $query
|
||||
->condition('title', $title)
|
||||
->range(0, 1)
|
||||
->execute();
|
||||
|
||||
return !empty($result);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel;
|
||||
|
||||
use Drupal\migrate\MigrateExecutable;
|
||||
use Drupal\taxonomy\Entity\Term;
|
||||
use Drupal\taxonomy\Entity\Vocabulary;
|
||||
|
||||
/**
|
||||
* Tests setting of bundles on content entity migrations.
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class MigrateBundleTest extends MigrateTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['taxonomy', 'text', 'user', 'system'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installEntitySchema('user');
|
||||
$this->installEntitySchema('taxonomy_vocabulary');
|
||||
$this->installEntitySchema('taxonomy_term');
|
||||
$this->installConfig(['taxonomy']);
|
||||
// Set up two vocabularies (taxonomy bundles).
|
||||
Vocabulary::create(['vid' => 'tags', 'name' => 'Tags']);
|
||||
Vocabulary::create(['vid' => 'categories', 'name' => 'Categories']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests setting the bundle in the destination.
|
||||
*/
|
||||
public function testDestinationBundle() {
|
||||
$term_data_rows = [
|
||||
['id' => 1, 'name' => 'Category 1'],
|
||||
];
|
||||
$ids = ['id' => ['type' => 'integer']];
|
||||
$definition = [
|
||||
'id' => 'terms',
|
||||
'migration_tags' => ['Bundle test'],
|
||||
'source' => [
|
||||
'plugin' => 'embedded_data',
|
||||
'data_rows' => $term_data_rows,
|
||||
'ids' => $ids,
|
||||
],
|
||||
'process' => [
|
||||
'tid' => 'id',
|
||||
'name' => 'name',
|
||||
],
|
||||
'destination' => [
|
||||
'plugin' => 'entity:taxonomy_term',
|
||||
'default_bundle' => 'categories',
|
||||
],
|
||||
'migration_dependencies' => [],
|
||||
];
|
||||
|
||||
$term_migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition);
|
||||
|
||||
// Import and validate the term entity was created with the correct bundle.
|
||||
$term_executable = new MigrateExecutable($term_migration, $this);
|
||||
$term_executable->import();
|
||||
/** @var \Drupal\taxonomy\Entity\Term $term */
|
||||
$term = Term::load(1);
|
||||
$this->assertEquals($term->bundle(), 'categories');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests setting the bundle in the process pipeline.
|
||||
*/
|
||||
public function testProcessBundle() {
|
||||
$term_data_rows = [
|
||||
['id' => 1, 'vocab' => 'categories', 'name' => 'Category 1'],
|
||||
['id' => 2, 'vocab' => 'tags', 'name' => 'Tag 1'],
|
||||
];
|
||||
$ids = ['id' => ['type' => 'integer']];
|
||||
$definition = [
|
||||
'id' => 'terms',
|
||||
'migration_tags' => ['Bundle test'],
|
||||
'source' => [
|
||||
'plugin' => 'embedded_data',
|
||||
'data_rows' => $term_data_rows,
|
||||
'ids' => $ids,
|
||||
],
|
||||
'process' => [
|
||||
'tid' => 'id',
|
||||
'vid' => 'vocab',
|
||||
'name' => 'name',
|
||||
],
|
||||
'destination' => [
|
||||
'plugin' => 'entity:taxonomy_term',
|
||||
],
|
||||
'migration_dependencies' => [],
|
||||
];
|
||||
|
||||
$term_migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition);
|
||||
|
||||
// Import and validate the term entities were created with the correct bundle.
|
||||
$term_executable = new MigrateExecutable($term_migration, $this);
|
||||
$term_executable->import();
|
||||
/** @var \Drupal\taxonomy\Entity\Term $term */
|
||||
$term = Term::load(1);
|
||||
$this->assertEquals($term->bundle(), 'categories');
|
||||
$term = Term::load(2);
|
||||
$this->assertEquals($term->bundle(), 'tags');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests setting bundles both in process and destination.
|
||||
*/
|
||||
public function testMixedBundles() {
|
||||
$term_data_rows = [
|
||||
['id' => 1, 'vocab' => 'categories', 'name' => 'Category 1'],
|
||||
['id' => 2, 'name' => 'Tag 1'],
|
||||
];
|
||||
$ids = ['id' => ['type' => 'integer']];
|
||||
$definition = [
|
||||
'id' => 'terms',
|
||||
'migration_tags' => ['Bundle test'],
|
||||
'source' => [
|
||||
'plugin' => 'embedded_data',
|
||||
'data_rows' => $term_data_rows,
|
||||
'ids' => $ids,
|
||||
],
|
||||
'process' => [
|
||||
'tid' => 'id',
|
||||
'vid' => 'vocab',
|
||||
'name' => 'name',
|
||||
],
|
||||
'destination' => [
|
||||
'plugin' => 'entity:taxonomy_term',
|
||||
// When no vocab is provided, the destination bundle is applied.
|
||||
'default_bundle' => 'tags',
|
||||
],
|
||||
'migration_dependencies' => [],
|
||||
];
|
||||
|
||||
$term_migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition);
|
||||
|
||||
// Import and validate the term entities were created with the correct bundle.
|
||||
$term_executable = new MigrateExecutable($term_migration, $this);
|
||||
$term_executable->import();
|
||||
/** @var \Drupal\taxonomy\Entity\Term $term */
|
||||
$term = Term::load(1);
|
||||
$this->assertEquals($term->bundle(), 'categories');
|
||||
$term = Term::load(2);
|
||||
$this->assertEquals($term->bundle(), 'tags');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel;
|
||||
|
||||
use Drupal\migrate\MigrateExecutable;
|
||||
|
||||
/**
|
||||
* Tests rolling back of configuration objects.
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class MigrateConfigRollbackTest extends MigrateTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['system', 'language', 'config_translation'];
|
||||
|
||||
/**
|
||||
* Tests rolling back configuration.
|
||||
*/
|
||||
public function testConfigRollback() {
|
||||
// Use system.site configuration to demonstrate importing and rolling back
|
||||
// configuration.
|
||||
$variable = [
|
||||
[
|
||||
'id' => 'site_name',
|
||||
'site_name' => 'Some site',
|
||||
'site_slogan' => 'Awesome slogan',
|
||||
],
|
||||
];
|
||||
$ids = [
|
||||
'id' =>
|
||||
[
|
||||
'type' => 'string',
|
||||
],
|
||||
];
|
||||
$definition = [
|
||||
'id' => 'config',
|
||||
'migration_tags' => ['Import and rollback test'],
|
||||
'source' => [
|
||||
'plugin' => 'embedded_data',
|
||||
'data_rows' => $variable,
|
||||
'ids' => $ids,
|
||||
],
|
||||
'process' => [
|
||||
'name' => 'site_name',
|
||||
'slogan' => 'site_slogan',
|
||||
],
|
||||
'destination' => [
|
||||
'plugin' => 'config',
|
||||
'config_name' => 'system.site',
|
||||
],
|
||||
];
|
||||
|
||||
/** @var \Drupal\migrate\Plugin\Migration $config_migration */
|
||||
$config_migration = \Drupal::service('plugin.manager.migration')
|
||||
->createStubMigration($definition);
|
||||
$config_id_map = $config_migration->getIdMap();
|
||||
|
||||
// Rollback is not enabled for configuration translations.
|
||||
$this->assertFalse($config_migration->getDestinationPlugin()->supportsRollback());
|
||||
|
||||
// Import and validate config entities were created.
|
||||
$config_executable = new MigrateExecutable($config_migration, $this);
|
||||
$config_executable->import();
|
||||
$config = $this->config('system.site');
|
||||
$this->assertSame('Some site', $config->get('name'));
|
||||
$this->assertSame('Awesome slogan', $config->get('slogan'));
|
||||
$map_row = $config_id_map->getRowBySource(['id' => $variable[0]['id']]);
|
||||
$this->assertNotNull($map_row['destid1']);
|
||||
|
||||
// Rollback and verify the configuration changes are still there.
|
||||
$config_executable->rollback();
|
||||
$config = $this->config('system.site');
|
||||
$this->assertSame('Some site', $config->get('name'));
|
||||
$this->assertSame('Awesome slogan', $config->get('slogan'));
|
||||
// Confirm the map row is deleted.
|
||||
$this->assertFalse($config_id_map->getRowBySource(['id' => $variable[0]['id']]));
|
||||
|
||||
// We use system configuration to demonstrate importing and rolling back
|
||||
// configuration translations.
|
||||
$i18n_variable = [
|
||||
[
|
||||
'id' => 'site_name',
|
||||
'language' => 'fr',
|
||||
'site_name' => 'fr - Some site',
|
||||
'site_slogan' => 'fr - Awesome slogan',
|
||||
],
|
||||
[
|
||||
'id' => 'site_name',
|
||||
'language' => 'is',
|
||||
'site_name' => 'is - Some site',
|
||||
'site_slogan' => 'is - Awesome slogan',
|
||||
],
|
||||
];
|
||||
$ids = [
|
||||
'id' =>
|
||||
[
|
||||
'type' => 'string',
|
||||
],
|
||||
'language' =>
|
||||
[
|
||||
'type' => 'string',
|
||||
],
|
||||
];
|
||||
$definition = [
|
||||
'id' => 'i18n_config',
|
||||
'migration_tags' => ['Import and rollback test'],
|
||||
'source' => [
|
||||
'plugin' => 'embedded_data',
|
||||
'data_rows' => $i18n_variable,
|
||||
'ids' => $ids,
|
||||
],
|
||||
'process' => [
|
||||
'langcode' => 'language',
|
||||
'name' => 'site_name',
|
||||
'slogan' => 'site_slogan',
|
||||
],
|
||||
'destination' => [
|
||||
'plugin' => 'config',
|
||||
'config_name' => 'system.site',
|
||||
'translations' => 'true',
|
||||
],
|
||||
];
|
||||
|
||||
$config_migration = \Drupal::service('plugin.manager.migration')
|
||||
->createStubMigration($definition);
|
||||
$config_id_map = $config_migration->getIdMap();
|
||||
|
||||
// Rollback is enabled for configuration translations.
|
||||
$this->assertTrue($config_migration->getDestinationPlugin()->supportsRollback());
|
||||
|
||||
// Import and validate config entities were created.
|
||||
$config_executable = new MigrateExecutable($config_migration, $this);
|
||||
$config_executable->import();
|
||||
|
||||
$language_manager = \Drupal::service('language_manager');
|
||||
foreach ($i18n_variable as $row) {
|
||||
$langcode = $row['language'];
|
||||
/** @var \Drupal\language\Config\LanguageConfigOverride $config_translation */
|
||||
$config_translation = $language_manager->getLanguageConfigOverride($langcode, 'system.site');
|
||||
$this->assertSame($row['site_name'], $config_translation->get('name'));
|
||||
$this->assertSame($row['site_slogan'], $config_translation->get('slogan'));
|
||||
$map_row = $config_id_map->getRowBySource(['id' => $row['id'], 'language' => $row['language']]);
|
||||
$this->assertNotNull($map_row['destid1']);
|
||||
}
|
||||
|
||||
// Rollback and verify the translation have been removed.
|
||||
$config_executable->rollback();
|
||||
foreach ($i18n_variable as $row) {
|
||||
$langcode = $row['language'];
|
||||
$config_translation = $language_manager->getLanguageConfigOverride($langcode, 'system.site');
|
||||
$this->assertNull($config_translation->get('name'));
|
||||
$this->assertNull($config_translation->get('slogan'));
|
||||
// Confirm the map row is deleted.
|
||||
$map_row = $config_id_map->getRowBySource(['id' => $row['id'], 'language' => $langcode]);
|
||||
$this->assertFalse($map_row);
|
||||
}
|
||||
|
||||
// Test that the configuration is still present.
|
||||
$config = $this->config('system.site');
|
||||
$this->assertSame('Some site', $config->get('name'));
|
||||
$this->assertSame('Awesome slogan', $config->get('slogan'));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Allows tests to alter dumps after they have loaded.
|
||||
*
|
||||
* @see \Drupal\migrate_drupal\Tests\d6\MigrateFileTest
|
||||
*/
|
||||
interface MigrateDumpAlterInterface {
|
||||
|
||||
/**
|
||||
* Allows tests to alter dumps after they have loaded.
|
||||
*
|
||||
* @param \Drupal\KernelTests\KernelTestBase $test
|
||||
* The test that is being run.
|
||||
*/
|
||||
public static function migrateDumpAlter(KernelTestBase $test);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests the EmbeddedDataSource plugin.
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class MigrateEmbeddedDataTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['migrate'];
|
||||
|
||||
/**
|
||||
* Tests the embedded_data source plugin.
|
||||
*/
|
||||
public function testEmbeddedData() {
|
||||
$data_rows = [
|
||||
['key' => '1', 'field1' => 'f1value1', 'field2' => 'f2value1'],
|
||||
['key' => '2', 'field1' => 'f1value2', 'field2' => 'f2value2'],
|
||||
];
|
||||
$ids = ['key' => ['type' => 'integer']];
|
||||
$definition = [
|
||||
'migration_tags' => ['Embedded data test'],
|
||||
'source' => [
|
||||
'plugin' => 'embedded_data',
|
||||
'data_rows' => $data_rows,
|
||||
'ids' => $ids,
|
||||
],
|
||||
'process' => [],
|
||||
'destination' => ['plugin' => 'null'],
|
||||
];
|
||||
|
||||
$migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition);
|
||||
$source = $migration->getSourcePlugin();
|
||||
|
||||
// Validate the plugin returns the source data that was provided.
|
||||
$results = [];
|
||||
/** @var \Drupal\migrate\Row $row */
|
||||
foreach ($source as $row) {
|
||||
// The plugin should not mark any rows as stubs. We need to use
|
||||
// assertSame() here because assertFalse() will pass falsy values (e.g.,
|
||||
// empty arrays).
|
||||
$this->assertSame(FALSE, $row->isStub());
|
||||
|
||||
$data_row = $row->getSource();
|
||||
// The "data" row returned by getSource() also includes all source
|
||||
// configuration - we remove it so we see only the data itself.
|
||||
unset($data_row['plugin']);
|
||||
unset($data_row['data_rows']);
|
||||
unset($data_row['ids']);
|
||||
$results[] = $data_row;
|
||||
}
|
||||
$this->assertIdentical($results, $data_rows);
|
||||
|
||||
// Validate the public APIs.
|
||||
$this->assertIdentical($source->count(), count($data_rows));
|
||||
$this->assertIdentical($source->getIds(), $ids);
|
||||
$expected_fields = [
|
||||
'key' => 'key',
|
||||
'field1' => 'field1',
|
||||
'field2' => 'field2',
|
||||
];
|
||||
$this->assertIdentical($source->fields(), $expected_fields);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,304 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel;
|
||||
|
||||
use Drupal\entity_test\Entity\EntityTestMul;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\migrate\MigrateExecutable;
|
||||
use Drupal\migrate\Plugin\migrate\destination\EntityContentBase;
|
||||
use Drupal\migrate\Plugin\MigrateIdMapInterface;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\migrate\Row;
|
||||
use Drupal\migrate_entity_test\Entity\StringIdEntityTest;
|
||||
|
||||
/**
|
||||
* Tests the EntityContentBase destination.
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class MigrateEntityContentBaseTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['migrate', 'user', 'language', 'entity_test'];
|
||||
|
||||
/**
|
||||
* The storage for entity_test_mul.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\ContentEntityStorageInterface
|
||||
*/
|
||||
protected $storage;
|
||||
|
||||
/**
|
||||
* A content migrate destination.
|
||||
*
|
||||
* @var \Drupal\migrate\Plugin\MigrateDestinationInterface
|
||||
*/
|
||||
protected $destination;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Enable two required fields with default values: a single-value field and
|
||||
// a multi-value field.
|
||||
\Drupal::state()->set('entity_test.required_default_field', TRUE);
|
||||
\Drupal::state()->set('entity_test.required_multi_default_field', TRUE);
|
||||
$this->installEntitySchema('entity_test_mul');
|
||||
|
||||
ConfigurableLanguage::createFromLangcode('en')->save();
|
||||
ConfigurableLanguage::createFromLangcode('fr')->save();
|
||||
|
||||
$this->storage = $this->container->get('entity_type.manager')->getStorage('entity_test_mul');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the existing translations of an entity.
|
||||
*
|
||||
* @param int $id
|
||||
* The entity ID.
|
||||
* @param string $default
|
||||
* The expected default translation language code.
|
||||
* @param string[] $others
|
||||
* The expected other translation language codes.
|
||||
*/
|
||||
protected function assertTranslations($id, $default, $others = []) {
|
||||
$entity = $this->storage->load($id);
|
||||
$this->assertNotEmpty($entity, "Entity exists");
|
||||
$this->assertEquals($default, $entity->language()->getId(), "Entity default translation");
|
||||
$translations = array_keys($entity->getTranslationLanguages(FALSE));
|
||||
sort($others);
|
||||
sort($translations);
|
||||
$this->assertEquals($others, $translations, "Entity translations");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the destination plugin to test.
|
||||
*
|
||||
* @param array $configuration
|
||||
* The plugin configuration.
|
||||
*/
|
||||
protected function createDestination(array $configuration) {
|
||||
$this->destination = new EntityContentBase(
|
||||
$configuration,
|
||||
'fake_plugin_id',
|
||||
[],
|
||||
$this->createMock(MigrationInterface::class),
|
||||
$this->storage,
|
||||
[],
|
||||
$this->container->get('entity_field.manager'),
|
||||
$this->container->get('plugin.manager.field.field_type')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test importing and rolling back translated entities.
|
||||
*/
|
||||
public function testTranslated() {
|
||||
// Create a destination.
|
||||
$this->createDestination(['translations' => TRUE]);
|
||||
|
||||
// Create some pre-existing entities.
|
||||
$this->storage->create(['id' => 1, 'langcode' => 'en'])->save();
|
||||
$this->storage->create(['id' => 2, 'langcode' => 'fr'])->save();
|
||||
$translated = $this->storage->create(['id' => 3, 'langcode' => 'en']);
|
||||
$translated->save();
|
||||
$translated->addTranslation('fr')->save();
|
||||
|
||||
// Pre-assert that things are as expected.
|
||||
$this->assertTranslations(1, 'en');
|
||||
$this->assertTranslations(2, 'fr');
|
||||
$this->assertTranslations(3, 'en', ['fr']);
|
||||
$this->assertNull($this->storage->load(4));
|
||||
|
||||
$destination_rows = [
|
||||
// Existing default translation.
|
||||
['id' => 1, 'langcode' => 'en', 'action' => MigrateIdMapInterface::ROLLBACK_PRESERVE],
|
||||
// New translation.
|
||||
['id' => 2, 'langcode' => 'en', 'action' => MigrateIdMapInterface::ROLLBACK_DELETE],
|
||||
// Existing non-default translation.
|
||||
['id' => 3, 'langcode' => 'fr', 'action' => MigrateIdMapInterface::ROLLBACK_PRESERVE],
|
||||
// Brand new row.
|
||||
['id' => 4, 'langcode' => 'fr', 'action' => MigrateIdMapInterface::ROLLBACK_DELETE],
|
||||
];
|
||||
$rollback_actions = [];
|
||||
|
||||
// Import some rows.
|
||||
foreach ($destination_rows as $idx => $destination_row) {
|
||||
$row = new Row();
|
||||
foreach ($destination_row as $key => $value) {
|
||||
$row->setDestinationProperty($key, $value);
|
||||
}
|
||||
$this->destination->import($row);
|
||||
|
||||
// Check that the rollback action is correct, and save it.
|
||||
$this->assertEquals($destination_row['action'], $this->destination->rollbackAction());
|
||||
$rollback_actions[$idx] = $this->destination->rollbackAction();
|
||||
}
|
||||
|
||||
$this->assertTranslations(1, 'en');
|
||||
$this->assertTranslations(2, 'fr', ['en']);
|
||||
$this->assertTranslations(3, 'en', ['fr']);
|
||||
$this->assertTranslations(4, 'fr');
|
||||
|
||||
// Rollback the rows.
|
||||
foreach ($destination_rows as $idx => $destination_row) {
|
||||
if ($rollback_actions[$idx] == MigrateIdMapInterface::ROLLBACK_DELETE) {
|
||||
$this->destination->rollback($destination_row);
|
||||
}
|
||||
}
|
||||
|
||||
// No change, update of existing translation.
|
||||
$this->assertTranslations(1, 'en');
|
||||
// Remove added translation.
|
||||
$this->assertTranslations(2, 'fr');
|
||||
// No change, update of existing translation.
|
||||
$this->assertTranslations(3, 'en', ['fr']);
|
||||
// No change, can't remove default translation.
|
||||
$this->assertTranslations(4, 'fr');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests creation of ID columns table with definitions taken from entity type.
|
||||
*/
|
||||
public function testEntityWithStringId() {
|
||||
$this->enableModules(['migrate_entity_test']);
|
||||
$this->installEntitySchema('migrate_string_id_entity_test');
|
||||
|
||||
$definition = [
|
||||
'source' => [
|
||||
'plugin' => 'embedded_data',
|
||||
'data_rows' => [
|
||||
['id' => 123, 'version' => 'foo'],
|
||||
// This integer needs an 'int' schema with 'big' size. If 'destid1'
|
||||
// is not correctly taking the definition from the destination entity
|
||||
// type, the import will fail with a SQL exception.
|
||||
['id' => 123456789012, 'version' => 'bar'],
|
||||
],
|
||||
'ids' => [
|
||||
'id' => ['type' => 'integer', 'size' => 'big'],
|
||||
'version' => ['type' => 'string'],
|
||||
],
|
||||
],
|
||||
'process' => [
|
||||
'id' => 'id',
|
||||
'version' => 'version',
|
||||
],
|
||||
'destination' => [
|
||||
'plugin' => 'entity:migrate_string_id_entity_test',
|
||||
],
|
||||
];
|
||||
|
||||
$migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition);
|
||||
$executable = new MigrateExecutable($migration);
|
||||
$result = $executable->import();
|
||||
$this->assertEquals(MigrationInterface::RESULT_COMPLETED, $result);
|
||||
|
||||
/** @var \Drupal\migrate\Plugin\MigrateIdMapInterface $id_map_plugin */
|
||||
$id_map_plugin = $migration->getIdMap();
|
||||
|
||||
// Check that the destination has been stored.
|
||||
$map_row = $id_map_plugin->getRowBySource(['id' => 123, 'version' => 'foo']);
|
||||
$this->assertEquals(123, $map_row['destid1']);
|
||||
$map_row = $id_map_plugin->getRowBySource(['id' => 123456789012, 'version' => 'bar']);
|
||||
$this->assertEquals(123456789012, $map_row['destid1']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests empty destinations.
|
||||
*/
|
||||
public function testEmptyDestinations() {
|
||||
$this->enableModules(['migrate_entity_test']);
|
||||
$this->installEntitySchema('migrate_string_id_entity_test');
|
||||
|
||||
$definition = [
|
||||
'source' => [
|
||||
'plugin' => 'embedded_data',
|
||||
'data_rows' => [
|
||||
['id' => 123, 'version' => 'foo'],
|
||||
// This integer needs an 'int' schema with 'big' size. If 'destid1'
|
||||
// is not correctly taking the definition from the destination entity
|
||||
// type, the import will fail with an SQL exception.
|
||||
['id' => 123456789012, 'version' => 'bar'],
|
||||
],
|
||||
'ids' => [
|
||||
'id' => ['type' => 'integer', 'size' => 'big'],
|
||||
'version' => ['type' => 'string'],
|
||||
],
|
||||
'constants' => ['null' => NULL],
|
||||
],
|
||||
'process' => [
|
||||
'id' => 'id',
|
||||
'version' => 'version',
|
||||
],
|
||||
'destination' => [
|
||||
'plugin' => 'entity:migrate_string_id_entity_test',
|
||||
],
|
||||
];
|
||||
|
||||
$migration = \Drupal::service('plugin.manager.migration')
|
||||
->createStubMigration($definition);
|
||||
$executable = new MigrateExecutable($migration);
|
||||
$executable->import();
|
||||
|
||||
/** @var \Drupal\migrate_entity_test\Entity\StringIdEntityTest $entity */
|
||||
$entity = StringIdEntityTest::load('123');
|
||||
$this->assertSame('foo', $entity->version->value);
|
||||
$entity = StringIdEntityTest::load('123456789012');
|
||||
$this->assertSame('bar', $entity->version->value);
|
||||
|
||||
// Rerun the migration forcing the version to NULL.
|
||||
$definition['process'] = [
|
||||
'id' => 'id',
|
||||
'version' => 'constants/null',
|
||||
];
|
||||
|
||||
$migration = \Drupal::service('plugin.manager.migration')
|
||||
->createStubMigration($definition);
|
||||
$executable = new MigrateExecutable($migration);
|
||||
$executable->import();
|
||||
|
||||
/** @var \Drupal\migrate_entity_test\Entity\StringIdEntityTest $entity */
|
||||
$entity = StringIdEntityTest::load('123');
|
||||
$this->assertNull($entity->version->value);
|
||||
$entity = StringIdEntityTest::load('123456789012');
|
||||
$this->assertNull($entity->version->value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests stub rows.
|
||||
*/
|
||||
public function testStubRows() {
|
||||
// Create a destination.
|
||||
$this->createDestination([]);
|
||||
|
||||
// Import a stub row.
|
||||
$row = new Row([], [], TRUE);
|
||||
$row->setDestinationProperty('type', 'test');
|
||||
$ids = $this->destination->import($row);
|
||||
$this->assertCount(1, $ids);
|
||||
|
||||
// Make sure the entity was saved.
|
||||
$entity = EntityTestMul::load(reset($ids));
|
||||
$this->assertInstanceOf(EntityTestMul::class, $entity);
|
||||
// Make sure the default value was applied to the required fields.
|
||||
$single_field_name = 'required_default_field';
|
||||
$single_default_value = $entity->getFieldDefinition($single_field_name)->getDefaultValueLiteral();
|
||||
$this->assertSame($single_default_value, $entity->get($single_field_name)->getValue());
|
||||
|
||||
$multi_field_name = 'required_multi_default_field';
|
||||
$multi_default_value = $entity->getFieldDefinition($multi_field_name)->getDefaultValueLiteral();
|
||||
$count = 3;
|
||||
$this->assertCount($count, $multi_default_value);
|
||||
for ($i = 0; $i < $count; ++$i) {
|
||||
$this->assertSame($multi_default_value[$i], $entity->get($multi_field_name)->get($i)->getValue());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\migrate\Event\MigrateEvents;
|
||||
use Drupal\migrate\Event\MigrateIdMapMessageEvent;
|
||||
use Drupal\migrate\MigrateExecutable;
|
||||
use Drupal\user\Plugin\Validation\Constraint\UserNameConstraint;
|
||||
|
||||
/**
|
||||
* Tests validation of an entity during migration.
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class MigrateEntityContentValidationTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['migrate', 'system', 'user', 'entity_test'];
|
||||
|
||||
/**
|
||||
* Messages accumulated during the migration run.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $messages = [];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installConfig(['system', 'user']);
|
||||
$this->installEntitySchema('user');
|
||||
$this->installEntitySchema('entity_test');
|
||||
|
||||
$this->container
|
||||
->get('event_dispatcher')
|
||||
->addListener(MigrateEvents::IDMAP_MESSAGE, [$this, 'mapMessageRecorder']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests an import with invalid data and checks error messages.
|
||||
*/
|
||||
public function test1() {
|
||||
// Make sure that a user with uid 2 exists.
|
||||
$this->container
|
||||
->get('entity_type.manager')
|
||||
->getStorage('user')
|
||||
->create([
|
||||
'uid' => 2,
|
||||
'name' => $this->randomMachineName(),
|
||||
'status' => 1,
|
||||
])
|
||||
->save();
|
||||
|
||||
$this->runImport([
|
||||
'source' => [
|
||||
'plugin' => 'embedded_data',
|
||||
'data_rows' => [
|
||||
[
|
||||
'id' => '1',
|
||||
'name' => $this->randomString(256),
|
||||
'user_id' => '1',
|
||||
],
|
||||
[
|
||||
'id' => '2',
|
||||
'name' => $this->randomString(32),
|
||||
'user_id' => '1',
|
||||
],
|
||||
[
|
||||
'id' => '3',
|
||||
'name' => $this->randomString(32),
|
||||
'user_id' => '2',
|
||||
],
|
||||
],
|
||||
'ids' => [
|
||||
'id' => ['type' => 'integer'],
|
||||
],
|
||||
],
|
||||
'process' => [
|
||||
'id' => 'id',
|
||||
'name' => 'name',
|
||||
'user_id' => 'user_id',
|
||||
],
|
||||
'destination' => [
|
||||
'plugin' => 'entity:entity_test',
|
||||
'validate' => TRUE,
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertSame('1: [entity_test: 1]: name.0.value=<em class="placeholder">Name</em>: may not be longer than 32 characters.||user_id.0.target_id=The referenced entity (<em class="placeholder">user</em>: <em class="placeholder">1</em>) does not exist.', $this->messages[0], 'First message should have 2 validation errors.');
|
||||
$this->assertSame('2: [entity_test: 2]: user_id.0.target_id=The referenced entity (<em class="placeholder">user</em>: <em class="placeholder">1</em>) does not exist.', $this->messages[1], 'Second message should have 1 validation error.');
|
||||
$this->assertArrayNotHasKey(2, $this->messages, 'Third message should not exist.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests an import with invalid data and checks error messages.
|
||||
*/
|
||||
public function test2() {
|
||||
$long_username = $this->randomString(61);
|
||||
$username_constraint = new UserNameConstraint();
|
||||
|
||||
$this->runImport([
|
||||
'source' => [
|
||||
'plugin' => 'embedded_data',
|
||||
'data_rows' => [
|
||||
[
|
||||
'id' => 1,
|
||||
'name' => $long_username,
|
||||
],
|
||||
[
|
||||
'id' => 2,
|
||||
'name' => $this->randomString(32),
|
||||
],
|
||||
[
|
||||
'id' => 3,
|
||||
'name' => $this->randomString(32),
|
||||
],
|
||||
],
|
||||
'ids' => [
|
||||
'id' => ['type' => 'integer'],
|
||||
],
|
||||
],
|
||||
'process' => [
|
||||
'name' => 'name',
|
||||
],
|
||||
'destination' => [
|
||||
'plugin' => 'entity:user',
|
||||
'validate' => TRUE,
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertSame(sprintf('1: [user]: name=%s||name=%s||mail=Email field is required.', $username_constraint->illegalMessage, t($username_constraint->tooLongMessage, ['%name' => $long_username, '%max' => 60])), $this->messages[0], 'First message should have 3 validation errors.');
|
||||
$this->assertSame(sprintf('2: [user]: name=%s||mail=Email field is required.', $username_constraint->illegalMessage), $this->messages[1], 'Second message should have 2 validation errors.');
|
||||
$this->assertSame(sprintf('3: [user]: name=%s||mail=Email field is required.', $username_constraint->illegalMessage), $this->messages[2], 'Third message should have 2 validation errors.');
|
||||
$this->assertArrayNotHasKey(3, $this->messages, 'Fourth message should not exist.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Reacts to map message event.
|
||||
*
|
||||
* @param \Drupal\migrate\Event\MigrateIdMapMessageEvent $event
|
||||
* The migration event.
|
||||
*/
|
||||
public function mapMessageRecorder(MigrateIdMapMessageEvent $event) {
|
||||
$this->messages[] = implode(',', $event->getSourceIdValues()) . ': ' . $event->getMessage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs an import of a migration.
|
||||
*
|
||||
* @param array $definition
|
||||
* The migration definition.
|
||||
*
|
||||
* @throws \Exception
|
||||
* @throws \Drupal\migrate\MigrateException
|
||||
*/
|
||||
protected function runImport(array $definition) {
|
||||
// Reset the list of messages from a previous migration.
|
||||
$this->messages = [];
|
||||
|
||||
(new MigrateExecutable($this->container->get('plugin.manager.migration')->createStubMigration($definition)))->import();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel;
|
||||
|
||||
use Drupal\migrate\Event\MigrateImportEvent;
|
||||
use Drupal\migrate\Event\MigrateMapDeleteEvent;
|
||||
use Drupal\migrate\Event\MigrateMapSaveEvent;
|
||||
use Drupal\migrate\Event\MigratePostRowSaveEvent;
|
||||
use Drupal\migrate\Event\MigratePreRowSaveEvent;
|
||||
use Drupal\migrate\Event\MigrateEvents;
|
||||
use Drupal\migrate\MigrateExecutable;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests events fired on migrations.
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class MigrateEventsTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* State service for recording information received by event listeners.
|
||||
*
|
||||
* @var \Drupal\Core\State\State
|
||||
*/
|
||||
protected $state;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['migrate', 'migrate_events_test'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->state = \Drupal::state();
|
||||
\Drupal::service('event_dispatcher')->addListener(MigrateEvents::MAP_SAVE,
|
||||
[$this, 'mapSaveEventRecorder']);
|
||||
\Drupal::service('event_dispatcher')->addListener(MigrateEvents::MAP_DELETE,
|
||||
[$this, 'mapDeleteEventRecorder']);
|
||||
\Drupal::service('event_dispatcher')->addListener(MigrateEvents::PRE_IMPORT,
|
||||
[$this, 'preImportEventRecorder']);
|
||||
\Drupal::service('event_dispatcher')->addListener(MigrateEvents::POST_IMPORT,
|
||||
[$this, 'postImportEventRecorder']);
|
||||
\Drupal::service('event_dispatcher')->addListener(MigrateEvents::PRE_ROW_SAVE,
|
||||
[$this, 'preRowSaveEventRecorder']);
|
||||
\Drupal::service('event_dispatcher')->addListener(MigrateEvents::POST_ROW_SAVE,
|
||||
[$this, 'postRowSaveEventRecorder']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests migration events.
|
||||
*/
|
||||
public function testMigrateEvents() {
|
||||
// Run a simple little migration, which should trigger one of each event
|
||||
// other than map_delete.
|
||||
$definition = [
|
||||
'migration_tags' => ['Event test'],
|
||||
'source' => [
|
||||
'plugin' => 'embedded_data',
|
||||
'data_rows' => [
|
||||
['data' => 'dummy value'],
|
||||
],
|
||||
'ids' => [
|
||||
'data' => ['type' => 'string'],
|
||||
],
|
||||
],
|
||||
'process' => ['value' => 'data'],
|
||||
'destination' => ['plugin' => 'dummy'],
|
||||
];
|
||||
|
||||
$migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition);
|
||||
|
||||
$executable = new MigrateExecutable($migration);
|
||||
// As the import runs, events will be dispatched, recording the received
|
||||
// information in state.
|
||||
$executable->import();
|
||||
|
||||
// Validate from the recorded state that the events were received.
|
||||
$event = $this->state->get('migrate_events_test.pre_import_event', []);
|
||||
$this->assertIdentical($event['event_name'], MigrateEvents::PRE_IMPORT);
|
||||
$this->assertIdentical($event['migration']->id(), $migration->id());
|
||||
|
||||
$event = $this->state->get('migrate_events_test.post_import_event', []);
|
||||
$this->assertIdentical($event['event_name'], MigrateEvents::POST_IMPORT);
|
||||
$this->assertIdentical($event['migration']->id(), $migration->id());
|
||||
|
||||
$event = $this->state->get('migrate_events_test.map_save_event', []);
|
||||
$this->assertIdentical($event['event_name'], MigrateEvents::MAP_SAVE);
|
||||
// Validating the last row processed.
|
||||
$this->assertIdentical($event['fields']['sourceid1'], 'dummy value');
|
||||
$this->assertIdentical($event['fields']['destid1'], 'dummy value');
|
||||
$this->assertIdentical($event['fields']['source_row_status'], 0);
|
||||
|
||||
$event = $this->state->get('migrate_events_test.map_delete_event', []);
|
||||
$this->assertIdentical($event, []);
|
||||
|
||||
$event = $this->state->get('migrate_events_test.pre_row_save_event', []);
|
||||
$this->assertIdentical($event['event_name'], MigrateEvents::PRE_ROW_SAVE);
|
||||
$this->assertIdentical($event['migration']->id(), $migration->id());
|
||||
// Validating the last row processed.
|
||||
$this->assertIdentical($event['row']->getSourceProperty('data'), 'dummy value');
|
||||
|
||||
$event = $this->state->get('migrate_events_test.post_row_save_event', []);
|
||||
$this->assertIdentical($event['event_name'], MigrateEvents::POST_ROW_SAVE);
|
||||
$this->assertIdentical($event['migration']->id(), $migration->id());
|
||||
// Validating the last row processed.
|
||||
$this->assertIdentical($event['row']->getSourceProperty('data'), 'dummy value');
|
||||
$this->assertIdentical($event['destination_id_values']['value'], 'dummy value');
|
||||
|
||||
// Generate a map delete event.
|
||||
$migration->getIdMap()->delete(['data' => 'dummy value']);
|
||||
$event = $this->state->get('migrate_events_test.map_delete_event', []);
|
||||
$this->assertIdentical($event['event_name'], MigrateEvents::MAP_DELETE);
|
||||
$this->assertIdentical($event['source_id'], ['data' => 'dummy value']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reacts to map save event.
|
||||
*
|
||||
* @param \Drupal\migrate\Event\MigrateMapSaveEvent $event
|
||||
* The migration event.
|
||||
* @param string $name
|
||||
* The event name.
|
||||
*/
|
||||
public function mapSaveEventRecorder(MigrateMapSaveEvent $event, $name) {
|
||||
$this->state->set('migrate_events_test.map_save_event', [
|
||||
'event_name' => $name,
|
||||
'map' => $event->getMap(),
|
||||
'fields' => $event->getFields(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reacts to map delete event.
|
||||
*
|
||||
* @param \Drupal\migrate\Event\MigrateMapDeleteEvent $event
|
||||
* The migration event.
|
||||
* @param string $name
|
||||
* The event name.
|
||||
*/
|
||||
public function mapDeleteEventRecorder(MigrateMapDeleteEvent $event, $name) {
|
||||
$this->state->set('migrate_events_test.map_delete_event', [
|
||||
'event_name' => $name,
|
||||
'map' => $event->getMap(),
|
||||
'source_id' => $event->getSourceId(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reacts to pre-import event.
|
||||
*
|
||||
* @param \Drupal\migrate\Event\MigrateImportEvent $event
|
||||
* The migration event.
|
||||
* @param string $name
|
||||
* The event name.
|
||||
*/
|
||||
public function preImportEventRecorder(MigrateImportEvent $event, $name) {
|
||||
$this->state->set('migrate_events_test.pre_import_event', [
|
||||
'event_name' => $name,
|
||||
'migration' => $event->getMigration(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reacts to post-import event.
|
||||
*
|
||||
* @param \Drupal\migrate\Event\MigrateImportEvent $event
|
||||
* The migration event.
|
||||
* @param string $name
|
||||
* The event name.
|
||||
*/
|
||||
public function postImportEventRecorder(MigrateImportEvent $event, $name) {
|
||||
$this->state->set('migrate_events_test.post_import_event', [
|
||||
'event_name' => $name,
|
||||
'migration' => $event->getMigration(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reacts to pre-row-save event.
|
||||
*
|
||||
* @param \Drupal\migrate\Event\MigratePreRowSaveEvent $event
|
||||
* The migration event.
|
||||
* @param string $name
|
||||
* The event name.
|
||||
*/
|
||||
public function preRowSaveEventRecorder(MigratePreRowSaveEvent $event, $name) {
|
||||
$this->state->set('migrate_events_test.pre_row_save_event', [
|
||||
'event_name' => $name,
|
||||
'migration' => $event->getMigration(),
|
||||
'row' => $event->getRow(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reacts to post-row-save event.
|
||||
*
|
||||
* @param \Drupal\migrate\Event\MigratePostRowSaveEvent $event
|
||||
* The migration event.
|
||||
* @param string $name
|
||||
* The event name.
|
||||
*/
|
||||
public function postRowSaveEventRecorder(MigratePostRowSaveEvent $event, $name) {
|
||||
$this->state->set('migrate_events_test.post_row_save_event', [
|
||||
'event_name' => $name,
|
||||
'migration' => $event->getMigration(),
|
||||
'row' => $event->getRow(),
|
||||
'destination_id_values' => $event->getDestinationIdValues(),
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel;
|
||||
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
|
||||
/**
|
||||
* Tests the MigrateExecutable class.
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class MigrateExecutableTest extends MigrateTestBase {
|
||||
|
||||
public static $modules = [
|
||||
'entity_test',
|
||||
'user',
|
||||
];
|
||||
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
$this->installEntitySchema('user');
|
||||
$this->installEntitySchema('entity_test');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the MigrateExecutable class.
|
||||
*/
|
||||
public function testMigrateExecutable() {
|
||||
$data_rows = [
|
||||
['key' => '1', 'field1' => 'f1value1', 'field2' => 'f2value1'],
|
||||
['key' => '2', 'field1' => 'f1value2', 'field2' => 'f2value2'],
|
||||
];
|
||||
$ids = ['key' => ['type' => 'integer']];
|
||||
$definition = [
|
||||
'migration_tags' => ['Embedded data test'],
|
||||
'source' => [
|
||||
'plugin' => 'embedded_data',
|
||||
'data_rows' => $data_rows,
|
||||
'ids' => $ids,
|
||||
],
|
||||
'process' => [],
|
||||
'destination' => ['plugin' => 'entity:entity_test'],
|
||||
];
|
||||
|
||||
$migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition);
|
||||
$executable = new TestMigrateExecutable($migration);
|
||||
$this->assertEquals(MigrationInterface::RESULT_COMPLETED, $executable->import());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel;
|
||||
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\migrate\MigrateExecutable;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
|
||||
/**
|
||||
* Tests migrating non-Drupal translated content.
|
||||
*
|
||||
* Ensure it's possible to migrate in translations, even if there's no nid or
|
||||
* tnid property on the source.
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class MigrateExternalTranslatedTest extends MigrateTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'system',
|
||||
'user',
|
||||
'language',
|
||||
'node',
|
||||
'field',
|
||||
'migrate_external_translated_test',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
$this->installSchema('system', ['sequences']);
|
||||
$this->installSchema('node', ['node_access']);
|
||||
$this->installEntitySchema('user');
|
||||
$this->installEntitySchema('node');
|
||||
|
||||
// Create some languages.
|
||||
ConfigurableLanguage::createFromLangcode('en')->save();
|
||||
ConfigurableLanguage::createFromLangcode('fr')->save();
|
||||
ConfigurableLanguage::createFromLangcode('es')->save();
|
||||
|
||||
// Create a content type.
|
||||
NodeType::create([
|
||||
'type' => 'external_test',
|
||||
'name' => 'Test node type',
|
||||
])->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test importing and rolling back our data.
|
||||
*/
|
||||
public function testMigrations() {
|
||||
/** @var \Drupal\Core\Entity\ContentEntityStorageInterface $storage */
|
||||
$storage = $this->container->get('entity_type.manager')->getStorage('node');
|
||||
$this->assertCount(0, $storage->loadMultiple());
|
||||
|
||||
// Run the migrations.
|
||||
$migration_ids = ['external_translated_test_node', 'external_translated_test_node_translation'];
|
||||
$this->executeMigrations($migration_ids);
|
||||
$this->assertCount(3, $storage->loadMultiple());
|
||||
|
||||
$node = $storage->load(1);
|
||||
$this->assertEquals('en', $node->language()->getId());
|
||||
$this->assertEquals('Cat', $node->title->value);
|
||||
$this->assertEquals('Chat', $node->getTranslation('fr')->title->value);
|
||||
$this->assertEquals('Gato', $node->getTranslation('es')->title->value);
|
||||
|
||||
$node = $storage->load(2);
|
||||
$this->assertEquals('en', $node->language()->getId());
|
||||
$this->assertEquals('Dog', $node->title->value);
|
||||
$this->assertEquals('Chien', $node->getTranslation('fr')->title->value);
|
||||
$this->assertFalse($node->hasTranslation('es'), "No spanish translation for node 2");
|
||||
|
||||
$node = $storage->load(3);
|
||||
$this->assertEquals('en', $node->language()->getId());
|
||||
$this->assertEquals('Monkey', $node->title->value);
|
||||
$this->assertFalse($node->hasTranslation('fr'), "No french translation for node 3");
|
||||
$this->assertFalse($node->hasTranslation('es'), "No spanish translation for node 3");
|
||||
|
||||
$this->assertNull($storage->load(4), "No node 4 migrated");
|
||||
|
||||
// Roll back the migrations.
|
||||
foreach ($migration_ids as $migration_id) {
|
||||
$migration = $this->getMigration($migration_id);
|
||||
$executable = new MigrateExecutable($migration, $this);
|
||||
$executable->rollback();
|
||||
}
|
||||
|
||||
$this->assertCount(0, $storage->loadMultiple());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel;
|
||||
|
||||
use Drupal\migrate\Event\MigratePostRowSaveEvent;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\migrate\Event\MigrateEvents;
|
||||
use Drupal\migrate\MigrateExecutable;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests interruptions triggered during migrations.
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class MigrateInterruptionTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['migrate', 'migrate_events_test'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
\Drupal::service('event_dispatcher')->addListener(MigrateEvents::POST_ROW_SAVE,
|
||||
[$this, 'postRowSaveEventRecorder']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests migration interruptions.
|
||||
*/
|
||||
public function testMigrateEvents() {
|
||||
// Run a simple little migration, which should trigger one of each event
|
||||
// other than map_delete.
|
||||
$definition = [
|
||||
'migration_tags' => ['Interruption test'],
|
||||
'source' => [
|
||||
'plugin' => 'embedded_data',
|
||||
'data_rows' => [
|
||||
['data' => 'dummy value'],
|
||||
['data' => 'dummy value2'],
|
||||
],
|
||||
'ids' => [
|
||||
'data' => ['type' => 'string'],
|
||||
],
|
||||
],
|
||||
'process' => ['value' => 'data'],
|
||||
'destination' => ['plugin' => 'dummy'],
|
||||
];
|
||||
|
||||
$migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition);
|
||||
|
||||
$executable = new MigrateExecutable($migration);
|
||||
// When the import runs, the first row imported will trigger an
|
||||
// interruption.
|
||||
$result = $executable->import();
|
||||
|
||||
$this->assertEqual($result, MigrationInterface::RESULT_INCOMPLETE);
|
||||
|
||||
// The status should have been reset to IDLE.
|
||||
$this->assertEqual($migration->getStatus(), MigrationInterface::STATUS_IDLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reacts to post-row-save event.
|
||||
*
|
||||
* @param \Drupal\migrate\Event\MigratePostRowSaveEvent $event
|
||||
* The migration event.
|
||||
* @param string $name
|
||||
* The event name.
|
||||
*/
|
||||
public function postRowSaveEventRecorder(MigratePostRowSaveEvent $event, $name) {
|
||||
$event->getMigration()->interruptMigration(MigrationInterface::RESULT_INCOMPLETE);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel;
|
||||
|
||||
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
|
||||
use Drupal\migrate\MigrateException;
|
||||
use Drupal\Tests\node\Traits\ContentTypeCreationTrait;
|
||||
|
||||
/**
|
||||
* Tests the Migrate Lookup service.
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class MigrateLookupTest extends MigrateTestBase {
|
||||
|
||||
use ContentTypeCreationTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'system',
|
||||
'node',
|
||||
'field',
|
||||
'user',
|
||||
'text',
|
||||
'migrate_lookup_test',
|
||||
];
|
||||
|
||||
/**
|
||||
* The migration lookup service.
|
||||
*
|
||||
* @var \Drupal\migrate\MigrateLookupInterface
|
||||
*/
|
||||
protected $migrateLookup;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
$this->setTestLogger();
|
||||
$this->migrateLookup = $this->container->get('migrate.lookup');
|
||||
$this->installEntitySchema('node');
|
||||
$this->installEntitySchema('user');
|
||||
$this->installConfig(['node', 'user']);
|
||||
$this->createContentType(['type' => 'node_lookup']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests scenarios around single id lookups.
|
||||
*/
|
||||
public function testSingleLookup() {
|
||||
$this->executeMigration('sample_lookup_migration');
|
||||
|
||||
// Test numerically indexed source id.
|
||||
$result = $this->migrateLookup->lookup('sample_lookup_migration', [17]);
|
||||
$this->assertSame('1', $result[0]['nid']);
|
||||
|
||||
// Test associatively indexed source id.
|
||||
$result = $this->migrateLookup->lookup('sample_lookup_migration', ['id' => 25]);
|
||||
$this->assertSame('2', $result[0]['nid']);
|
||||
|
||||
// Test lookup not found.
|
||||
$result = $this->migrateLookup->lookup('sample_lookup_migration', [1337]);
|
||||
$this->assertSame([], $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests an invalid lookup.
|
||||
*/
|
||||
public function testInvalidIdLookup() {
|
||||
$this->executeMigration('sample_lookup_migration');
|
||||
$this->expectException(MigrateException::class);
|
||||
$this->expectExceptionMessage("Extra unknown items for map migrate_map_sample_lookup_migration in source IDs: array (\n 'invalid_id' => 25,\n)");
|
||||
|
||||
// Test invalidly indexed source id.
|
||||
$this->migrateLookup->lookup('sample_lookup_migration', ['invalid_id' => 25]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests an invalid lookup.
|
||||
*/
|
||||
public function testInvalidMigrationLookup() {
|
||||
$this->expectException(PluginNotFoundException::class);
|
||||
$this->expectExceptionMessage("Plugin ID 'invalid_migration' was not found.");
|
||||
// Test invalid migration_id.
|
||||
$this->migrateLookup->lookup('invalid_migration', ['id' => 1337]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests lookups with multiple source ids.
|
||||
*/
|
||||
public function testMultipleSourceIds() {
|
||||
$this->executeMigration('sample_lookup_migration_multiple_source_ids');
|
||||
|
||||
// Test with full set of numerically indexed source ids.
|
||||
$result = $this->migrateLookup->lookup('sample_lookup_migration_multiple_source_ids', [
|
||||
25,
|
||||
26,
|
||||
]);
|
||||
$this->assertCount(1, $result);
|
||||
$this->assertSame('3', $result[0]['nid']);
|
||||
|
||||
// Test with full set of associatively indexed source ids.
|
||||
$result = $this->migrateLookup->lookup('sample_lookup_migration_multiple_source_ids', [
|
||||
'id' => 17,
|
||||
'version_id' => 17,
|
||||
]);
|
||||
$this->assertCount(1, $result);
|
||||
$this->assertSame('1', $result[0]['nid']);
|
||||
|
||||
// Test with full set of associatively indexed source ids in the wrong
|
||||
// order.
|
||||
$result = $this->migrateLookup->lookup('sample_lookup_migration_multiple_source_ids', [
|
||||
'version_id' => 26,
|
||||
'id' => 25,
|
||||
]);
|
||||
$this->assertCount(1, $result);
|
||||
$this->assertSame('3', $result[0]['nid']);
|
||||
|
||||
// Test with a partial set of numerically indexed ids.
|
||||
$result = $this->migrateLookup->lookup('sample_lookup_migration_multiple_source_ids', [25]);
|
||||
$this->assertCount(2, $result);
|
||||
$this->assertSame('2', $result[0]['nid']);
|
||||
$this->assertSame('3', $result[1]['nid']);
|
||||
|
||||
// Test with a partial set of associatively indexed ids.
|
||||
$result = $this->migrateLookup->lookup('sample_lookup_migration_multiple_source_ids', ['version_id' => 25]);
|
||||
$this->assertCount(1, $result);
|
||||
$this->assertSame('2', $result[0]['nid']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests looking up against multiple migrations at once.
|
||||
*
|
||||
* @throws \Drupal\Component\Plugin\Exception\PluginException
|
||||
* @throws \Drupal\migrate\MigrateException
|
||||
*/
|
||||
public function testMultipleMigrationLookup() {
|
||||
$migrations = [
|
||||
'sample_lookup_migration',
|
||||
'sample_lookup_migration_2',
|
||||
];
|
||||
foreach ($migrations as $migration) {
|
||||
$this->executeMigration($migration);
|
||||
}
|
||||
|
||||
// Test numerically indexed source id.
|
||||
$result = $this->migrateLookup->lookup($migrations, [17]);
|
||||
$this->assertSame('1', $result[0]['nid']);
|
||||
|
||||
// Test associatively indexed source id.
|
||||
$result = $this->migrateLookup->lookup($migrations, ['id' => 35]);
|
||||
$this->assertSame('4', $result[0]['nid']);
|
||||
|
||||
// Test lookup not found.
|
||||
$result = $this->migrateLookup->lookup($migrations, [1337]);
|
||||
$this->assertSame([], $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a lookup with string source ids.
|
||||
*/
|
||||
public function testLookupWithStringIds() {
|
||||
$this->executeMigration('sample_lookup_migration_string_ids');
|
||||
|
||||
// Test numerically indexed source id.
|
||||
$result = $this->migrateLookup->lookup('sample_lookup_migration_string_ids', ['node1']);
|
||||
$this->assertSame('10', $result[0]['nid']);
|
||||
|
||||
// Test associatively indexed source id.
|
||||
$result = $this->migrateLookup->lookup('sample_lookup_migration_string_ids', ['id' => 'node2']);
|
||||
$this->assertSame('11', $result[0]['nid']);
|
||||
|
||||
// Test lookup not found.
|
||||
$result = $this->migrateLookup->lookup('sample_lookup_migration_string_ids', ['node1337']);
|
||||
$this->assertSame([], $result);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\migrate\Event\MigrateEvents;
|
||||
use Drupal\migrate\Event\MigrateIdMapMessageEvent;
|
||||
use Drupal\migrate\MigrateExecutable;
|
||||
use Drupal\migrate\MigrateMessageInterface;
|
||||
use Drupal\migrate\Plugin\migrate\id_map\Sql;
|
||||
|
||||
/**
|
||||
* Tests whether idmap messages are sent to message interface when requested.
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class MigrateMessageTest extends KernelTestBase implements MigrateMessageInterface {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['migrate', 'system'];
|
||||
|
||||
/**
|
||||
* Migration to run.
|
||||
*
|
||||
* @var \Drupal\migrate\Plugin\MigrationInterface
|
||||
*/
|
||||
protected $migration;
|
||||
|
||||
/**
|
||||
* Messages accumulated during the migration run.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $messages = [];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installConfig(['system']);
|
||||
|
||||
// A simple migration, which will generate a message to the ID map because
|
||||
// the concat plugin throws an exception if its source is not an array.
|
||||
$definition = [
|
||||
'migration_tags' => ['Message test'],
|
||||
'source' => [
|
||||
'plugin' => 'embedded_data',
|
||||
'data_rows' => [
|
||||
['name' => 'source_message', 'value' => 'a message'],
|
||||
],
|
||||
'ids' => [
|
||||
'name' => ['type' => 'string'],
|
||||
],
|
||||
],
|
||||
'process' => [
|
||||
'message' => [
|
||||
'plugin' => 'concat',
|
||||
'source' => 'value',
|
||||
],
|
||||
],
|
||||
'destination' => [
|
||||
'plugin' => 'config',
|
||||
'config_name' => 'system.maintenance',
|
||||
],
|
||||
];
|
||||
|
||||
$this->migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests migration interruptions.
|
||||
*/
|
||||
public function testMessagesNotTeed() {
|
||||
// We don't ask for messages to be teed, so don't expect any.
|
||||
$executable = new MigrateExecutable($this->migration, $this);
|
||||
$executable->import();
|
||||
$this->assertCount(0, $this->messages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests migration interruptions.
|
||||
*/
|
||||
public function testMessagesTeed() {
|
||||
// Ask to receive any messages sent to the idmap.
|
||||
\Drupal::service('event_dispatcher')->addListener(MigrateEvents::IDMAP_MESSAGE,
|
||||
[$this, 'mapMessageRecorder']);
|
||||
$executable = new MigrateExecutable($this->migration, $this);
|
||||
$executable->import();
|
||||
$this->assertCount(1, $this->messages);
|
||||
$this->assertIdentical(reset($this->messages), "source_message: 'a message' is not an array");
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the return value of getMessages().
|
||||
*
|
||||
* This method returns an iterator of StdClass objects. Check that these
|
||||
* objects have the expected keys.
|
||||
*/
|
||||
public function testGetMessages() {
|
||||
$expected_message = (object) [
|
||||
'src_name' => 'source_message',
|
||||
'dest_config_name' => NULL,
|
||||
'msgid' => '1',
|
||||
Sql::SOURCE_IDS_HASH => '170cde81762e22552d1b1578cf3804c89afefe9efbc7cc835185d7141060b032',
|
||||
'level' => '1',
|
||||
'message' => "'a message' is not an array",
|
||||
];
|
||||
$executable = new MigrateExecutable($this->migration, $this);
|
||||
$executable->import();
|
||||
$count = 0;
|
||||
foreach ($this->migration->getIdMap()->getMessages() as $message) {
|
||||
++$count;
|
||||
$this->assertEqual($message, $expected_message);
|
||||
}
|
||||
$this->assertEqual($count, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reacts to map message event.
|
||||
*
|
||||
* @param \Drupal\migrate\Event\MigrateIdMapMessageEvent $event
|
||||
* The migration event.
|
||||
* @param string $name
|
||||
* The event name.
|
||||
*/
|
||||
public function mapMessageRecorder(MigrateIdMapMessageEvent $event, $name) {
|
||||
if ($event->getLevel() == MigrationInterface::MESSAGE_NOTICE ||
|
||||
$event->getLevel() == MigrationInterface::MESSAGE_INFORMATIONAL) {
|
||||
$type = 'status';
|
||||
}
|
||||
else {
|
||||
$type = 'error';
|
||||
}
|
||||
$source_id_string = implode(',', $event->getSourceIdValues());
|
||||
$this->display($source_id_string . ': ' . $event->getMessage(), $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function display($message, $type = 'status') {
|
||||
$this->messages[] = $message;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel;
|
||||
|
||||
use Drupal\migrate\MigrateExecutable;
|
||||
use Drupal\taxonomy\Entity\Vocabulary;
|
||||
|
||||
/**
|
||||
* Tests rolling back of imports.
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class MigrateRollbackEntityConfigTest extends MigrateTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = [
|
||||
'field',
|
||||
'taxonomy',
|
||||
'text',
|
||||
'language',
|
||||
'config_translation',
|
||||
'user',
|
||||
'system',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installEntitySchema('user');
|
||||
$this->installEntitySchema('taxonomy_vocabulary');
|
||||
$this->installEntitySchema('taxonomy_term');
|
||||
$this->installConfig(['taxonomy']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests rolling back configuration entity translations.
|
||||
*/
|
||||
public function testConfigEntityRollback() {
|
||||
// We use vocabularies to demonstrate importing and rolling back
|
||||
// configuration entities with translations. First, import vocabularies.
|
||||
$vocabulary_data_rows = [
|
||||
['id' => '1', 'name' => 'categories', 'weight' => '2'],
|
||||
['id' => '2', 'name' => 'tags', 'weight' => '1'],
|
||||
];
|
||||
$ids = ['id' => ['type' => 'integer']];
|
||||
$definition = [
|
||||
'id' => 'vocabularies',
|
||||
'migration_tags' => ['Import and rollback test'],
|
||||
'source' => [
|
||||
'plugin' => 'embedded_data',
|
||||
'data_rows' => $vocabulary_data_rows,
|
||||
'ids' => $ids,
|
||||
],
|
||||
'process' => [
|
||||
'vid' => 'id',
|
||||
'name' => 'name',
|
||||
'weight' => 'weight',
|
||||
],
|
||||
'destination' => ['plugin' => 'entity:taxonomy_vocabulary'],
|
||||
];
|
||||
|
||||
/** @var \Drupal\migrate\Plugin\Migration $vocabulary_migration */
|
||||
$vocabulary_migration = \Drupal::service('plugin.manager.migration')
|
||||
->createStubMigration($definition);
|
||||
$vocabulary_id_map = $vocabulary_migration->getIdMap();
|
||||
|
||||
$this->assertTrue($vocabulary_migration->getDestinationPlugin()
|
||||
->supportsRollback());
|
||||
|
||||
// Import and validate vocabulary config entities were created.
|
||||
$vocabulary_executable = new MigrateExecutable($vocabulary_migration, $this);
|
||||
$vocabulary_executable->import();
|
||||
foreach ($vocabulary_data_rows as $row) {
|
||||
/** @var \Drupal\taxonomy\Entity\Vocabulary $vocabulary */
|
||||
$vocabulary = Vocabulary::load($row['id']);
|
||||
$this->assertNotEmpty($vocabulary);
|
||||
$map_row = $vocabulary_id_map->getRowBySource(['id' => $row['id']]);
|
||||
$this->assertNotNull($map_row['destid1']);
|
||||
}
|
||||
|
||||
// Second, import translations of the vocabulary name property.
|
||||
$vocabulary_i18n_data_rows = [
|
||||
[
|
||||
'id' => '1',
|
||||
'name' => '1',
|
||||
'language' => 'fr',
|
||||
'property' => 'name',
|
||||
'translation' => 'fr - categories',
|
||||
],
|
||||
[
|
||||
'id' => '2',
|
||||
'name' => '2',
|
||||
'language' => 'fr',
|
||||
'property' => 'name',
|
||||
'translation' => 'fr - tags',
|
||||
],
|
||||
];
|
||||
$ids = [
|
||||
'id' => ['type' => 'integer'],
|
||||
'language' => ['type' => 'string'],
|
||||
];
|
||||
$definition = [
|
||||
'id' => 'i18n_vocabularies',
|
||||
'migration_tags' => ['Import and rollback test'],
|
||||
'source' => [
|
||||
'plugin' => 'embedded_data',
|
||||
'data_rows' => $vocabulary_i18n_data_rows,
|
||||
'ids' => $ids,
|
||||
'constants' => [
|
||||
'name' => 'name',
|
||||
],
|
||||
],
|
||||
'process' => [
|
||||
'vid' => 'id',
|
||||
'langcode' => 'language',
|
||||
'property' => 'constants/name',
|
||||
'translation' => 'translation',
|
||||
],
|
||||
'destination' => [
|
||||
'plugin' => 'entity:taxonomy_vocabulary',
|
||||
'translations' => 'true',
|
||||
],
|
||||
];
|
||||
|
||||
$vocabulary_i18n__migration = \Drupal::service('plugin.manager.migration')
|
||||
->createStubMigration($definition);
|
||||
$vocabulary_i18n_id_map = $vocabulary_i18n__migration->getIdMap();
|
||||
|
||||
$this->assertTrue($vocabulary_i18n__migration->getDestinationPlugin()
|
||||
->supportsRollback());
|
||||
|
||||
// Import and validate vocabulary config entities were created.
|
||||
$vocabulary_i18n_executable = new MigrateExecutable($vocabulary_i18n__migration, $this);
|
||||
$vocabulary_i18n_executable->import();
|
||||
|
||||
$language_manager = \Drupal::service('language_manager');
|
||||
foreach ($vocabulary_i18n_data_rows as $row) {
|
||||
$langcode = $row['language'];
|
||||
$id = 'taxonomy.vocabulary.' . $row['id'];
|
||||
/** @var \Drupal\language\Config\LanguageConfigOverride $config_translation */
|
||||
$config_translation = $language_manager->getLanguageConfigOverride($langcode, $id);
|
||||
$this->assertSame($row['translation'], $config_translation->get('name'));
|
||||
$map_row = $vocabulary_i18n_id_map->getRowBySource(['id' => $row['id'], 'language' => $row['language']]);
|
||||
$this->assertNotNull($map_row['destid1']);
|
||||
}
|
||||
|
||||
// Perform the rollback and confirm the translation was deleted and the map
|
||||
// table row removed.
|
||||
$vocabulary_i18n_executable->rollback();
|
||||
foreach ($vocabulary_i18n_data_rows as $row) {
|
||||
$langcode = $row['language'];
|
||||
$id = 'taxonomy.vocabulary.' . $row['id'];
|
||||
/** @var \Drupal\language\Config\LanguageConfigOverride $config_translation */
|
||||
$config_translation = $language_manager->getLanguageConfigOverride($langcode, $id);
|
||||
$this->assertNull($config_translation->get('name'));
|
||||
$map_row = $vocabulary_i18n_id_map->getRowBySource(['id' => $row['id'], 'language' => $row['language']]);
|
||||
$this->assertFalse($map_row);
|
||||
}
|
||||
|
||||
// Confirm the original vocabulary still exists.
|
||||
foreach ($vocabulary_data_rows as $row) {
|
||||
/** @var \Drupal\taxonomy\Entity\Vocabulary $vocabulary */
|
||||
$vocabulary = Vocabulary::load($row['id']);
|
||||
$this->assertNotEmpty($vocabulary);
|
||||
$map_row = $vocabulary_id_map->getRowBySource(['id' => $row['id']]);
|
||||
$this->assertNotNull($map_row['destid1']);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel;
|
||||
|
||||
use Drupal\migrate\MigrateExecutable;
|
||||
use Drupal\migrate\Plugin\MigrateIdMapInterface;
|
||||
use Drupal\migrate\Row;
|
||||
use Drupal\taxonomy\Entity\Term;
|
||||
use Drupal\taxonomy\Entity\Vocabulary;
|
||||
|
||||
/**
|
||||
* Tests rolling back of imports.
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class MigrateRollbackTest extends MigrateTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['field', 'taxonomy', 'text', 'user', 'system'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installEntitySchema('user');
|
||||
$this->installEntitySchema('taxonomy_vocabulary');
|
||||
$this->installEntitySchema('taxonomy_term');
|
||||
$this->installConfig(['taxonomy']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests rolling back configuration and content entities.
|
||||
*/
|
||||
public function testRollback() {
|
||||
// We use vocabularies to demonstrate importing and rolling back
|
||||
// configuration entities.
|
||||
$vocabulary_data_rows = [
|
||||
['id' => '1', 'name' => 'categories', 'weight' => '2'],
|
||||
['id' => '2', 'name' => 'tags', 'weight' => '1'],
|
||||
];
|
||||
$ids = ['id' => ['type' => 'integer']];
|
||||
$definition = [
|
||||
'id' => 'vocabularies',
|
||||
'migration_tags' => ['Import and rollback test'],
|
||||
'source' => [
|
||||
'plugin' => 'embedded_data',
|
||||
'data_rows' => $vocabulary_data_rows,
|
||||
'ids' => $ids,
|
||||
],
|
||||
'process' => [
|
||||
'vid' => 'id',
|
||||
'name' => 'name',
|
||||
'weight' => 'weight',
|
||||
],
|
||||
'destination' => ['plugin' => 'entity:taxonomy_vocabulary'],
|
||||
];
|
||||
|
||||
$vocabulary_migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition);
|
||||
$vocabulary_id_map = $vocabulary_migration->getIdMap();
|
||||
|
||||
$this->assertTrue($vocabulary_migration->getDestinationPlugin()->supportsRollback());
|
||||
|
||||
// Import and validate vocabulary config entities were created.
|
||||
$vocabulary_executable = new MigrateExecutable($vocabulary_migration, $this);
|
||||
$vocabulary_executable->import();
|
||||
foreach ($vocabulary_data_rows as $row) {
|
||||
/** @var \Drupal\taxonomy\Entity\Vocabulary $vocabulary */
|
||||
$vocabulary = Vocabulary::load($row['id']);
|
||||
$this->assertNotEmpty($vocabulary);
|
||||
$map_row = $vocabulary_id_map->getRowBySource(['id' => $row['id']]);
|
||||
$this->assertNotNull($map_row['destid1']);
|
||||
}
|
||||
|
||||
// We use taxonomy terms to demonstrate importing and rolling back content
|
||||
// entities.
|
||||
$term_data_rows = [
|
||||
['id' => '1', 'vocab' => '1', 'name' => 'music'],
|
||||
['id' => '2', 'vocab' => '2', 'name' => 'Bach'],
|
||||
['id' => '3', 'vocab' => '2', 'name' => 'Beethoven'],
|
||||
];
|
||||
$ids = ['id' => ['type' => 'integer']];
|
||||
$definition = [
|
||||
'id' => 'terms',
|
||||
'migration_tags' => ['Import and rollback test'],
|
||||
'source' => [
|
||||
'plugin' => 'embedded_data',
|
||||
'data_rows' => $term_data_rows,
|
||||
'ids' => $ids,
|
||||
],
|
||||
'process' => [
|
||||
'tid' => 'id',
|
||||
'vid' => 'vocab',
|
||||
'name' => 'name',
|
||||
],
|
||||
'destination' => ['plugin' => 'entity:taxonomy_term'],
|
||||
'migration_dependencies' => ['required' => ['vocabularies']],
|
||||
];
|
||||
|
||||
$term_migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition);
|
||||
$term_id_map = $term_migration->getIdMap();
|
||||
|
||||
$this->assertTrue($term_migration->getDestinationPlugin()->supportsRollback());
|
||||
|
||||
// Pre-create a term, to make sure it isn't deleted on rollback.
|
||||
$preserved_term_ids[] = 1;
|
||||
$new_term = Term::create(['tid' => 1, 'vid' => 1, 'name' => 'music']);
|
||||
$new_term->save();
|
||||
|
||||
// Import and validate term entities were created.
|
||||
$term_executable = new MigrateExecutable($term_migration, $this);
|
||||
$term_executable->import();
|
||||
// Also explicitly mark one row to be preserved on rollback.
|
||||
$preserved_term_ids[] = 2;
|
||||
$map_row = $term_id_map->getRowBySource(['id' => 2]);
|
||||
$dummy_row = new Row(['id' => 2], $ids);
|
||||
$term_id_map->saveIdMapping($dummy_row, [$map_row['destid1']],
|
||||
$map_row['source_row_status'], MigrateIdMapInterface::ROLLBACK_PRESERVE);
|
||||
|
||||
foreach ($term_data_rows as $row) {
|
||||
/** @var \Drupal\taxonomy\Entity\Term $term */
|
||||
$term = Term::load($row['id']);
|
||||
$this->assertNotEmpty($term);
|
||||
$map_row = $term_id_map->getRowBySource(['id' => $row['id']]);
|
||||
$this->assertNotNull($map_row['destid1']);
|
||||
}
|
||||
|
||||
// Add a failed row to test if this can be rolled back without errors.
|
||||
$this->mockFailure($term_migration, ['id' => '4', 'vocab' => '2', 'name' => 'FAIL']);
|
||||
|
||||
// Rollback and verify the entities are gone.
|
||||
$term_executable->rollback();
|
||||
foreach ($term_data_rows as $row) {
|
||||
$term = Term::load($row['id']);
|
||||
if (in_array($row['id'], $preserved_term_ids)) {
|
||||
$this->assertNotNull($term);
|
||||
}
|
||||
else {
|
||||
$this->assertNull($term);
|
||||
}
|
||||
$map_row = $term_id_map->getRowBySource(['id' => $row['id']]);
|
||||
$this->assertFalse($map_row);
|
||||
}
|
||||
$vocabulary_executable->rollback();
|
||||
foreach ($vocabulary_data_rows as $row) {
|
||||
$term = Vocabulary::load($row['id']);
|
||||
$this->assertNull($term);
|
||||
$map_row = $vocabulary_id_map->getRowBySource(['id' => $row['id']]);
|
||||
$this->assertFalse($map_row);
|
||||
}
|
||||
|
||||
// Test that simple configuration is not rollbackable.
|
||||
$term_setting_rows = [
|
||||
['id' => 1, 'override_selector' => '0', 'terms_per_page_admin' => '10'],
|
||||
];
|
||||
$ids = ['id' => ['type' => 'integer']];
|
||||
$definition = [
|
||||
'id' => 'taxonomy_settings',
|
||||
'migration_tags' => ['Import and rollback test'],
|
||||
'source' => [
|
||||
'plugin' => 'embedded_data',
|
||||
'data_rows' => $term_setting_rows,
|
||||
'ids' => $ids,
|
||||
],
|
||||
'process' => [
|
||||
'override_selector' => 'override_selector',
|
||||
'terms_per_page_admin' => 'terms_per_page_admin',
|
||||
],
|
||||
'destination' => [
|
||||
'plugin' => 'config',
|
||||
'config_name' => 'taxonomy.settings',
|
||||
],
|
||||
'migration_dependencies' => ['required' => ['vocabularies']],
|
||||
];
|
||||
|
||||
$settings_migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition);
|
||||
$this->assertFalse($settings_migration->getDestinationPlugin()->supportsRollback());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\migrate\MigrateExecutable;
|
||||
use Drupal\migrate\Plugin\MigrateIdMapInterface;
|
||||
|
||||
/**
|
||||
* Tests row skips triggered during hook_migrate_prepare_row().
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class MigrateSkipRowTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['migrate', 'migrate_prepare_row_test'];
|
||||
|
||||
/**
|
||||
* Tests migration interruptions.
|
||||
*/
|
||||
public function testPrepareRowSkip() {
|
||||
// Run a simple little migration with two data rows which should be skipped
|
||||
// in different ways.
|
||||
$definition = [
|
||||
'migration_tags' => ['prepare_row test'],
|
||||
'source' => [
|
||||
'plugin' => 'embedded_data',
|
||||
'data_rows' => [
|
||||
['id' => '1', 'data' => 'skip_and_record'],
|
||||
['id' => '2', 'data' => 'skip_and_dont_record'],
|
||||
],
|
||||
'ids' => [
|
||||
'id' => ['type' => 'string'],
|
||||
],
|
||||
],
|
||||
'process' => ['value' => 'data'],
|
||||
'destination' => [
|
||||
'plugin' => 'config',
|
||||
'config_name' => 'migrate_test.settings',
|
||||
],
|
||||
'load' => ['plugin' => 'null'],
|
||||
];
|
||||
|
||||
$migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition);
|
||||
|
||||
$executable = new MigrateExecutable($migration);
|
||||
$result = $executable->import();
|
||||
$this->assertEqual($result, MigrationInterface::RESULT_COMPLETED);
|
||||
|
||||
/** @var \Drupal\migrate\Plugin\MigrateIdMapInterface $id_map_plugin */
|
||||
$id_map_plugin = $migration->getIdMap();
|
||||
// The first row is recorded in the map as ignored.
|
||||
$map_row = $id_map_plugin->getRowBySource(['id' => 1]);
|
||||
$this->assertEqual(MigrateIdMapInterface::STATUS_IGNORED, $map_row['source_row_status']);
|
||||
// Check that no message has been logged for the first exception.
|
||||
$messages = $id_map_plugin->getMessages(['id' => 1])->fetchAll();
|
||||
$this->assertEmpty($messages);
|
||||
|
||||
// The second row is not recorded in the map.
|
||||
$map_row = $id_map_plugin->getRowBySource(['id' => 2]);
|
||||
$this->assertFalse($map_row);
|
||||
// Check that the correct message has been logged for the second exception.
|
||||
$messages = $id_map_plugin->getMessages(['id' => 2])->fetchAll();
|
||||
$this->assertCount(1, $messages);
|
||||
$message = reset($messages);
|
||||
$this->assertEquals('skip_and_dont_record message', $message->message);
|
||||
$this->assertEquals(MigrationInterface::MESSAGE_INFORMATIONAL, $message->level);
|
||||
|
||||
// Insert a custom processor in the process flow.
|
||||
$definition['process']['value'] = [
|
||||
'source' => 'data',
|
||||
'plugin' => 'test_skip_row_process',
|
||||
];
|
||||
// Change data to avoid triggering again hook_migrate_prepare_row().
|
||||
$definition['source']['data_rows'] = [
|
||||
['id' => '1', 'data' => 'skip_and_record (use plugin)'],
|
||||
['id' => '2', 'data' => 'skip_and_dont_record (use plugin)'],
|
||||
];
|
||||
$migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition);
|
||||
|
||||
$executable = new MigrateExecutable($migration);
|
||||
$result = $executable->import();
|
||||
$this->assertEquals($result, MigrationInterface::RESULT_COMPLETED);
|
||||
|
||||
$id_map_plugin = $migration->getIdMap();
|
||||
|
||||
// The first row is recorded in the map as ignored.
|
||||
$map_row = $id_map_plugin->getRowBySource(['id' => 1]);
|
||||
$this->assertEquals(MigrateIdMapInterface::STATUS_IGNORED, $map_row['source_row_status']);
|
||||
// The second row is not recorded in the map.
|
||||
$map_row = $id_map_plugin->getRowBySource(['id' => 2]);
|
||||
$this->assertFalse($map_row);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\migrate\Plugin\MigrateIdMapInterface;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\migrate\Row;
|
||||
|
||||
/**
|
||||
* Base class for tests of Migrate source plugins.
|
||||
*/
|
||||
abstract class MigrateSourceTestBase extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['migrate'];
|
||||
|
||||
/**
|
||||
* The mocked migration.
|
||||
*
|
||||
* @var \Drupal\migrate\Plugin\MigrationInterface|\Prophecy\Prophecy\ObjectProphecy
|
||||
*/
|
||||
protected $migration;
|
||||
|
||||
/**
|
||||
* The source plugin under test.
|
||||
*
|
||||
* @var \Drupal\migrate\Plugin\MigrateSourceInterface
|
||||
*/
|
||||
protected $plugin;
|
||||
|
||||
/**
|
||||
* The data provider.
|
||||
*
|
||||
* @see \Drupal\Tests\migrate\Kernel\MigrateSourceTestBase::testSource
|
||||
*
|
||||
* @return array
|
||||
* Array of data sets to test, each of which is a numerically indexed array
|
||||
* with the following elements:
|
||||
* - An array of source data, which can be optionally processed and set up
|
||||
* by subclasses.
|
||||
* - An array of expected result rows.
|
||||
* - (optional) The number of result rows the plugin under test is expected
|
||||
* to return. If this is not a numeric value, the plugin will not be
|
||||
* counted.
|
||||
* - (optional) Array of configuration options for the plugin under test.
|
||||
*/
|
||||
abstract public function providerSource();
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create a mock migration. This will be injected into the source plugin
|
||||
// under test.
|
||||
$this->migration = $this->prophesize(MigrationInterface::class);
|
||||
|
||||
$this->migration->id()->willReturn(
|
||||
$this->randomMachineName(16)
|
||||
);
|
||||
// Prophesize a useless ID map plugin and an empty set of destination IDs.
|
||||
// Calling code can override these prophecies later and set up different
|
||||
// behaviors.
|
||||
$this->migration->getIdMap()->willReturn(
|
||||
$this->prophesize(MigrateIdMapInterface::class)->reveal()
|
||||
);
|
||||
$this->migration->getDestinationIds()->willReturn([]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the plugin to be tested by reading the class @covers annotation.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getPluginClass() {
|
||||
$annotations = $this->getAnnotations();
|
||||
|
||||
if (isset($annotations['class']['covers'])) {
|
||||
return $annotations['class']['covers'][0];
|
||||
}
|
||||
else {
|
||||
$this->fail('No plugin class was specified');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates the source plugin under test.
|
||||
*
|
||||
* @param array $configuration
|
||||
* The source plugin configuration.
|
||||
*
|
||||
* @return \Drupal\migrate\Plugin\MigrateSourceInterface|object
|
||||
* The fully configured source plugin.
|
||||
*/
|
||||
protected function getPlugin(array $configuration) {
|
||||
// Only create the plugin once per test.
|
||||
if ($this->plugin) {
|
||||
return $this->plugin;
|
||||
}
|
||||
|
||||
$class = ltrim($this->getPluginClass(), '\\');
|
||||
|
||||
/** @var \Drupal\migrate\Plugin\MigratePluginManager $plugin_manager */
|
||||
$plugin_manager = $this->container->get('plugin.manager.migrate.source');
|
||||
|
||||
foreach ($plugin_manager->getDefinitions() as $id => $definition) {
|
||||
if (ltrim($definition['class'], '\\') == $class) {
|
||||
$this->plugin = $plugin_manager
|
||||
->createInstance($id, $configuration, $this->migration->reveal());
|
||||
|
||||
$this->migration
|
||||
->getSourcePlugin()
|
||||
->willReturn($this->plugin);
|
||||
|
||||
return $this->plugin;
|
||||
}
|
||||
}
|
||||
$this->fail('No plugin found for class ' . $class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the source plugin against a particular data set.
|
||||
*
|
||||
* @param array $source_data
|
||||
* The source data that the source plugin will read.
|
||||
* @param array $expected_data
|
||||
* The result rows the source plugin is expected to return.
|
||||
* @param mixed $expected_count
|
||||
* (optional) How many rows the source plugin is expected to return.
|
||||
* Defaults to count($expected_data). If set to a non-null, non-numeric
|
||||
* value (like FALSE or 'nope'), the source plugin will not be counted.
|
||||
* @param array $configuration
|
||||
* (optional) Configuration for the source plugin.
|
||||
* @param mixed $high_water
|
||||
* (optional) The value of the high water field.
|
||||
*
|
||||
* @dataProvider providerSource
|
||||
*/
|
||||
public function testSource(array $source_data, array $expected_data, $expected_count = NULL, array $configuration = [], $high_water = NULL) {
|
||||
$plugin = $this->getPlugin($configuration);
|
||||
|
||||
// All source plugins must define IDs.
|
||||
$this->assertNotEmpty($plugin->getIds());
|
||||
|
||||
// If there is a high water mark, set it in the high water storage.
|
||||
if (isset($high_water)) {
|
||||
$this->container
|
||||
->get('keyvalue')
|
||||
->get('migrate:high_water')
|
||||
->set($this->migration->reveal()->id(), $high_water);
|
||||
}
|
||||
|
||||
if (is_null($expected_count)) {
|
||||
$expected_count = count($expected_data);
|
||||
}
|
||||
// If an expected count was given, assert it only if the plugin is
|
||||
// countable.
|
||||
if (is_numeric($expected_count)) {
|
||||
$this->assertInstanceOf('\Countable', $plugin);
|
||||
$this->assertCount($expected_count, $plugin);
|
||||
}
|
||||
|
||||
$i = 0;
|
||||
/** @var \Drupal\migrate\Row $row */
|
||||
foreach ($plugin as $row) {
|
||||
$this->assertInstanceOf(Row::class, $row);
|
||||
|
||||
$expected = $expected_data[$i++];
|
||||
$actual = $row->getSource();
|
||||
|
||||
foreach ($expected as $key => $value) {
|
||||
$this->assertArrayHasKey($key, $actual);
|
||||
|
||||
$msg = sprintf("Value at 'array[%s][%s]' is not correct.", $i - 1, $key);
|
||||
if (is_array($value)) {
|
||||
ksort($value);
|
||||
ksort($actual[$key]);
|
||||
$this->assertEquals($value, $actual[$key], $msg);
|
||||
}
|
||||
else {
|
||||
$this->assertEquals((string) $value, (string) $actual[$key], $msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
// False positives occur if the foreach is not entered. So, confirm the
|
||||
// foreach loop was entered if the expected count is greater than 0.
|
||||
if ($expected_count > 0) {
|
||||
$this->assertGreaterThan(0, $i);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel;
|
||||
|
||||
use Drupal\Core\Database\Driver\sqlite\Connection;
|
||||
|
||||
/**
|
||||
* Base class for tests of Migrate source plugins that use a database.
|
||||
*/
|
||||
abstract class MigrateSqlSourceTestBase extends MigrateSourceTestBase {
|
||||
|
||||
/**
|
||||
* Builds an in-memory SQLite database from a set of source data.
|
||||
*
|
||||
* @param array $source_data
|
||||
* The source data, keyed by table name. Each table is an array containing
|
||||
* the rows in that table.
|
||||
*
|
||||
* @return \Drupal\Core\Database\Driver\sqlite\Connection
|
||||
* The SQLite database connection.
|
||||
*/
|
||||
protected function getDatabase(array $source_data) {
|
||||
// Create an in-memory SQLite database. Plugins can interact with it like
|
||||
// any other database, and it will cease to exist when the connection is
|
||||
// closed.
|
||||
$connection_options = ['database' => ':memory:'];
|
||||
$pdo = Connection::open($connection_options);
|
||||
$connection = new Connection($pdo, $connection_options);
|
||||
|
||||
// Create the tables and fill them with data.
|
||||
foreach ($source_data as $table => $rows) {
|
||||
// Use the biggest row to build the table schema.
|
||||
$counts = array_map('count', $rows);
|
||||
asort($counts);
|
||||
end($counts);
|
||||
$pilot = $rows[key($counts)];
|
||||
|
||||
$connection->schema()
|
||||
->createTable($table, [
|
||||
// SQLite uses loose affinity typing, so it's OK for every field to
|
||||
// be a text field.
|
||||
'fields' => array_map(function () {
|
||||
return ['type' => 'text'];
|
||||
}, $pilot),
|
||||
]);
|
||||
|
||||
$fields = array_keys($pilot);
|
||||
$insert = $connection->insert($table)->fields($fields);
|
||||
array_walk($rows, [$insert, 'values']);
|
||||
$insert->execute();
|
||||
}
|
||||
|
||||
return $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the source plugin against a particular data set.
|
||||
*
|
||||
* @param array $source_data
|
||||
* The source data that the plugin will read. See getDatabase() for the
|
||||
* expected format.
|
||||
* @param array $expected_data
|
||||
* The result rows the plugin is expected to return.
|
||||
* @param int $expected_count
|
||||
* (optional) How many rows the source plugin is expected to return.
|
||||
* @param array $configuration
|
||||
* (optional) Configuration for the source plugin.
|
||||
* @param mixed $high_water
|
||||
* (optional) The value of the high water field.
|
||||
*
|
||||
* @dataProvider providerSource
|
||||
*
|
||||
* @requires extension pdo_sqlite
|
||||
*/
|
||||
public function testSource(array $source_data, array $expected_data, $expected_count = NULL, array $configuration = [], $high_water = NULL) {
|
||||
$plugin = $this->getPlugin($configuration);
|
||||
|
||||
// Since we don't yet inject the database connection, we need to use a
|
||||
// reflection hack to set it in the plugin instance.
|
||||
$reflector = new \ReflectionObject($plugin);
|
||||
$property = $reflector->getProperty('database');
|
||||
$property->setAccessible(TRUE);
|
||||
$property->setValue($plugin, $this->getDatabase($source_data));
|
||||
|
||||
parent::testSource($source_data, $expected_data, $expected_count, $configuration, $high_water);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel;
|
||||
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
|
||||
/**
|
||||
* Tests migration status tracking.
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class MigrateStatusTest extends MigrateTestBase {
|
||||
|
||||
/**
|
||||
* Tests different connection types.
|
||||
*/
|
||||
public function testStatus() {
|
||||
// Create a minimally valid migration.
|
||||
$definition = [
|
||||
'id' => 'migrate_status_test',
|
||||
'migration_tags' => ['Testing'],
|
||||
'source' => ['plugin' => 'empty'],
|
||||
'destination' => [
|
||||
'plugin' => 'config',
|
||||
'config_name' => 'migrate_test.settings',
|
||||
],
|
||||
'process' => ['foo' => 'bar'],
|
||||
];
|
||||
$migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition);
|
||||
|
||||
// Default status is idle.
|
||||
$status = $migration->getStatus();
|
||||
$this->assertIdentical($status, MigrationInterface::STATUS_IDLE);
|
||||
|
||||
// Test setting and retrieving all known status values.
|
||||
$status_list = [
|
||||
MigrationInterface::STATUS_IDLE,
|
||||
MigrationInterface::STATUS_IMPORTING,
|
||||
MigrationInterface::STATUS_ROLLING_BACK,
|
||||
MigrationInterface::STATUS_STOPPING,
|
||||
MigrationInterface::STATUS_DISABLED,
|
||||
];
|
||||
foreach ($status_list as $status) {
|
||||
$migration->setStatus($status);
|
||||
$this->assertIdentical($migration->getStatus(), $status);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel;
|
||||
|
||||
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
|
||||
use Drupal\Tests\node\Traits\ContentTypeCreationTrait;
|
||||
|
||||
/**
|
||||
* Tests the migrate.stub Service.
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class MigrateStubTest extends MigrateTestBase {
|
||||
use ContentTypeCreationTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'system',
|
||||
'node',
|
||||
'field',
|
||||
'user',
|
||||
'text',
|
||||
'migrate_stub_test',
|
||||
];
|
||||
|
||||
/**
|
||||
* The migrate stub service.
|
||||
*
|
||||
* @var \Drupal\migrate\MigrateStubInterface
|
||||
*/
|
||||
protected $migrateStub;
|
||||
|
||||
/**
|
||||
* The migration lookup service.
|
||||
*
|
||||
* @var \Drupal\migrate\MigrateLookupInterface
|
||||
*/
|
||||
protected $migrateLookup;
|
||||
|
||||
/**
|
||||
* The migration plugin manager.
|
||||
*
|
||||
* @var \Drupal\migrate\Plugin\MigrationPluginManagerInterface
|
||||
*/
|
||||
protected $migrationPluginManager;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->setTestLogger();
|
||||
$this->migrateStub = $this->container->get('migrate.stub');
|
||||
$this->migrateLookup = $this->container->get('migrate.lookup');
|
||||
$this->migrationPluginManager = $this->container->get('plugin.manager.migration');
|
||||
$this->installEntitySchema('node');
|
||||
$this->installEntitySchema('user');
|
||||
$this->installSchema('node', 'node_access');
|
||||
$this->installConfig(['node', 'user']);
|
||||
$this->createContentType(['type' => 'node_lookup']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests stub creation.
|
||||
*/
|
||||
public function testCreateStub() {
|
||||
$this->assertSame([], $this->migrateLookup->lookup('sample_stubbing_migration', [17]));
|
||||
$ids = $this->migrateStub->createStub('sample_stubbing_migration', [17]);
|
||||
$this->assertSame([$ids], $this->migrateLookup->lookup('sample_stubbing_migration', [17]));
|
||||
$this->assertNotNull(\Drupal::entityTypeManager()->getStorage('node')->load($ids['nid']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests raw stub creation.
|
||||
*/
|
||||
public function testCreateStubRawReturn() {
|
||||
$this->assertSame([], $this->migrateLookup->lookup('sample_stubbing_migration', [17]));
|
||||
$ids = $this->migrateStub->createStub('sample_stubbing_migration', [17], [], FALSE);
|
||||
$this->assertSame($ids, [$this->migrateLookup->lookup('sample_stubbing_migration', [17])[0]['nid']]);
|
||||
$this->assertNotNull(\Drupal::entityTypeManager()->getStorage('node')->load($ids[0]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests stub creation with default values.
|
||||
*/
|
||||
public function testStubWithDefaultValues() {
|
||||
$this->assertSame([], $this->migrateLookup->lookup('sample_stubbing_migration', [17]));
|
||||
$ids = $this->migrateStub->createStub('sample_stubbing_migration', [17], ['title' => "Placeholder for source id 17"]);
|
||||
$this->assertSame([$ids], $this->migrateLookup->lookup('sample_stubbing_migration', [17]));
|
||||
$node = \Drupal::entityTypeManager()->getStorage('node')->load($ids['nid']);
|
||||
$this->assertNotNull($node);
|
||||
// Test that our default value was set as the node title.
|
||||
$this->assertSame("Placeholder for source id 17", $node->label());
|
||||
|
||||
// Test that running the migration replaces the node title.
|
||||
$this->executeMigration('sample_stubbing_migration');
|
||||
$node = \Drupal::entityTypeManager()->getStorage('node')->loadUnchanged($ids['nid']);
|
||||
$this->assertSame("Sample 1", $node->label());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test invalid source id count.
|
||||
*/
|
||||
public function testInvalidSourceIdCount() {
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('Expected and provided source id counts do not match.');
|
||||
$this->migrateStub->createStub('sample_stubbing_migration_with_multiple_source_ids', [17]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests invalid source ids keys.
|
||||
*/
|
||||
public function testInvalidSourceIdKeys() {
|
||||
$this->expectException(\InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('version_id is defined as a source ID but has no value.');
|
||||
$this->migrateStub->createStub('sample_stubbing_migration_with_multiple_source_ids', ['id' => 17, 'not_a_key' => 17]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that an exception is thrown if a migration does not exist.
|
||||
*/
|
||||
public function testErrorOnMigrationNotFound() {
|
||||
$this->expectException(PluginNotFoundException::class);
|
||||
$this->expectExceptionMessage("Plugin ID 'nonexistent_migration' was not found.");
|
||||
$this->migrateStub->createStub('nonexistent_migration', [1]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel;
|
||||
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\Logger\LoggerChannelInterface;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\migrate\MigrateExecutable;
|
||||
use Drupal\migrate\MigrateMessageInterface;
|
||||
use Drupal\migrate\Plugin\MigrateIdMapInterface;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\migrate\Row;
|
||||
|
||||
/**
|
||||
* Creates abstract base class for migration tests.
|
||||
*/
|
||||
abstract class MigrateTestBase extends KernelTestBase implements MigrateMessageInterface {
|
||||
|
||||
/**
|
||||
* TRUE to collect messages instead of displaying them.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $collectMessages = FALSE;
|
||||
|
||||
/**
|
||||
* A two dimensional array of messages.
|
||||
*
|
||||
* The first key is the type of message, the second is just numeric. Values
|
||||
* are the messages.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $migrateMessages;
|
||||
|
||||
/**
|
||||
* The primary migration being tested.
|
||||
*
|
||||
* @var \Drupal\migrate\Plugin\MigrationInterface
|
||||
*/
|
||||
protected $migration;
|
||||
|
||||
/**
|
||||
* The source database connection.
|
||||
*
|
||||
* @var \Drupal\Core\Database\Connection
|
||||
*/
|
||||
protected $sourceDatabase;
|
||||
|
||||
/**
|
||||
* A logger prophecy object.
|
||||
*
|
||||
* Using ::setTestLogger(), this prophecy will be configured and injected into
|
||||
* the container. Using $this->logger->function(args)->shouldHaveBeenCalled()
|
||||
* you can assert that the logger was called.
|
||||
*
|
||||
* @var \Prophecy\Prophecy\ObjectProphecy
|
||||
*/
|
||||
protected $logger;
|
||||
|
||||
public static $modules = ['migrate'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->createMigrationConnection();
|
||||
$this->sourceDatabase = Database::getConnection('default', 'migrate');
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the database connection to the prefixed one.
|
||||
*
|
||||
* @todo Remove when we don't use global. https://www.drupal.org/node/2552791
|
||||
*/
|
||||
private function createMigrationConnection() {
|
||||
// If the backup already exists, something went terribly wrong.
|
||||
// This case is possible, because database connection info is a static
|
||||
// global state construct on the Database class, which at least persists
|
||||
// for all test methods executed in one PHP process.
|
||||
if (Database::getConnectionInfo('simpletest_original_migrate')) {
|
||||
throw new \RuntimeException("Bad Database connection state: 'simpletest_original_migrate' connection key already exists. Broken test?");
|
||||
}
|
||||
|
||||
// Clone the current connection and replace the current prefix.
|
||||
$connection_info = Database::getConnectionInfo('migrate');
|
||||
if ($connection_info) {
|
||||
Database::renameConnection('migrate', 'simpletest_original_migrate');
|
||||
}
|
||||
$connection_info = Database::getConnectionInfo('default');
|
||||
foreach ($connection_info as $target => $value) {
|
||||
$prefix = is_array($value['prefix']) ? $value['prefix']['default'] : $value['prefix'];
|
||||
// Simpletest uses 7 character prefixes at most so this can't cause
|
||||
// collisions.
|
||||
$connection_info[$target]['prefix']['default'] = $prefix . '0';
|
||||
|
||||
// Add the original simpletest prefix so SQLite can attach its database.
|
||||
// @see \Drupal\Core\Database\Driver\sqlite\Connection::init()
|
||||
$connection_info[$target]['prefix'][$value['prefix']['default']] = $value['prefix']['default'];
|
||||
}
|
||||
Database::addConnectionInfo('migrate', 'default', $connection_info['default']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function tearDown() {
|
||||
$this->cleanupMigrateConnection();
|
||||
parent::tearDown();
|
||||
$this->databaseDumpFiles = [];
|
||||
$this->collectMessages = FALSE;
|
||||
unset($this->migration, $this->migrateMessages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the test migrate connection.
|
||||
*
|
||||
* @todo Remove when we don't use global. https://www.drupal.org/node/2552791
|
||||
*/
|
||||
private function cleanupMigrateConnection() {
|
||||
Database::removeConnection('migrate');
|
||||
$original_connection_info = Database::getConnectionInfo('simpletest_original_migrate');
|
||||
if ($original_connection_info) {
|
||||
Database::renameConnection('simpletest_original_migrate', 'migrate');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare any dependent migrations.
|
||||
*
|
||||
* @param array $id_mappings
|
||||
* A list of ID mappings keyed by migration IDs. Each ID mapping is a list
|
||||
* of two arrays, the first are source IDs and the second are destination
|
||||
* IDs.
|
||||
*/
|
||||
protected function prepareMigrations(array $id_mappings) {
|
||||
$manager = $this->container->get('plugin.manager.migration');
|
||||
foreach ($id_mappings as $migration_id => $data) {
|
||||
foreach ($manager->createInstances($migration_id) as $migration) {
|
||||
$id_map = $migration->getIdMap();
|
||||
$id_map->setMessage($this);
|
||||
$source_ids = $migration->getSourcePlugin()->getIds();
|
||||
foreach ($data as $id_mapping) {
|
||||
$row = new Row(array_combine(array_keys($source_ids), $id_mapping[0]), $source_ids);
|
||||
$id_map->saveIdMapping($row, $id_mapping[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify a migration's configuration before executing it.
|
||||
*
|
||||
* @param \Drupal\migrate\Plugin\MigrationInterface $migration
|
||||
* The migration to execute.
|
||||
*/
|
||||
protected function prepareMigration(MigrationInterface $migration) {
|
||||
// Default implementation for test classes not requiring modification.
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a single migration.
|
||||
*
|
||||
* @param string|\Drupal\migrate\Plugin\MigrationInterface $migration
|
||||
* The migration to execute, or its ID.
|
||||
*/
|
||||
protected function executeMigration($migration) {
|
||||
if (is_string($migration)) {
|
||||
$this->migration = $this->getMigration($migration);
|
||||
}
|
||||
else {
|
||||
$this->migration = $migration;
|
||||
}
|
||||
if ($this instanceof MigrateDumpAlterInterface) {
|
||||
static::migrateDumpAlter($this);
|
||||
}
|
||||
|
||||
$this->prepareMigration($this->migration);
|
||||
(new MigrateExecutable($this->migration, $this))->import();
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a set of migrations in dependency order.
|
||||
*
|
||||
* @param string[] $ids
|
||||
* Array of migration IDs, in any order. If any of these migrations use a
|
||||
* deriver, the derivatives will be made before execution.
|
||||
*/
|
||||
protected function executeMigrations(array $ids) {
|
||||
$manager = $this->container->get('plugin.manager.migration');
|
||||
array_walk($ids, function ($id) use ($manager) {
|
||||
// This is possibly a base plugin ID and we want to run all derivatives.
|
||||
$instances = $manager->createInstances($id);
|
||||
$this->assertNotEmpty($instances, sprintf("No migrations created for id '%s'.", $id));
|
||||
array_walk($instances, [$this, 'executeMigration']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function display($message, $type = 'status') {
|
||||
if ($this->collectMessages) {
|
||||
$this->migrateMessages[$type][] = $message;
|
||||
}
|
||||
else {
|
||||
$this->assert($type == 'status', $message, 'migrate');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start collecting messages and erase previous messages.
|
||||
*/
|
||||
public function startCollectingMessages() {
|
||||
$this->collectMessages = TRUE;
|
||||
$this->migrateMessages = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop collecting messages.
|
||||
*/
|
||||
public function stopCollectingMessages() {
|
||||
$this->collectMessages = FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Records a failure in the map table of a specific migration.
|
||||
*
|
||||
* This is done in order to test scenarios which require a failed row.
|
||||
*
|
||||
* @param string|\Drupal\migrate\Plugin\MigrationInterface $migration
|
||||
* The migration entity, or its ID.
|
||||
* @param array $row
|
||||
* The raw source row which "failed".
|
||||
* @param int $status
|
||||
* (optional) The failure status. Should be one of the
|
||||
* MigrateIdMapInterface::STATUS_* constants. Defaults to
|
||||
* MigrateIdMapInterface::STATUS_FAILED.
|
||||
*/
|
||||
protected function mockFailure($migration, array $row, $status = MigrateIdMapInterface::STATUS_FAILED) {
|
||||
if (is_string($migration)) {
|
||||
$migration = $this->getMigration($migration);
|
||||
}
|
||||
/** @var \Drupal\migrate\Plugin\MigrationInterface $migration */
|
||||
$destination = array_map(function () {
|
||||
return NULL;
|
||||
}, $migration->getDestinationPlugin()->getIds());
|
||||
$row = new Row($row, $migration->getSourcePlugin()->getIds());
|
||||
$migration->getIdMap()->saveIdMapping($row, $destination, $status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the migration plugin.
|
||||
*
|
||||
* @param $plugin_id
|
||||
* The plugin ID of the migration to get.
|
||||
*
|
||||
* @return \Drupal\migrate\Plugin\Migration
|
||||
* The migration plugin.
|
||||
*/
|
||||
protected function getMigration($plugin_id) {
|
||||
return $this->container->get('plugin.manager.migration')->createInstance($plugin_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Injects the test logger into the container.
|
||||
*/
|
||||
protected function setTestLogger() {
|
||||
$this->logger = $this->prophesize(LoggerChannelInterface::class);
|
||||
$this->container->set('logger.channel.migrate', $this->logger->reveal());
|
||||
\Drupal::setContainer($this->container);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests the migration plugin.
|
||||
*
|
||||
* @group migrate
|
||||
*
|
||||
* @coversDefaultClass \Drupal\migrate\Plugin\Migration
|
||||
*/
|
||||
class MigrationTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Enable field because we are using one of its source plugins.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['migrate', 'field'];
|
||||
|
||||
/**
|
||||
* Tests Migration::set().
|
||||
*
|
||||
* @covers ::set
|
||||
*/
|
||||
public function testSetInvalidation() {
|
||||
$migration = \Drupal::service('plugin.manager.migration')->createStubMigration([
|
||||
'source' => ['plugin' => 'empty'],
|
||||
'destination' => ['plugin' => 'entity:entity_view_mode'],
|
||||
]);
|
||||
$this->assertEqual('empty', $migration->getSourcePlugin()->getPluginId());
|
||||
$this->assertEqual('entity:entity_view_mode', $migration->getDestinationPlugin()->getPluginId());
|
||||
|
||||
// Test the source plugin is invalidated.
|
||||
$migration->set('source', ['plugin' => 'embedded_data', 'data_rows' => [], 'ids' => []]);
|
||||
$this->assertEqual('embedded_data', $migration->getSourcePlugin()->getPluginId());
|
||||
|
||||
// Test the destination plugin is invalidated.
|
||||
$migration->set('destination', ['plugin' => 'null']);
|
||||
$this->assertEqual('null', $migration->getDestinationPlugin()->getPluginId());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel;
|
||||
|
||||
/**
|
||||
* @deprecated in drupal:8.7.0 and is removed from drupal:9.0.0. Use
|
||||
* \Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase::migrateFields()
|
||||
* instead.
|
||||
*/
|
||||
|
||||
use Drupal\comment\Entity\CommentType;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
|
||||
/**
|
||||
* Provides methods for testing node and comment combinations.
|
||||
*/
|
||||
trait NodeCommentCombinationTrait {
|
||||
|
||||
/**
|
||||
* Creates a node type with a corresponding comment type.
|
||||
*
|
||||
* @param string $node_type
|
||||
* The node type ID.
|
||||
* @param string $comment_type
|
||||
* (optional) The comment type ID, if not provided defaults to
|
||||
* comment_node_{type}.
|
||||
*/
|
||||
protected function createNodeCommentCombination($node_type, $comment_type = NULL) {
|
||||
@trigger_error('NodeCommentCombinationTrait::createNodeCommentCombination() is deprecated in Drupal 8.7.x, will be removed before Drupal 9.0.0. Use \Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase::migrateFields() instead.', E_USER_DEPRECATED);
|
||||
|
||||
if (!$comment_type) {
|
||||
$comment_type = "comment_node_$node_type";
|
||||
}
|
||||
NodeType::create([
|
||||
'type' => $node_type,
|
||||
'label' => $this->randomString(),
|
||||
])->save();
|
||||
|
||||
CommentType::create([
|
||||
'id' => $comment_type,
|
||||
'label' => $this->randomString(),
|
||||
'target_entity_type_id' => 'node',
|
||||
])->save();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel\Plugin;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\migrate\MigrateExecutableInterface;
|
||||
use Drupal\migrate\Row;
|
||||
use Drupal\user\Entity\User;
|
||||
|
||||
/**
|
||||
* Tests the EntityExists process plugin.
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class EntityExistsTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['migrate', 'system', 'user'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installSchema('system', ['sequences']);
|
||||
$this->installEntitySchema('user');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the EntityExists plugin.
|
||||
*/
|
||||
public function testEntityExists() {
|
||||
$user = User::create([
|
||||
'name' => $this->randomString(),
|
||||
]);
|
||||
$user->save();
|
||||
$uid = $user->id();
|
||||
|
||||
$plugin = \Drupal::service('plugin.manager.migrate.process')
|
||||
->createInstance('entity_exists', [
|
||||
'entity_type' => 'user',
|
||||
]);
|
||||
$executable = $this->prophesize(MigrateExecutableInterface::class)->reveal();
|
||||
$row = new Row();
|
||||
|
||||
// Ensure that the entity ID is returned if it really exists.
|
||||
$value = $plugin->transform($uid, $executable, $row, 'buffalo');
|
||||
$this->assertSame($uid, $value);
|
||||
|
||||
// Ensure that the plugin returns FALSE if the entity doesn't exist.
|
||||
$value = $plugin->transform(420, $executable, $row, 'buffalo');
|
||||
$this->assertFalse($value);
|
||||
|
||||
// Make sure the plugin can gracefully handle an array as input.
|
||||
$value = $plugin->transform([$uid, 420], $executable, $row, 'buffalo');
|
||||
$this->assertSame($uid, $value);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel\Plugin;
|
||||
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\Tests\migrate\Kernel\MigrateTestBase;
|
||||
use Drupal\Tests\node\Traits\ContentTypeCreationTrait;
|
||||
|
||||
/**
|
||||
* Tests the EntityRevision destination plugin.
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class EntityRevisionTest extends MigrateTestBase {
|
||||
|
||||
use ContentTypeCreationTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'content_translation',
|
||||
'field',
|
||||
'filter',
|
||||
'language',
|
||||
'node',
|
||||
'system',
|
||||
'text',
|
||||
'user',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installEntitySchema('node');
|
||||
$this->installEntitySchema('user');
|
||||
$this->installConfig('node');
|
||||
$this->installSchema('node', ['node_access']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that EntityRevision correctly handles revision translations.
|
||||
*/
|
||||
public function testRevisionTranslation() {
|
||||
ConfigurableLanguage::createFromLangcode('fr')->save();
|
||||
|
||||
/** @var \Drupal\node\NodeInterface $node */
|
||||
$node = Node::create([
|
||||
'type' => $this->createContentType()->id(),
|
||||
'title' => 'Default 1',
|
||||
]);
|
||||
$node->addTranslation('fr', [
|
||||
'title' => 'French 1',
|
||||
]);
|
||||
$node->save();
|
||||
$node->setNewRevision();
|
||||
$node->setTitle('Default 2');
|
||||
$node->getTranslation('fr')->setTitle('French 2');
|
||||
$node->save();
|
||||
|
||||
$migration = [
|
||||
'source' => [
|
||||
'plugin' => 'embedded_data',
|
||||
'data_rows' => [
|
||||
[
|
||||
'nid' => $node->id(),
|
||||
'vid' => $node->getRevisionId(),
|
||||
'langcode' => 'fr',
|
||||
'title' => 'Titre nouveau, tabarnak!',
|
||||
],
|
||||
],
|
||||
'ids' => [
|
||||
'nid' => [
|
||||
'type' => 'integer',
|
||||
],
|
||||
'vid' => [
|
||||
'type' => 'integer',
|
||||
],
|
||||
'langcode' => [
|
||||
'type' => 'string',
|
||||
],
|
||||
],
|
||||
],
|
||||
'process' => [
|
||||
'nid' => 'nid',
|
||||
'vid' => 'vid',
|
||||
'langcode' => 'langcode',
|
||||
'title' => 'title',
|
||||
],
|
||||
'destination' => [
|
||||
'plugin' => 'entity_revision:node',
|
||||
'translations' => TRUE,
|
||||
],
|
||||
];
|
||||
|
||||
/** @var \Drupal\migrate\Plugin\MigrationInterface $migration */
|
||||
$migration = $this->container
|
||||
->get('plugin.manager.migration')
|
||||
->createStubMigration($migration);
|
||||
|
||||
$this->executeMigration($migration);
|
||||
|
||||
// The entity_revision destination uses the revision ID and langcode as its
|
||||
// keys (the langcode is only used if the destination is configured for
|
||||
// translation), so we should be able to look up the source IDs by revision
|
||||
// ID and langcode.
|
||||
$source_ids = $migration->getIdMap()->lookupSourceID([
|
||||
'vid' => $node->getRevisionId(),
|
||||
'langcode' => 'fr',
|
||||
]);
|
||||
$this->assertNotEmpty($source_ids);
|
||||
$this->assertSame($node->id(), $source_ids['nid']);
|
||||
$this->assertSame($node->getRevisionId(), $source_ids['vid']);
|
||||
$this->assertSame('fr', $source_ids['langcode']);
|
||||
|
||||
// Confirm the french revision was used in the migration, instead of the
|
||||
// default revision.
|
||||
/** @var \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager */
|
||||
$entity_type_manager = \Drupal::entityTypeManager();
|
||||
$revision = $entity_type_manager->getStorage('node')->loadRevision(1);
|
||||
$this->assertSame('Default 1', $revision->label());
|
||||
$this->assertSame('French 1', $revision->getTranslation('fr')->label());
|
||||
$revision = $entity_type_manager->getStorage('node')->loadRevision(2);
|
||||
$this->assertSame('Default 2', $revision->label());
|
||||
$this->assertSame('Titre nouveau, tabarnak!', $revision->getTranslation('fr')->label());
|
||||
}
|
||||
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel\Plugin;
|
||||
|
||||
/**
|
||||
* Extends MigrationProvidersExistTest to test with deprecated modules.
|
||||
*
|
||||
* @see \Drupal\Tests\DeprecatedModulesTestTrait::removeDeprecatedModules()
|
||||
*
|
||||
* @group migrate_drupal_ui
|
||||
* @group legacy
|
||||
*/
|
||||
class LegacyMigrationProvidersExistTest extends MigrationProvidersExistTest {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $excludeDeprecated = FALSE;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel\Plugin;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\migrate\MigrateExecutableInterface;
|
||||
use Drupal\migrate\Row;
|
||||
|
||||
/**
|
||||
* Tests the Log process plugin.
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class LogTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['migrate'];
|
||||
|
||||
/**
|
||||
* Test the Log plugin.
|
||||
*/
|
||||
public function testLog() {
|
||||
$plugin = \Drupal::service('plugin.manager.migrate.process')
|
||||
->createInstance('log');
|
||||
$executable = $this->prophesize(MigrateExecutableInterface::class)->reveal();
|
||||
$row = new Row();
|
||||
$log_message = "Testing the log message";
|
||||
|
||||
// Ensure the log is getting saved.
|
||||
$saved_message = $plugin->transform($log_message, $executable, $row, 'buffalo');
|
||||
$this->assertSame($log_message, $saved_message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel\Plugin;
|
||||
|
||||
use Drupal\Tests\migrate_drupal\Kernel\MigrateDrupalTestBase;
|
||||
|
||||
/**
|
||||
* Tests that migrations exist in the migration_templates directory.
|
||||
*
|
||||
* @group migrate
|
||||
* @group legacy
|
||||
*/
|
||||
class MigrationDirectoryTest extends MigrateDrupalTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['migration_directory_test'];
|
||||
|
||||
/**
|
||||
* Tests that migrations in the migration_templates directory are created.
|
||||
*
|
||||
* @expectedDeprecation Use of the /migration_templates directory to store migration configuration files is deprecated in Drupal 8.1.0 and will be removed before Drupal 9.0.0. See https://www.drupal.org/node/2920988.
|
||||
*/
|
||||
public function testMigrationDirectory() {
|
||||
/** @var \Drupal\migrate\Plugin\MigrationPluginManager $plugin_manager */
|
||||
$plugin_manager = $this->container->get('plugin.manager.migration');
|
||||
// Tests that a migration in directory 'migration_templates' is discovered.
|
||||
$this->assertTrue($plugin_manager->hasDefinition('migration_templates_test'));
|
||||
}
|
||||
|
||||
}
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel\Plugin;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests the migration plugin manager.
|
||||
*
|
||||
* @coversDefaultClass \Drupal\migrate\Plugin\MigratePluginManager
|
||||
* @group migrate
|
||||
*/
|
||||
class MigrationPluginConfigurationTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'migrate',
|
||||
'migrate_drupal',
|
||||
// Test with a simple migration.
|
||||
'ban',
|
||||
'locale',
|
||||
];
|
||||
|
||||
/**
|
||||
* Test merging configuration into a plugin through the plugin manager.
|
||||
*
|
||||
* @dataProvider mergeProvider
|
||||
*/
|
||||
public function testConfigurationMerge($id, $configuration, $expected) {
|
||||
/** @var \Drupal\migrate\Plugin\MigrationInterface $migration */
|
||||
$migration = $this->container->get('plugin.manager.migration')
|
||||
->createInstance($id, $configuration);
|
||||
$source_configuration = $migration->getSourceConfiguration();
|
||||
$this->assertEquals($expected, $source_configuration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide configuration data for testing.
|
||||
*/
|
||||
public function mergeProvider() {
|
||||
return [
|
||||
// Tests adding new configuration to a migration.
|
||||
[
|
||||
// New configuration.
|
||||
'd7_blocked_ips',
|
||||
[
|
||||
'source' => [
|
||||
'constants' => [
|
||||
'added_setting' => 'Ban them all!',
|
||||
],
|
||||
],
|
||||
],
|
||||
// Expected final source configuration.
|
||||
[
|
||||
'plugin' => 'd7_blocked_ips',
|
||||
'constants' => [
|
||||
'added_setting' => 'Ban them all!',
|
||||
],
|
||||
],
|
||||
],
|
||||
// Tests overriding pre-existing configuration in a migration.
|
||||
[
|
||||
// New configuration.
|
||||
'd7_blocked_ips',
|
||||
[
|
||||
'source' => [
|
||||
'plugin' => 'a_different_plugin',
|
||||
],
|
||||
],
|
||||
// Expected final source configuration.
|
||||
[
|
||||
'plugin' => 'a_different_plugin',
|
||||
],
|
||||
],
|
||||
// New configuration.
|
||||
[
|
||||
'locale_settings',
|
||||
[
|
||||
'source' => [
|
||||
'plugin' => 'variable',
|
||||
'variables' => [
|
||||
'locale_cache_strings',
|
||||
'locale_js_directory',
|
||||
],
|
||||
'source_module' => 'locale',
|
||||
],
|
||||
],
|
||||
// Expected final source and process configuration.
|
||||
[
|
||||
'plugin' => 'variable',
|
||||
'variables' => [
|
||||
'locale_cache_strings',
|
||||
'locale_js_directory',
|
||||
],
|
||||
'source_module' => 'locale',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel\Plugin;
|
||||
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\migrate\Exception\RequirementsException;
|
||||
use Drupal\migrate\Plugin\migrate\source\SqlBase;
|
||||
use Drupal\migrate\Plugin\RequirementsInterface;
|
||||
use Drupal\Tests\field\Traits\EntityReferenceTestTrait;
|
||||
|
||||
/**
|
||||
* Tests the migration plugin manager.
|
||||
*
|
||||
* @coversDefaultClass \Drupal\migrate\Plugin\MigratePluginManager
|
||||
* @group migrate
|
||||
*/
|
||||
class MigrationPluginListTest extends KernelTestBase {
|
||||
|
||||
use EntityReferenceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'migrate',
|
||||
// Test with all modules containing Drupal migrations.
|
||||
'action',
|
||||
'aggregator',
|
||||
'ban',
|
||||
'block',
|
||||
'block_content',
|
||||
'book',
|
||||
'comment',
|
||||
'contact',
|
||||
'content_translation',
|
||||
'dblog',
|
||||
'field',
|
||||
'file',
|
||||
'filter',
|
||||
'forum',
|
||||
'image',
|
||||
'language',
|
||||
'locale',
|
||||
'menu_link_content',
|
||||
'menu_ui',
|
||||
'node',
|
||||
'options',
|
||||
'path',
|
||||
'search',
|
||||
'shortcut',
|
||||
'statistics',
|
||||
'syslog',
|
||||
'system',
|
||||
'taxonomy',
|
||||
'text',
|
||||
'tracker',
|
||||
'update',
|
||||
'user',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('user');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getDefinitions
|
||||
*/
|
||||
public function testGetDefinitions() {
|
||||
// Create an entity reference field to make sure that migrations derived by
|
||||
// EntityReferenceTranslationDeriver do not get discovered without
|
||||
// migrate_drupal enabled.
|
||||
$this->createEntityReferenceField('user', 'user', 'field_entity_reference', 'Entity Reference', 'node');
|
||||
|
||||
// Make sure retrieving all the core migration plugins does not throw any
|
||||
// errors.
|
||||
$migration_plugins = $this->container->get('plugin.manager.migration')->getDefinitions();
|
||||
// All the plugins provided by core depend on migrate_drupal.
|
||||
$this->assertEmpty($migration_plugins);
|
||||
|
||||
// Enable a module that provides migrations that do not depend on
|
||||
// migrate_drupal.
|
||||
$this->enableModules(['migrate_external_translated_test']);
|
||||
$migration_plugins = $this->container->get('plugin.manager.migration')->getDefinitions();
|
||||
// All the plugins provided by migrate_external_translated_test do not
|
||||
// depend on migrate_drupal.
|
||||
$this::assertArrayHasKey('external_translated_test_node', $migration_plugins);
|
||||
$this::assertArrayHasKey('external_translated_test_node_translation', $migration_plugins);
|
||||
|
||||
// Disable the test module and the list should be empty again.
|
||||
$this->disableModules(['migrate_external_translated_test']);
|
||||
$migration_plugins = $this->container->get('plugin.manager.migration')->getDefinitions();
|
||||
// All the plugins provided by core depend on migrate_drupal.
|
||||
$this->assertEmpty($migration_plugins);
|
||||
|
||||
// Enable migrate_drupal to test that the plugins can now be discovered.
|
||||
$this->enableModules(['migrate_drupal']);
|
||||
|
||||
// Make sure retrieving these migration plugins in the absence of a database
|
||||
// connection does not throw any errors.
|
||||
$migration_plugins = $this->container->get('plugin.manager.migration')->createInstances([]);
|
||||
// Any database-based source plugins should fail a requirements test in the
|
||||
// absence of a source database connection (e.g., a connection with the
|
||||
// 'migrate' key).
|
||||
$source_plugins = array_map(function ($migration_plugin) {
|
||||
return $migration_plugin->getSourcePlugin();
|
||||
}, $migration_plugins);
|
||||
foreach ($source_plugins as $id => $source_plugin) {
|
||||
if ($source_plugin instanceof RequirementsInterface) {
|
||||
try {
|
||||
$source_plugin->checkRequirements();
|
||||
}
|
||||
catch (RequirementsException $e) {
|
||||
unset($source_plugins[$id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Without a connection defined, no database-based plugins should be
|
||||
// returned.
|
||||
foreach ($source_plugins as $id => $source_plugin) {
|
||||
$this->assertNotInstanceOf(SqlBase::class, $source_plugin);
|
||||
}
|
||||
|
||||
// Set up a migrate database connection so that plugin discovery works.
|
||||
// Clone the current connection and replace the current prefix.
|
||||
$connection_info = Database::getConnectionInfo('migrate');
|
||||
if ($connection_info) {
|
||||
Database::renameConnection('migrate', 'simpletest_original_migrate');
|
||||
}
|
||||
$connection_info = Database::getConnectionInfo('default');
|
||||
foreach ($connection_info as $target => $value) {
|
||||
$prefix = is_array($value['prefix']) ? $value['prefix']['default'] : $value['prefix'];
|
||||
// Simpletest uses 7 character prefixes at most so this can't cause
|
||||
// collisions.
|
||||
$connection_info[$target]['prefix']['default'] = $prefix . '0';
|
||||
|
||||
// Add the original simpletest prefix so SQLite can attach its database.
|
||||
// @see \Drupal\Core\Database\Driver\sqlite\Connection::init()
|
||||
$connection_info[$target]['prefix'][$value['prefix']['default']] = $value['prefix']['default'];
|
||||
}
|
||||
Database::addConnectionInfo('migrate', 'default', $connection_info['default']);
|
||||
|
||||
$migration_plugins = $this->container->get('plugin.manager.migration')->getDefinitions();
|
||||
// All the plugins provided by core depend on migrate_drupal.
|
||||
$this->assertNotEmpty($migration_plugins);
|
||||
|
||||
// Test that migrations derived by EntityReferenceTranslationDeriver are
|
||||
// discovered now that migrate_drupal is enabled.
|
||||
$this->assertArrayHasKey('d6_entity_reference_translation:user__user', $migration_plugins);
|
||||
$this->assertArrayHasKey('d7_entity_reference_translation:user__user', $migration_plugins);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel\Plugin;
|
||||
|
||||
use Drupal\KernelTests\FileSystemModuleDiscoveryDataProviderTrait;
|
||||
use Drupal\migrate\Plugin\Exception\BadPluginDefinitionException;
|
||||
use Drupal\migrate_drupal\Plugin\MigrateFieldPluginManager;
|
||||
use Drupal\Tests\DeprecatedModulesTestTrait;
|
||||
use Drupal\Tests\migrate_drupal\Kernel\MigrateDrupalTestBase;
|
||||
|
||||
/**
|
||||
* Tests that modules exist for all source and destination plugins.
|
||||
*
|
||||
* @group migrate_drupal_ui
|
||||
*/
|
||||
class MigrationProvidersExistTest extends MigrateDrupalTestBase {
|
||||
|
||||
use DeprecatedModulesTestTrait;
|
||||
use FileSystemModuleDiscoveryDataProviderTrait;
|
||||
|
||||
/**
|
||||
* Tests that a missing source_module property raises an exception.
|
||||
*/
|
||||
public function testSourceProvider() {
|
||||
$this->enableModules(['migration_provider_test']);
|
||||
$this->expectException(BadPluginDefinitionException::class);
|
||||
$this->expectExceptionMessage('The no_source_module plugin must define the source_module property.');
|
||||
$this->container->get('plugin.manager.migration')->getDefinition('migration_provider_no_annotation');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that modules exist for all source plugins.
|
||||
*/
|
||||
public function testProvidersExist() {
|
||||
$this->enableAllModules();
|
||||
|
||||
/** @var \Drupal\migrate\Plugin\MigrateSourcePluginManager $plugin_manager */
|
||||
$plugin_manager = $this->container->get('plugin.manager.migrate.source');
|
||||
|
||||
foreach ($plugin_manager->getDefinitions() as $definition) {
|
||||
$id = $definition['id'];
|
||||
$this->assertArrayHasKey('source_module', $definition, "No source_module property in '$id'");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable all available modules.
|
||||
*/
|
||||
protected function enableAllModules() {
|
||||
// Install all available modules.
|
||||
$module_handler = $this->container->get('module_handler');
|
||||
$modules = $this->coreModuleListDataProvider();
|
||||
$modules_enabled = $module_handler->getModuleList();
|
||||
$modules_to_enable = array_keys(array_diff_key($modules, $modules_enabled));
|
||||
$this->enableModules($modules_to_enable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that modules exist for all field plugins.
|
||||
*/
|
||||
public function testFieldProvidersExist() {
|
||||
$expected_mappings = [
|
||||
'userreference' => [
|
||||
'source_module' => 'userreference',
|
||||
'destination_module' => 'core',
|
||||
],
|
||||
'nodereference' => [
|
||||
'source_module' => 'nodereference',
|
||||
'destination_module' => 'core',
|
||||
],
|
||||
'optionwidgets' => [
|
||||
'source_module' => 'optionwidgets',
|
||||
'destination_module' => 'options',
|
||||
],
|
||||
'list' => [
|
||||
'source_module' => 'list',
|
||||
'destination_module' => 'options',
|
||||
],
|
||||
'options' => [
|
||||
'source_module' => 'options',
|
||||
'destination_module' => 'options',
|
||||
],
|
||||
'filefield' => [
|
||||
'source_module' => 'filefield',
|
||||
'destination_module' => 'file',
|
||||
],
|
||||
'imagefield' => [
|
||||
'source_module' => 'imagefield',
|
||||
'destination_module' => 'image',
|
||||
],
|
||||
'file' => [
|
||||
'source_module' => 'file',
|
||||
'destination_module' => 'file',
|
||||
],
|
||||
'image' => [
|
||||
'source_module' => 'image',
|
||||
'destination_module' => 'image',
|
||||
],
|
||||
'phone' => [
|
||||
'source_module' => 'phone',
|
||||
'destination_module' => 'telephone',
|
||||
],
|
||||
'link' => [
|
||||
'source_module' => 'link',
|
||||
'destination_module' => 'link',
|
||||
],
|
||||
'link_field' => [
|
||||
'source_module' => 'link',
|
||||
'destination_module' => 'link',
|
||||
],
|
||||
'd6_text' => [
|
||||
'source_module' => 'text',
|
||||
'destination_module' => 'text',
|
||||
],
|
||||
'd7_text' => [
|
||||
'source_module' => 'text',
|
||||
'destination_module' => 'text',
|
||||
],
|
||||
'taxonomy_term_reference' => [
|
||||
'source_module' => 'taxonomy',
|
||||
'destination_module' => 'core',
|
||||
],
|
||||
'date' => [
|
||||
'source_module' => 'date',
|
||||
'destination_module' => 'datetime',
|
||||
],
|
||||
'datetime' => [
|
||||
'source_module' => 'date',
|
||||
'destination_module' => 'datetime',
|
||||
],
|
||||
'email' => [
|
||||
'source_module' => 'email',
|
||||
'destination_module' => 'core',
|
||||
],
|
||||
'number_default' => [
|
||||
'source_module' => 'number',
|
||||
'destination_module' => 'core',
|
||||
],
|
||||
'entityreference' => [
|
||||
'source_module' => 'entityreference',
|
||||
'destination_module' => 'core',
|
||||
],
|
||||
];
|
||||
$this->enableAllModules();
|
||||
|
||||
$definitions = $this->container->get('plugin.manager.migrate.field')->getDefinitions();
|
||||
foreach ($definitions as $key => $definition) {
|
||||
$this->assertArrayHasKey($key, $expected_mappings);
|
||||
$this->assertEquals($expected_mappings[$key]['source_module'], $definition['source_module']);
|
||||
$this->assertEquals($expected_mappings[$key]['destination_module'], $definition['destination_module']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a missing required definition.
|
||||
*
|
||||
* @param array $definitions
|
||||
* A field plugin definition.
|
||||
* @param string $missing_property
|
||||
* The name of the property missing from the definition.
|
||||
*
|
||||
* @dataProvider fieldPluginDefinitionsProvider
|
||||
*/
|
||||
public function testFieldProviderMissingRequiredProperty(array $definitions, $missing_property) {
|
||||
$discovery = $this->getMockBuilder(MigrateFieldPluginManager::class)
|
||||
->disableOriginalConstructor()
|
||||
->setMethods(['getDefinitions'])
|
||||
->getMock();
|
||||
$discovery->method('getDefinitions')
|
||||
->willReturn($definitions);
|
||||
|
||||
$plugin_manager = $this->getMockBuilder(MigrateFieldPluginManager::class)
|
||||
->disableOriginalConstructor()
|
||||
->setMethods(['getDiscovery'])
|
||||
->getMock();
|
||||
$plugin_manager->method('getDiscovery')
|
||||
->willReturn($discovery);
|
||||
|
||||
$this->expectException(BadPluginDefinitionException::class);
|
||||
$this->expectExceptionMessage("The missing_{$missing_property} plugin must define the $missing_property property.");
|
||||
$plugin_manager->getDefinitions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for field plugin definitions.
|
||||
*
|
||||
* @return array
|
||||
* Array of plugin definitions.
|
||||
*/
|
||||
public function fieldPluginDefinitionsProvider() {
|
||||
return [
|
||||
'missing_core_scenario' => [
|
||||
'definitions' => [
|
||||
'missing_core' => [
|
||||
'source_module' => 'migrate',
|
||||
'destination_module' => 'migrate',
|
||||
'id' => 'missing_core',
|
||||
'class' => 'foo',
|
||||
'provider' => 'foo',
|
||||
],
|
||||
],
|
||||
'missing_property' => 'core',
|
||||
],
|
||||
'missing_source_scenario' => [
|
||||
'definitions' => [
|
||||
'missing_source_module' => [
|
||||
'core' => [6, 7],
|
||||
'destination_module' => 'migrate',
|
||||
'id' => 'missing_source_module',
|
||||
'class' => 'foo',
|
||||
'provider' => 'foo',
|
||||
],
|
||||
],
|
||||
'missing_property' => 'source_module',
|
||||
],
|
||||
'missing_destination_scenario' => [
|
||||
'definitions' => [
|
||||
'missing_destination_module' => [
|
||||
'core' => [6, 7],
|
||||
'source_module' => 'migrate',
|
||||
'id' => 'missing_destination_module',
|
||||
'class' => 'foo',
|
||||
'provider' => 'foo',
|
||||
],
|
||||
],
|
||||
'missing_property' => 'destination_module',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel\Plugin;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\migrate\MigrateException;
|
||||
use Drupal\migrate\MigrateSkipRowException;
|
||||
|
||||
/**
|
||||
* Tests the migration plugin.
|
||||
*
|
||||
* @coversDefaultClass \Drupal\migrate\Plugin\Migration
|
||||
* @group migrate
|
||||
*/
|
||||
class MigrationTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['migrate'];
|
||||
|
||||
/**
|
||||
* Tests Migration::getProcessPlugins()
|
||||
*
|
||||
* @covers ::getProcessPlugins
|
||||
*/
|
||||
public function testGetProcessPlugins() {
|
||||
$migration = \Drupal::service('plugin.manager.migration')->createStubMigration([]);
|
||||
$this->assertEquals([], $migration->getProcessPlugins([]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests Migration::getProcessPlugins() throws an exception.
|
||||
*
|
||||
* @covers ::getProcessPlugins
|
||||
*/
|
||||
public function testGetProcessPluginsException() {
|
||||
$migration = \Drupal::service('plugin.manager.migration')->createStubMigration([]);
|
||||
$this->expectException(MigrateException::class);
|
||||
$this->expectExceptionMessage('Invalid process configuration for foobar');
|
||||
$migration->getProcessPlugins(['foobar' => ['plugin' => 'get']]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests Migration::getProcessPlugins()
|
||||
*
|
||||
* @param array $process
|
||||
* The migration process pipeline.
|
||||
*
|
||||
* @covers ::getProcessPlugins
|
||||
*
|
||||
* @dataProvider getProcessPluginsExceptionMessageProvider
|
||||
*/
|
||||
public function testGetProcessPluginsExceptionMessage(array $process) {
|
||||
// Test with an invalid process pipeline.
|
||||
$plugin_definition = [
|
||||
'id' => 'foo',
|
||||
'process' => $process,
|
||||
];
|
||||
|
||||
reset($process);
|
||||
$destination = key(($process));
|
||||
|
||||
$migration = \Drupal::service('plugin.manager.migration')
|
||||
->createStubMigration($plugin_definition);
|
||||
$this->expectException(MigrateException::class);
|
||||
$this->expectExceptionMessage("Invalid process for destination '$destination' in migration 'foo'");
|
||||
$migration->getProcessPlugins();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides data for testing invalid process pipeline.
|
||||
*/
|
||||
public function getProcessPluginsExceptionMessageProvider() {
|
||||
return [
|
||||
[
|
||||
'Null' =>
|
||||
[
|
||||
'dest' => NULL,
|
||||
],
|
||||
],
|
||||
[
|
||||
'boolean' =>
|
||||
[
|
||||
'dest' => TRUE,
|
||||
],
|
||||
],
|
||||
[
|
||||
'integer' =>
|
||||
[
|
||||
'dest' => 2370,
|
||||
],
|
||||
],
|
||||
[
|
||||
'float' =>
|
||||
[
|
||||
'dest' => 1.61,
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests Migration::getMigrationDependencies()
|
||||
*
|
||||
* @covers ::getMigrationDependencies
|
||||
*/
|
||||
public function testGetMigrationDependencies() {
|
||||
$plugin_manager = \Drupal::service('plugin.manager.migration');
|
||||
$plugin_definition = [
|
||||
'process' => [
|
||||
'f1' => 'bar',
|
||||
'f2' => [
|
||||
'plugin' => 'migration',
|
||||
'migration' => 'm1',
|
||||
],
|
||||
'f3' => [
|
||||
'plugin' => 'sub_process',
|
||||
'process' => [
|
||||
'target_id' => [
|
||||
'plugin' => 'migration',
|
||||
'migration' => 'm2',
|
||||
],
|
||||
],
|
||||
],
|
||||
'f4' => [
|
||||
'plugin' => 'migration_lookup',
|
||||
'migration' => 'm3',
|
||||
],
|
||||
'f5' => [
|
||||
'plugin' => 'sub_process',
|
||||
'process' => [
|
||||
'target_id' => [
|
||||
'plugin' => 'migration_lookup',
|
||||
'migration' => 'm4',
|
||||
],
|
||||
],
|
||||
],
|
||||
'f6' => [
|
||||
'plugin' => 'iterator',
|
||||
'process' => [
|
||||
'target_id' => [
|
||||
'plugin' => 'migration_lookup',
|
||||
'migration' => 'm5',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
$migration = $plugin_manager->createStubMigration($plugin_definition);
|
||||
$this->assertSame(['required' => [], 'optional' => ['m1', 'm2', 'm3', 'm4', 'm5']], $migration->getMigrationDependencies());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests Migration::getDestinationIds()
|
||||
*
|
||||
* @covers ::getDestinationIds
|
||||
*/
|
||||
public function testGetDestinationIds() {
|
||||
$migration = \Drupal::service('plugin.manager.migration')->createStubMigration(['destinationIds' => ['foo' => 'bar']]);
|
||||
$destination_ids = $migration->getDestinationIds();
|
||||
$this->assertNotEmpty($destination_ids, 'Destination ids are not empty');
|
||||
$this->assertEquals(['foo' => 'bar'], $destination_ids, 'Destination ids match the expected values.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests Migration::getTrackLastImported()
|
||||
*
|
||||
* @covers ::getTrackLastImported
|
||||
* @covers ::isTrackLastImported
|
||||
*/
|
||||
public function testGetTrackLastImported() {
|
||||
$migration = \Drupal::service('plugin.manager.migration')->createStubMigration([]);
|
||||
$migration->setTrackLastImported(TRUE);
|
||||
$this->assertEquals(TRUE, $migration->getTrackLastImported());
|
||||
$this->assertEquals(TRUE, $migration->isTrackLastImported());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests Migration::getDestinationPlugin()
|
||||
*
|
||||
* @covers ::getDestinationPlugin
|
||||
*/
|
||||
public function testGetDestinationPlugin() {
|
||||
$migration = \Drupal::service('plugin.manager.migration')->createStubMigration(['destination' => ['no_stub' => TRUE]]);
|
||||
$this->expectException(MigrateSkipRowException::class);
|
||||
$this->expectExceptionMessage("Stub requested but not made because no_stub configuration is set.");
|
||||
$migration->getDestinationPlugin(TRUE);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\migrate\MigrateException;
|
||||
use Drupal\migrate\Plugin\MigrateIdMapInterface;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\Core\Database\Driver\sqlite\Connection;
|
||||
|
||||
/**
|
||||
* Tests query batching.
|
||||
*
|
||||
* @covers \Drupal\migrate_query_batch_test\Plugin\migrate\source\QueryBatchTest
|
||||
* @group migrate
|
||||
*/
|
||||
class QueryBatchTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* The mocked migration.
|
||||
*
|
||||
* @var \Drupal\migrate\Plugin\MigrationInterface|\Prophecy\Prophecy\ObjectProphecy
|
||||
*/
|
||||
protected $migration;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'migrate',
|
||||
'migrate_query_batch_test',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create a mock migration. This will be injected into the source plugin
|
||||
// under test.
|
||||
$this->migration = $this->prophesize(MigrationInterface::class);
|
||||
|
||||
$this->migration->id()->willReturn(
|
||||
$this->randomMachineName(16)
|
||||
);
|
||||
// Prophesize a useless ID map plugin and an empty set of destination IDs.
|
||||
// Calling code can override these prophecies later and set up different
|
||||
// behaviors.
|
||||
$this->migration->getIdMap()->willReturn(
|
||||
$this->prophesize(MigrateIdMapInterface::class)->reveal()
|
||||
);
|
||||
$this->migration->getDestinationIds()->willReturn([]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a negative batch size throws an exception.
|
||||
*/
|
||||
public function testBatchSizeNegative() {
|
||||
$this->expectException(MigrateException::class);
|
||||
$this->expectExceptionMessage('batch_size must be greater than or equal to zero');
|
||||
$plugin = $this->getPlugin(['batch_size' => -1]);
|
||||
$plugin->next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a non integer batch size throws an exception.
|
||||
*/
|
||||
public function testBatchSizeNonInteger() {
|
||||
$this->expectException(MigrateException::class);
|
||||
$this->expectExceptionMessage('batch_size must be greater than or equal to zero');
|
||||
$plugin = $this->getPlugin(['batch_size' => '1']);
|
||||
$plugin->next();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function queryDataProvider() {
|
||||
// Define the parameters for building the data array. The first element is
|
||||
// the number of source data rows, the second is the batch size to set on
|
||||
// the plugin configuration.
|
||||
$test_parameters = [
|
||||
// Test when batch size is 0.
|
||||
[200, 0],
|
||||
// Test when rows mod batch size is 0.
|
||||
[200, 20],
|
||||
// Test when rows mod batch size is > 0.
|
||||
[200, 30],
|
||||
// Test when batch size = row count.
|
||||
[200, 200],
|
||||
// Test when batch size > row count.
|
||||
[200, 300],
|
||||
];
|
||||
|
||||
// Build the data provider array. The provider array consists of the source
|
||||
// data rows, the expected result data, the expected count, the plugin
|
||||
// configuration, the expected batch size and the expected batch count.
|
||||
$table = 'query_batch_test';
|
||||
$tests = [];
|
||||
$data_set = 0;
|
||||
foreach ($test_parameters as $data) {
|
||||
list($num_rows, $batch_size) = $data;
|
||||
for ($i = 0; $i < $num_rows; $i++) {
|
||||
$tests[$data_set]['source_data'][$table][] = [
|
||||
'id' => $i,
|
||||
'data' => $this->randomString(),
|
||||
];
|
||||
}
|
||||
$tests[$data_set]['expected_data'] = $tests[$data_set]['source_data'][$table];
|
||||
$tests[$data_set][2] = $num_rows;
|
||||
// Plugin configuration array.
|
||||
$tests[$data_set][3] = ['batch_size' => $batch_size];
|
||||
// Expected batch size.
|
||||
$tests[$data_set][4] = $batch_size;
|
||||
// Expected batch count is 0 unless a batch size is set.
|
||||
$expected_batch_count = 0;
|
||||
if ($batch_size > 0) {
|
||||
$expected_batch_count = (int) ($num_rows / $batch_size);
|
||||
if ($num_rows % $batch_size) {
|
||||
// If there is a remainder an extra batch is needed to get the
|
||||
// remaining rows.
|
||||
$expected_batch_count++;
|
||||
}
|
||||
}
|
||||
$tests[$data_set][5] = $expected_batch_count;
|
||||
$data_set++;
|
||||
}
|
||||
return $tests;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests query batch size.
|
||||
*
|
||||
* @param array $source_data
|
||||
* The source data, keyed by table name. Each table is an array containing
|
||||
* the rows in that table.
|
||||
* @param array $expected_data
|
||||
* The result rows the plugin is expected to return.
|
||||
* @param int $num_rows
|
||||
* How many rows the source plugin is expected to return.
|
||||
* @param array $configuration
|
||||
* Configuration for the source plugin specifying the batch size.
|
||||
* @param int $expected_batch_size
|
||||
* The expected batch size, will be set to zero for invalid batch sizes.
|
||||
* @param int $expected_batch_count
|
||||
* The total number of batches.
|
||||
*
|
||||
* @dataProvider queryDataProvider
|
||||
*/
|
||||
public function testQueryBatch($source_data, $expected_data, $num_rows, $configuration, $expected_batch_size, $expected_batch_count) {
|
||||
$plugin = $this->getPlugin($configuration);
|
||||
|
||||
// Since we don't yet inject the database connection, we need to use a
|
||||
// reflection hack to set it in the plugin instance.
|
||||
$reflector = new \ReflectionObject($plugin);
|
||||
$property = $reflector->getProperty('database');
|
||||
$property->setAccessible(TRUE);
|
||||
|
||||
$connection = $this->getDatabase($source_data);
|
||||
$property->setValue($plugin, $connection);
|
||||
|
||||
// Test the results.
|
||||
$i = 0;
|
||||
/** @var \Drupal\migrate\Row $row */
|
||||
foreach ($plugin as $row) {
|
||||
|
||||
$expected = $expected_data[$i++];
|
||||
$actual = $row->getSource();
|
||||
|
||||
foreach ($expected as $key => $value) {
|
||||
$this->assertArrayHasKey($key, $actual);
|
||||
$this->assertSame((string) $value, (string) $actual[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
// Test that all rows were retrieved.
|
||||
self::assertSame($num_rows, $i);
|
||||
|
||||
// Test the batch size.
|
||||
if (is_null($expected_batch_size)) {
|
||||
$expected_batch_size = $configuration['batch_size'];
|
||||
}
|
||||
$property = $reflector->getProperty('batchSize');
|
||||
$property->setAccessible(TRUE);
|
||||
self::assertSame($expected_batch_size, $property->getValue($plugin));
|
||||
|
||||
// Test the batch count.
|
||||
if (is_null($expected_batch_count)) {
|
||||
$expected_batch_count = intdiv($num_rows, $expected_batch_size);
|
||||
if ($num_rows % $configuration['batch_size']) {
|
||||
$expected_batch_count++;
|
||||
}
|
||||
}
|
||||
$property = $reflector->getProperty('batch');
|
||||
$property->setAccessible(TRUE);
|
||||
self::assertSame($expected_batch_count, $property->getValue($plugin));
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates the source plugin under test.
|
||||
*
|
||||
* @param array $configuration
|
||||
* The source plugin configuration.
|
||||
*
|
||||
* @return \Drupal\migrate\Plugin\MigrateSourceInterface|object
|
||||
* The fully configured source plugin.
|
||||
*/
|
||||
protected function getPlugin($configuration) {
|
||||
/** @var \Drupal\migrate\Plugin\MigratePluginManager $plugin_manager */
|
||||
$plugin_manager = $this->container->get('plugin.manager.migrate.source');
|
||||
$plugin = $plugin_manager->createInstance('query_batch_test', $configuration, $this->migration->reveal());
|
||||
|
||||
$this->migration
|
||||
->getSourcePlugin()
|
||||
->willReturn($plugin);
|
||||
return $plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an in-memory SQLite database from a set of source data.
|
||||
*
|
||||
* @param array $source_data
|
||||
* The source data, keyed by table name. Each table is an array containing
|
||||
* the rows in that table.
|
||||
*
|
||||
* @return \Drupal\Core\Database\Driver\sqlite\Connection
|
||||
* The SQLite database connection.
|
||||
*/
|
||||
protected function getDatabase(array $source_data) {
|
||||
// Create an in-memory SQLite database. Plugins can interact with it like
|
||||
// any other database, and it will cease to exist when the connection is
|
||||
// closed.
|
||||
$connection_options = ['database' => ':memory:'];
|
||||
$pdo = Connection::open($connection_options);
|
||||
$connection = new Connection($pdo, $connection_options);
|
||||
|
||||
// Create the tables and fill them with data.
|
||||
foreach ($source_data as $table => $rows) {
|
||||
// Use the biggest row to build the table schema.
|
||||
$counts = array_map('count', $rows);
|
||||
asort($counts);
|
||||
end($counts);
|
||||
$pilot = $rows[key($counts)];
|
||||
|
||||
$connection->schema()
|
||||
->createTable($table, [
|
||||
// SQLite uses loose affinity typing, so it's OK for every field to
|
||||
// be a text field.
|
||||
'fields' => array_map(function () {
|
||||
return ['type' => 'text'];
|
||||
}, $pilot),
|
||||
]);
|
||||
|
||||
$fields = array_keys($pilot);
|
||||
$insert = $connection->insert($table)->fields($fields);
|
||||
array_walk($rows, [$insert, 'values']);
|
||||
$insert->execute();
|
||||
}
|
||||
return $connection;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Tests\migrate\Kernel\SqlBaseTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel;
|
||||
|
||||
use Drupal\Core\Database\Query\ConditionInterface;
|
||||
use Drupal\Core\Database\Query\SelectInterface;
|
||||
use Drupal\Core\Database\StatementInterface;
|
||||
use Drupal\migrate\Exception\RequirementsException;
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\migrate\Plugin\migrate\source\SqlBase;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
|
||||
/**
|
||||
* Tests the functionality of SqlBase.
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class SqlBaseTest extends MigrateTestBase {
|
||||
|
||||
/**
|
||||
* The (probably mocked) migration under test.
|
||||
*
|
||||
* @var \Drupal\migrate\Plugin\MigrationInterface
|
||||
*/
|
||||
protected $migration;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->migration = $this->createMock(MigrationInterface::class);
|
||||
$this->migration->method('id')->willReturn('fubar');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests different connection types.
|
||||
*/
|
||||
public function testConnectionTypes() {
|
||||
$sql_base = new TestSqlBase([], $this->migration);
|
||||
|
||||
// Verify that falling back to the default 'migrate' connection (defined in
|
||||
// the base class) works.
|
||||
$this->assertSame('default', $sql_base->getDatabase()->getTarget());
|
||||
$this->assertSame('migrate', $sql_base->getDatabase()->getKey());
|
||||
|
||||
// Verify the fallback state key overrides the 'migrate' connection.
|
||||
$target = 'test_fallback_target';
|
||||
$key = 'test_fallback_key';
|
||||
$config = ['target' => $target, 'key' => $key];
|
||||
$database_state_key = 'test_fallback_state';
|
||||
\Drupal::state()->set($database_state_key, $config);
|
||||
\Drupal::state()->set('migrate.fallback_state_key', $database_state_key);
|
||||
// Create a test connection using the default database configuration.
|
||||
Database::addConnectionInfo($key, $target, Database::getConnectionInfo('default')['default']);
|
||||
$this->assertSame($sql_base->getDatabase()->getTarget(), $target);
|
||||
$this->assertSame($sql_base->getDatabase()->getKey(), $key);
|
||||
|
||||
// Verify that setting explicit connection information overrides fallbacks.
|
||||
$target = 'test_db_target';
|
||||
$key = 'test_migrate_connection';
|
||||
$config = ['target' => $target, 'key' => $key];
|
||||
$sql_base->setConfiguration($config);
|
||||
Database::addConnectionInfo($key, $target, Database::getConnectionInfo('default')['default']);
|
||||
|
||||
// Validate we have injected our custom key and target.
|
||||
$this->assertSame($sql_base->getDatabase()->getTarget(), $target);
|
||||
$this->assertSame($sql_base->getDatabase()->getKey(), $key);
|
||||
|
||||
// Now test we can have SqlBase create the connection from an info array.
|
||||
$sql_base = new TestSqlBase([], $this->migration);
|
||||
|
||||
$target = 'test_db_target2';
|
||||
$key = 'test_migrate_connection2';
|
||||
$database = Database::getConnectionInfo('default')['default'];
|
||||
$config = ['target' => $target, 'key' => $key, 'database' => $database];
|
||||
$sql_base->setConfiguration($config);
|
||||
|
||||
// Call getDatabase() to get the connection defined.
|
||||
$sql_base->getDatabase();
|
||||
|
||||
// Validate the connection has been created with the right values.
|
||||
$this->assertSame(Database::getConnectionInfo($key)[$target], $database);
|
||||
|
||||
// Now, test this all works when using state to store db info.
|
||||
$target = 'test_state_db_target';
|
||||
$key = 'test_state_migrate_connection';
|
||||
$config = ['target' => $target, 'key' => $key];
|
||||
$database_state_key = 'migrate_sql_base_test';
|
||||
\Drupal::state()->set($database_state_key, $config);
|
||||
$sql_base->setConfiguration(['database_state_key' => $database_state_key]);
|
||||
Database::addConnectionInfo($key, $target, Database::getConnectionInfo('default')['default']);
|
||||
|
||||
// Validate we have injected our custom key and target.
|
||||
$this->assertSame($sql_base->getDatabase()->getTarget(), $target);
|
||||
$this->assertSame($sql_base->getDatabase()->getKey(), $key);
|
||||
|
||||
// Now test we can have SqlBase create the connection from an info array.
|
||||
$sql_base = new TestSqlBase([], $this->migration);
|
||||
|
||||
$target = 'test_state_db_target2';
|
||||
$key = 'test_state_migrate_connection2';
|
||||
$database = Database::getConnectionInfo('default')['default'];
|
||||
$config = ['target' => $target, 'key' => $key, 'database' => $database];
|
||||
$database_state_key = 'migrate_sql_base_test2';
|
||||
\Drupal::state()->set($database_state_key, $config);
|
||||
$sql_base->setConfiguration(['database_state_key' => $database_state_key]);
|
||||
|
||||
// Call getDatabase() to get the connection defined.
|
||||
$sql_base->getDatabase();
|
||||
|
||||
// Validate the connection has been created with the right values.
|
||||
$this->assertSame(Database::getConnectionInfo($key)[$target], $database);
|
||||
|
||||
// Verify that falling back to 'migrate' when the connection is not defined
|
||||
// throws a RequirementsException.
|
||||
\Drupal::state()->delete('migrate.fallback_state_key');
|
||||
$sql_base->setConfiguration([]);
|
||||
Database::renameConnection('migrate', 'fallback_connection');
|
||||
$this->expectException(RequirementsException::class);
|
||||
$this->expectExceptionMessage('No database connection configured for source plugin');
|
||||
$sql_base->getDatabase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that SqlBase respects high-water values.
|
||||
*
|
||||
* @param mixed $high_water
|
||||
* (optional) The high-water value to set.
|
||||
* @param array $query_result
|
||||
* (optional) The expected query results.
|
||||
*
|
||||
* @dataProvider highWaterDataProvider
|
||||
*/
|
||||
public function testHighWater($high_water = NULL, array $query_result = []) {
|
||||
$configuration = [
|
||||
'high_water_property' => [
|
||||
'name' => 'order',
|
||||
],
|
||||
];
|
||||
$source = new TestSqlBase($configuration, $this->migration);
|
||||
|
||||
if ($high_water) {
|
||||
$source->getHighWaterStorage()->set($this->migration->id(), $high_water);
|
||||
}
|
||||
|
||||
$statement = $this->createMock(StatementInterface::class);
|
||||
$statement->expects($this->atLeastOnce())->method('setFetchMode')->with(\PDO::FETCH_ASSOC);
|
||||
$query = $this->createMock(SelectInterface::class);
|
||||
$query->method('execute')->willReturn($statement);
|
||||
$query->expects($this->atLeastOnce())->method('orderBy')->with('order', 'ASC');
|
||||
|
||||
$condition_group = $this->createMock(ConditionInterface::class);
|
||||
$query->method('orConditionGroup')->willReturn($condition_group);
|
||||
|
||||
$source->setQuery($query);
|
||||
$source->rewind();
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for ::testHighWater().
|
||||
*
|
||||
* @return array
|
||||
* The scenarios to test.
|
||||
*/
|
||||
public function highWaterDataProvider() {
|
||||
return [
|
||||
'no high-water value set' => [],
|
||||
'high-water value set' => [33],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A dummy source to help with testing SqlBase.
|
||||
*
|
||||
* @package Drupal\migrate\Plugin\migrate\source
|
||||
*/
|
||||
class TestSqlBase extends SqlBase {
|
||||
|
||||
/**
|
||||
* The query to execute.
|
||||
*
|
||||
* @var \Drupal\Core\Database\Query\SelectInterface
|
||||
*/
|
||||
protected $query;
|
||||
|
||||
/**
|
||||
* Overrides the constructor so we can create one easily.
|
||||
*
|
||||
* @param array $configuration
|
||||
* The plugin instance configuration.
|
||||
* @param \Drupal\migrate\Plugin\MigrationInterface $migration
|
||||
* (optional) The migration being run.
|
||||
*/
|
||||
public function __construct(array $configuration = [], MigrationInterface $migration = NULL) {
|
||||
parent::__construct($configuration, 'sql_base', [], $migration, \Drupal::state());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the database without caching it.
|
||||
*/
|
||||
public function getDatabase() {
|
||||
$this->database = NULL;
|
||||
return parent::getDatabase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows us to set the configuration from a test.
|
||||
*
|
||||
* @param array $config
|
||||
* The config array.
|
||||
*/
|
||||
public function setConfiguration($config) {
|
||||
$this->configuration = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIds() {}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fields() {}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function query() {
|
||||
return $this->query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the query to execute.
|
||||
*
|
||||
* @param \Drupal\Core\Database\Query\SelectInterface $query
|
||||
* The query to execute.
|
||||
*/
|
||||
public function setQuery(SelectInterface $query) {
|
||||
$this->query = $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getHighWaterStorage() {
|
||||
return parent::getHighWaterStorage();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel;
|
||||
|
||||
/**
|
||||
* Class to test FilterIterators.
|
||||
*/
|
||||
class TestFilterIterator extends \FilterIterator {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function accept() {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel;
|
||||
|
||||
use Drupal\migrate\MigrateExecutable;
|
||||
|
||||
/**
|
||||
* Tests MigrateExecutable.
|
||||
*/
|
||||
class TestMigrateExecutable extends MigrateExecutable {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getIdMap() {
|
||||
// This adds test coverage that this works.
|
||||
return new TestFilterIterator(parent::getIdMap());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getSource() {
|
||||
// This adds test coverage that this works.
|
||||
return new TestFilterIterator(parent::getSource());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel;
|
||||
|
||||
/**
|
||||
* Tests migration track changes property.
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class TrackChangesTest extends MigrateTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'system',
|
||||
'user',
|
||||
'taxonomy',
|
||||
'migrate',
|
||||
'migrate_track_changes_test',
|
||||
'text',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
// Create source test table.
|
||||
$this->sourceDatabase->schema()->createTable('track_changes_term', [
|
||||
'fields' => [
|
||||
'tid' => [
|
||||
'description' => 'Serial',
|
||||
'type' => 'serial',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
],
|
||||
'name' => [
|
||||
'description' => 'Name',
|
||||
'type' => 'varchar',
|
||||
'length' => 128,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
],
|
||||
'description' => [
|
||||
'description' => 'Name',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => FALSE,
|
||||
'default' => '',
|
||||
],
|
||||
],
|
||||
'primary key' => [
|
||||
'tid',
|
||||
],
|
||||
'description' => 'Contains taxonomy terms to import',
|
||||
]);
|
||||
|
||||
// Add 4 items to source table.
|
||||
$this->sourceDatabase->insert('track_changes_term')
|
||||
->fields([
|
||||
'name',
|
||||
'description',
|
||||
])
|
||||
->values([
|
||||
'name' => 'Item 1',
|
||||
'description' => 'Text item 1',
|
||||
])
|
||||
->values([
|
||||
'name' => 'Item 2',
|
||||
'description' => 'Text item 2',
|
||||
])
|
||||
->values([
|
||||
'name' => 'Item 3',
|
||||
'description' => 'Text item 3',
|
||||
])
|
||||
->values([
|
||||
'name' => 'Item 4',
|
||||
'description' => 'Text item 4',
|
||||
])
|
||||
->execute();
|
||||
|
||||
$this->installEntitySchema('taxonomy_term');
|
||||
$this->installEntitySchema('user');
|
||||
|
||||
$this->executeMigration('track_changes_test');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests track changes property of SqlBase.
|
||||
*/
|
||||
public function testTrackChanges() {
|
||||
// Assert all of the terms have been imported.
|
||||
$this->assertTermExists('name', 'Item 1');
|
||||
$this->assertTermExists('name', 'Item 2');
|
||||
$this->assertTermExists('description', 'Text item 3');
|
||||
$this->assertTermExists('description', 'Text item 4');
|
||||
|
||||
// Update Item 1 triggering its track_changes by name.
|
||||
$this->sourceDatabase->update('track_changes_term')
|
||||
->fields([
|
||||
'name' => 'Item 1 updated',
|
||||
])
|
||||
->condition('name', 'Item 1')
|
||||
->execute();
|
||||
|
||||
// Update Item 2 keeping it's track_changes name the same.
|
||||
$this->sourceDatabase->update('track_changes_term')
|
||||
->fields([
|
||||
'name' => 'Item 2',
|
||||
])
|
||||
->condition('name', 'Item 2')
|
||||
->execute();
|
||||
|
||||
// Update Item 3 triggering its track_changes by field.
|
||||
$this->sourceDatabase->update('track_changes_term')
|
||||
->fields([
|
||||
'description' => 'Text item 3 updated',
|
||||
])
|
||||
->condition('name', 'Item 3')
|
||||
->execute();
|
||||
|
||||
// Update Item 2 keeping it's track_changes field the same.
|
||||
$this->sourceDatabase->update('track_changes_term')
|
||||
->fields([
|
||||
'description' => 'Text item 4',
|
||||
])
|
||||
->condition('name', 'Item 4')
|
||||
->execute();
|
||||
|
||||
// Execute migration again.
|
||||
$this->executeMigration('track_changes_test');
|
||||
|
||||
// Item with name changes should be updated.
|
||||
$this->assertTermExists('name', 'Item 1 updated');
|
||||
$this->assertTermDoesNotExist('name', 'Item 1');
|
||||
|
||||
// Item without name changes should not be updated.
|
||||
$this->assertTermExists('name', 'Item 2');
|
||||
|
||||
// Item with field changes should be updated.
|
||||
$this->assertTermExists('description', 'Text item 3 updated');
|
||||
$this->assertTermDoesNotExist('description', 'Text item 3');
|
||||
|
||||
// Item without field changes should not be updated.
|
||||
$this->assertTermExists('description', 'Text item 4');
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that term with given name exists.
|
||||
*
|
||||
* @param string $property
|
||||
* Property to evaluate.
|
||||
* @param string $value
|
||||
* Value to evaluate.
|
||||
*/
|
||||
protected function assertTermExists($property, $value) {
|
||||
self::assertTrue($this->termExists($property, $value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that term with given title does not exist.
|
||||
*
|
||||
* @param string $property
|
||||
* Property to evaluate.
|
||||
* @param string $value
|
||||
* Value to evaluate.
|
||||
*/
|
||||
protected function assertTermDoesNotExist($property, $value) {
|
||||
self::assertFalse($this->termExists($property, $value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if term with given name exists.
|
||||
*
|
||||
* @param string $property
|
||||
* Property to evaluate.
|
||||
* @param string $value
|
||||
* Value to evaluate.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function termExists($property, $value) {
|
||||
$property = $property === 'description' ? 'description__value' : $property;
|
||||
$query = \Drupal::entityQuery('taxonomy_term');
|
||||
$result = $query
|
||||
->condition($property, $value)
|
||||
->range(0, 1)
|
||||
->execute();
|
||||
|
||||
return !empty($result);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel\process;
|
||||
|
||||
use Drupal\Core\StreamWrapper\StreamWrapperInterface;
|
||||
use Drupal\KernelTests\Core\File\FileTestBase;
|
||||
use Drupal\migrate\MigrateException;
|
||||
use Drupal\migrate\Plugin\migrate\process\Download;
|
||||
use Drupal\migrate\MigrateExecutableInterface;
|
||||
use Drupal\migrate\Row;
|
||||
use GuzzleHttp\Client;
|
||||
|
||||
/**
|
||||
* Tests the download process plugin.
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class DownloadTest extends FileTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['system'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->container->get('stream_wrapper_manager')->registerWrapper('temporary', 'Drupal\Core\StreamWrapper\TemporaryStream', StreamWrapperInterface::LOCAL_NORMAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a download that overwrites an existing local file.
|
||||
*/
|
||||
public function testOverwritingDownload() {
|
||||
// Create a pre-existing file at the destination, to test overwrite behavior.
|
||||
$destination_uri = $this->createUri('existing_file.txt');
|
||||
|
||||
// Test destructive download.
|
||||
$actual_destination = $this->doTransform($destination_uri);
|
||||
$this->assertSame($destination_uri, $actual_destination, 'Import returned a destination that was not renamed');
|
||||
$this->assertFileNotExists('public://existing_file_0.txt');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a download that renames the downloaded file if there's a collision.
|
||||
*/
|
||||
public function testNonDestructiveDownload() {
|
||||
// Create a pre-existing file at the destination, to test overwrite behavior.
|
||||
$destination_uri = $this->createUri('another_existing_file.txt');
|
||||
|
||||
// Test non-destructive download.
|
||||
$actual_destination = $this->doTransform($destination_uri, ['file_exists' => 'rename']);
|
||||
$this->assertSame('public://another_existing_file_0.txt', $actual_destination, 'Import returned a renamed destination');
|
||||
$this->assertFileExists($actual_destination);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that an exception is thrown if the destination URI is not writable.
|
||||
*/
|
||||
public function testWriteProtectedDestination() {
|
||||
// Create a pre-existing file at the destination, to test overwrite behavior.
|
||||
$destination_uri = $this->createUri('not-writable.txt');
|
||||
|
||||
// Make the destination non-writable.
|
||||
$this->container
|
||||
->get('file_system')
|
||||
->chmod($destination_uri, 0444);
|
||||
|
||||
// Pass or fail, we'll need to make the file writable again so the test
|
||||
// can clean up after itself.
|
||||
$fix_permissions = function () use ($destination_uri) {
|
||||
$this->container
|
||||
->get('file_system')
|
||||
->chmod($destination_uri, 0755);
|
||||
};
|
||||
|
||||
try {
|
||||
$this->doTransform($destination_uri);
|
||||
$fix_permissions();
|
||||
$this->fail('MigrateException was not thrown for non-writable destination URI.');
|
||||
}
|
||||
catch (MigrateException $e) {
|
||||
$this->assertTrue(TRUE, 'MigrateException was thrown for non-writable destination URI.');
|
||||
$fix_permissions();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs an input value through the download plugin.
|
||||
*
|
||||
* @param string $destination_uri
|
||||
* The destination URI to download to.
|
||||
* @param array $configuration
|
||||
* (optional) Configuration for the download plugin.
|
||||
*
|
||||
* @return string
|
||||
* The local URI of the downloaded file.
|
||||
*/
|
||||
protected function doTransform($destination_uri, $configuration = []) {
|
||||
// Prepare a mock HTTP client.
|
||||
$this->container->set('http_client', $this->createMock(Client::class));
|
||||
|
||||
// Instantiate the plugin statically so it can pull dependencies out of
|
||||
// the container.
|
||||
$plugin = Download::create($this->container, $configuration, 'download', []);
|
||||
|
||||
// Execute the transformation.
|
||||
$executable = $this->createMock(MigrateExecutableInterface::class);
|
||||
$row = new Row([], []);
|
||||
|
||||
// Return the downloaded file's local URI.
|
||||
$value = [
|
||||
'http://drupal.org/favicon.ico',
|
||||
$destination_uri,
|
||||
];
|
||||
return $plugin->transform($value, $executable, $row, 'foobaz');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel\process;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\migrate\MigrateExecutable;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
|
||||
/**
|
||||
* Tests the extract process plugin.
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class ExtractTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['migrate'];
|
||||
|
||||
/**
|
||||
* Returns test migration definition.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDefinition() {
|
||||
return [
|
||||
'source' => [
|
||||
'plugin' => 'embedded_data',
|
||||
'data_rows' => [],
|
||||
'ids' => [
|
||||
'id' => ['type' => 'string'],
|
||||
],
|
||||
],
|
||||
'process' => [
|
||||
'first' => [
|
||||
'plugin' => 'extract',
|
||||
'index' => [0],
|
||||
'source' => 'simple_array',
|
||||
],
|
||||
'second' => [
|
||||
'plugin' => 'extract',
|
||||
'index' => [1],
|
||||
'source' => 'complex_array',
|
||||
],
|
||||
],
|
||||
'destination' => [
|
||||
'plugin' => 'config',
|
||||
'config_name' => 'migrate_test.settings',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests multiple value handling.
|
||||
*
|
||||
* @dataProvider multipleValueProviderSource
|
||||
*
|
||||
* @param array $source_data
|
||||
* @param array $expected_data
|
||||
*/
|
||||
public function testMultipleValueExplode(array $source_data, array $expected_data) {
|
||||
$definition = $this->getDefinition();
|
||||
$definition['source']['data_rows'] = [$source_data];
|
||||
|
||||
$migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition);
|
||||
|
||||
$executable = new MigrateExecutable($migration);
|
||||
$result = $executable->import();
|
||||
|
||||
// Migration needs to succeed before further assertions are made.
|
||||
$this->assertSame(MigrationInterface::RESULT_COMPLETED, $result);
|
||||
|
||||
// Compare with expected data.
|
||||
$this->assertEquals($expected_data, \Drupal::config('migrate_test.settings')->get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides multiple source data for "extract" process plugin test.
|
||||
*/
|
||||
public function multipleValueProviderSource() {
|
||||
$tests = [
|
||||
[
|
||||
'source_data' => [
|
||||
'id' => '1',
|
||||
'simple_array' => ['alpha', 'beta'],
|
||||
'complex_array' => [['alpha', 'beta'], ['psi', 'omega']],
|
||||
],
|
||||
'expected_data' => [
|
||||
'first' => 'alpha',
|
||||
'second' => ['psi', 'omega'],
|
||||
],
|
||||
],
|
||||
[
|
||||
'source_data' => [
|
||||
'id' => '2',
|
||||
'simple_array' => ['one'],
|
||||
'complex_array' => [0, 1],
|
||||
],
|
||||
'expected_data' => [
|
||||
'first' => 'one',
|
||||
'second' => 1,
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
return $tests;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel\process;
|
||||
|
||||
use Drupal\Core\StreamWrapper\StreamWrapperInterface;
|
||||
use Drupal\KernelTests\Core\File\FileTestBase;
|
||||
use Drupal\migrate\MigrateException;
|
||||
use Drupal\migrate\Plugin\migrate\process\FileCopy;
|
||||
use Drupal\migrate\MigrateExecutableInterface;
|
||||
use Drupal\migrate\Plugin\MigrateProcessInterface;
|
||||
use Drupal\migrate\Row;
|
||||
use GuzzleHttp\Client;
|
||||
|
||||
/**
|
||||
* Tests the file_copy process plugin.
|
||||
*
|
||||
* @coversDefaultClass \Drupal\migrate\Plugin\migrate\process\FileCopy
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class FileCopyTest extends FileTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['migrate', 'system'];
|
||||
|
||||
/**
|
||||
* The file system service.
|
||||
*
|
||||
* @var \Drupal\Core\File\FileSystemInterface
|
||||
*/
|
||||
protected $fileSystem;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->fileSystem = $this->container->get('file_system');
|
||||
$this->container->get('stream_wrapper_manager')->registerWrapper('temporary', 'Drupal\Core\StreamWrapper\TemporaryStream', StreamWrapperInterface::LOCAL_NORMAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test successful imports/copies.
|
||||
*/
|
||||
public function testSuccessfulCopies() {
|
||||
$file = $this->createUri(NULL, NULL, 'temporary');
|
||||
$file_absolute = $this->fileSystem->realpath($file);
|
||||
$data_sets = [
|
||||
// Test a local to local copy.
|
||||
[
|
||||
$this->root . '/core/tests/fixtures/files/image-test.jpg',
|
||||
'public://file1.jpg',
|
||||
],
|
||||
// Test a temporary file using an absolute path.
|
||||
[
|
||||
$file_absolute,
|
||||
'temporary://test.jpg',
|
||||
],
|
||||
// Test a temporary file using a relative path.
|
||||
[
|
||||
$file_absolute,
|
||||
'temporary://core/tests/fixtures/files/test.jpg',
|
||||
],
|
||||
];
|
||||
foreach ($data_sets as $data) {
|
||||
list($source_path, $destination_path) = $data;
|
||||
$actual_destination = $this->doTransform($source_path, $destination_path);
|
||||
$this->assertFileExists($destination_path);
|
||||
// Make sure we didn't accidentally do a move.
|
||||
$this->assertFileExists($source_path);
|
||||
$this->assertSame($actual_destination, $destination_path, 'The import returned the copied filename.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test successful file reuse.
|
||||
*
|
||||
* @dataProvider providerSuccessfulReuse
|
||||
*
|
||||
* @param string $source_path
|
||||
* Source path to copy from.
|
||||
* @param string $destination_path
|
||||
* The destination path to copy to.
|
||||
*/
|
||||
public function testSuccessfulReuse($source_path, $destination_path) {
|
||||
$file_reuse = $this->doTransform($source_path, $destination_path);
|
||||
clearstatcache(TRUE, $destination_path);
|
||||
|
||||
$timestamp = (new \SplFileInfo($file_reuse))->getMTime();
|
||||
$this->assertIsInt($timestamp);
|
||||
|
||||
// We need to make sure the modified timestamp on the file is sooner than
|
||||
// the attempted migration.
|
||||
sleep(1);
|
||||
$configuration = ['file_exists' => 'use existing'];
|
||||
$this->doTransform($source_path, $destination_path, $configuration);
|
||||
clearstatcache(TRUE, $destination_path);
|
||||
$modified_timestamp = (new \SplFileInfo($destination_path))->getMTime();
|
||||
$this->assertEquals($timestamp, $modified_timestamp);
|
||||
|
||||
$this->doTransform($source_path, $destination_path);
|
||||
clearstatcache(TRUE, $destination_path);
|
||||
$modified_timestamp = (new \SplFileInfo($destination_path))->getMTime();
|
||||
$this->assertGreaterThan($timestamp, $modified_timestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the source and destination path files.
|
||||
*/
|
||||
public function providerSuccessfulReuse() {
|
||||
return [
|
||||
[
|
||||
'local_source_path' => static::getDrupalRoot() . '/core/tests/fixtures/files/image-test.jpg',
|
||||
'local_destination_path' => 'public://file1.jpg',
|
||||
],
|
||||
[
|
||||
'remote_source_path' => 'https://www.drupal.org/favicon.ico',
|
||||
'remote_destination_path' => 'public://file2.jpg',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test successful moves.
|
||||
*/
|
||||
public function testSuccessfulMoves() {
|
||||
$file_1 = $this->createUri(NULL, NULL, 'temporary');
|
||||
$file_1_absolute = $this->fileSystem->realpath($file_1);
|
||||
$file_2 = $this->createUri(NULL, NULL, 'temporary');
|
||||
$file_2_absolute = $this->fileSystem->realpath($file_2);
|
||||
$local_file = $this->createUri(NULL, NULL, 'public');
|
||||
$data_sets = [
|
||||
// Test a local to local copy.
|
||||
[
|
||||
$local_file,
|
||||
'public://file1.jpg',
|
||||
],
|
||||
// Test a temporary file using an absolute path.
|
||||
[
|
||||
$file_1_absolute,
|
||||
'temporary://test.jpg',
|
||||
],
|
||||
// Test a temporary file using a relative path.
|
||||
[
|
||||
$file_2_absolute,
|
||||
'temporary://core/tests/fixtures/files/test.jpg',
|
||||
],
|
||||
];
|
||||
foreach ($data_sets as $data) {
|
||||
list($source_path, $destination_path) = $data;
|
||||
$actual_destination = $this->doTransform($source_path, $destination_path, ['move' => TRUE]);
|
||||
$this->assertFileExists($destination_path);
|
||||
$this->assertFileNotExists($source_path);
|
||||
$this->assertSame($actual_destination, $destination_path, 'The importer returned the moved filename.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that non-existent files throw an exception.
|
||||
*/
|
||||
public function testNonExistentSourceFile() {
|
||||
$source = '/non/existent/file';
|
||||
$this->expectException(MigrateException::class);
|
||||
$this->expectExceptionMessage("File '/non/existent/file' does not exist");
|
||||
$this->doTransform($source, 'public://wontmatter.jpg');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that non-writable destination throw an exception.
|
||||
*
|
||||
* @covers ::transform
|
||||
*/
|
||||
public function testNonWritableDestination() {
|
||||
$source = $this->createUri('file.txt', NULL, 'temporary');
|
||||
|
||||
// Create the parent location.
|
||||
$this->createDirectory('public://dir');
|
||||
|
||||
// Copy the file under public://dir/subdir1/.
|
||||
$this->doTransform($source, 'public://dir/subdir1/file.txt');
|
||||
|
||||
// Check that 'subdir1' was created and the file was successfully migrated.
|
||||
$this->assertFileExists('public://dir/subdir1/file.txt');
|
||||
|
||||
// Remove all permissions from public://dir to trigger a failure when
|
||||
// trying to create a subdirectory 'subdir2' inside public://dir.
|
||||
$this->fileSystem->chmod('public://dir', 0);
|
||||
|
||||
// Check that the proper exception is raised.
|
||||
$this->expectException(MigrateException::class);
|
||||
$this->expectExceptionMessage("Could not create or write to directory 'public://dir/subdir2'");
|
||||
$this->doTransform($source, 'public://dir/subdir2/file.txt');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the 'rename' overwrite mode.
|
||||
*/
|
||||
public function testRenameFile() {
|
||||
$source = $this->createUri(NULL, NULL, 'temporary');
|
||||
$destination = $this->createUri('foo.txt', NULL, 'public');
|
||||
$expected_destination = 'public://foo_0.txt';
|
||||
$actual_destination = $this->doTransform($source, $destination, ['file_exists' => 'rename']);
|
||||
$this->assertFileExists($expected_destination);
|
||||
$this->assertSame($actual_destination, $expected_destination, 'The importer returned the renamed filename.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that remote URIs are delegated to the download plugin.
|
||||
*/
|
||||
public function testDownloadRemoteUri() {
|
||||
$download_plugin = $this->createMock(MigrateProcessInterface::class);
|
||||
$download_plugin->expects($this->once())->method('transform');
|
||||
|
||||
$plugin = new FileCopy(
|
||||
[],
|
||||
$this->randomMachineName(),
|
||||
[],
|
||||
$this->container->get('stream_wrapper_manager'),
|
||||
$this->container->get('file_system'),
|
||||
$download_plugin
|
||||
);
|
||||
|
||||
$plugin->transform(
|
||||
['http://drupal.org/favicon.ico', '/destination/path'],
|
||||
$this->createMock(MigrateExecutableInterface::class),
|
||||
new Row([], []),
|
||||
$this->randomMachineName()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do an import using the destination.
|
||||
*
|
||||
* @param string $source_path
|
||||
* Source path to copy from.
|
||||
* @param string $destination_path
|
||||
* The destination path to copy to.
|
||||
* @param array $configuration
|
||||
* Process plugin configuration settings.
|
||||
*
|
||||
* @return string
|
||||
* The URI of the copied file.
|
||||
*/
|
||||
protected function doTransform($source_path, $destination_path, $configuration = []) {
|
||||
// Prepare a mock HTTP client.
|
||||
$this->container->set('http_client', $this->createMock(Client::class));
|
||||
|
||||
$plugin = FileCopy::create($this->container, $configuration, 'file_copy', []);
|
||||
$executable = $this->prophesize(MigrateExecutableInterface::class)->reveal();
|
||||
$row = new Row([], []);
|
||||
|
||||
return $plugin->transform([$source_path, $destination_path], $executable, $row, 'foobaz');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel\process;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\migrate\MigrateExecutable;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
|
||||
/**
|
||||
* Tests process pipelines with scalar and multiple values handling.
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class HandleMultiplesTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['migrate'];
|
||||
|
||||
/**
|
||||
* Provides the test migration definition.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDefinition() {
|
||||
return [
|
||||
'source' => [
|
||||
'plugin' => 'embedded_data',
|
||||
'data_rows' => [],
|
||||
'ids' => [
|
||||
'id' => ['type' => 'string'],
|
||||
],
|
||||
],
|
||||
'process' => [
|
||||
// Process pipeline for testing values from string to array to string.
|
||||
'first' => [
|
||||
// Expects a string and returns an array.
|
||||
[
|
||||
'plugin' => 'explode',
|
||||
'source' => 'scalar',
|
||||
'delimiter' => '/',
|
||||
],
|
||||
// Expects an array and returns a string.
|
||||
[
|
||||
'plugin' => 'extract',
|
||||
'index' => [1],
|
||||
],
|
||||
// Expects a string and returns a string.
|
||||
[
|
||||
'plugin' => 'callback',
|
||||
'callable' => 'strtoupper',
|
||||
],
|
||||
],
|
||||
// Process pipeline for testing values from array to string to array.
|
||||
'second' => [
|
||||
// Expects an array and returns a string.
|
||||
[
|
||||
'plugin' => 'extract',
|
||||
'source' => 'multiple',
|
||||
'index' => [1],
|
||||
],
|
||||
// Expects a string and returns a string.
|
||||
[
|
||||
'plugin' => 'callback',
|
||||
'callable' => 'strtoupper',
|
||||
],
|
||||
// Expects a string and returns an array.
|
||||
[
|
||||
'plugin' => 'explode',
|
||||
'delimiter' => '/',
|
||||
],
|
||||
],
|
||||
],
|
||||
'destination' => [
|
||||
'plugin' => 'config',
|
||||
'config_name' => 'migrate_test.settings',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests process pipelines with scalar and multiple values handling.
|
||||
*
|
||||
* @dataProvider scalarAndMultipleValuesProviderSource
|
||||
*
|
||||
* @param array $source_data
|
||||
* @param array $expected_data
|
||||
*/
|
||||
public function testScalarAndMultipleValues(array $source_data, array $expected_data) {
|
||||
$definition = $this->getDefinition();
|
||||
$definition['source']['data_rows'] = [$source_data];
|
||||
|
||||
$migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition);
|
||||
|
||||
$executable = new MigrateExecutable($migration);
|
||||
$result = $executable->import();
|
||||
|
||||
// Migration needs to succeed before further assertions are made.
|
||||
$this->assertSame(MigrationInterface::RESULT_COMPLETED, $result);
|
||||
|
||||
// Compare with expected data.
|
||||
$this->assertEquals($expected_data, \Drupal::config('migrate_test.settings')->get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the source data with scalar and multiple values.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function scalarAndMultipleValuesProviderSource() {
|
||||
return [
|
||||
[
|
||||
'source_data' => [
|
||||
'id' => '1',
|
||||
// Source value for the first pipeline.
|
||||
'scalar' => 'foo/bar',
|
||||
// Source value for the second pipeline.
|
||||
'multiple' => [
|
||||
'foo',
|
||||
'bar/baz',
|
||||
],
|
||||
],
|
||||
'expected_data' => [
|
||||
// Expected value from the first pipeline.
|
||||
'first' => 'BAR',
|
||||
// Expected value from the second pipeline.
|
||||
'second' => [
|
||||
'BAR',
|
||||
'BAZ',
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,274 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel\process;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\migrate\MigrateExecutableInterface;
|
||||
use Drupal\migrate\Plugin\migrate\process\Route;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\migrate\Row;
|
||||
use Drupal\Tests\user\Traits\UserCreationTrait;
|
||||
|
||||
/**
|
||||
* Tests the route process plugin.
|
||||
*
|
||||
* @coversDefaultClass \Drupal\migrate\Plugin\migrate\process\Route
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class RouteTest extends KernelTestBase {
|
||||
|
||||
use UserCreationTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['user', 'system'];
|
||||
|
||||
/**
|
||||
* Tests Route plugin based on providerTestRoute() values.
|
||||
*
|
||||
* @param mixed $value
|
||||
* Input value for the Route process plugin.
|
||||
* @param array $expected
|
||||
* The expected results from the Route transform process.
|
||||
*
|
||||
* @dataProvider providerTestRoute
|
||||
*/
|
||||
public function testRoute($value, $expected) {
|
||||
$actual = $this->doTransform($value);
|
||||
$this->assertSame($expected, $actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testRoute().
|
||||
*
|
||||
* @return array
|
||||
* An array of arrays, where the first element is the input to the Route
|
||||
* process plugin, and the second is the expected results.
|
||||
*/
|
||||
public function providerTestRoute() {
|
||||
// Internal link tests.
|
||||
// Valid link path and options.
|
||||
$values[0] = [
|
||||
'user/login',
|
||||
[
|
||||
'attributes' => [
|
||||
'title' => 'Test menu link 1',
|
||||
],
|
||||
],
|
||||
];
|
||||
$expected[0] = [
|
||||
'route_name' => 'user.login',
|
||||
'route_parameters' => [],
|
||||
'options' => [
|
||||
'query' => [],
|
||||
'attributes' => [
|
||||
'title' => 'Test menu link 1',
|
||||
],
|
||||
],
|
||||
'url' => NULL,
|
||||
];
|
||||
|
||||
// Valid link path and empty options.
|
||||
$values[1] = [
|
||||
'user/login',
|
||||
[],
|
||||
];
|
||||
$expected[1] = [
|
||||
'route_name' => 'user.login',
|
||||
'route_parameters' => [],
|
||||
'options' => [
|
||||
'query' => [],
|
||||
],
|
||||
'url' => NULL,
|
||||
];
|
||||
|
||||
// Valid link path and no options.
|
||||
$values[2] = 'user/login';
|
||||
$expected[2] = [
|
||||
'route_name' => 'user.login',
|
||||
'route_parameters' => [],
|
||||
'options' => [
|
||||
'query' => [],
|
||||
],
|
||||
'url' => NULL,
|
||||
];
|
||||
|
||||
// Invalid link path.
|
||||
$values[3] = 'users';
|
||||
$expected[3] = [];
|
||||
|
||||
// Valid link path with parameter.
|
||||
$values[4] = [
|
||||
'system/timezone/nzdt',
|
||||
[
|
||||
'attributes' => [
|
||||
'title' => 'Show NZDT',
|
||||
],
|
||||
],
|
||||
];
|
||||
$expected[4] = [
|
||||
'route_name' => 'system.timezone',
|
||||
'route_parameters' => [
|
||||
'abbreviation' => 'nzdt',
|
||||
'offset' => -1,
|
||||
'is_daylight_saving_time' => NULL,
|
||||
],
|
||||
'options' => [
|
||||
'query' => [],
|
||||
'attributes' => [
|
||||
'title' => 'Show NZDT',
|
||||
],
|
||||
],
|
||||
'url' => NULL,
|
||||
];
|
||||
|
||||
// External link tests.
|
||||
// Valid external link path and options.
|
||||
$values[5] = [
|
||||
'https://www.drupal.org',
|
||||
[
|
||||
'attributes' => [
|
||||
'title' => 'Drupal',
|
||||
],
|
||||
],
|
||||
];
|
||||
$expected[5] = [
|
||||
'route_name' => NULL,
|
||||
'route_parameters' => [],
|
||||
'options' => [
|
||||
'attributes' => [
|
||||
'title' => 'Drupal',
|
||||
],
|
||||
],
|
||||
'url' => 'https://www.drupal.org',
|
||||
];
|
||||
|
||||
// Valid external link path and options.
|
||||
$values[6] = [
|
||||
'https://www.drupal.org/user/1/edit?pass-reset-token=QgtDKcRV4e4fjg6v2HTa6CbWx-XzMZ5XBZTufinqsM73qIhscIuU_BjZ6J2tv4dQI6N50ZJOag',
|
||||
[
|
||||
'attributes' => [
|
||||
'title' => 'Drupal password reset',
|
||||
],
|
||||
],
|
||||
];
|
||||
$expected[6] = [
|
||||
'route_name' => NULL,
|
||||
'route_parameters' => [],
|
||||
'options' => [
|
||||
'attributes' => [
|
||||
'title' => 'Drupal password reset',
|
||||
],
|
||||
],
|
||||
'url' => 'https://www.drupal.org/user/1/edit?pass-reset-token=QgtDKcRV4e4fjg6v2HTa6CbWx-XzMZ5XBZTufinqsM73qIhscIuU_BjZ6J2tv4dQI6N50ZJOag',
|
||||
];
|
||||
|
||||
return [
|
||||
// Test data for internal paths.
|
||||
// Test with valid link path and options.
|
||||
[$values[0], $expected[0]],
|
||||
// Test with valid link path and empty options.
|
||||
[$values[1], $expected[1]],
|
||||
// Test with valid link path and no options.
|
||||
[$values[2], $expected[2]],
|
||||
// Test with Invalid link path.
|
||||
[$values[3], $expected[3]],
|
||||
// Test with Valid link path with query options and parameters.
|
||||
[$values[4], $expected[4]],
|
||||
|
||||
// Test data for external paths.
|
||||
// Test with external link path and options.
|
||||
[$values[5], $expected[5]],
|
||||
// Test with valid link path and query options.
|
||||
[$values[6], $expected[6]],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests Route plugin based on providerTestRoute() values.
|
||||
*
|
||||
* @param mixed $value
|
||||
* Input value for the Route process plugin.
|
||||
* @param array $expected
|
||||
* The expected results from the Route transform process.
|
||||
*
|
||||
* @dataProvider providerTestRouteWithParamQuery
|
||||
*/
|
||||
public function testRouteWithParamQuery($value, $expected) {
|
||||
// Create a user so that user/1/edit is a valid path.
|
||||
$this->setUpCurrentUser();
|
||||
$this->installConfig(['user']);
|
||||
|
||||
$actual = $this->doTransform($value);
|
||||
$this->assertSame($expected, $actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testRouteWithParamQuery().
|
||||
*
|
||||
* @return array
|
||||
* An array of arrays, where the first element is the input to the Route
|
||||
* process plugin, and the second is the expected results.
|
||||
*/
|
||||
public function providerTestRouteWithParamQuery() {
|
||||
$values = [];
|
||||
$expected = [];
|
||||
// Valid link path with query options and parameters.
|
||||
$values[0] = [
|
||||
'user/1/edit',
|
||||
[
|
||||
'attributes' => [
|
||||
'title' => 'Edit admin',
|
||||
],
|
||||
'query' => [
|
||||
'destination' => '/admin/people',
|
||||
],
|
||||
],
|
||||
];
|
||||
$expected[0] = [
|
||||
'route_name' => 'entity.user.edit_form',
|
||||
'route_parameters' => [
|
||||
'user' => '1',
|
||||
],
|
||||
'options' => [
|
||||
'attributes' => [
|
||||
'title' => 'Edit admin',
|
||||
],
|
||||
'query' => [
|
||||
'destination' => '/admin/people',
|
||||
],
|
||||
],
|
||||
'url' => NULL,
|
||||
];
|
||||
|
||||
return [
|
||||
// Test with valid link path with parameters and options.
|
||||
[$values[0], $expected[0]],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms link path data to a route.
|
||||
*
|
||||
* @param array|string $value
|
||||
* Source link path information.
|
||||
*
|
||||
* @return array
|
||||
* The route information based on the source link_path.
|
||||
*/
|
||||
protected function doTransform($value) {
|
||||
// Rebuild the routes.
|
||||
$this->container->get('router.builder')->rebuild();
|
||||
$pathValidator = $this->container->get('path.validator');
|
||||
$row = new Row();
|
||||
$migration = $this->prophesize(MigrationInterface::class)->reveal();
|
||||
$executable = $this->prophesize(MigrateExecutableInterface::class)->reveal();
|
||||
|
||||
$plugin = new Route([], 'route', [], $migration, $pathValidator);
|
||||
$actual = $plugin->transform($value, $executable, $row, 'destinationproperty');
|
||||
return $actual;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Unit\Event;
|
||||
|
||||
use Drupal\migrate\Event\EventBase;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\migrate\Event\EventBase
|
||||
* @group migrate
|
||||
*/
|
||||
class EventBaseTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* Test getMigration method.
|
||||
*
|
||||
* @covers ::__construct
|
||||
* @covers ::getMigration
|
||||
*/
|
||||
public function testGetMigration() {
|
||||
$migration = $this->prophesize('\Drupal\migrate\Plugin\MigrationInterface')->reveal();
|
||||
$message_service = $this->prophesize('\Drupal\migrate\MigrateMessageInterface')->reveal();
|
||||
$row = $this->prophesize('\Drupal\migrate\Row')->reveal();
|
||||
$event = new EventBase($migration, $message_service, $row, [1, 2, 3]);
|
||||
$this->assertSame($migration, $event->getMigration());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test logging a message.
|
||||
*
|
||||
* @covers ::__construct
|
||||
* @covers ::logMessage
|
||||
*/
|
||||
public function testLogMessage() {
|
||||
$migration = $this->prophesize('\Drupal\migrate\Plugin\MigrationInterface')->reveal();
|
||||
$message_service = $this->prophesize('\Drupal\migrate\MigrateMessageInterface');
|
||||
$event = new EventBase($migration, $message_service->reveal());
|
||||
// Assert that the intended calls to the services happen.
|
||||
$message_service->display('status message', 'status')->shouldBeCalledTimes(1);
|
||||
$event->logMessage('status message');
|
||||
$message_service->display('warning message', 'warning')->shouldBeCalledTimes(1);
|
||||
$event->logMessage('warning message', 'warning');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Unit\Event;
|
||||
|
||||
use Drupal\migrate\Event\MigrateImportEvent;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\migrate\Event\MigrateImportEvent
|
||||
* @group migrate
|
||||
*/
|
||||
class MigrateImportEventTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* Test getMigration method.
|
||||
*
|
||||
* @covers ::__construct
|
||||
* @covers ::getMigration
|
||||
*/
|
||||
public function testGetMigration() {
|
||||
$migration = $this->prophesize('\Drupal\migrate\Plugin\MigrationInterface')->reveal();
|
||||
$message_service = $this->prophesize('\Drupal\migrate\MigrateMessageInterface')->reveal();
|
||||
$event = new MigrateImportEvent($migration, $message_service);
|
||||
$this->assertSame($migration, $event->getMigration());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test logging a message.
|
||||
*
|
||||
* @covers ::__construct
|
||||
* @covers ::logMessage
|
||||
*/
|
||||
public function testLogMessage() {
|
||||
$migration = $this->prophesize('\Drupal\migrate\Plugin\MigrationInterface');
|
||||
$message_service = $this->prophesize('\Drupal\migrate\MigrateMessageInterface');
|
||||
$event = new MigrateImportEvent($migration->reveal(), $message_service->reveal());
|
||||
// Assert that the intended calls to the services happen.
|
||||
$message_service->display('status message', 'status')->shouldBeCalledTimes(1);
|
||||
$event->logMessage('status message');
|
||||
$message_service->display('warning message', 'warning')->shouldBeCalledTimes(1);
|
||||
$event->logMessage('warning message', 'warning');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Unit\Event;
|
||||
|
||||
use Drupal\migrate\Event\MigratePostRowSaveEvent;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\migrate\Event\MigratePostRowSaveEvent
|
||||
* @group migrate
|
||||
*/
|
||||
class MigratePostRowSaveEventTest extends EventBaseTest {
|
||||
|
||||
/**
|
||||
* Test getDestinationIdValues method.
|
||||
*
|
||||
* @covers ::__construct
|
||||
* @covers ::getDestinationIdValues
|
||||
*/
|
||||
public function testGetDestinationIdValues() {
|
||||
$migration = $this->prophesize('\Drupal\migrate\Plugin\MigrationInterface')->reveal();
|
||||
$message_service = $this->prophesize('\Drupal\migrate\MigrateMessageInterface')->reveal();
|
||||
$row = $this->prophesize('\Drupal\migrate\Row')->reveal();
|
||||
$event = new MigratePostRowSaveEvent($migration, $message_service, $row, [1, 2, 3]);
|
||||
$this->assertSame([1, 2, 3], $event->getDestinationIdValues());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test getRow method.
|
||||
*
|
||||
* @covers ::__construct
|
||||
* @covers ::getRow
|
||||
*/
|
||||
public function testGetRow() {
|
||||
$migration = $this->prophesize('\Drupal\migrate\Plugin\MigrationInterface')->reveal();
|
||||
$message_service = $this->prophesize('\Drupal\migrate\MigrateMessageInterface');
|
||||
$row = $this->prophesize('\Drupal\migrate\Row')->reveal();
|
||||
$event = new MigratePostRowSaveEvent($migration, $message_service->reveal(), $row, [1, 2, 3]);
|
||||
$this->assertSame($row, $event->getRow());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Unit\Event;
|
||||
|
||||
use Drupal\migrate\Event\MigratePreRowSaveEvent;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\migrate\Event\MigratePreRowSaveEvent
|
||||
* @group migrate
|
||||
*/
|
||||
class MigratePreRowSaveEventTest extends EventBaseTest {
|
||||
|
||||
/**
|
||||
* Test getRow method.
|
||||
*
|
||||
* @covers ::__construct
|
||||
* @covers ::getRow
|
||||
*/
|
||||
public function testGetRow() {
|
||||
$migration = $this->prophesize('\Drupal\migrate\Plugin\MigrationInterface')->reveal();
|
||||
$message_service = $this->prophesize('\Drupal\migrate\MigrateMessageInterface')->reveal();
|
||||
$row = $this->prophesize('\Drupal\migrate\Row')->reveal();
|
||||
$event = new MigratePreRowSaveEvent($migration, $message_service, $row);
|
||||
$this->assertSame($row, $event->getRow());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Unit\Exception;
|
||||
|
||||
use Drupal\migrate\Exception\RequirementsException;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\migrate\Exception\RequirementsException
|
||||
* @group migrate
|
||||
*/
|
||||
class RequirementsExceptionTest extends UnitTestCase {
|
||||
|
||||
protected $missingRequirements = ['random_jackson_pivot', '51_Eridani_b'];
|
||||
|
||||
/**
|
||||
* @covers ::getRequirements
|
||||
*/
|
||||
public function testGetRequirements() {
|
||||
$exception = new RequirementsException('Missing requirements ', ['requirements' => $this->missingRequirements]);
|
||||
$this->assertArrayEquals(['requirements' => $this->missingRequirements], $exception->getRequirements());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getRequirementsString
|
||||
* @dataProvider getRequirementsProvider
|
||||
*/
|
||||
public function testGetExceptionString($expected, $message, $requirements) {
|
||||
$exception = new RequirementsException($message, $requirements);
|
||||
$this->assertEquals($expected, $exception->getRequirementsString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a list of requirements to test.
|
||||
*/
|
||||
public function getRequirementsProvider() {
|
||||
return [
|
||||
[
|
||||
'requirements: random_jackson_pivot.',
|
||||
'Single Requirement',
|
||||
['requirements' => $this->missingRequirements[0]],
|
||||
],
|
||||
[
|
||||
'requirements: random_jackson_pivot. requirements: 51_Eridani_b.',
|
||||
'Multiple Requirements',
|
||||
['requirements' => $this->missingRequirements],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Unit;
|
||||
|
||||
/**
|
||||
* Tests the \Drupal\migrate\MigrateExecutable::memoryExceeded() method.
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class MigrateExecutableMemoryExceededTest extends MigrateTestCase {
|
||||
|
||||
/**
|
||||
* The mocked migration entity.
|
||||
*
|
||||
* @var \Drupal\migrate\Plugin\MigrationInterface|\PHPUnit\Framework\MockObject\MockObject
|
||||
*/
|
||||
protected $migration;
|
||||
|
||||
/**
|
||||
* The mocked migrate message.
|
||||
*
|
||||
* @var \Drupal\migrate\MigrateMessageInterface|\PHPUnit\Framework\MockObject\MockObject
|
||||
*/
|
||||
protected $message;
|
||||
|
||||
/**
|
||||
* The tested migrate executable.
|
||||
*
|
||||
* @var \Drupal\Tests\migrate\Unit\TestMigrateExecutable
|
||||
*/
|
||||
protected $executable;
|
||||
|
||||
/**
|
||||
* The migration configuration, initialized to set the ID to test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $migrationConfiguration = [
|
||||
'id' => 'test',
|
||||
];
|
||||
|
||||
/**
|
||||
* The php.ini memory_limit value.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $memoryLimit = 10000000;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->migration = $this->getMigration();
|
||||
$this->message = $this->createMock('Drupal\migrate\MigrateMessageInterface');
|
||||
|
||||
$this->executable = new TestMigrateExecutable($this->migration, $this->message);
|
||||
$this->executable->setStringTranslation($this->getStringTranslationStub());
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the actual test.
|
||||
*
|
||||
* @param string $message
|
||||
* The second message to assert.
|
||||
* @param bool $memory_exceeded
|
||||
* Whether to test the memory exceeded case.
|
||||
* @param int|null $memory_usage_first
|
||||
* (optional) The first memory usage value. Defaults to NULL.
|
||||
* @param int|null $memory_usage_second
|
||||
* (optional) The fake amount of memory usage reported after memory reclaim.
|
||||
* Defaults to NULL.
|
||||
* @param int|null $memory_limit
|
||||
* (optional) The memory limit. Defaults to NULL.
|
||||
*/
|
||||
protected function runMemoryExceededTest($message, $memory_exceeded, $memory_usage_first = NULL, $memory_usage_second = NULL, $memory_limit = NULL) {
|
||||
$this->executable->setMemoryLimit($memory_limit ?: $this->memoryLimit);
|
||||
$this->executable->setMemoryUsage($memory_usage_first ?: $this->memoryLimit, $memory_usage_second ?: $this->memoryLimit);
|
||||
$this->executable->setMemoryThreshold(0.85);
|
||||
if ($message) {
|
||||
$this->executable->message->expects($this->at(0))
|
||||
->method('display')
|
||||
->with($this->callback(function ($subject) {
|
||||
return mb_stripos((string) $subject, 'reclaiming memory') !== FALSE;
|
||||
}));
|
||||
$this->executable->message->expects($this->at(1))
|
||||
->method('display')
|
||||
->with($this->callback(function ($subject) use ($message) {
|
||||
return mb_stripos((string) $subject, $message) !== FALSE;
|
||||
}));
|
||||
}
|
||||
else {
|
||||
$this->executable->message->expects($this->never())
|
||||
->method($this->anything());
|
||||
}
|
||||
$result = $this->executable->memoryExceeded();
|
||||
$this->assertEquals($memory_exceeded, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests memoryExceeded method when a new batch is needed.
|
||||
*/
|
||||
public function testMemoryExceededNewBatch() {
|
||||
// First case try reset and then start new batch.
|
||||
$this->runMemoryExceededTest('starting new batch', TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests memoryExceeded method when enough is cleared.
|
||||
*/
|
||||
public function testMemoryExceededClearedEnough() {
|
||||
$this->runMemoryExceededTest('reclaimed enough', FALSE, $this->memoryLimit, $this->memoryLimit * 0.75);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests memoryExceeded when memory usage is not exceeded.
|
||||
*/
|
||||
public function testMemoryNotExceeded() {
|
||||
$this->runMemoryExceededTest('', FALSE, floor($this->memoryLimit * 0.85) - 1);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,497 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Unit;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\migrate\Plugin\MigrateProcessInterface;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\migrate\Plugin\MigrateIdMapInterface;
|
||||
use Drupal\migrate\MigrateException;
|
||||
use Drupal\migrate\Row;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\migrate\MigrateExecutable
|
||||
* @group migrate
|
||||
*/
|
||||
class MigrateExecutableTest extends MigrateTestCase {
|
||||
|
||||
/**
|
||||
* The mocked migration entity.
|
||||
*
|
||||
* @var \Drupal\migrate\Plugin\MigrationInterface|\PHPUnit\Framework\MockObject\MockObject
|
||||
*/
|
||||
protected $migration;
|
||||
|
||||
/**
|
||||
* The mocked migrate message.
|
||||
*
|
||||
* @var \Drupal\migrate\MigrateMessageInterface|\PHPUnit\Framework\MockObject\MockObject
|
||||
*/
|
||||
protected $message;
|
||||
|
||||
/**
|
||||
* The tested migrate executable.
|
||||
*
|
||||
* @var \Drupal\Tests\migrate\Unit\TestMigrateExecutable
|
||||
*/
|
||||
protected $executable;
|
||||
|
||||
/**
|
||||
* The migration's configuration values.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $migrationConfiguration = [
|
||||
'id' => 'test',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->migration = $this->getMigration();
|
||||
$this->message = $this->createMock('Drupal\migrate\MigrateMessageInterface');
|
||||
$event_dispatcher = $this->createMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
|
||||
$this->executable = new TestMigrateExecutable($this->migration, $this->message, $event_dispatcher);
|
||||
$this->executable->setStringTranslation($this->getStringTranslationStub());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests an import with an incomplete rewinding.
|
||||
*/
|
||||
public function testImportWithFailingRewind() {
|
||||
$exception_message = $this->getRandomGenerator()->string();
|
||||
$source = $this->createMock('Drupal\migrate\Plugin\MigrateSourceInterface');
|
||||
$source->expects($this->once())
|
||||
->method('rewind')
|
||||
->will($this->throwException(new \Exception($exception_message)));
|
||||
|
||||
$this->migration->expects($this->any())
|
||||
->method('getSourcePlugin')
|
||||
->will($this->returnValue($source));
|
||||
|
||||
// Ensure that a message with the proper message was added.
|
||||
$this->message->expects($this->once())
|
||||
->method('display')
|
||||
->with("Migration failed with source plugin exception: " . Html::escape($exception_message));
|
||||
|
||||
$result = $this->executable->import();
|
||||
$this->assertEquals(MigrationInterface::RESULT_FAILED, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the import method with a valid row.
|
||||
*/
|
||||
public function testImportWithValidRow() {
|
||||
$source = $this->getMockSource();
|
||||
|
||||
$row = $this->getMockBuilder('Drupal\migrate\Row')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$row->expects($this->once())
|
||||
->method('getSourceIdValues')
|
||||
->will($this->returnValue(['id' => 'test']));
|
||||
|
||||
$this->idMap->expects($this->once())
|
||||
->method('lookupDestinationIds')
|
||||
->with(['id' => 'test'])
|
||||
->will($this->returnValue([['test']]));
|
||||
|
||||
$source->expects($this->once())
|
||||
->method('current')
|
||||
->will($this->returnValue($row));
|
||||
|
||||
$this->executable->setSource($source);
|
||||
|
||||
$this->migration->expects($this->once())
|
||||
->method('getProcessPlugins')
|
||||
->will($this->returnValue([]));
|
||||
|
||||
$destination = $this->createMock('Drupal\migrate\Plugin\MigrateDestinationInterface');
|
||||
$destination->expects($this->once())
|
||||
->method('import')
|
||||
->with($row, ['test'])
|
||||
->will($this->returnValue(['id' => 'test']));
|
||||
|
||||
$this->migration
|
||||
->method('getDestinationPlugin')
|
||||
->willReturn($destination);
|
||||
|
||||
$this->assertSame(MigrationInterface::RESULT_COMPLETED, $this->executable->import());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the import method with a valid row.
|
||||
*/
|
||||
public function testImportWithValidRowWithoutDestinationId() {
|
||||
$source = $this->getMockSource();
|
||||
|
||||
$row = $this->getMockBuilder('Drupal\migrate\Row')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$row->expects($this->once())
|
||||
->method('getSourceIdValues')
|
||||
->will($this->returnValue(['id' => 'test']));
|
||||
|
||||
$this->idMap->expects($this->once())
|
||||
->method('lookupDestinationIds')
|
||||
->with(['id' => 'test'])
|
||||
->will($this->returnValue([['test']]));
|
||||
|
||||
$source->expects($this->once())
|
||||
->method('current')
|
||||
->will($this->returnValue($row));
|
||||
|
||||
$this->executable->setSource($source);
|
||||
|
||||
$this->migration->expects($this->once())
|
||||
->method('getProcessPlugins')
|
||||
->will($this->returnValue([]));
|
||||
|
||||
$destination = $this->createMock('Drupal\migrate\Plugin\MigrateDestinationInterface');
|
||||
$destination->expects($this->once())
|
||||
->method('import')
|
||||
->with($row, ['test'])
|
||||
->will($this->returnValue(TRUE));
|
||||
|
||||
$this->migration
|
||||
->method('getDestinationPlugin')
|
||||
->willReturn($destination);
|
||||
|
||||
$this->idMap->expects($this->never())
|
||||
->method('saveIdMapping');
|
||||
|
||||
$this->assertSame(MigrationInterface::RESULT_COMPLETED, $this->executable->import());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the import method with a valid row.
|
||||
*/
|
||||
public function testImportWithValidRowNoDestinationValues() {
|
||||
$source = $this->getMockSource();
|
||||
|
||||
$row = $this->getMockBuilder('Drupal\migrate\Row')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$row->expects($this->once())
|
||||
->method('getSourceIdValues')
|
||||
->will($this->returnValue(['id' => 'test']));
|
||||
|
||||
$source->expects($this->once())
|
||||
->method('current')
|
||||
->will($this->returnValue($row));
|
||||
|
||||
$this->executable->setSource($source);
|
||||
|
||||
$this->migration->expects($this->once())
|
||||
->method('getProcessPlugins')
|
||||
->will($this->returnValue([]));
|
||||
|
||||
$destination = $this->createMock('Drupal\migrate\Plugin\MigrateDestinationInterface');
|
||||
$destination->expects($this->once())
|
||||
->method('import')
|
||||
->with($row, ['test'])
|
||||
->will($this->returnValue([]));
|
||||
|
||||
$this->migration
|
||||
->method('getDestinationPlugin')
|
||||
->willReturn($destination);
|
||||
|
||||
$this->idMap->expects($this->once())
|
||||
->method('saveIdMapping')
|
||||
->with($row, [], MigrateIdMapInterface::STATUS_FAILED, NULL);
|
||||
|
||||
$this->idMap->expects($this->once())
|
||||
->method('messageCount')
|
||||
->will($this->returnValue(0));
|
||||
|
||||
$this->idMap->expects($this->once())
|
||||
->method('saveMessage');
|
||||
|
||||
$this->idMap->expects($this->once())
|
||||
->method('lookupDestinationIds')
|
||||
->with(['id' => 'test'])
|
||||
->will($this->returnValue([['test']]));
|
||||
|
||||
$this->message->expects($this->once())
|
||||
->method('display')
|
||||
->with('New object was not saved, no error provided');
|
||||
|
||||
$this->assertSame(MigrationInterface::RESULT_COMPLETED, $this->executable->import());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the import method with a thrown MigrateException.
|
||||
*
|
||||
* The MigrationException in this case is being thrown from the destination.
|
||||
*/
|
||||
public function testImportWithValidRowWithDestinationMigrateException() {
|
||||
$exception_message = $this->getRandomGenerator()->string();
|
||||
$source = $this->getMockSource();
|
||||
|
||||
$row = $this->getMockBuilder('Drupal\migrate\Row')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$row->expects($this->once())
|
||||
->method('getSourceIdValues')
|
||||
->will($this->returnValue(['id' => 'test']));
|
||||
|
||||
$source->expects($this->once())
|
||||
->method('current')
|
||||
->will($this->returnValue($row));
|
||||
|
||||
$this->executable->setSource($source);
|
||||
|
||||
$this->migration->expects($this->once())
|
||||
->method('getProcessPlugins')
|
||||
->will($this->returnValue([]));
|
||||
|
||||
$destination = $this->createMock('Drupal\migrate\Plugin\MigrateDestinationInterface');
|
||||
$destination->expects($this->once())
|
||||
->method('import')
|
||||
->with($row, ['test'])
|
||||
->will($this->throwException(new MigrateException($exception_message)));
|
||||
|
||||
$this->migration
|
||||
->method('getDestinationPlugin')
|
||||
->willReturn($destination);
|
||||
|
||||
$this->idMap->expects($this->once())
|
||||
->method('saveIdMapping')
|
||||
->with($row, [], MigrateIdMapInterface::STATUS_FAILED, NULL);
|
||||
|
||||
$this->idMap->expects($this->once())
|
||||
->method('saveMessage');
|
||||
|
||||
$this->idMap->expects($this->once())
|
||||
->method('lookupDestinationIds')
|
||||
->with(['id' => 'test'])
|
||||
->will($this->returnValue([['test']]));
|
||||
|
||||
$this->assertSame(MigrationInterface::RESULT_COMPLETED, $this->executable->import());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the import method with a thrown MigrateException.
|
||||
*
|
||||
* The MigrationException in this case is being thrown from a process plugin.
|
||||
*/
|
||||
public function testImportWithValidRowWithProcesMigrateException() {
|
||||
$exception_message = $this->getRandomGenerator()->string();
|
||||
$source = $this->getMockSource();
|
||||
|
||||
$row = $this->getMockBuilder('Drupal\migrate\Row')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$row->expects($this->once())
|
||||
->method('getSourceIdValues')
|
||||
->willReturn(['id' => 'test']);
|
||||
|
||||
$source->expects($this->once())
|
||||
->method('current')
|
||||
->willReturn($row);
|
||||
|
||||
$this->executable->setSource($source);
|
||||
|
||||
$this->migration->expects($this->once())
|
||||
->method('getProcessPlugins')
|
||||
->willThrowException(new MigrateException($exception_message));
|
||||
|
||||
$destination = $this->createMock('Drupal\migrate\Plugin\MigrateDestinationInterface');
|
||||
$destination->expects($this->never())
|
||||
->method('import');
|
||||
|
||||
$this->migration
|
||||
->method('getDestinationPlugin')
|
||||
->willReturn($destination);
|
||||
|
||||
$this->idMap->expects($this->once())
|
||||
->method('saveIdMapping')
|
||||
->with($row, [], MigrateIdMapInterface::STATUS_FAILED, NULL);
|
||||
|
||||
$this->idMap->expects($this->once())
|
||||
->method('saveMessage');
|
||||
|
||||
$this->idMap->expects($this->never())
|
||||
->method('lookupDestinationIds');
|
||||
|
||||
$this->assertSame(MigrationInterface::RESULT_COMPLETED, $this->executable->import());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the import method with a regular Exception being thrown.
|
||||
*/
|
||||
public function testImportWithValidRowWithException() {
|
||||
$exception_message = $this->getRandomGenerator()->string();
|
||||
$source = $this->getMockSource();
|
||||
|
||||
$row = $this->getMockBuilder('Drupal\migrate\Row')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$row->expects($this->once())
|
||||
->method('getSourceIdValues')
|
||||
->will($this->returnValue(['id' => 'test']));
|
||||
|
||||
$source->expects($this->once())
|
||||
->method('current')
|
||||
->will($this->returnValue($row));
|
||||
|
||||
$this->executable->setSource($source);
|
||||
|
||||
$this->migration->expects($this->once())
|
||||
->method('getProcessPlugins')
|
||||
->will($this->returnValue([]));
|
||||
|
||||
$destination = $this->createMock('Drupal\migrate\Plugin\MigrateDestinationInterface');
|
||||
$destination->expects($this->once())
|
||||
->method('import')
|
||||
->with($row, ['test'])
|
||||
->will($this->throwException(new \Exception($exception_message)));
|
||||
|
||||
$this->migration
|
||||
->method('getDestinationPlugin')
|
||||
->willReturn($destination);
|
||||
|
||||
$this->idMap->expects($this->once())
|
||||
->method('saveIdMapping')
|
||||
->with($row, [], MigrateIdMapInterface::STATUS_FAILED, NULL);
|
||||
|
||||
$this->idMap->expects($this->once())
|
||||
->method('saveMessage');
|
||||
|
||||
$this->idMap->expects($this->once())
|
||||
->method('lookupDestinationIds')
|
||||
->with(['id' => 'test'])
|
||||
->will($this->returnValue([['test']]));
|
||||
|
||||
$this->message->expects($this->once())
|
||||
->method('display')
|
||||
->with($exception_message);
|
||||
|
||||
$this->assertSame(MigrationInterface::RESULT_COMPLETED, $this->executable->import());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the processRow method.
|
||||
*/
|
||||
public function testProcessRow() {
|
||||
$expected = [
|
||||
'test' => 'test destination',
|
||||
'test1' => 'test1 destination',
|
||||
];
|
||||
foreach ($expected as $key => $value) {
|
||||
$plugins[$key][0] = $this->createMock('Drupal\migrate\Plugin\MigrateProcessInterface');
|
||||
$plugins[$key][0]->expects($this->once())
|
||||
->method('getPluginDefinition')
|
||||
->will($this->returnValue([]));
|
||||
$plugins[$key][0]->expects($this->once())
|
||||
->method('transform')
|
||||
->will($this->returnValue($value));
|
||||
}
|
||||
$this->migration->expects($this->once())
|
||||
->method('getProcessPlugins')
|
||||
->with(NULL)
|
||||
->will($this->returnValue($plugins));
|
||||
$row = new Row();
|
||||
$this->executable->processRow($row);
|
||||
foreach ($expected as $key => $value) {
|
||||
$this->assertSame($row->getDestinationProperty($key), $value);
|
||||
}
|
||||
$this->assertSame(count($row->getDestination()), count($expected));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the processRow method with an empty pipeline.
|
||||
*/
|
||||
public function testProcessRowEmptyPipeline() {
|
||||
$this->migration->expects($this->once())
|
||||
->method('getProcessPlugins')
|
||||
->with(NULL)
|
||||
->will($this->returnValue(['test' => []]));
|
||||
$row = new Row();
|
||||
$this->executable->processRow($row);
|
||||
$this->assertSame($row->getDestination(), []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the processRow pipeline exception.
|
||||
*/
|
||||
public function testProcessRowPipelineException() {
|
||||
$row = new Row();
|
||||
$plugin = $this->prophesize(MigrateProcessInterface::class);
|
||||
$plugin->getPluginDefinition()->willReturn(['handle_multiples' => FALSE]);
|
||||
$plugin->transform(NULL, $this->executable, $row, 'destination_id')
|
||||
->willReturn('transform_return_string');
|
||||
$plugin->multiple()->willReturn(TRUE);
|
||||
$plugin->getPluginId()->willReturn('plugin_id');
|
||||
$plugin = $plugin->reveal();
|
||||
$plugins['destination_id'] = [$plugin, $plugin];
|
||||
$this->migration->method('getProcessPlugins')->willReturn($plugins);
|
||||
|
||||
$this->expectException(MigrateException::class);
|
||||
$this->expectExceptionMessage('Pipeline failed at plugin_id plugin for destination destination_id: transform_return_string received instead of an array,');
|
||||
$this->executable->processRow($row);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the processRow method.
|
||||
*/
|
||||
public function testProcessRowEmptyDestination() {
|
||||
$expected = [
|
||||
'test' => 'test destination',
|
||||
'test1' => 'test1 destination',
|
||||
'test2' => NULL,
|
||||
];
|
||||
$row = new Row();
|
||||
$plugins = [];
|
||||
foreach ($expected as $key => $value) {
|
||||
$plugin = $this->prophesize(MigrateProcessInterface::class);
|
||||
$plugin->getPluginDefinition()->willReturn([]);
|
||||
$plugin->transform(NULL, $this->executable, $row, $key)->willReturn($value);
|
||||
$plugin->multiple()->willReturn(TRUE);
|
||||
$plugins[$key][0] = $plugin->reveal();
|
||||
}
|
||||
$this->migration->method('getProcessPlugins')->willReturn($plugins);
|
||||
$this->executable->processRow($row);
|
||||
foreach ($expected as $key => $value) {
|
||||
$this->assertSame($value, $row->getDestinationProperty($key));
|
||||
}
|
||||
$this->assertCount(2, $row->getDestination());
|
||||
$this->assertSame(['test2'], $row->getEmptyDestinationProperties());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a mock migration source instance.
|
||||
*
|
||||
* @return \Drupal\migrate\Plugin\MigrateSourceInterface|\PHPUnit\Framework\MockObject\MockObject
|
||||
* The mocked migration source.
|
||||
*/
|
||||
protected function getMockSource() {
|
||||
$iterator = $this->createMock('\Iterator');
|
||||
|
||||
$class = 'Drupal\migrate\Plugin\migrate\source\SourcePluginBase';
|
||||
$source = $this->getMockBuilder($class)
|
||||
->disableOriginalConstructor()
|
||||
->setMethods(get_class_methods($class))
|
||||
->getMockForAbstractClass();
|
||||
$source->expects($this->once())
|
||||
->method('rewind')
|
||||
->will($this->returnValue(TRUE));
|
||||
$source->expects($this->any())
|
||||
->method('initializeIterator')
|
||||
->will($this->returnValue([]));
|
||||
$source->expects($this->any())
|
||||
->method('valid')
|
||||
->will($this->onConsecutiveCalls(TRUE, FALSE));
|
||||
|
||||
return $source;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Unit;
|
||||
|
||||
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
|
||||
use Drupal\migrate\MigrateLookup;
|
||||
use Drupal\migrate\Plugin\MigrateDestinationInterface;
|
||||
use Drupal\migrate\Plugin\MigrateIdMapInterface;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\migrate\Plugin\MigrationPluginManagerInterface;
|
||||
|
||||
/**
|
||||
* Provides unit testing for the migration lookup service.
|
||||
*
|
||||
* @group migrate
|
||||
*
|
||||
* @coversDefaultClass \Drupal\migrate\MigrateLookup
|
||||
*/
|
||||
class MigrateLookupTest extends MigrateTestCase {
|
||||
|
||||
/**
|
||||
* Tests the lookup function.
|
||||
*
|
||||
* @covers ::lookup
|
||||
*/
|
||||
public function testLookup() {
|
||||
$source_ids = ['id' => '1'];
|
||||
|
||||
$destination_ids = [[2]];
|
||||
|
||||
$id_map = $this->prophesize(MigrateIdMapInterface::class);
|
||||
$id_map->lookupDestinationIds($source_ids)->willReturn($destination_ids);
|
||||
|
||||
$destination = $this->prophesize(MigrateDestinationInterface::class);
|
||||
$destination->getIds()->willReturn(['id' => ['type' => 'integer']]);
|
||||
|
||||
$migration = $this->prophesize(MigrationInterface::class);
|
||||
$migration->getIdMap()->willReturn($id_map->reveal());
|
||||
$migration->getDestinationPlugin()->willReturn($destination->reveal());
|
||||
|
||||
$plugin_manager = $this->prophesize(MigrationPluginManagerInterface::class);
|
||||
$plugin_manager->createInstances('test_migration')->willReturn([$migration->reveal()]);
|
||||
|
||||
$lookup = new MigrateLookup($plugin_manager->reveal());
|
||||
|
||||
$this->assertSame([['id' => 2]], $lookup->lookup('test_migration', $source_ids));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that an appropriate message is logged if a PluginException is thrown.
|
||||
*/
|
||||
public function testExceptionOnMigrationNotFound() {
|
||||
$migration_plugin_manager = $this->prophesize(MigrationPluginManagerInterface::class);
|
||||
$migration_plugin_manager->createInstances('bad_plugin')->willReturn([]);
|
||||
$this->expectException(PluginNotFoundException::class);
|
||||
$lookup = new MigrateLookup($migration_plugin_manager->reveal());
|
||||
$lookup->lookup('bad_plugin', [1]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Unit;
|
||||
|
||||
use Drupal\migrate\Plugin\migrate\id_map\NullIdMap;
|
||||
|
||||
/**
|
||||
* Tests the NULL ID map plugin.
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class MigrateNullIdMapTest extends MigrateTestCase {
|
||||
|
||||
/**
|
||||
* Tests the NULL ID map get message iterator method.
|
||||
*
|
||||
* @group legacy
|
||||
*
|
||||
* @expectedDeprecation getMessageIterator() is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use getMessages() instead. See https://www.drupal.org/node/3060969
|
||||
*/
|
||||
public function testGetMessageIterator() {
|
||||
$id_map = new NullIdMap([], 'null', NULL);
|
||||
$id_map->getMessageIterator();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,555 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Tests\migrate\Unit\MigrateSourceTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\Tests\migrate\Unit;
|
||||
|
||||
use Drupal\Core\Cache\CacheBackendInterface;
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\KeyValueStore\KeyValueFactoryInterface;
|
||||
use Drupal\Core\KeyValueStore\KeyValueStoreInterface;
|
||||
use Drupal\migrate\MigrateException;
|
||||
use Drupal\migrate\MigrateExecutable;
|
||||
use Drupal\migrate\MigrateSkipRowException;
|
||||
use Drupal\migrate\Plugin\migrate\source\SourcePluginBase;
|
||||
use Drupal\migrate\Plugin\MigrateIdMapInterface;
|
||||
use Drupal\migrate\Row;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\migrate\Plugin\migrate\source\SourcePluginBase
|
||||
* @group migrate
|
||||
*/
|
||||
class MigrateSourceTest extends MigrateTestCase {
|
||||
|
||||
/**
|
||||
* Override the migration config.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $defaultMigrationConfiguration = [
|
||||
'id' => 'test_migration',
|
||||
'source' => [],
|
||||
];
|
||||
|
||||
/**
|
||||
* Test row data.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $row = ['test_sourceid1' => '1', 'timestamp' => 500];
|
||||
|
||||
/**
|
||||
* Test source ids.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $sourceIds = ['test_sourceid1' => 'test_sourceid1'];
|
||||
|
||||
/**
|
||||
* The migration entity.
|
||||
*
|
||||
* @var \Drupal\migrate\Plugin\MigrationInterface
|
||||
*/
|
||||
protected $migration;
|
||||
|
||||
/**
|
||||
* The migrate executable.
|
||||
*
|
||||
* @var \Drupal\migrate\MigrateExecutable
|
||||
*/
|
||||
protected $executable;
|
||||
|
||||
/**
|
||||
* Gets the source plugin to test.
|
||||
*
|
||||
* @param array $configuration
|
||||
* (optional) The source configuration. Defaults to an empty array.
|
||||
* @param array $migrate_config
|
||||
* (optional) The migration configuration to be used in
|
||||
* parent::getMigration(). Defaults to an empty array.
|
||||
* @param int $status
|
||||
* (optional) The default status for the new rows to be imported. Defaults
|
||||
* to MigrateIdMapInterface::STATUS_NEEDS_UPDATE.
|
||||
*
|
||||
* @return \Drupal\migrate\Plugin\MigrateSourceInterface
|
||||
* A mocked source plugin.
|
||||
*/
|
||||
protected function getSource($configuration = [], $migrate_config = [], $status = MigrateIdMapInterface::STATUS_NEEDS_UPDATE, $high_water_value = NULL) {
|
||||
$container = new ContainerBuilder();
|
||||
\Drupal::setContainer($container);
|
||||
|
||||
$key_value = $this->createMock(KeyValueStoreInterface::class);
|
||||
|
||||
$key_value_factory = $this->createMock(KeyValueFactoryInterface::class);
|
||||
$key_value_factory
|
||||
->method('get')
|
||||
->with('migrate:high_water')
|
||||
->willReturn($key_value);
|
||||
$container->set('keyvalue', $key_value_factory);
|
||||
|
||||
$container->set('cache.migrate', $this->createMock(CacheBackendInterface::class));
|
||||
|
||||
$this->migrationConfiguration = $this->defaultMigrationConfiguration + $migrate_config;
|
||||
$this->migration = parent::getMigration();
|
||||
$this->executable = $this->getMigrateExecutable($this->migration);
|
||||
|
||||
// Update the idMap for Source so the default is that the row has already
|
||||
// been imported. This allows us to use the highwater mark to decide on the
|
||||
// outcome of whether we choose to import the row.
|
||||
$id_map_array = ['original_hash' => '', 'hash' => '', 'source_row_status' => $status];
|
||||
$this->idMap
|
||||
->expects($this->any())
|
||||
->method('getRowBySource')
|
||||
->willReturn($id_map_array);
|
||||
|
||||
$constructor_args = [$configuration, 'd6_action', [], $this->migration];
|
||||
$methods = ['getModuleHandler', 'fields', 'getIds', '__toString', 'prepareRow', 'initializeIterator'];
|
||||
$source_plugin = $this->getMockBuilder(SourcePluginBase::class)
|
||||
->setMethods($methods)
|
||||
->setConstructorArgs($constructor_args)
|
||||
->getMock();
|
||||
|
||||
$source_plugin
|
||||
->method('fields')
|
||||
->willReturn([]);
|
||||
$source_plugin
|
||||
->method('getIds')
|
||||
->willReturn([]);
|
||||
$source_plugin
|
||||
->method('__toString')
|
||||
->willReturn('');
|
||||
$source_plugin
|
||||
->method('prepareRow')
|
||||
->willReturn(empty($migrate_config['prepare_row_false']));
|
||||
|
||||
$rows = [$this->row];
|
||||
if (isset($configuration['high_water_property']) && isset($high_water_value)) {
|
||||
$property = $configuration['high_water_property']['name'];
|
||||
$rows = array_filter($rows, function (array $row) use ($property, $high_water_value) {
|
||||
return $row[$property] >= $high_water_value;
|
||||
});
|
||||
}
|
||||
$iterator = new \ArrayIterator($rows);
|
||||
|
||||
$source_plugin
|
||||
->method('initializeIterator')
|
||||
->willReturn($iterator);
|
||||
|
||||
$module_handler = $this->createMock(ModuleHandlerInterface::class);
|
||||
$source_plugin
|
||||
->method('getModuleHandler')
|
||||
->willReturn($module_handler);
|
||||
|
||||
$this->migration
|
||||
->method('getSourcePlugin')
|
||||
->willReturn($source_plugin);
|
||||
|
||||
return $source_plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::__construct
|
||||
*/
|
||||
public function testHighwaterTrackChangesIncompatible() {
|
||||
$source_config = ['track_changes' => TRUE, 'high_water_property' => ['name' => 'something']];
|
||||
$this->expectException(MigrateException::class);
|
||||
$this->getSource($source_config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the source count is correct.
|
||||
*
|
||||
* @covers ::count
|
||||
*/
|
||||
public function testCount() {
|
||||
// Mock the cache to validate set() receives appropriate arguments.
|
||||
$container = new ContainerBuilder();
|
||||
$cache = $this->createMock(CacheBackendInterface::class);
|
||||
$cache->expects($this->any())->method('set')
|
||||
->with($this->isType('string'), $this->isType('int'), $this->isType('int'));
|
||||
$container->set('cache.migrate', $cache);
|
||||
\Drupal::setContainer($container);
|
||||
|
||||
// Test that the basic count works.
|
||||
$source = $this->getSource();
|
||||
$this->assertEquals(1, $source->count());
|
||||
|
||||
// Test caching the count works.
|
||||
$source = $this->getSource(['cache_counts' => TRUE]);
|
||||
$this->assertEquals(1, $source->count());
|
||||
|
||||
// Test the skip argument.
|
||||
$source = $this->getSource(['skip_count' => TRUE]);
|
||||
$this->assertEquals(-1, $source->count());
|
||||
|
||||
$this->migrationConfiguration['id'] = 'test_migration';
|
||||
$migration = $this->getMigration();
|
||||
$source = new StubSourceGeneratorPlugin([], '', [], $migration);
|
||||
|
||||
// Test the skipCount property's default value.
|
||||
$this->assertEquals(-1, $source->count());
|
||||
|
||||
// Test the count value using a generator.
|
||||
$source = new StubSourceGeneratorPlugin(['skip_count' => FALSE], '', [], $migration);
|
||||
$this->assertEquals(3, $source->count());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the key can be set for the count cache.
|
||||
*
|
||||
* @covers ::count
|
||||
*/
|
||||
public function testCountCacheKey() {
|
||||
// Mock the cache to validate set() receives appropriate arguments.
|
||||
$container = new ContainerBuilder();
|
||||
$cache = $this->createMock(CacheBackendInterface::class);
|
||||
$cache->expects($this->any())->method('set')
|
||||
->with('test_key', $this->isType('int'), $this->isType('int'));
|
||||
$container->set('cache.migrate', $cache);
|
||||
\Drupal::setContainer($container);
|
||||
|
||||
// Test caching the count with a configured key works.
|
||||
$source = $this->getSource(['cache_counts' => TRUE, 'cache_key' => 'test_key']);
|
||||
$this->assertEquals(1, $source->count());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that we don't get a row if prepareRow() is false.
|
||||
*/
|
||||
public function testPrepareRowFalse() {
|
||||
$source = $this->getSource([], ['prepare_row_false' => TRUE]);
|
||||
|
||||
$source->rewind();
|
||||
$this->assertNull($source->current(), 'No row is available when prepareRow() is false.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that $row->needsUpdate() works as expected.
|
||||
*/
|
||||
public function testNextNeedsUpdate() {
|
||||
$source = $this->getSource();
|
||||
|
||||
// $row->needsUpdate() === TRUE so we get a row.
|
||||
$source->rewind();
|
||||
$this->assertTrue(is_a($source->current(), 'Drupal\migrate\Row'), '$row->needsUpdate() is TRUE so we got a row.');
|
||||
|
||||
// Test that we don't get a row when the incoming row is marked as imported.
|
||||
$source = $this->getSource([], [], MigrateIdMapInterface::STATUS_IMPORTED);
|
||||
$source->rewind();
|
||||
$this->assertNull($source->current(), 'Row was already imported, should be NULL');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that an outdated highwater mark does not cause a row to be imported.
|
||||
*/
|
||||
public function testOutdatedHighwater() {
|
||||
$configuration = [
|
||||
'high_water_property' => [
|
||||
'name' => 'timestamp',
|
||||
],
|
||||
];
|
||||
$source = $this->getSource($configuration, [], MigrateIdMapInterface::STATUS_IMPORTED, $this->row['timestamp'] + 1);
|
||||
|
||||
// The current highwater mark is now higher than the row timestamp so no row
|
||||
// is expected.
|
||||
$source->rewind();
|
||||
$this->assertNull($source->current(), 'Original highwater mark is higher than incoming row timestamp.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that a highwater mark newer than our saved one imports a row.
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function testNewHighwater() {
|
||||
$configuration = [
|
||||
'high_water_property' => [
|
||||
'name' => 'timestamp',
|
||||
],
|
||||
];
|
||||
// Set a highwater property field for source. Now we should have a row
|
||||
// because the row timestamp is greater than the current highwater mark.
|
||||
$source = $this->getSource($configuration, [], MigrateIdMapInterface::STATUS_IMPORTED, $this->row['timestamp'] - 1);
|
||||
|
||||
$source->rewind();
|
||||
$this->assertInstanceOf(Row::class, $source->current());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test basic row preparation.
|
||||
*
|
||||
* @covers ::prepareRow
|
||||
*/
|
||||
public function testPrepareRow() {
|
||||
$this->migrationConfiguration['id'] = 'test_migration';
|
||||
|
||||
// Get a new migration with an id.
|
||||
$migration = $this->getMigration();
|
||||
$source = new StubSourcePlugin([], '', [], $migration);
|
||||
$row = new Row();
|
||||
|
||||
$module_handler = $this->prophesize(ModuleHandlerInterface::class);
|
||||
$module_handler->invokeAll('migrate_prepare_row', [$row, $source, $migration])
|
||||
->willReturn([TRUE, TRUE])
|
||||
->shouldBeCalled();
|
||||
$module_handler->invokeAll('migrate_' . $migration->id() . '_prepare_row', [$row, $source, $migration])
|
||||
->willReturn([TRUE, TRUE])
|
||||
->shouldBeCalled();
|
||||
$source->setModuleHandler($module_handler->reveal());
|
||||
|
||||
// Ensure we don't log this to the mapping table.
|
||||
$this->idMap->expects($this->never())
|
||||
->method('saveIdMapping');
|
||||
|
||||
$this->assertTrue($source->prepareRow($row));
|
||||
|
||||
// Track_changes...
|
||||
$source = new StubSourcePlugin(['track_changes' => TRUE], '', [], $migration);
|
||||
$row2 = $this->prophesize(Row::class);
|
||||
$row2->rehash()
|
||||
->shouldBeCalled();
|
||||
$module_handler->invokeAll('migrate_prepare_row', [$row2, $source, $migration])
|
||||
->willReturn([TRUE, TRUE])
|
||||
->shouldBeCalled();
|
||||
$module_handler->invokeAll('migrate_' . $migration->id() . '_prepare_row', [$row2, $source, $migration])
|
||||
->willReturn([TRUE, TRUE])
|
||||
->shouldBeCalled();
|
||||
$source->setModuleHandler($module_handler->reveal());
|
||||
$this->assertTrue($source->prepareRow($row2->reveal()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that global prepare hooks can skip rows.
|
||||
*
|
||||
* @covers ::prepareRow
|
||||
*/
|
||||
public function testPrepareRowGlobalPrepareSkip() {
|
||||
$this->migrationConfiguration['id'] = 'test_migration';
|
||||
|
||||
$migration = $this->getMigration();
|
||||
$source = new StubSourcePlugin([], '', [], $migration);
|
||||
$row = new Row();
|
||||
|
||||
$module_handler = $this->prophesize(ModuleHandlerInterface::class);
|
||||
// Return a failure from a prepare row hook.
|
||||
$module_handler->invokeAll('migrate_prepare_row', [$row, $source, $migration])
|
||||
->willReturn([TRUE, FALSE, TRUE])
|
||||
->shouldBeCalled();
|
||||
$module_handler->invokeAll('migrate_' . $migration->id() . '_prepare_row', [$row, $source, $migration])
|
||||
->willReturn([TRUE, TRUE])
|
||||
->shouldBeCalled();
|
||||
$source->setModuleHandler($module_handler->reveal());
|
||||
|
||||
$this->idMap->expects($this->once())
|
||||
->method('saveIdMapping')
|
||||
->with($row, [], MigrateIdMapInterface::STATUS_IGNORED);
|
||||
|
||||
$this->assertFalse($source->prepareRow($row));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that migrate specific prepare hooks can skip rows.
|
||||
*
|
||||
* @covers ::prepareRow
|
||||
*/
|
||||
public function testPrepareRowMigratePrepareSkip() {
|
||||
$this->migrationConfiguration['id'] = 'test_migration';
|
||||
|
||||
$migration = $this->getMigration();
|
||||
$source = new StubSourcePlugin([], '', [], $migration);
|
||||
$row = new Row();
|
||||
|
||||
$module_handler = $this->prophesize(ModuleHandlerInterface::class);
|
||||
// Return a failure from a prepare row hook.
|
||||
$module_handler->invokeAll('migrate_prepare_row', [$row, $source, $migration])
|
||||
->willReturn([TRUE, TRUE])
|
||||
->shouldBeCalled();
|
||||
$module_handler->invokeAll('migrate_' . $migration->id() . '_prepare_row', [$row, $source, $migration])
|
||||
->willReturn([TRUE, FALSE, TRUE])
|
||||
->shouldBeCalled();
|
||||
$source->setModuleHandler($module_handler->reveal());
|
||||
|
||||
$this->idMap->expects($this->once())
|
||||
->method('saveIdMapping')
|
||||
->with($row, [], MigrateIdMapInterface::STATUS_IGNORED);
|
||||
|
||||
$this->assertFalse($source->prepareRow($row));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that a skip exception during prepare hooks correctly skips.
|
||||
*
|
||||
* @covers ::prepareRow
|
||||
*/
|
||||
public function testPrepareRowPrepareException() {
|
||||
$this->migrationConfiguration['id'] = 'test_migration';
|
||||
|
||||
$migration = $this->getMigration();
|
||||
$source = new StubSourcePlugin([], '', [], $migration);
|
||||
$row = new Row();
|
||||
|
||||
$module_handler = $this->prophesize(ModuleHandlerInterface::class);
|
||||
// Return a failure from a prepare row hook.
|
||||
$module_handler->invokeAll('migrate_prepare_row', [$row, $source, $migration])
|
||||
->willReturn([TRUE, TRUE])
|
||||
->shouldBeCalled();
|
||||
$module_handler->invokeAll('migrate_' . $migration->id() . '_prepare_row', [$row, $source, $migration])
|
||||
->willThrow(new MigrateSkipRowException())
|
||||
->shouldBeCalled();
|
||||
$source->setModuleHandler($module_handler->reveal());
|
||||
|
||||
// This will only be called on the first prepare because the second
|
||||
// explicitly avoids it.
|
||||
$this->idMap->expects($this->once())
|
||||
->method('saveIdMapping')
|
||||
->with($row, [], MigrateIdMapInterface::STATUS_IGNORED);
|
||||
$this->assertFalse($source->prepareRow($row));
|
||||
|
||||
// Throw an exception the second time that avoids mapping.
|
||||
$e = new MigrateSkipRowException('', FALSE);
|
||||
$module_handler->invokeAll('migrate_' . $migration->id() . '_prepare_row', [$row, $source, $migration])
|
||||
->willThrow($e)
|
||||
->shouldBeCalled();
|
||||
$this->assertFalse($source->prepareRow($row));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that cacheCounts, skipCount, trackChanges preserve their default
|
||||
* values.
|
||||
*/
|
||||
public function testDefaultPropertiesValues() {
|
||||
$this->migrationConfiguration['id'] = 'test_migration';
|
||||
$migration = $this->getMigration();
|
||||
$source = new StubSourceGeneratorPlugin([], '', [], $migration);
|
||||
|
||||
// Test the default value of the skipCount Value;
|
||||
$this->assertTrue($source->getSkipCount());
|
||||
$this->assertTrue($source->getCacheCounts());
|
||||
$this->assertTrue($source->getTrackChanges());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a mock executable for the test.
|
||||
*
|
||||
* @param \Drupal\migrate\Plugin\MigrationInterface $migration
|
||||
* The migration entity.
|
||||
*
|
||||
* @return \Drupal\migrate\MigrateExecutable
|
||||
* The migrate executable.
|
||||
*/
|
||||
protected function getMigrateExecutable($migration) {
|
||||
/** @var \Drupal\migrate\MigrateMessageInterface $message */
|
||||
$message = $this->createMock('Drupal\migrate\MigrateMessageInterface');
|
||||
/** @var \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher */
|
||||
$event_dispatcher = $this->createMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
|
||||
return new MigrateExecutable($migration, $message, $event_dispatcher);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Stubbed source plugin for testing base class implementations.
|
||||
*/
|
||||
class StubSourcePlugin extends SourcePluginBase {
|
||||
|
||||
/**
|
||||
* Helper for setting internal module handler implementation.
|
||||
*
|
||||
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
||||
* The module handler.
|
||||
*/
|
||||
public function setModuleHandler(ModuleHandlerInterface $module_handler) {
|
||||
$this->moduleHandler = $module_handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fields() {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __toString() {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIds() {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function initializeIterator() {
|
||||
return [];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Stubbed source plugin with a generator as iterator. Also it overwrites the
|
||||
* $skipCount, $cacheCounts and $trackChanges properties.
|
||||
*/
|
||||
class StubSourceGeneratorPlugin extends StubSourcePlugin {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $skipCount = TRUE;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $cacheCounts = TRUE;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $trackChanges = TRUE;
|
||||
|
||||
/**
|
||||
* Return the skipCount value.
|
||||
*/
|
||||
public function getSkipCount() {
|
||||
return $this->skipCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the cacheCounts value.
|
||||
*/
|
||||
public function getCacheCounts() {
|
||||
return $this->cacheCounts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the trackChanges value.
|
||||
*/
|
||||
public function getTrackChanges() {
|
||||
return $this->trackChanges;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function initializeIterator() {
|
||||
$data = [
|
||||
['title' => 'foo'],
|
||||
['title' => 'bar'],
|
||||
['title' => 'iggy'],
|
||||
];
|
||||
foreach ($data as $row) {
|
||||
yield $row;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Unit;
|
||||
|
||||
use Drupal\migrate\Plugin\MigrateIdMapInterface;
|
||||
|
||||
/**
|
||||
* Tests the SQL ID map plugin ensureTables() method.
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class MigrateSqlIdMapEnsureTablesTest extends MigrateTestCase {
|
||||
|
||||
/**
|
||||
* The migration configuration, initialized to set the ID and destination IDs.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $migrationConfiguration = [
|
||||
'id' => 'sql_idmap_test',
|
||||
];
|
||||
|
||||
/**
|
||||
* Tests the ensureTables method when the tables do not exist.
|
||||
*/
|
||||
public function testEnsureTablesNotExist() {
|
||||
$fields['source_ids_hash'] = [
|
||||
'type' => 'varchar',
|
||||
'length' => 64,
|
||||
'not null' => 1,
|
||||
'description' => 'Hash of source ids. Used as primary key',
|
||||
];
|
||||
$fields['sourceid1'] = [
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
];
|
||||
$fields['sourceid2'] = [
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
];
|
||||
$fields['destid1'] = [
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => FALSE,
|
||||
];
|
||||
$fields['source_row_status'] = [
|
||||
'type' => 'int',
|
||||
'size' => 'tiny',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => MigrateIdMapInterface::STATUS_IMPORTED,
|
||||
'description' => 'Indicates current status of the source row',
|
||||
];
|
||||
$fields['rollback_action'] = [
|
||||
'type' => 'int',
|
||||
'size' => 'tiny',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => MigrateIdMapInterface::ROLLBACK_DELETE,
|
||||
'description' => 'Flag indicating what to do for this item on rollback',
|
||||
];
|
||||
$fields['last_imported'] = [
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'description' => 'UNIX timestamp of the last time this row was imported',
|
||||
];
|
||||
$fields['hash'] = [
|
||||
'type' => 'varchar',
|
||||
'length' => '64',
|
||||
'not null' => FALSE,
|
||||
'description' => 'Hash of source row data, for detecting changes',
|
||||
];
|
||||
$map_table_schema = [
|
||||
'description' => 'Mappings from source identifier value(s) to destination identifier value(s).',
|
||||
'fields' => $fields,
|
||||
'primary key' => ['source_ids_hash'],
|
||||
'indexes' => [
|
||||
'source' => ['sourceid1', 'sourceid2'],
|
||||
],
|
||||
];
|
||||
$schema = $this->getMockBuilder('Drupal\Core\Database\Schema')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$schema->expects($this->at(0))
|
||||
->method('tableExists')
|
||||
->with('migrate_map_sql_idmap_test')
|
||||
->will($this->returnValue(FALSE));
|
||||
$schema->expects($this->at(1))
|
||||
->method('createTable')
|
||||
->with('migrate_map_sql_idmap_test', $map_table_schema);
|
||||
// Now do the message table.
|
||||
$fields = [];
|
||||
$fields['msgid'] = [
|
||||
'type' => 'serial',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
];
|
||||
$fields['source_ids_hash'] = [
|
||||
'type' => 'varchar',
|
||||
'length' => 64,
|
||||
'not null' => 1,
|
||||
'description' => 'Hash of source ids. Used as primary key',
|
||||
];
|
||||
$fields['level'] = [
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 1,
|
||||
];
|
||||
$fields['message'] = [
|
||||
'type' => 'text',
|
||||
'size' => 'medium',
|
||||
'not null' => TRUE,
|
||||
];
|
||||
$table_schema = [
|
||||
'description' => 'Messages generated during a migration process',
|
||||
'fields' => $fields,
|
||||
'primary key' => ['msgid'],
|
||||
];
|
||||
|
||||
$schema->expects($this->at(2))
|
||||
->method('tableExists')
|
||||
->with('migrate_message_sql_idmap_test')
|
||||
->will($this->returnValue(FALSE));
|
||||
$schema->expects($this->at(3))
|
||||
->method('createTable')
|
||||
->with('migrate_message_sql_idmap_test', $table_schema);
|
||||
$schema->expects($this->any())
|
||||
->method($this->anything());
|
||||
$this->runEnsureTablesTest($schema);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the ensureTables method when the tables exist.
|
||||
*/
|
||||
public function testEnsureTablesExist() {
|
||||
$schema = $this->getMockBuilder('Drupal\Core\Database\Schema')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$schema->expects($this->at(0))
|
||||
->method('tableExists')
|
||||
->with('migrate_map_sql_idmap_test')
|
||||
->will($this->returnValue(TRUE));
|
||||
$schema->expects($this->at(1))
|
||||
->method('fieldExists')
|
||||
->with('migrate_map_sql_idmap_test', 'rollback_action')
|
||||
->will($this->returnValue(FALSE));
|
||||
$field_schema = [
|
||||
'type' => 'int',
|
||||
'size' => 'tiny',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
'description' => 'Flag indicating what to do for this item on rollback',
|
||||
];
|
||||
$schema->expects($this->at(2))
|
||||
->method('addField')
|
||||
->with('migrate_map_sql_idmap_test', 'rollback_action', $field_schema);
|
||||
$schema->expects($this->at(3))
|
||||
->method('fieldExists')
|
||||
->with('migrate_map_sql_idmap_test', 'hash')
|
||||
->will($this->returnValue(FALSE));
|
||||
$field_schema = [
|
||||
'type' => 'varchar',
|
||||
'length' => '64',
|
||||
'not null' => FALSE,
|
||||
'description' => 'Hash of source row data, for detecting changes',
|
||||
];
|
||||
$schema->expects($this->at(4))
|
||||
->method('addField')
|
||||
->with('migrate_map_sql_idmap_test', 'hash', $field_schema);
|
||||
$schema->expects($this->at(5))
|
||||
->method('fieldExists')
|
||||
->with('migrate_map_sql_idmap_test', 'source_ids_hash')
|
||||
->will($this->returnValue(FALSE));
|
||||
$field_schema = [
|
||||
'type' => 'varchar',
|
||||
'length' => '64',
|
||||
'not null' => TRUE,
|
||||
'description' => 'Hash of source ids. Used as primary key',
|
||||
];
|
||||
$schema->expects($this->at(6))
|
||||
->method('addField')
|
||||
->with('migrate_map_sql_idmap_test', 'source_ids_hash', $field_schema);
|
||||
$schema->expects($this->exactly(7))
|
||||
->method($this->anything());
|
||||
$this->runEnsureTablesTest($schema);
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually run the test.
|
||||
*
|
||||
* @param array $schema
|
||||
* The mock schema object with expectations set. The Sql constructor calls
|
||||
* ensureTables() which in turn calls this object and the expectations on
|
||||
* it are the actual test and there are no additional asserts added.
|
||||
*/
|
||||
protected function runEnsureTablesTest($schema) {
|
||||
$database = $this->getMockBuilder('Drupal\Core\Database\Connection')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$database->expects($this->any())
|
||||
->method('schema')
|
||||
->willReturn($schema);
|
||||
$migration = $this->getMigration();
|
||||
$plugin = $this->createMock('Drupal\migrate\Plugin\MigrateSourceInterface');
|
||||
$plugin->expects($this->any())
|
||||
->method('getIds')
|
||||
->willReturn([
|
||||
'source_id_property' => [
|
||||
'type' => 'integer',
|
||||
],
|
||||
'source_id_property_2' => [
|
||||
'type' => 'integer',
|
||||
],
|
||||
]);
|
||||
$migration->expects($this->any())
|
||||
->method('getSourcePlugin')
|
||||
->willReturn($plugin);
|
||||
$plugin = $this->createMock('Drupal\migrate\Plugin\MigrateSourceInterface');
|
||||
$plugin->expects($this->any())
|
||||
->method('getIds')
|
||||
->willReturn([
|
||||
'destination_id_property' => [
|
||||
'type' => 'string',
|
||||
],
|
||||
]);
|
||||
$migration->expects($this->any())
|
||||
->method('getDestinationPlugin')
|
||||
->willReturn($plugin);
|
||||
/** @var \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher */
|
||||
$event_dispatcher = $this->createMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
|
||||
$map = new TestSqlIdMap($database, [], 'sql', [], $migration, $event_dispatcher);
|
||||
$map->getDatabase();
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,178 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Unit;
|
||||
|
||||
use Drupal\Core\Database\Query\SelectInterface;
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\Core\DependencyInjection\ContainerNotInitializedException;
|
||||
use Drupal\Core\KeyValueStore\KeyValueFactoryInterface;
|
||||
use Drupal\Core\KeyValueStore\KeyValueStoreInterface;
|
||||
|
||||
/**
|
||||
* Base class for Migrate module source unit tests.
|
||||
*
|
||||
* @deprecated in drupal:8.2.0 and is removed from drupal:9.0.0. Use
|
||||
* \Drupal\Tests\migrate\Kernel\MigrateSqlSourceTestBase instead.
|
||||
*/
|
||||
abstract class MigrateSqlSourceTestCase extends MigrateTestCase {
|
||||
|
||||
/**
|
||||
* The tested source plugin.
|
||||
*
|
||||
* @var \Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase
|
||||
*/
|
||||
protected $source;
|
||||
|
||||
/**
|
||||
* The database contents.
|
||||
*
|
||||
* Database contents represents a mocked database. It should contain an
|
||||
* associative array with the table name as key, and as many nested arrays as
|
||||
* the number of mocked rows. Each of those faked rows must be another array
|
||||
* with the column name as the key and the value as the cell.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $databaseContents = [];
|
||||
|
||||
/**
|
||||
* The plugin class under test.
|
||||
*
|
||||
* The plugin system is not working during unit testing so the source plugin
|
||||
* class needs to be manually specified.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const PLUGIN_CLASS = '';
|
||||
|
||||
/**
|
||||
* The high water mark at the beginning of the import operation.
|
||||
*
|
||||
* Once the migration is run, we save a mark of the migrated sources, so the
|
||||
* migration can run again and update only new sources or changed sources.
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
const ORIGINAL_HIGH_WATER = NULL;
|
||||
|
||||
/**
|
||||
* Expected results after the source parsing.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $expectedResults = [];
|
||||
|
||||
/**
|
||||
* Expected count of source rows.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $expectedCount = 0;
|
||||
|
||||
/**
|
||||
* The source plugin instance under test.
|
||||
*
|
||||
* @var \Drupal\migrate\Plugin\MigrateSourceInterface
|
||||
*/
|
||||
protected $plugin;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
$module_handler = $this->createMock('Drupal\Core\Extension\ModuleHandlerInterface');
|
||||
$state = $this->createMock('Drupal\Core\State\StateInterface');
|
||||
$entity_manager = $this->createMock('Drupal\Core\Entity\EntityManagerInterface');
|
||||
|
||||
// Mock a key-value store to return high-water values.
|
||||
$key_value = $this->createMock(KeyValueStoreInterface::class);
|
||||
|
||||
// SourcePluginBase does not yet support full dependency injection so we
|
||||
// need to make sure that \Drupal::keyValue() works as expected by mocking
|
||||
// the keyvalue service.
|
||||
$key_value_factory = $this->createMock(KeyValueFactoryInterface::class);
|
||||
$key_value_factory
|
||||
->method('get')
|
||||
->with('migrate:high_water')
|
||||
->willReturn($key_value);
|
||||
|
||||
try {
|
||||
$container = \Drupal::getContainer();
|
||||
}
|
||||
catch (ContainerNotInitializedException $e) {
|
||||
$container = new ContainerBuilder();
|
||||
}
|
||||
$container->set('keyvalue', $key_value_factory);
|
||||
\Drupal::setContainer($container);
|
||||
|
||||
$migration = $this->getMigration();
|
||||
|
||||
// Set the high water value.
|
||||
\Drupal::keyValue('migrate:high_water')
|
||||
->expects($this->any())
|
||||
->method('get')
|
||||
->willReturn(static::ORIGINAL_HIGH_WATER);
|
||||
|
||||
// Setup the plugin.
|
||||
$plugin_class = static::PLUGIN_CLASS;
|
||||
$plugin = new $plugin_class($this->migrationConfiguration['source'], $this->migrationConfiguration['source']['plugin'], [], $migration, $state, $entity_manager);
|
||||
|
||||
// Do some reflection to set the database and moduleHandler.
|
||||
$plugin_reflection = new \ReflectionClass($plugin);
|
||||
$database_property = $plugin_reflection->getProperty('database');
|
||||
$database_property->setAccessible(TRUE);
|
||||
$module_handler_property = $plugin_reflection->getProperty('moduleHandler');
|
||||
$module_handler_property->setAccessible(TRUE);
|
||||
|
||||
// Set the database and the module handler onto our plugin.
|
||||
$database_property->setValue($plugin, $this->getDatabase($this->databaseContents + ['test_map' => []]));
|
||||
$module_handler_property->setValue($plugin, $module_handler);
|
||||
|
||||
$plugin->setStringTranslation($this->getStringTranslationStub());
|
||||
$migration->expects($this->any())
|
||||
->method('getSourcePlugin')
|
||||
->will($this->returnValue($plugin));
|
||||
$this->source = $plugin;
|
||||
$this->expectedCount = count($this->expectedResults);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the source returns the same rows as expected.
|
||||
*/
|
||||
public function testRetrieval() {
|
||||
$this->assertInstanceOf(SelectInterface::class, $this->source->query());
|
||||
$this->queryResultTest($this->source, $this->expectedResults);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the source returns the row count expected.
|
||||
*/
|
||||
public function testSourceCount() {
|
||||
$count = $this->source->count();
|
||||
$this->assertTrue(is_numeric($count));
|
||||
$this->assertEquals($this->expectedCount, $count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the source defines a valid ID.
|
||||
*/
|
||||
public function testSourceId() {
|
||||
$this->assertNotEmpty($this->source->getIds());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value on a row for a given key.
|
||||
*
|
||||
* @param \Drupal\migrate\Row $row
|
||||
* The row identifier.
|
||||
* @param string $key
|
||||
* The key identifier.
|
||||
*
|
||||
* @return mixed
|
||||
* The value on a row for a given key.
|
||||
*/
|
||||
protected function getValue($row, $key) {
|
||||
return $row->getSourceProperty($key);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Unit;
|
||||
|
||||
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
|
||||
use Drupal\migrate\MigrateStub;
|
||||
use Drupal\migrate\Plugin\MigrateDestinationInterface;
|
||||
use Drupal\migrate\Plugin\MigrateIdMapInterface;
|
||||
use Drupal\migrate\Plugin\MigrateSourceInterface;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\migrate\Plugin\MigrationPluginManagerInterface;
|
||||
use Drupal\migrate\Row;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Prophecy\Argument;
|
||||
|
||||
/**
|
||||
* Tests the migrate stub service.
|
||||
*
|
||||
* @group migrate
|
||||
*
|
||||
* @coversDefaultClass \Drupal\migrate\MigrateStub
|
||||
*/
|
||||
class MigrateStubTest extends TestCase {
|
||||
|
||||
/**
|
||||
* The plugin manager prophecy.
|
||||
*
|
||||
* @var \Prophecy\Prophecy\ObjectProphecy
|
||||
*/
|
||||
protected $migrationPluginManager;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->migrationPluginManager = $this->prophesize(MigrationPluginManagerInterface::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests stubbing.
|
||||
*
|
||||
* @covers ::createStub
|
||||
*/
|
||||
public function testCreateStub() {
|
||||
$destination_plugin = $this->prophesize(MigrateDestinationInterface::class);
|
||||
$destination_plugin->import(Argument::type(Row::class))->willReturn(['id' => 2]);
|
||||
|
||||
$source_plugin = $this->prophesize(MigrateSourceInterface::class);
|
||||
$source_plugin->getIds()->willReturn(['id' => ['type' => 'integer']]);
|
||||
|
||||
$id_map = $this->prophesize(MigrateIdMapInterface::class);
|
||||
|
||||
$migration = $this->prophesize(MigrationInterface::class);
|
||||
$migration->getIdMap()->willReturn($id_map->reveal());
|
||||
$migration->getDestinationPlugin(TRUE)->willReturn($destination_plugin->reveal());
|
||||
$migration->getProcessPlugins([])->willReturn([]);
|
||||
$migration->getProcess()->willReturn([]);
|
||||
$migration->getSourceConfiguration()->willReturn([]);
|
||||
$migration->getSourcePlugin()->willReturn($source_plugin->reveal());
|
||||
|
||||
$this->migrationPluginManager->createInstances(['test_migration'])->willReturn([$migration->reveal()]);
|
||||
|
||||
$stub = new MigrateStub($this->migrationPluginManager->reveal());
|
||||
|
||||
$this->assertSame(['id' => 2], $stub->createStub('test_migration', ['id' => 1], []));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that an error is logged if the plugin manager throws an exception.
|
||||
*/
|
||||
public function testExceptionOnPluginNotFound() {
|
||||
$this->migrationPluginManager->createInstances(['test_migration'])->willReturn([]);
|
||||
$this->expectException(PluginNotFoundException::class);
|
||||
$stub = new MigrateStub($this->migrationPluginManager->reveal());
|
||||
$stub->createStub('test_migration', [1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that an error is logged on derived migrations.
|
||||
*/
|
||||
public function testExceptionOnDerivedMigration() {
|
||||
$this->migrationPluginManager->createInstances(['test_migration'])->willReturn([
|
||||
'test_migration:d1' => $this->prophesize(MigrationInterface::class)->reveal(),
|
||||
'test_migration:d2' => $this->prophesize(MigrationInterface::class)->reveal(),
|
||||
]);
|
||||
$this->expectException(\LogicException::class);
|
||||
$this->expectExceptionMessage('Cannot stub derivable migration "test_migration". You must specify the id of a specific derivative to stub.');
|
||||
$stub = new MigrateStub($this->migrationPluginManager->reveal());
|
||||
$stub->createStub('test_migration', [1]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Unit;
|
||||
|
||||
use Drupal\Core\Database\Driver\sqlite\Connection;
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* Provides setup and helper methods for Migrate module tests.
|
||||
*/
|
||||
abstract class MigrateTestCase extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* An array of migration configuration values.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $migrationConfiguration = [];
|
||||
|
||||
/**
|
||||
* The migration ID map.
|
||||
*
|
||||
* @var \Drupal\migrate\Plugin\MigrateIdMapInterface|\PHPUnit\Framework\MockObject\MockObject
|
||||
*/
|
||||
protected $idMap;
|
||||
|
||||
/**
|
||||
* Local store for mocking setStatus()/getStatus().
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $migrationStatus = MigrationInterface::STATUS_IDLE;
|
||||
|
||||
/**
|
||||
* Retrieves a mocked migration.
|
||||
*
|
||||
* @return \Drupal\migrate\Plugin\MigrationInterface|\PHPUnit\Framework\MockObject\MockObject
|
||||
* The mocked migration.
|
||||
*/
|
||||
protected function getMigration() {
|
||||
$this->migrationConfiguration += ['migrationClass' => 'Drupal\migrate\Plugin\Migration'];
|
||||
$this->idMap = $this->createMock('Drupal\migrate\Plugin\MigrateIdMapInterface');
|
||||
|
||||
$this->idMap
|
||||
->method('getQualifiedMapTableName')
|
||||
->willReturn('test_map');
|
||||
|
||||
$migration = $this->getMockBuilder($this->migrationConfiguration['migrationClass'])
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$migration->method('checkRequirements')
|
||||
->willReturn(TRUE);
|
||||
|
||||
$migration->method('getIdMap')
|
||||
->willReturn($this->idMap);
|
||||
|
||||
// We need the state to be toggled throughout the test so we store the value
|
||||
// on the test class and use a return callback.
|
||||
$migration->expects($this->any())
|
||||
->method('getStatus')
|
||||
->willReturnCallback(function () {
|
||||
return $this->migrationStatus;
|
||||
});
|
||||
$migration->expects($this->any())
|
||||
->method('setStatus')
|
||||
->willReturnCallback(function ($status) {
|
||||
$this->migrationStatus = $status;
|
||||
});
|
||||
|
||||
$migration->method('getMigrationDependencies')
|
||||
->willReturn([
|
||||
'required' => [],
|
||||
'optional' => [],
|
||||
]);
|
||||
|
||||
$configuration = &$this->migrationConfiguration;
|
||||
|
||||
$migration->method('set')
|
||||
->willReturnCallback(function ($argument, $value) use (&$configuration) {
|
||||
$configuration[$argument] = $value;
|
||||
});
|
||||
|
||||
$migration->method('id')
|
||||
->willReturn($configuration['id']);
|
||||
|
||||
return $migration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an SQLite database connection object for use in tests.
|
||||
*
|
||||
* @param array $database_contents
|
||||
* The database contents faked as an array. Each key is a table name, each
|
||||
* value is a list of table rows, an associative array of field => value.
|
||||
* @param array $connection_options
|
||||
* (optional) Options for the database connection. Defaults to an empty
|
||||
* array.
|
||||
*
|
||||
* @return \Drupal\Core\Database\Driver\sqlite\Connection
|
||||
* The database connection.
|
||||
*/
|
||||
protected function getDatabase(array $database_contents, $connection_options = []) {
|
||||
if (extension_loaded('pdo_sqlite')) {
|
||||
$connection_options['database'] = ':memory:';
|
||||
$pdo = Connection::open($connection_options);
|
||||
$connection = new Connection($pdo, $connection_options);
|
||||
}
|
||||
else {
|
||||
$this->markTestSkipped('The pdo_sqlite extension is not available.');
|
||||
}
|
||||
|
||||
// Initialize the DIC with a fake module handler for alterable queries.
|
||||
$container = new ContainerBuilder();
|
||||
$container->set('module_handler', $this->createMock('\Drupal\Core\Extension\ModuleHandlerInterface'));
|
||||
\Drupal::setContainer($container);
|
||||
|
||||
// Create the tables and load them up with data, skipping empty ones.
|
||||
foreach (array_filter($database_contents) as $table => $rows) {
|
||||
$pilot_row = reset($rows);
|
||||
$connection->schema()->createTable($table, $this->createSchemaFromRow($pilot_row));
|
||||
|
||||
$insert = $connection->insert($table)->fields(array_keys($pilot_row));
|
||||
array_walk($rows, [$insert, 'values']);
|
||||
$insert->execute();
|
||||
}
|
||||
|
||||
return $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a table schema from a row.
|
||||
*
|
||||
* @param array $row
|
||||
* The reference row on which to base the schema.
|
||||
*
|
||||
* @return array
|
||||
* The Schema API-ready table schema.
|
||||
*/
|
||||
protected function createSchemaFromRow(array $row) {
|
||||
// SQLite uses loose ("affinity") typing, so it is OK for every column to be
|
||||
// a text field.
|
||||
$fields = array_map(function () {
|
||||
return ['type' => 'text'];
|
||||
}, $row);
|
||||
return ['fields' => $fields];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a query.
|
||||
*
|
||||
* @param array|\Traversable $iter
|
||||
* The countable. foreach-able actual results if a query is being run.
|
||||
* @param array $expected_results
|
||||
* An array of expected results.
|
||||
*/
|
||||
public function queryResultTest($iter, $expected_results) {
|
||||
$this->assertSame(count($expected_results), count($iter), 'Number of results match');
|
||||
$count = 0;
|
||||
foreach ($iter as $data_row) {
|
||||
$expected_row = $expected_results[$count];
|
||||
$count++;
|
||||
foreach ($expected_row as $key => $expected_value) {
|
||||
$this->retrievalAssertHelper($expected_value, $this->getValue($data_row, $key), sprintf('Value matches for key "%s"', $key));
|
||||
}
|
||||
}
|
||||
$this->assertSame(count($expected_results), $count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value on a row for a given key.
|
||||
*
|
||||
* @param array $row
|
||||
* The row information.
|
||||
* @param string $key
|
||||
* The key identifier.
|
||||
*
|
||||
* @return mixed
|
||||
* The value on a row for a given key.
|
||||
*/
|
||||
protected function getValue($row, $key) {
|
||||
return $row[$key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts tested values during test retrieval.
|
||||
*
|
||||
* @param mixed $expected_value
|
||||
* The incoming expected value to test.
|
||||
* @param mixed $actual_value
|
||||
* The incoming value itself.
|
||||
* @param string $message
|
||||
* The tested result as a formatted string.
|
||||
*/
|
||||
protected function retrievalAssertHelper($expected_value, $actual_value, $message) {
|
||||
if (is_array($expected_value)) {
|
||||
// If the expected and actual values are empty, no need to array compare.
|
||||
if (empty($expected_value && $actual_value)) {
|
||||
return;
|
||||
}
|
||||
$this->assertArrayEquals($expected_value, $actual_value, $message);
|
||||
}
|
||||
else {
|
||||
$this->assertSame((string) $expected_value, (string) $actual_value, $message);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Unit;
|
||||
|
||||
use Drupal\migrate\Plugin\Migration;
|
||||
use Drupal\migrate\Plugin\MigrationPluginManager;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\migrate\Plugin\MigrationPluginManager
|
||||
* @group migrate
|
||||
*/
|
||||
class MigrationPluginManagerTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* A plugin manager.
|
||||
*
|
||||
* @var \Drupal\migrate\Plugin\MigrationPluginManager
|
||||
*/
|
||||
protected $pluginManager;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Get a plugin manager for testing.
|
||||
$module_handler = $this->createMock('Drupal\Core\Extension\ModuleHandlerInterface');
|
||||
$cache_backend = $this->createMock('Drupal\Core\Cache\CacheBackendInterface');
|
||||
$language_manager = $this->createMock('Drupal\Core\Language\LanguageManagerInterface');
|
||||
$this->pluginManager = new MigrationPluginManager($module_handler, $cache_backend, $language_manager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests building dependencies for multiple migrations.
|
||||
*
|
||||
* @dataProvider dependencyProvider
|
||||
*/
|
||||
public function testDependencyBuilding($migrations_data, $result_ids) {
|
||||
$migrations = [];
|
||||
foreach ($migrations_data as $migration_id => $migration_data) {
|
||||
$migrations[$migration_id] = new TestMigrationMock($migration_id, $migration_data['dependencies']);
|
||||
}
|
||||
|
||||
$ordered_migrations = $this->pluginManager->buildDependencyMigration($migrations, []);
|
||||
|
||||
// Verify results.
|
||||
$this->assertEquals($result_ids, array_keys($ordered_migrations));
|
||||
foreach ($migrations_data as $migration_id => $migration_data) {
|
||||
$migration = $migrations[$migration_id];
|
||||
|
||||
$requirements = $migration_data['result_requirements'];
|
||||
if (empty($requirements)) {
|
||||
$this->assertEquals([], $migration->set);
|
||||
}
|
||||
else {
|
||||
$requirements = array_combine($requirements, $requirements);
|
||||
|
||||
$this->assertCount(1, $migration->set);
|
||||
list($set_prop, $set_requirements) = reset($migration->set);
|
||||
$this->assertEquals('requirements', $set_prop);
|
||||
$this->assertEquals($requirements, $set_requirements);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide dependency data for testing.
|
||||
*/
|
||||
public function dependencyProvider() {
|
||||
return [
|
||||
// Just one migration, with no dependencies.
|
||||
[
|
||||
[
|
||||
'm1' => [
|
||||
'dependencies' => [],
|
||||
'result_requirements' => [],
|
||||
],
|
||||
],
|
||||
['m1'],
|
||||
],
|
||||
|
||||
// Just one migration, with required dependencies.
|
||||
[
|
||||
[
|
||||
'm1' => [
|
||||
'dependencies' => [
|
||||
'required' => ['required1', 'required2'],
|
||||
],
|
||||
'result_requirements' => ['required1', 'required2'],
|
||||
],
|
||||
],
|
||||
['m1'],
|
||||
],
|
||||
|
||||
// Just one migration, with optional dependencies.
|
||||
[
|
||||
[
|
||||
'm1' => [
|
||||
'dependencies' => [
|
||||
'optional' => ['optional1'],
|
||||
],
|
||||
'result_requirements' => [],
|
||||
],
|
||||
],
|
||||
['m1'],
|
||||
],
|
||||
|
||||
// Multiple migrations.
|
||||
[
|
||||
[
|
||||
'm1' => [
|
||||
'dependencies' => [
|
||||
'required' => ['required1', 'required2'],
|
||||
],
|
||||
'result_requirements' => ['required1', 'required2'],
|
||||
],
|
||||
'm2' => [
|
||||
'dependencies' => [
|
||||
'optional' => ['optional1'],
|
||||
],
|
||||
'result_requirements' => [],
|
||||
],
|
||||
],
|
||||
['m1', 'm2'],
|
||||
],
|
||||
|
||||
// Multiple migrations, reordered due to optional requirement.
|
||||
[
|
||||
[
|
||||
'm1' => [
|
||||
'dependencies' => [
|
||||
'optional' => ['m2'],
|
||||
],
|
||||
'result_requirements' => [],
|
||||
],
|
||||
'm2' => [
|
||||
'dependencies' => [
|
||||
'optional' => ['optional1'],
|
||||
],
|
||||
'result_requirements' => [],
|
||||
],
|
||||
],
|
||||
['m2', 'm1'],
|
||||
],
|
||||
|
||||
// Ensure that optional requirements aren't turned into required ones,
|
||||
// if the last migration has no optional deps.
|
||||
[
|
||||
[
|
||||
'm1' => [
|
||||
'dependencies' => [
|
||||
'optional' => ['m2'],
|
||||
],
|
||||
'result_requirements' => [],
|
||||
],
|
||||
'm2' => [
|
||||
'dependencies' => [],
|
||||
'result_requirements' => [],
|
||||
],
|
||||
],
|
||||
['m2', 'm1'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A mock migration plugin.
|
||||
*
|
||||
* Why are we using a custom class here?
|
||||
*
|
||||
* 1. The function buildDependencyMigration() calls $migration->set(), which
|
||||
* is not actually in MigrationInterface.
|
||||
*
|
||||
* 2. The function buildDependencyMigration() calls array_multisort on an
|
||||
* array with mocks in it. PHPUnit mocks are really complex, and if PHP tries
|
||||
* to compare them it will die with "Nesting level too deep".
|
||||
*/
|
||||
class TestMigrationMock extends Migration {
|
||||
/**
|
||||
* The values passed into set().
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $set = [];
|
||||
|
||||
/**
|
||||
* TestMigrationMock constructor.
|
||||
*/
|
||||
public function __construct($id, $dependencies) {
|
||||
// Intentionally ignore parent constructor.
|
||||
$this->id = $id;
|
||||
$this->dependencies = $dependencies;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function id() {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getMigrationDependencies() {
|
||||
return $this->dependencies;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function set($prop, $value) {
|
||||
$this->set[] = func_get_args();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Tests\migrate\Unit\MigrationTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\Tests\migrate\Unit;
|
||||
|
||||
use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\migrate\Plugin\Migration;
|
||||
use Drupal\migrate\Exception\RequirementsException;
|
||||
use Drupal\migrate\Plugin\MigrateDestinationInterface;
|
||||
use Drupal\migrate\Plugin\MigrateSourceInterface;
|
||||
use Drupal\migrate\Plugin\MigrationPluginManagerInterface;
|
||||
use Drupal\migrate\Plugin\RequirementsInterface;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\migrate\Plugin\Migration
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class MigrationTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* Tests checking requirements for source plugins.
|
||||
*
|
||||
* @covers ::checkRequirements
|
||||
*/
|
||||
public function testRequirementsForSourcePlugin() {
|
||||
$migration = new TestMigration();
|
||||
|
||||
$source_plugin = $this->createMock('Drupal\Tests\migrate\Unit\RequirementsAwareSourceInterface');
|
||||
$source_plugin->expects($this->once())
|
||||
->method('checkRequirements')
|
||||
->willThrowException(new RequirementsException('Missing source requirement', ['key' => 'value']));
|
||||
$destination_plugin = $this->createMock('Drupal\Tests\migrate\Unit\RequirementsAwareDestinationInterface');
|
||||
|
||||
$migration->setSourcePlugin($source_plugin);
|
||||
$migration->setDestinationPlugin($destination_plugin);
|
||||
|
||||
$this->expectException(RequirementsException::class);
|
||||
$this->expectExceptionMessage('Missing source requirement');
|
||||
$migration->checkRequirements();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests checking requirements for destination plugins.
|
||||
*
|
||||
* @covers ::checkRequirements
|
||||
*/
|
||||
public function testRequirementsForDestinationPlugin() {
|
||||
$migration = new TestMigration();
|
||||
|
||||
$source_plugin = $this->createMock('Drupal\migrate\Plugin\MigrateSourceInterface');
|
||||
$destination_plugin = $this->createMock('Drupal\Tests\migrate\Unit\RequirementsAwareDestinationInterface');
|
||||
$destination_plugin->expects($this->once())
|
||||
->method('checkRequirements')
|
||||
->willThrowException(new RequirementsException('Missing destination requirement', ['key' => 'value']));
|
||||
|
||||
$migration->setSourcePlugin($source_plugin);
|
||||
$migration->setDestinationPlugin($destination_plugin);
|
||||
|
||||
$this->expectException(RequirementsException::class);
|
||||
$this->expectExceptionMessage('Missing destination requirement');
|
||||
$migration->checkRequirements();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests checking requirements for destination plugins.
|
||||
*
|
||||
* @covers ::checkRequirements
|
||||
*/
|
||||
public function testRequirementsForMigrations() {
|
||||
$migration = new TestMigration();
|
||||
|
||||
// Setup source and destination plugins without any requirements.
|
||||
$source_plugin = $this->createMock('Drupal\migrate\Plugin\MigrateSourceInterface');
|
||||
$destination_plugin = $this->createMock('Drupal\migrate\Plugin\MigrateDestinationInterface');
|
||||
$migration->setSourcePlugin($source_plugin);
|
||||
$migration->setDestinationPlugin($destination_plugin);
|
||||
|
||||
$plugin_manager = $this->createMock('Drupal\migrate\Plugin\MigrationPluginManagerInterface');
|
||||
$migration->setMigrationPluginManager($plugin_manager);
|
||||
|
||||
// We setup the requirements that test_a doesn't exist and test_c is not
|
||||
// completed yet.
|
||||
$migration->setRequirements(['test_a', 'test_b', 'test_c', 'test_d']);
|
||||
|
||||
$migration_b = $this->createMock(MigrationInterface::class);
|
||||
$migration_c = $this->createMock(MigrationInterface::class);
|
||||
$migration_d = $this->createMock(MigrationInterface::class);
|
||||
|
||||
$migration_b->expects($this->once())
|
||||
->method('allRowsProcessed')
|
||||
->willReturn(TRUE);
|
||||
$migration_c->expects($this->once())
|
||||
->method('allRowsProcessed')
|
||||
->willReturn(FALSE);
|
||||
$migration_d->expects($this->once())
|
||||
->method('allRowsProcessed')
|
||||
->willReturn(TRUE);
|
||||
|
||||
$plugin_manager->expects($this->once())
|
||||
->method('createInstances')
|
||||
->with(['test_a', 'test_b', 'test_c', 'test_d'])
|
||||
->willReturn(['test_b' => $migration_b, 'test_c' => $migration_c, 'test_d' => $migration_d]);
|
||||
|
||||
$this->expectException(RequirementsException::class);
|
||||
$this->expectExceptionMessage('Missing migrations test_a, test_c');
|
||||
$migration->checkRequirements();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests valid migration dependencies configuration returns expected values.
|
||||
*
|
||||
* @param array|null $source
|
||||
* The migration dependencies configuration being tested.
|
||||
* @param array $expected_value
|
||||
* The migration dependencies configuration array expected.
|
||||
*
|
||||
* @covers ::getMigrationDependencies
|
||||
* @dataProvider getValidMigrationDependenciesProvider
|
||||
*
|
||||
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
|
||||
*/
|
||||
public function testMigrationDependenciesWithValidConfig($source, array $expected_value) {
|
||||
$migration = new TestMigration();
|
||||
if (!is_null($source)) {
|
||||
$migration->set('migration_dependencies', $source);
|
||||
}
|
||||
$this->assertSame($migration->getMigrationDependencies(), $expected_value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that getting migration dependencies fails with invalid configuration.
|
||||
*
|
||||
* @covers ::getMigrationDependencies
|
||||
*/
|
||||
public function testMigrationDependenciesWithInvalidConfig() {
|
||||
$migration = new TestMigration();
|
||||
|
||||
// Set the plugin ID to test the returned message.
|
||||
$plugin_id = 'test_migration';
|
||||
$migration->setPluginId($plugin_id);
|
||||
|
||||
// Migration dependencies expects ['optional' => []] or ['required' => []]].
|
||||
$migration->set('migration_dependencies', ['test_migration_dependency']);
|
||||
|
||||
$this->expectException(InvalidPluginDefinitionException::class);
|
||||
$this->expectExceptionMessage("Invalid migration dependencies configuration for migration {$plugin_id}");
|
||||
$migration->getMigrationDependencies();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides data for valid migration configuration test.
|
||||
*/
|
||||
public function getValidMigrationDependenciesProvider() {
|
||||
return [
|
||||
[
|
||||
'source' => NULL,
|
||||
'expected_value' => ['required' => [], 'optional' => []],
|
||||
],
|
||||
[
|
||||
'source' => [],
|
||||
'expected_value' => ['required' => [], 'optional' => []],
|
||||
],
|
||||
[
|
||||
'source' => ['required' => ['test_migration']],
|
||||
'expected_value' => ['required' => ['test_migration'], 'optional' => []],
|
||||
],
|
||||
[
|
||||
'source' => ['optional' => ['test_migration']],
|
||||
'expected_value' => ['optional' => ['test_migration'], 'required' => []],
|
||||
],
|
||||
[
|
||||
'source' => ['required' => ['req_test_migration'], 'optional' => ['opt_test_migration']],
|
||||
'expected_value' => ['required' => ['req_test_migration'], 'optional' => ['opt_test_migration']],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the TestMigration class.
|
||||
*/
|
||||
class TestMigration extends Migration {
|
||||
|
||||
/**
|
||||
* Constructs an instance of TestMigration object.
|
||||
*/
|
||||
public function __construct() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the migration ID (machine name).
|
||||
*
|
||||
* @param string $plugin_id
|
||||
* The plugin_id of the plugin instance.
|
||||
*/
|
||||
public function setPluginId($plugin_id) {
|
||||
$this->pluginId = $plugin_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the requirements values.
|
||||
*
|
||||
* @param array $requirements
|
||||
* The array of requirement values.
|
||||
*/
|
||||
public function setRequirements(array $requirements) {
|
||||
$this->requirements = $requirements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the source Plugin.
|
||||
*
|
||||
* @param \Drupal\migrate\Plugin\MigrateSourceInterface $source_plugin
|
||||
* The source Plugin.
|
||||
*/
|
||||
public function setSourcePlugin(MigrateSourceInterface $source_plugin) {
|
||||
$this->sourcePlugin = $source_plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the destination Plugin.
|
||||
*
|
||||
* @param \Drupal\migrate\Plugin\MigrateDestinationInterface $destination_plugin
|
||||
* The destination Plugin.
|
||||
*/
|
||||
public function setDestinationPlugin(MigrateDestinationInterface $destination_plugin) {
|
||||
$this->destinationPlugin = $destination_plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the plugin manager service.
|
||||
*
|
||||
* @param \Drupal\migrate\Plugin\MigrationPluginManagerInterface $plugin_manager
|
||||
* The plugin manager service.
|
||||
*/
|
||||
public function setMigrationPluginManager(MigrationPluginManagerInterface $plugin_manager) {
|
||||
$this->migrationPluginManager = $plugin_manager;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the RequirementsAwareSourceInterface.
|
||||
*/
|
||||
interface RequirementsAwareSourceInterface extends MigrateSourceInterface, RequirementsInterface {}
|
||||
|
||||
/**
|
||||
* Defines the RequirementsAwareDestinationInterface.
|
||||
*/
|
||||
interface RequirementsAwareDestinationInterface extends MigrateDestinationInterface, RequirementsInterface {}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Unit\Plugin\migrate\destination;
|
||||
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Language\LanguageManagerInterface;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Drupal\migrate\Exception\RequirementsException;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\migrate\Plugin\migrate\destination\Config;
|
||||
|
||||
/**
|
||||
* Tests check requirements exception on DestinationBase.
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class CheckRequirementsTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* Tests the check requirements exception message.
|
||||
*/
|
||||
public function testException() {
|
||||
$destination = new Config(
|
||||
['config_name' => 'test'],
|
||||
'test',
|
||||
[],
|
||||
$this->prophesize(MigrationInterface::class)->reveal(),
|
||||
$this->prophesize(ConfigFactoryInterface::class)->reveal(),
|
||||
$this->prophesize(LanguageManagerInterface::class)->reveal()
|
||||
);
|
||||
$this->expectException(RequirementsException::class);
|
||||
$this->expectExceptionMessage("Destination plugin 'test' did not meet the requirements");
|
||||
$destination->checkRequirements();
|
||||
}
|
||||
|
||||
}
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Tests\migrate\Unit\Plugin\migrate\destination\EntityContentBaseTest
|
||||
*/
|
||||
|
||||
namespace Drupal\Tests\migrate\Unit\Plugin\migrate\destination;
|
||||
|
||||
use Drupal\Core\Entity\ContentEntityInterface;
|
||||
use Drupal\Core\Field\FieldTypePluginManagerInterface;
|
||||
use Drupal\migrate\MigrateException;
|
||||
use Drupal\migrate\Plugin\migrate\destination\EntityContentBase;
|
||||
use Drupal\migrate\Plugin\MigrateIdMapInterface;
|
||||
use Drupal\migrate\Row;
|
||||
|
||||
/**
|
||||
* Tests base entity migration destination functionality.
|
||||
*
|
||||
* @coversDefaultClass \Drupal\migrate\Plugin\migrate\destination\EntityContentBase
|
||||
* @group migrate
|
||||
*/
|
||||
class EntityContentBaseTest extends EntityTestBase {
|
||||
|
||||
/**
|
||||
* Test basic entity save.
|
||||
*
|
||||
* @covers ::import
|
||||
*/
|
||||
public function testImport() {
|
||||
$bundles = [];
|
||||
$destination = new EntityTestDestination([], '', [],
|
||||
$this->migration->reveal(),
|
||||
$this->storage->reveal(),
|
||||
$bundles,
|
||||
$this->entityFieldManager->reveal(),
|
||||
$this->prophesize(FieldTypePluginManagerInterface::class)->reveal());
|
||||
$entity = $this->prophesize(ContentEntityInterface::class);
|
||||
$entity->isValidationRequired()
|
||||
->shouldBeCalledTimes(1);
|
||||
// Assert that save is called.
|
||||
$entity->save()
|
||||
->shouldBeCalledTimes(1);
|
||||
// Set an id for the entity
|
||||
$entity->id()
|
||||
->willReturn(5);
|
||||
$destination->setEntity($entity->reveal());
|
||||
// Ensure the id is saved entity id is returned from import.
|
||||
$this->assertEquals([5], $destination->import(new Row()));
|
||||
// Assert that import set the rollback action.
|
||||
$this->assertEquals(MigrateIdMapInterface::ROLLBACK_DELETE, $destination->rollbackAction());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test row skipping when we can't get an entity to save.
|
||||
*
|
||||
* @covers ::import
|
||||
*/
|
||||
public function testImportEntityLoadFailure() {
|
||||
$bundles = [];
|
||||
$destination = new EntityTestDestination([], '', [],
|
||||
$this->migration->reveal(),
|
||||
$this->storage->reveal(),
|
||||
$bundles,
|
||||
$this->entityFieldManager->reveal(),
|
||||
$this->prophesize(FieldTypePluginManagerInterface::class)->reveal());
|
||||
$destination->setEntity(FALSE);
|
||||
$this->expectException(MigrateException::class);
|
||||
$this->expectExceptionMessage('Unable to get entity');
|
||||
$destination->import(new Row());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that translation destination fails for untranslatable entities.
|
||||
*/
|
||||
public function testUntranslatable() {
|
||||
// An entity type without a language.
|
||||
$this->entityType->getKey('langcode')->willReturn('');
|
||||
$this->entityType->getKey('id')->willReturn('id');
|
||||
$this->entityFieldManager->getBaseFieldDefinitions('foo')
|
||||
->willReturn(['id' => BaseFieldDefinitionTest::create('integer')]);
|
||||
|
||||
$destination = new EntityTestDestination(
|
||||
['translations' => TRUE],
|
||||
'',
|
||||
[],
|
||||
$this->migration->reveal(),
|
||||
$this->storage->reveal(),
|
||||
[],
|
||||
$this->entityFieldManager->reveal(),
|
||||
$this->prophesize(FieldTypePluginManagerInterface::class)->reveal()
|
||||
);
|
||||
$this->expectException(MigrateException::class);
|
||||
$this->expectExceptionMessage('The "foo" entity type does not support translations.');
|
||||
$destination->getIds();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Stub class for testing EntityContentBase methods.
|
||||
*
|
||||
* We want to test things without testing the base class implementations.
|
||||
*/
|
||||
class EntityTestDestination extends EntityContentBase {
|
||||
|
||||
private $entity = NULL;
|
||||
|
||||
public function setEntity($entity) {
|
||||
$this->entity = $entity;
|
||||
}
|
||||
|
||||
protected function getEntity(Row $row, array $old_destination_id_values) {
|
||||
return $this->entity;
|
||||
}
|
||||
|
||||
public static function getEntityTypeId($plugin_id) {
|
||||
return 'foo';
|
||||
}
|
||||
|
||||
}
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\migrate\Unit\Plugin\migrate\destination;
|
||||
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Field\FieldTypePluginManagerInterface;
|
||||
use Drupal\migrate\MigrateException;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\migrate\Plugin\migrate\destination\EntityRevision;
|
||||
use Drupal\migrate\Row;
|
||||
|
||||
/**
|
||||
* Tests entity revision destination functionality.
|
||||
*
|
||||
* @coversDefaultClass \Drupal\migrate\Plugin\migrate\destination\EntityRevision
|
||||
* @group migrate
|
||||
*/
|
||||
class EntityRevisionTest extends EntityTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->migration = $this->prophesize(MigrationInterface::class);
|
||||
$this->storage = $this->prophesize(EntityStorageInterface::class);
|
||||
|
||||
$this->entityType = $this->prophesize(EntityTypeInterface::class);
|
||||
$this->entityType->getSingularLabel()->willReturn('foo');
|
||||
$this->entityType->getPluralLabel()->willReturn('bar');
|
||||
$this->storage->getEntityType()->willReturn($this->entityType->reveal());
|
||||
$this->storage->getEntityTypeId()->willReturn('foo');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that revision destination fails for unrevisionable entities.
|
||||
*/
|
||||
public function testUnrevisionable() {
|
||||
$this->entityType->getKey('id')->willReturn('id');
|
||||
$this->entityType->getKey('revision')->willReturn('');
|
||||
$this->entityFieldManager->getBaseFieldDefinitions('foo')
|
||||
->willReturn([
|
||||
'id' => BaseFieldDefinitionTest::create('integer'),
|
||||
]);
|
||||
|
||||
$destination = new EntityRevisionTestDestination(
|
||||
[],
|
||||
'',
|
||||
[],
|
||||
$this->migration->reveal(),
|
||||
$this->storage->reveal(),
|
||||
[],
|
||||
$this->entityFieldManager->reveal(),
|
||||
$this->prophesize(FieldTypePluginManagerInterface::class)->reveal()
|
||||
);
|
||||
$this->expectException(MigrateException::class);
|
||||
$this->expectExceptionMessage('The "foo" entity type does not support revisions.');
|
||||
$destination->getIds();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that translation destination fails for untranslatable entities.
|
||||
*/
|
||||
public function testUntranslatable() {
|
||||
$this->entityType->getKey('id')->willReturn('id');
|
||||
$this->entityType->getKey('revision')->willReturn('vid');
|
||||
$this->entityType->getKey('langcode')->willReturn('');
|
||||
$this->entityFieldManager->getBaseFieldDefinitions('foo')
|
||||
->willReturn([
|
||||
'id' => BaseFieldDefinitionTest::create('integer'),
|
||||
'vid' => BaseFieldDefinitionTest::create('integer'),
|
||||
]);
|
||||
|
||||
$destination = new EntityRevisionTestDestination(
|
||||
['translations' => TRUE],
|
||||
'',
|
||||
[],
|
||||
$this->migration->reveal(),
|
||||
$this->storage->reveal(),
|
||||
[],
|
||||
$this->entityFieldManager->reveal(),
|
||||
$this->prophesize(FieldTypePluginManagerInterface::class)->reveal()
|
||||
);
|
||||
$this->expectException(MigrateException::class);
|
||||
$this->expectExceptionMessage('The "foo" entity type does not support translations.');
|
||||
$destination->getIds();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Stub class for testing EntityRevision methods.
|
||||
*/
|
||||
class EntityRevisionTestDestination extends EntityRevision {
|
||||
|
||||
private $entity = NULL;
|
||||
|
||||
public function setEntity($entity) {
|
||||
$this->entity = $entity;
|
||||
}
|
||||
|
||||
protected function getEntity(Row $row, array $old_destination_id_values) {
|
||||
return $this->entity;
|
||||
}
|
||||
|
||||
public static function getEntityTypeId($plugin_id) {
|
||||
return 'foo';
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user