Initial Drupal 11 with DDEV setup
This commit is contained in:
@ -0,0 +1,5 @@
|
||||
name: Test display of migrate message
|
||||
type: module
|
||||
description: Tests the display of migrate messages.
|
||||
package: Testing
|
||||
version: VERSION
|
||||
@ -0,0 +1,13 @@
|
||||
id: custom_test
|
||||
label: Test display of upgrade messages
|
||||
source:
|
||||
plugin: embedded_data
|
||||
data_rows:
|
||||
- id: 0
|
||||
ids:
|
||||
id:
|
||||
type: integer
|
||||
process:
|
||||
id: id
|
||||
destination:
|
||||
plugin: null
|
||||
@ -0,0 +1,9 @@
|
||||
id: custom_test_db
|
||||
label: Test display of upgrade messages
|
||||
source:
|
||||
plugin: extension
|
||||
name: i18n_menu
|
||||
process:
|
||||
id: id
|
||||
destination:
|
||||
plugin: null
|
||||
@ -0,0 +1,5 @@
|
||||
name: Cacheable Embedded Data Test
|
||||
type: module
|
||||
description: Module containing a cacheable embedded data source.
|
||||
package: Testing
|
||||
version: VERSION
|
||||
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\migrate_cache_counts_test\Plugin\migrate\source;
|
||||
|
||||
use Drupal\migrate\Attribute\MigrateSource;
|
||||
use Drupal\migrate\Plugin\migrate\source\EmbeddedDataSource;
|
||||
use Drupal\migrate\Plugin\migrate\source\SourcePluginBase;
|
||||
|
||||
/**
|
||||
* A copy of embedded_data which allows caching the count.
|
||||
*/
|
||||
#[MigrateSource('cacheable_embedded_data')]
|
||||
class CacheableEmbeddedDataSource extends EmbeddedDataSource {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function count($refresh = FALSE): int {
|
||||
return SourcePluginBase::count($refresh);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doCount() {
|
||||
return parent::count(TRUE);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
name: 'Migrate entity test'
|
||||
type: module
|
||||
description: 'Support module for entity destination test.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\migrate_entity_test\Entity;
|
||||
|
||||
use Drupal\Core\Entity\Attribute\ContentEntityType;
|
||||
use Drupal\Core\StringTranslation\TranslatableMarkup;
|
||||
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: new TranslatableMarkup('String id entity test'),
|
||||
entity_keys: [
|
||||
'id' => 'id',
|
||||
],
|
||||
base_table: 'migrate_entity_test_string_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'),
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,4 @@
|
||||
name: 'Migrate events test'
|
||||
type: module
|
||||
package: Testing
|
||||
version: VERSION
|
||||
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\migrate_events_test\Plugin\migrate\destination;
|
||||
|
||||
use Drupal\migrate\Attribute\MigrateDestination;
|
||||
use Drupal\migrate\Plugin\migrate\destination\DestinationBase;
|
||||
use Drupal\migrate\Row;
|
||||
|
||||
/**
|
||||
* Migration dummy destination.
|
||||
*/
|
||||
#[MigrateDestination(
|
||||
id: 'dummy',
|
||||
requirements_met: TRUE
|
||||
)]
|
||||
class DummyDestination extends DestinationBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIds() {
|
||||
$ids['value']['type'] = 'string';
|
||||
return $ids;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fields() {
|
||||
return ['value' => 'Dummy value'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function import(Row $row, array $old_destination_id_values = []) {
|
||||
return ['value' => $row->getDestinationProperty('value')];
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
name: 'Migration Expected Migrations Test'
|
||||
type: module
|
||||
description: 'Provides test migrations to test getMigrationDependencies.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
dependencies:
|
||||
- drupal:migrate
|
||||
@ -0,0 +1,7 @@
|
||||
id: m1
|
||||
label: "Sample Migration 1"
|
||||
source:
|
||||
plugin: empty
|
||||
process: {}
|
||||
destination:
|
||||
plugin: 'null'
|
||||
@ -0,0 +1,7 @@
|
||||
id: m2
|
||||
label: "Sample Migration 2"
|
||||
source:
|
||||
plugin: empty
|
||||
process: {}
|
||||
destination:
|
||||
plugin: 'null'
|
||||
@ -0,0 +1,7 @@
|
||||
id: m3
|
||||
label: "Sample Migration 3"
|
||||
source:
|
||||
plugin: empty
|
||||
process: {}
|
||||
destination:
|
||||
plugin: 'null'
|
||||
@ -0,0 +1,7 @@
|
||||
id: m4
|
||||
label: "Sample Migration 4"
|
||||
source:
|
||||
plugin: empty
|
||||
process: {}
|
||||
destination:
|
||||
plugin: 'null'
|
||||
@ -0,0 +1,7 @@
|
||||
id: m5
|
||||
label: "Sample Migration 5"
|
||||
source:
|
||||
plugin: empty
|
||||
process: {}
|
||||
destination:
|
||||
plugin: 'null'
|
||||
@ -0,0 +1,7 @@
|
||||
name: 'Migration external translated test'
|
||||
type: module
|
||||
package: Testing
|
||||
version: VERSION
|
||||
dependencies:
|
||||
- drupal:node
|
||||
- drupal:migrate
|
||||
@ -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
|
||||
@ -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
|
||||
@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\migrate_external_translated_test\Plugin\migrate\source;
|
||||
|
||||
use Drupal\migrate\Attribute\MigrateSource;
|
||||
use Drupal\migrate\Plugin\migrate\source\SourcePluginBase;
|
||||
|
||||
/**
|
||||
* A simple migrate source for our tests.
|
||||
*/
|
||||
#[MigrateSource('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' => 'es - Cat', 'lang' => 'Spanish'],
|
||||
['name' => 'dog', 'title' => 'Dog', 'lang' => 'English'],
|
||||
['name' => 'dog', 'title' => 'fr - Dog', '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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
langcode: en
|
||||
status: true
|
||||
name: High Water import node
|
||||
type: high_water_import_node
|
||||
description: null
|
||||
help: null
|
||||
new_revision: false
|
||||
preview_mode: 1
|
||||
display_submitted: true
|
||||
@ -0,0 +1,6 @@
|
||||
type: module
|
||||
name: Migration High Water Test
|
||||
description: 'Provides test fixtures for testing high water marks.'
|
||||
package: Testing
|
||||
dependencies:
|
||||
- drupal:migrate
|
||||
@ -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
|
||||
@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\migrate_high_water_test\Plugin\migrate\source;
|
||||
|
||||
use Drupal\migrate\Attribute\MigrateSource;
|
||||
use Drupal\migrate\Plugin\migrate\source\SqlBase;
|
||||
|
||||
/**
|
||||
* Source plugin for migration high water tests.
|
||||
*/
|
||||
#[MigrateSource('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' => 'Id',
|
||||
'title' => 'Title',
|
||||
'changed' => 'Changed',
|
||||
];
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIds() {
|
||||
return [
|
||||
'id' => [
|
||||
'type' => 'integer',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
name: 'Migration Lookup Test'
|
||||
type: module
|
||||
description: 'Provides test migrations to test migration lookup service.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
dependencies:
|
||||
- drupal:migrate
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -0,0 +1,6 @@
|
||||
name: 'Migration missing database test'
|
||||
type: module
|
||||
package: Testing
|
||||
version: VERSION
|
||||
dependencies:
|
||||
- drupal:migrate
|
||||
@ -0,0 +1,8 @@
|
||||
id: missing_database
|
||||
label: Migration using a SQL source
|
||||
source:
|
||||
plugin: migrate_missing_database_test
|
||||
process: {}
|
||||
destination:
|
||||
plugin: 'null'
|
||||
migration_dependencies: {}
|
||||
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\migrate_missing_database_test\Plugin\migrate\source;
|
||||
|
||||
use Drupal\Core\Database\Query\SelectInterface;
|
||||
use Drupal\migrate\Attribute\MigrateSource;
|
||||
use Drupal\migrate\Plugin\migrate\source\SqlBase;
|
||||
|
||||
/**
|
||||
* A simple migrate source for the missing database tests.
|
||||
*/
|
||||
#[MigrateSource(
|
||||
id: 'migrate_missing_database_test',
|
||||
requirements_met: TRUE,
|
||||
)]
|
||||
class MigrateMissingDatabaseSource extends SqlBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function query(): SelectInterface {
|
||||
$field_names = ['id'];
|
||||
$query = $this
|
||||
->select('missing_database', 'm')
|
||||
->fields('m', $field_names);
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fields(): array {
|
||||
$fields = [
|
||||
'id' => $this->t('ID'),
|
||||
];
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIds(): array {
|
||||
return [
|
||||
'id' => [
|
||||
'type' => 'integer',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@ -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
|
||||
@ -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'
|
||||
@ -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
|
||||
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
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('node_migration_no_migrate_drupal');
|
||||
$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.',
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
name: 'Migration plugin configuration rest'
|
||||
type: module
|
||||
description: 'Provides test migrations to test altering configuration.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
@ -0,0 +1,8 @@
|
||||
id: simple_migration
|
||||
label: Simple migration test
|
||||
source:
|
||||
plugin: simple_source
|
||||
process:
|
||||
foo: foo
|
||||
destination:
|
||||
plugin: null
|
||||
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\migrate_plugin_config_test\Plugin\migrate\source;
|
||||
|
||||
use Drupal\migrate\Attribute\MigrateSource;
|
||||
use Drupal\migrate\Plugin\migrate\source\SqlBase;
|
||||
use Drupal\Core\Database\Query\SelectInterface;
|
||||
|
||||
/**
|
||||
* Simple source for testing changing configuration.
|
||||
*/
|
||||
#[MigrateSource('simple_source')]
|
||||
class SimpleSource extends SqlBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function query(): SelectInterface {
|
||||
return $this->select('source_table', 's')->fields('s', ['id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fields(): array {
|
||||
return [
|
||||
'foo' => $this->t('Test field.'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIds(): array {
|
||||
return ['foo' => ['type' => 'string']];
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
name: 'Migrate module prepareRow tests'
|
||||
type: module
|
||||
description: 'Support module for source plugin prepareRow testing.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\migrate_prepare_row_test\Hook;
|
||||
|
||||
use Drupal\migrate\MigrateSkipRowException;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\migrate\Plugin\MigrateSourceInterface;
|
||||
use Drupal\migrate\Row;
|
||||
use Drupal\Core\Hook\Attribute\Hook;
|
||||
|
||||
/**
|
||||
* Hook implementations for migrate_prepare_row_test.
|
||||
*/
|
||||
class MigratePrepareRowTestHooks {
|
||||
|
||||
/**
|
||||
* Implements hook_migrate_prepare_row().
|
||||
*/
|
||||
#[Hook('migrate_prepare_row')]
|
||||
public function migratePrepareRow(Row $row, MigrateSourceInterface $source, MigrationInterface $migration): void {
|
||||
// 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_do_not_record') {
|
||||
// Don't record mapping but record a message.
|
||||
throw new MigrateSkipRowException('skip_and_do_not_record message', FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\migrate_prepare_row_test\Plugin\migrate\process;
|
||||
|
||||
use Drupal\migrate\Attribute\MigrateProcess;
|
||||
use Drupal\migrate\MigrateExecutableInterface;
|
||||
use Drupal\migrate\MigrateSkipRowException;
|
||||
use Drupal\migrate\ProcessPluginBase;
|
||||
use Drupal\migrate\Row;
|
||||
|
||||
/**
|
||||
* Provides a testing process plugin that skips rows.
|
||||
*/
|
||||
#[MigrateProcess('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_do_not_record (use plugin)') {
|
||||
throw new MigrateSkipRowException('', FALSE);
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
type: module
|
||||
name: Migrate query batch Source test
|
||||
description: 'Provides a database table and records for SQL import with batch testing.'
|
||||
package: Testing
|
||||
dependencies:
|
||||
- drupal:migrate
|
||||
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\migrate_query_batch_test\Plugin\migrate\source;
|
||||
|
||||
use Drupal\migrate\Attribute\MigrateSource;
|
||||
use Drupal\migrate\Plugin\migrate\source\SqlBase;
|
||||
|
||||
/**
|
||||
* Source plugin for migration high water tests.
|
||||
*/
|
||||
#[MigrateSource('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' => 'Id',
|
||||
'data' => 'data',
|
||||
];
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIds() {
|
||||
return [
|
||||
'id' => [
|
||||
'type' => 'integer',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
name: 'Migrate module prepareRow tests'
|
||||
type: module
|
||||
description: 'Support module for source plugin prepareRow testing.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\migrate_skip_all_rows_test\Hook;
|
||||
|
||||
use Drupal\migrate\MigrateSkipRowException;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\migrate\Plugin\MigrateSourceInterface;
|
||||
use Drupal\migrate\Row;
|
||||
use Drupal\Core\Hook\Attribute\Hook;
|
||||
|
||||
/**
|
||||
* Hook implementations for migrate_skip_all_rows_test.
|
||||
*/
|
||||
class MigrateSkipAllRowsTestHooks {
|
||||
|
||||
/**
|
||||
* Implements hook_migrate_prepare_row().
|
||||
*/
|
||||
#[Hook('migrate_prepare_row')]
|
||||
public function migratePrepareRow(Row $row, MigrateSourceInterface $source, MigrationInterface $migration): void {
|
||||
if (\Drupal::state()->get('migrate_skip_all_rows_test_migrate_prepare_row')) {
|
||||
throw new MigrateSkipRowException();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
name: 'Migrate module source annotation bc tests'
|
||||
type: module
|
||||
description: 'Support module for source plugin annotation discovery backwards compatibility tests'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\migrate_source_annotation_bc_test\Plugin\migrate\source;
|
||||
|
||||
use Drupal\migrate\Plugin\migrate\source\SourcePluginBase;
|
||||
|
||||
/**
|
||||
* A migration source plugin with annotations and a single provider.
|
||||
*
|
||||
* This plugin exists to test backwards compatibility of source plugin discovery
|
||||
* for plugin classes using annotations. This class has no providers other than
|
||||
* 'migrate_source_annotation_bc_test' and 'core'. This class and its annotation
|
||||
* should remain until annotation support is completely removed.
|
||||
*
|
||||
* @MigrateSource(
|
||||
* id = "annotated",
|
||||
* source_module = "migrate"
|
||||
* )
|
||||
*/
|
||||
class MigrateSourceWithAnnotations extends SourcePluginBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fields() {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __toString() {
|
||||
return 'Annotated';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIds() {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function initializeIterator() {
|
||||
return new \ArrayIterator();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\migrate_source_annotation_bc_test\Plugin\migrate\source;
|
||||
|
||||
use Drupal\migrate_drupal\Plugin\migrate\source\EmptySource;
|
||||
|
||||
/**
|
||||
* A migration source plugin with annotations and multiple providers.
|
||||
*
|
||||
* This plugin exists to test backwards compatibility of source plugin discovery
|
||||
* for plugin classes using annotations. This class has an additional provider,
|
||||
* because it extends a plugin in migrate_drupal. This class and its annotation
|
||||
* should remain until annotation support is completely removed.
|
||||
*
|
||||
* @MigrateSource(
|
||||
* id = "annotated_multiple_providers",
|
||||
* source_module = "migrate"
|
||||
* )
|
||||
*/
|
||||
class MigrateSourceWithAnnotationsMultipleProviders extends EmptySource {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function __toString() {
|
||||
return 'Annotated multiple providers';
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,4 @@
|
||||
type: module
|
||||
name: Migrate SqlBase count cache test
|
||||
description: Provides a source plugin to test that counts are cached in SQL sources.
|
||||
package: Testing
|
||||
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\migrate_sql_count_cache_test\Plugin\migrate\source;
|
||||
|
||||
use Drupal\migrate\Attribute\MigrateSource;
|
||||
use Drupal\migrate\Plugin\migrate\source\SqlBase;
|
||||
|
||||
/**
|
||||
* Source plugin for Sql count cache test.
|
||||
*/
|
||||
#[MigrateSource('sql_count_cache')]
|
||||
class SqlCountCache extends SqlBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fields() {
|
||||
return [
|
||||
'id' => $this->t('Id'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIds() {
|
||||
return [
|
||||
'id' => [
|
||||
'type' => 'integer',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function query() {
|
||||
return $this->select('source_table', 's')->fields('s', ['id']);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
name: 'Migrate SQL prepare query test'
|
||||
type: module
|
||||
description: 'Provides a source plugin to test prepare query method.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\migrate_sql_prepare_query_test\Plugin\migrate\source;
|
||||
|
||||
use Drupal\migrate\Attribute\MigrateSource;
|
||||
use Drupal\migrate\Plugin\migrate\source\SqlBase;
|
||||
|
||||
/**
|
||||
* Source plugin for prepare query test.
|
||||
*/
|
||||
#[MigrateSource('test_sql_prepare_query')]
|
||||
class TestSqlPrepareQuery extends SqlBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function query() {
|
||||
return $this->select('migrate_source_test')->fields('migrate_source_test');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function prepareQuery() {
|
||||
$this->query = parent::prepareQuery();
|
||||
$this->query->condition('name', 'foo', '!=');
|
||||
return $this->query;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIds() {
|
||||
return ['id' => ['type' => 'integer']];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fields() {
|
||||
return ['id' => 'ID', 'name' => 'Name'];
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
name: 'Migration Stub Test'
|
||||
type: module
|
||||
description: 'Provides test migrations to test migration stub service.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
dependencies:
|
||||
- drupal:migrate
|
||||
@ -0,0 +1,25 @@
|
||||
id: sample_stubbing_migration
|
||||
label: "Sample Stubbing Migration"
|
||||
source:
|
||||
plugin: embedded_data
|
||||
data_rows:
|
||||
- id: 17
|
||||
title: "Sample 1"
|
||||
body_value: "This is the body for ID 17"
|
||||
body_format: "plain_text"
|
||||
- id: 25
|
||||
title: "Sample 2"
|
||||
body_value: "This is the body for ID 25"
|
||||
body_format: "plain_text"
|
||||
- id: 33
|
||||
title: "Sample 3"
|
||||
ids:
|
||||
id:
|
||||
type: integer
|
||||
process:
|
||||
title: title
|
||||
body/0/value: body_value
|
||||
body/0/format: body_format
|
||||
destination:
|
||||
default_bundle: node_stub
|
||||
plugin: entity:node
|
||||
@ -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"
|
||||
body_value: "This is the body for ID 17"
|
||||
body_format: "plain_text"
|
||||
- id: 25
|
||||
version_id: 25
|
||||
title: "Sample 2"
|
||||
body_value: "This is the body for ID 25"
|
||||
body_format: "plain_text"
|
||||
ids:
|
||||
id:
|
||||
type: integer
|
||||
version_id:
|
||||
type: integer
|
||||
process:
|
||||
title: title
|
||||
body/0/value: body_value
|
||||
body/0/format: body_format
|
||||
destination:
|
||||
default_bundle: node_stub
|
||||
plugin: entity:node
|
||||
@ -0,0 +1,7 @@
|
||||
name: Migration Tag Test
|
||||
type: module
|
||||
description: Provide migrations for testing 'migration_tags' property
|
||||
package: Testing
|
||||
version: VERSION
|
||||
dependencies:
|
||||
- drupal:migrate
|
||||
@ -0,0 +1,16 @@
|
||||
id: tag_test_0
|
||||
label: migration tag test 0
|
||||
migration_tags:
|
||||
- test
|
||||
- tag_test_0
|
||||
source:
|
||||
plugin: embedded_data
|
||||
data_rows:
|
||||
- id: 0
|
||||
ids:
|
||||
id:
|
||||
type: integer
|
||||
process:
|
||||
id: id
|
||||
destination:
|
||||
plugin: null
|
||||
@ -0,0 +1,16 @@
|
||||
id: tag_test_1
|
||||
label: migration tag test 1
|
||||
migration_tags:
|
||||
- test
|
||||
- tag_test_1
|
||||
source:
|
||||
plugin: embedded_data
|
||||
data_rows:
|
||||
- id: 0
|
||||
ids:
|
||||
id:
|
||||
type: integer
|
||||
process:
|
||||
id: id
|
||||
destination:
|
||||
plugin: null
|
||||
@ -0,0 +1,13 @@
|
||||
id: tag_test_no_tag
|
||||
label: migration tag test no tags
|
||||
source:
|
||||
plugin: embedded_data
|
||||
data_rows:
|
||||
- id: 0
|
||||
ids:
|
||||
id:
|
||||
type: integer
|
||||
process:
|
||||
id: id
|
||||
destination:
|
||||
plugin: null
|
||||
@ -0,0 +1,8 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies: { }
|
||||
name: Track changes import term
|
||||
vid: track_changes_import_term
|
||||
description: ''
|
||||
weight: 0
|
||||
new_revision: false
|
||||
@ -0,0 +1,6 @@
|
||||
type: module
|
||||
name: Migration Track Changes Test
|
||||
description: 'Provides test fixtures for testing track changes marks.'
|
||||
package: Testing
|
||||
dependencies:
|
||||
- drupal:migrate
|
||||
@ -0,0 +1,19 @@
|
||||
id: track_changes_test
|
||||
label: Track changes test.
|
||||
source:
|
||||
plugin: track_changes_test
|
||||
track_changes: true
|
||||
batch_size: 1
|
||||
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'
|
||||
@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\migrate_track_changes_test\Plugin\migrate\source;
|
||||
|
||||
use Drupal\migrate\Attribute\MigrateSource;
|
||||
use Drupal\migrate\Plugin\migrate\source\SqlBase;
|
||||
|
||||
/**
|
||||
* Source plugin for migration track changes tests.
|
||||
*/
|
||||
#[MigrateSource('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' => 'Term id',
|
||||
'name' => 'Name',
|
||||
'description' => 'Description',
|
||||
];
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIds() {
|
||||
return [
|
||||
'tid' => [
|
||||
'type' => 'integer',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\migrate\Functional;
|
||||
|
||||
use Drupal\Tests\system\Functional\Module\GenericModuleTestBase;
|
||||
|
||||
/**
|
||||
* Generic module test for migrate.
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class GenericTest extends GenericModuleTestBase {}
|
||||
@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\migrate\Functional;
|
||||
|
||||
/**
|
||||
* Tests for the MigrateController class.
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class MigrateMessageControllerTest extends MigrateMessageTestBase {
|
||||
|
||||
/**
|
||||
* Tests the overview page for migrate messages.
|
||||
*
|
||||
* Tests the overview page with the following scenarios;
|
||||
* - No message tables.
|
||||
* - With message tables.
|
||||
*/
|
||||
public function testOverview(): void {
|
||||
$session = $this->assertSession();
|
||||
|
||||
// First, test with no source database or message tables.
|
||||
$this->drupalGet('/admin/reports/migration-messages');
|
||||
$session->titleEquals('Migration messages | Drupal');
|
||||
$session->pageTextContainsOnce('There are no migration message tables.');
|
||||
|
||||
// Create map and message tables.
|
||||
$this->createTables($this->migrationIds);
|
||||
|
||||
// Now, test with message tables.
|
||||
$this->drupalGet('/admin/reports/migration-messages');
|
||||
foreach ($this->migrationIds as $migration_id) {
|
||||
$session->pageTextContains($migration_id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the detail pages for migrate messages.
|
||||
*
|
||||
* Tests the detail page with the following scenarios;
|
||||
* - No source database connection or message tables with a valid and an
|
||||
* invalid migration.
|
||||
* - A source database connection with message tables with a valid and an
|
||||
* invalid migration.
|
||||
* - A source database connection with message tables and a source plugin
|
||||
* that does not have a description for a source ID in the values returned
|
||||
* from fields().
|
||||
*/
|
||||
public function testDetail(): void {
|
||||
$session = $this->assertSession();
|
||||
|
||||
// Details page with invalid migration.
|
||||
$this->drupalGet('/admin/reports/migration-messages/invalid');
|
||||
$session->statusCodeEquals(404);
|
||||
|
||||
// Details page with valid migration.
|
||||
$this->drupalGet('/admin/reports/migration-messages/custom_test');
|
||||
$session->statusCodeEquals(404);
|
||||
|
||||
// Create map and message tables.
|
||||
$this->createTables($this->migrationIds);
|
||||
|
||||
$not_available_text = "When there is an error processing a row, the migration system saves the error message but not the source ID(s) of the row. That is why some messages in this table have 'Not available' in the source ID column(s).";
|
||||
|
||||
// Test details page for each migration.
|
||||
foreach ($this->migrationIds as $migration_id) {
|
||||
$this->drupalGet("/admin/reports/migration-messages/$migration_id");
|
||||
$session->pageTextContains($migration_id);
|
||||
if ($migration_id == 'custom_test') {
|
||||
$session->pageTextContains('Not available');
|
||||
$session->pageTextContains($not_available_text);
|
||||
}
|
||||
}
|
||||
|
||||
// Details page with invalid migration.
|
||||
$this->drupalGet('/admin/reports/migration-messages/invalid');
|
||||
$session->statusCodeEquals(404);
|
||||
|
||||
// Details page for a migration without a map table.
|
||||
$this->database->schema()->dropTable('migrate_map_custom_test');
|
||||
$this->drupalGet('/admin/reports/migration-messages/custom_test');
|
||||
$session->statusCodeEquals(404);
|
||||
|
||||
// Details page for a migration with a map table but no message table.
|
||||
$this->createTables($this->migrationIds);
|
||||
$this->database->schema()->dropTable('migrate_message_custom_test');
|
||||
$this->drupalGet('/admin/reports/migration-messages/custom_test');
|
||||
$session->pageTextContains('The message table is missing for this migration.');
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\migrate\Functional;
|
||||
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
|
||||
/**
|
||||
* Tests for the MessageForm class.
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class MigrateMessageFormTest extends MigrateMessageTestBase {
|
||||
|
||||
/**
|
||||
* Tests the message form.
|
||||
*/
|
||||
public function testFilter(): void {
|
||||
$session = $this->assertSession();
|
||||
|
||||
// Create map and message tables.
|
||||
$this->createTables($this->migrationIds);
|
||||
|
||||
// Expected counts for each error level.
|
||||
$expected = [
|
||||
MigrationInterface::MESSAGE_ERROR => 3,
|
||||
MigrationInterface::MESSAGE_WARNING => 0,
|
||||
MigrationInterface::MESSAGE_NOTICE => 0,
|
||||
MigrationInterface::MESSAGE_INFORMATIONAL => 1,
|
||||
];
|
||||
|
||||
// Confirm that all the entries are displayed.
|
||||
$this->drupalGet('/admin/reports/migration-messages/custom_test');
|
||||
$session->statusCodeEquals(200);
|
||||
$messages = $this->getMessages();
|
||||
$this->assertCount(4, $messages);
|
||||
|
||||
// Set the filter to match each of the two filter-type attributes and
|
||||
// confirm the correct number of entries are displayed.
|
||||
foreach ($expected as $level => $expected_count) {
|
||||
$edit['severity[]'] = $level;
|
||||
$this->submitForm($edit, 'Filter');
|
||||
$count = $this->getLevelCounts($expected);
|
||||
$this->assertEquals($expected_count, $count[$level], sprintf('Count for level %s failed', $level));
|
||||
}
|
||||
|
||||
// Reset the filter
|
||||
$this->submitForm([], 'Reset');
|
||||
$messages = $this->getMessages();
|
||||
$this->assertCount(4, $messages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the count of migration messages by level.
|
||||
*
|
||||
* @param array $levels
|
||||
* The error levels to check.
|
||||
*
|
||||
* @return array
|
||||
* The count of each error level keyed by the error level.
|
||||
*/
|
||||
protected function getLevelCounts(array $levels): array {
|
||||
$entries = $this->getMessages();
|
||||
$count = array_fill(1, count($levels), 0);
|
||||
foreach ($entries as $entry) {
|
||||
if (array_key_exists($entry['severity'], $levels)) {
|
||||
$count[$entry['severity']]++;
|
||||
}
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the migrate messages.
|
||||
*
|
||||
* @return array[]
|
||||
* List of log events where each event is an array with following keys:
|
||||
* - msg_id: (string) A message id.
|
||||
* - severity: (int) The MigrationInterface error level.
|
||||
* - message: (string) The migration message.
|
||||
*/
|
||||
protected function getMessages(): array {
|
||||
$levels = [
|
||||
'Error' => MigrationInterface::MESSAGE_ERROR,
|
||||
'Warning' => MigrationInterface::MESSAGE_WARNING,
|
||||
'Notice' => MigrationInterface::MESSAGE_NOTICE,
|
||||
'Info' => MigrationInterface::MESSAGE_INFORMATIONAL,
|
||||
];
|
||||
$entries = [];
|
||||
$table = $this->xpath('.//table[@id="admin-migrate-msg"]/tbody/tr');
|
||||
foreach ($table as $row) {
|
||||
$cells = $row->findAll('css', 'td');
|
||||
if (count($cells) === 3) {
|
||||
$entries[] = [
|
||||
'msg_id' => $cells[0]->getText(),
|
||||
'severity' => $levels[$cells[1]->getText()],
|
||||
'message' => $cells[2]->getText(),
|
||||
];
|
||||
}
|
||||
}
|
||||
return $entries;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,239 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\migrate\Functional;
|
||||
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\migrate\Plugin\MigrateIdMapInterface;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
|
||||
// cspell:ignore destid sourceid
|
||||
|
||||
/**
|
||||
* Provides base class for testing migrate messages.
|
||||
*/
|
||||
abstract class MigrateMessageTestBase extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = [
|
||||
'message_test',
|
||||
'migrate',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* The database connection.
|
||||
*
|
||||
* @var \Drupal\Core\Database\Connection
|
||||
*/
|
||||
protected $database;
|
||||
|
||||
/**
|
||||
* Migration IDs.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $migrationIds = ['custom_test'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$user = $this->createUser(['view migration messages']);
|
||||
$this->drupalLogin($user);
|
||||
$this->database = \Drupal::database();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates map and message tables for testing.
|
||||
*
|
||||
* @see \Drupal\migrate\Plugin\migrate\id_map\Sql::ensureTables
|
||||
*/
|
||||
protected function createTables($migration_ids): void {
|
||||
foreach ($migration_ids as $migration_id) {
|
||||
$map_table_name = "migrate_map_$migration_id";
|
||||
$message_table_name = "migrate_message_$migration_id";
|
||||
|
||||
if (!$this->database->schema()->tableExists($map_table_name)) {
|
||||
$fields = [];
|
||||
$fields['source_ids_hash'] = [
|
||||
'type' => 'varchar',
|
||||
'length' => '64',
|
||||
'not null' => TRUE,
|
||||
];
|
||||
$fields['sourceid1'] = [
|
||||
'type' => 'varchar',
|
||||
'length' => '255',
|
||||
'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,
|
||||
];
|
||||
$fields['rollback_action'] = [
|
||||
'type' => 'int',
|
||||
'size' => 'tiny',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => MigrateIdMapInterface::ROLLBACK_DELETE,
|
||||
];
|
||||
$fields['last_imported'] = [
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
];
|
||||
$fields['hash'] = [
|
||||
'type' => 'varchar',
|
||||
'length' => '64',
|
||||
'not null' => FALSE,
|
||||
];
|
||||
$schema = [
|
||||
'description' => '',
|
||||
'fields' => $fields,
|
||||
'primary key' => ['source_ids_hash'],
|
||||
];
|
||||
$this->database->schema()->createTable($map_table_name, $schema);
|
||||
|
||||
$rows = [
|
||||
[
|
||||
'source_ids_hash' => '37c655d',
|
||||
'sourceid1' => 'navigation',
|
||||
'destid1' => 'tools',
|
||||
'source_row_status' => '0',
|
||||
'rollback_action' => '1',
|
||||
'last_imported' => '0',
|
||||
'hash' => '',
|
||||
],
|
||||
[
|
||||
'source_ids_hash' => '3a34190',
|
||||
'sourceid1' => 'menu-fixed-lang',
|
||||
'destid1' => 'menu-fixed-lang',
|
||||
'source_row_status' => '0',
|
||||
'rollback_action' => '0',
|
||||
'last_imported' => '0',
|
||||
'hash' => '',
|
||||
],
|
||||
[
|
||||
'source_ids_hash' => '3e51f67',
|
||||
'sourceid1' => 'management',
|
||||
'destid1' => 'admin',
|
||||
'source_row_status' => '0',
|
||||
'rollback_action' => '1',
|
||||
'last_imported' => '0',
|
||||
'hash' => '',
|
||||
],
|
||||
[
|
||||
'source_ids_hash' => '94a5caa',
|
||||
'sourceid1' => 'user-menu',
|
||||
'destid1' => 'account',
|
||||
'source_row_status' => '0',
|
||||
'rollback_action' => '1',
|
||||
'last_imported' => '0',
|
||||
'hash' => '',
|
||||
],
|
||||
[
|
||||
'source_ids_hash' => 'c0efbcca',
|
||||
'sourceid1' => 'main-menu',
|
||||
'destid1' => 'main',
|
||||
'source_row_status' => '0',
|
||||
'rollback_action' => '1',
|
||||
'last_imported' => '0',
|
||||
'hash' => '',
|
||||
],
|
||||
[
|
||||
'source_ids_hash' => 'f64cb72f',
|
||||
'sourceid1' => 'menu-test-menu',
|
||||
'destid1' => 'menu-test-menu',
|
||||
'source_row_status' => '0',
|
||||
'rollback_action' => '0',
|
||||
'last_imported' => '0',
|
||||
'hash' => '',
|
||||
],
|
||||
];
|
||||
foreach ($rows as $row) {
|
||||
$this->database->insert($map_table_name)->fields($row)->execute();
|
||||
}
|
||||
}
|
||||
|
||||
if (!$this->database->schema()->tableExists($message_table_name)) {
|
||||
$fields = [];
|
||||
$fields['msgid'] = [
|
||||
'type' => 'serial',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
];
|
||||
$fields['source_ids_hash'] = [
|
||||
'type' => 'varchar',
|
||||
'length' => '64',
|
||||
'not null' => TRUE,
|
||||
];
|
||||
$fields['level'] = [
|
||||
'type' => 'int',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
'default' => 1,
|
||||
];
|
||||
$fields['message'] = [
|
||||
'type' => 'text',
|
||||
'size' => 'medium',
|
||||
'not null' => TRUE,
|
||||
];
|
||||
$schema = [
|
||||
'description' => '',
|
||||
'fields' => $fields,
|
||||
'primary key' => ['msgid'],
|
||||
];
|
||||
$this->database->schema()->createTable($message_table_name, $schema);
|
||||
|
||||
$rows = [
|
||||
[
|
||||
'msgid' => '1',
|
||||
'source_ids_hash' => '28cfb3d1',
|
||||
'level' => (string) MigrationInterface::MESSAGE_ERROR,
|
||||
'message' => 'Config entities can not be stubbed.',
|
||||
],
|
||||
[
|
||||
'msgid' => '2',
|
||||
'source_ids_hash' => '28cfb3d1',
|
||||
'level' => (string) MigrationInterface::MESSAGE_ERROR,
|
||||
'message' => 'Config entities can not be stubbed.',
|
||||
],
|
||||
[
|
||||
'msgid' => '3',
|
||||
'source_ids_hash' => '05914d93',
|
||||
'level' => (string) MigrationInterface::MESSAGE_ERROR,
|
||||
'message' => 'Config entities can not be stubbed.',
|
||||
],
|
||||
[
|
||||
'msgid' => '4',
|
||||
'source_ids_hash' => '05914d93',
|
||||
'level' => (string) MigrationInterface::MESSAGE_INFORMATIONAL,
|
||||
'message' => 'Config entities can not be stubbed.',
|
||||
],
|
||||
];
|
||||
foreach ($rows as $row) {
|
||||
$this->database->insert($message_table_name)->fields($row)->execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
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(): void {
|
||||
parent::setUp();
|
||||
$this->createContentType(['type' => 'no_migrate_drupal']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests execution of a migration.
|
||||
*/
|
||||
public function testExecutionNoMigrateDrupal(): void {
|
||||
$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,94 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\migrate\Functional\process;
|
||||
|
||||
use Drupal\migrate\MigrateExecutable;
|
||||
use Drupal\migrate\Plugin\MigrateIdMapInterface;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
|
||||
// cspell:ignore destid
|
||||
|
||||
/**
|
||||
* Tests the 'download' process plugin.
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class DownloadFunctionalTest extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['migrate', 'file'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $defaultTheme = 'stark';
|
||||
|
||||
/**
|
||||
* Tests that an exception is thrown bu migration continues with the next row.
|
||||
*/
|
||||
public function testExceptionThrow(): void {
|
||||
$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(MigrationInterface::RESULT_COMPLETED, $result);
|
||||
|
||||
/** @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);
|
||||
|
||||
// Assert critical parts of the error message, but not the exact message,
|
||||
// since it depends on Guzzle's internal implementation of PSR-7.
|
||||
$id = $migration->getPluginId();
|
||||
$this->assertStringContainsString("$id:uri:download:", $message->message);
|
||||
$this->assertStringContainsString($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,123 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
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}
|
||||
*/
|
||||
protected static $modules = [
|
||||
'migrate',
|
||||
'migrate_drupal',
|
||||
'migrate_high_water_test',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static 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;
|
||||
}
|
||||
|
||||
}
|
||||
310
web/core/modules/migrate/tests/src/Kernel/HighWaterTest.php
Normal file
310
web/core/modules/migrate/tests/src/Kernel/HighWaterTest.php
Normal file
@ -0,0 +1,310 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel;
|
||||
|
||||
// cspell:ignore Highwater
|
||||
|
||||
/**
|
||||
* Tests migration high water property.
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class HighWaterTest extends MigrateTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = [
|
||||
'system',
|
||||
'user',
|
||||
'node',
|
||||
'migrate',
|
||||
'migrate_high_water_test',
|
||||
'field',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
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(): void {
|
||||
// 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 high water should not be updated.
|
||||
$this->assertNodeExists('Item 1');
|
||||
$this->assertNodeDoesNotExist('Item 1 updated');
|
||||
|
||||
// Item with equal high water should not be updated.
|
||||
$this->assertNodeExists('Item 2');
|
||||
$this->assertNodeDoesNotExist('Item 2 updated');
|
||||
|
||||
// Item with greater high water should be updated.
|
||||
$this->assertNodeExists('Item 3 updated');
|
||||
$this->assertNodeDoesNotExist('Item 3');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the high water value can be 0.
|
||||
*/
|
||||
public function testZeroHighwater(): void {
|
||||
// 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(): void {
|
||||
// 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(): void {
|
||||
// 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 high water should be updated.
|
||||
$this->assertNodeExists('Item 1 updated');
|
||||
$this->assertNodeDoesNotExist('Item 1');
|
||||
|
||||
// Item with equal high water should be updated.
|
||||
$this->assertNodeExists('Item 2 updated');
|
||||
$this->assertNodeDoesNotExist('Item 2');
|
||||
|
||||
// Item with greater high water 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.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
protected function assertNodeExists(string $title): void {
|
||||
self::assertTrue($this->nodeExists($title));
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that node with given title does not exist.
|
||||
*
|
||||
* @param string $title
|
||||
* Title of the node.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
protected function assertNodeDoesNotExist(string $title): void {
|
||||
self::assertFalse($this->nodeExists($title));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if node with given title exists.
|
||||
*
|
||||
* @param string $title
|
||||
* Title of the node.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if node exists, FALSE otherwise.
|
||||
*/
|
||||
protected function nodeExists($title): bool {
|
||||
$query = \Drupal::entityQuery('node')->accessCheck(FALSE);
|
||||
$result = $query
|
||||
->condition('title', $title)
|
||||
->range(0, 1)
|
||||
->execute();
|
||||
|
||||
return !empty($result);
|
||||
}
|
||||
|
||||
}
|
||||
157
web/core/modules/migrate/tests/src/Kernel/MigrateBundleTest.php
Normal file
157
web/core/modules/migrate/tests/src/Kernel/MigrateBundleTest.php
Normal file
@ -0,0 +1,157 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
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 {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['taxonomy', 'text', 'user', 'system'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->installEntitySchema('user');
|
||||
$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(): void {
|
||||
$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('categories', $term->bundle());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests setting the bundle in the process pipeline.
|
||||
*/
|
||||
public function testProcessBundle(): void {
|
||||
$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('categories', $term->bundle());
|
||||
$term = Term::load(2);
|
||||
$this->assertEquals('tags', $term->bundle());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests setting bundles both in process and destination.
|
||||
*/
|
||||
public function testMixedBundles(): void {
|
||||
$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('categories', $term->bundle());
|
||||
$term = Term::load(2);
|
||||
$this->assertEquals('tags', $term->bundle());
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,169 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel;
|
||||
|
||||
use Drupal\migrate\MigrateExecutable;
|
||||
|
||||
/**
|
||||
* Tests rolling back of configuration objects.
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class MigrateConfigRollbackTest extends MigrateTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['system', 'language', 'config_translation'];
|
||||
|
||||
/**
|
||||
* Tests rolling back configuration.
|
||||
*/
|
||||
public function testConfigRollback(): void {
|
||||
// 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,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
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,71 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests the EmbeddedDataSource plugin.
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class MigrateEmbeddedDataTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['migrate'];
|
||||
|
||||
/**
|
||||
* Tests the embedded_data source plugin.
|
||||
*/
|
||||
public function testEmbeddedData(): void {
|
||||
$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) {
|
||||
$this->assertFalse($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->assertSame($data_rows, $results);
|
||||
|
||||
// Validate the public APIs.
|
||||
$this->assertSameSize($data_rows, $source);
|
||||
$this->assertSame($ids, $source->getIds());
|
||||
$expected_fields = [
|
||||
'key' => 'key',
|
||||
'field1' => 'field1',
|
||||
'field2' => 'field2',
|
||||
];
|
||||
$this->assertSame($expected_fields, $source->fields());
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,447 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel;
|
||||
|
||||
use Drupal\Core\Entity\EntityFieldManager;
|
||||
use Drupal\entity_test\Entity\EntityTestMul;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
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_drupal\Tests\StubTestTrait;
|
||||
use Drupal\migrate_entity_test\Entity\StringIdEntityTest;
|
||||
|
||||
/**
|
||||
* Tests the EntityContentBase destination.
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class MigrateEntityContentBaseTest extends KernelTestBase {
|
||||
|
||||
use StubTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = [
|
||||
'entity_test',
|
||||
'field',
|
||||
'language',
|
||||
'migrate',
|
||||
'user',
|
||||
];
|
||||
|
||||
/**
|
||||
* 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(): void {
|
||||
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');
|
||||
|
||||
$this->installEntitySchema('entity_test_with_bundle');
|
||||
$this->installEntitySchema('entity_test_no_bundle');
|
||||
|
||||
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.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
protected function assertTranslations(int $id, string $default, array $others = []): void {
|
||||
$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): void {
|
||||
$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'),
|
||||
$this->container->get('account_switcher'),
|
||||
$this->container->get('entity_type.bundle.info'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests importing and rolling back translated entities.
|
||||
*/
|
||||
public function testTranslated(): void {
|
||||
// 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(): void {
|
||||
$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(): void {
|
||||
$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(): void {
|
||||
// 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());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests bundle is properly provided for stubs without bundle support.
|
||||
*
|
||||
* @todo Remove this test in when native PHP type-hints will be added for
|
||||
* EntityFieldManagerInterface::getFieldDefinitions(). See
|
||||
* https://www.drupal.org/project/drupal/issues/3050720.
|
||||
*/
|
||||
public function testBundleFallbackForStub(): void {
|
||||
$this->enableModules(['migrate_entity_test']);
|
||||
$this->installEntitySchema('migrate_string_id_entity_test');
|
||||
|
||||
$entity_type_manager = $this->container->get('entity_type.manager');
|
||||
$entity_type_bundle_info = $this->container->get('entity_type.bundle.info');
|
||||
$entity_display_repository = $this
|
||||
->container
|
||||
->get('entity_display.repository');
|
||||
$typed_data_manager = $this->container->get('typed_data_manager');
|
||||
$language_manager = $this->container->get('language_manager');
|
||||
$keyvalue = $this->container->get('keyvalue');
|
||||
$module_handler = $this->container->get('module_handler');
|
||||
$cache_discovery = $this->container->get('cache.discovery');
|
||||
$entity_last_installed_schema_repository = $this
|
||||
->container
|
||||
->get('entity.last_installed_schema.repository');
|
||||
|
||||
$decorated_entity_field_manager = new class ($entity_type_manager, $entity_type_bundle_info, $entity_display_repository, $typed_data_manager, $language_manager, $keyvalue, $module_handler, $cache_discovery, $entity_last_installed_schema_repository) extends EntityFieldManager {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFieldDefinitions($entity_type_id, $bundle) {
|
||||
if (\is_null($bundle)) {
|
||||
throw new \Exception("Bundle value shouldn't be NULL.");
|
||||
}
|
||||
|
||||
return parent::getFieldDefinitions($entity_type_id, $bundle);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
$this->container->set('entity_field.manager', $decorated_entity_field_manager);
|
||||
$this->createEntityStub('migrate_string_id_entity_test');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test destination fields() method.
|
||||
*/
|
||||
public function testFields(): void {
|
||||
$entity_type_manager = $this->container->get('entity_type.manager');
|
||||
// Create two bundles for the entity_test_with_bundle entity type.
|
||||
$bundle_storage = $entity_type_manager->getStorage('entity_test_bundle');
|
||||
$bundle_storage->create([
|
||||
'id' => 'test_bundle_no_fields',
|
||||
'label' => 'Test bundle without fields',
|
||||
])->save();
|
||||
$bundle_storage->create([
|
||||
'id' => 'test_bundle_with_fields',
|
||||
'label' => 'Test bundle with fields',
|
||||
])->save();
|
||||
|
||||
// Create a mock migration and get the destination plugin manager.
|
||||
$migration = $this->prophesize(MigrationInterface::class)->reveal();
|
||||
/** @var \Drupal\migrate\Plugin\MigrateDestinationPluginManager $manager */
|
||||
$manager = \Drupal::service('plugin.manager.migrate.destination');
|
||||
|
||||
// Test with an entity type with no bundles.
|
||||
$destination_plugin = $manager->createInstance('entity:entity_test_no_bundle', [], $migration);
|
||||
$fields = $destination_plugin->fields();
|
||||
$this->assertArrayHasKey('id', $fields);
|
||||
// Confirm the test field is not found.
|
||||
$this->assertArrayNotHasKey('field_text', $fields);
|
||||
|
||||
// Create a text field attached to the entity with no bundles.
|
||||
FieldStorageConfig::create([
|
||||
'type' => 'string',
|
||||
'entity_type' => 'entity_test_no_bundle',
|
||||
'field_name' => 'field_text',
|
||||
])->save();
|
||||
|
||||
FieldConfig::create([
|
||||
'entity_type' => 'entity_test_no_bundle',
|
||||
'bundle' => 'entity_test_no_bundle',
|
||||
'field_name' => 'field_text',
|
||||
])->save();
|
||||
|
||||
// Confirm that the 'field_text' is now found.
|
||||
$destination_plugin = $manager->createInstance('entity:entity_test_no_bundle', [], $migration);
|
||||
$fields = $destination_plugin->fields();
|
||||
$this->assertArrayHasKey('id', $fields);
|
||||
$this->assertArrayHasKey('field_text', $fields);
|
||||
|
||||
// Repeat the test with an entity with bundles.
|
||||
$destination_plugin = $manager->createInstance('entity:entity_test_with_bundle', [], $migration);
|
||||
$fields = $destination_plugin->fields();
|
||||
$this->assertArrayHasKey('id', $fields);
|
||||
$this->assertArrayNotHasKey('field_text', $fields);
|
||||
|
||||
// Create a text field attached to the entity with bundles.
|
||||
FieldStorageConfig::create([
|
||||
'type' => 'string',
|
||||
'entity_type' => 'entity_test_with_bundle',
|
||||
'field_name' => 'field_text',
|
||||
])->save();
|
||||
|
||||
FieldConfig::create([
|
||||
'entity_type' => 'entity_test_with_bundle',
|
||||
'bundle' => 'test_bundle_with_fields',
|
||||
'field_name' => 'field_text',
|
||||
])->save();
|
||||
|
||||
// Confirm that the 'field_text' is found when the default bundle is set.
|
||||
$destination_plugin = $manager->createInstance('entity:entity_test_with_bundle', ['default_bundle' => 'test_bundle_with_fields'], $migration);
|
||||
$fields = $destination_plugin->fields();
|
||||
$this->assertArrayHasKey('id', $fields);
|
||||
$this->assertArrayHasKey('field_text', $fields);
|
||||
|
||||
// Confirm that the 'field_text' is not found when the default bundle is not
|
||||
// set.
|
||||
$destination_plugin = $manager->createInstance('entity:entity_test_with_bundle', [], $migration);
|
||||
$fields = $destination_plugin->fields();
|
||||
$this->assertArrayHasKey('id', $fields);
|
||||
$this->assertArrayNotHasKey('field_text', $fields);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,281 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\filter\Entity\FilterFormat;
|
||||
use Drupal\filter\FilterFormatInterface;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\migrate\Event\MigrateEvents;
|
||||
use Drupal\migrate\Event\MigrateIdMapMessageEvent;
|
||||
use Drupal\migrate\MigrateExecutable;
|
||||
use Drupal\user\Entity\Role;
|
||||
use Drupal\user\Entity\User;
|
||||
use Drupal\user\Plugin\Validation\Constraint\UserNameConstraint;
|
||||
use Drupal\user\RoleInterface;
|
||||
|
||||
/**
|
||||
* Tests validation of an entity during migration.
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class MigrateEntityContentValidationTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = [
|
||||
'entity_test',
|
||||
'field',
|
||||
'filter',
|
||||
'filter_test',
|
||||
'migrate',
|
||||
'system',
|
||||
'text',
|
||||
'user',
|
||||
];
|
||||
|
||||
/**
|
||||
* Messages accumulated during the migration run.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $messages = [];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('user');
|
||||
$this->installEntitySchema('user_role');
|
||||
$this->installEntitySchema('entity_test');
|
||||
$this->installConfig(['field', 'filter_test', 'system', 'user']);
|
||||
|
||||
$this->container
|
||||
->get('event_dispatcher')
|
||||
->addListener(MigrateEvents::IDMAP_MESSAGE, [$this, 'mapMessageRecorder']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests an import with invalid data and checks error messages.
|
||||
*/
|
||||
public function test1(): void {
|
||||
// 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(64),
|
||||
'user_id' => '1',
|
||||
],
|
||||
[
|
||||
'id' => '3',
|
||||
'name' => $this->randomString(64),
|
||||
'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 64 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(): void {
|
||||
$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,
|
||||
],
|
||||
]);
|
||||
|
||||
$message = strtr($username_constraint->tooLongMessage, [
|
||||
'%name' => '<em class="placeholder">' . Html::escape($long_username) . '</em>',
|
||||
'%max' => '<em class="placeholder">' . 60 . '</em>',
|
||||
]);
|
||||
$this->assertSame(sprintf('1: [user]: name=%s||name=%s||mail=Email field is required.', $username_constraint->illegalMessage, $message), $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.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests validation for entities that are instances of EntityOwnerInterface.
|
||||
*/
|
||||
public function testEntityOwnerValidation(): void {
|
||||
// Text format access is impacted by user permissions.
|
||||
$filter_test_format = FilterFormat::load('filter_test');
|
||||
assert($filter_test_format instanceof FilterFormatInterface);
|
||||
|
||||
// Create 2 users, an admin user who has permission to use this text format
|
||||
// and another who does not have said access.
|
||||
$role = Role::create([
|
||||
'id' => 'admin',
|
||||
'label' => 'admin',
|
||||
'is_admin' => TRUE,
|
||||
]);
|
||||
assert($role instanceof RoleInterface);
|
||||
$role->grantPermission($filter_test_format->getPermissionName());
|
||||
$role->save();
|
||||
$admin_user = User::create([
|
||||
'name' => 'foobar',
|
||||
'mail' => 'foobar@example.com',
|
||||
]);
|
||||
$admin_user->addRole($role->id())->save();
|
||||
$normal_user = User::create([
|
||||
'name' => 'normal user',
|
||||
'mail' => 'normal@example.com',
|
||||
]);
|
||||
$normal_user->save();
|
||||
|
||||
// Add a "body" field with the text format.
|
||||
$field_name = $this->randomMachineName();
|
||||
$field_storage = FieldStorageConfig::create([
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'text',
|
||||
]);
|
||||
$field_storage->save();
|
||||
FieldConfig::create([
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => 'entity_test',
|
||||
])->save();
|
||||
|
||||
// Attempt to migrate entities. The first record is owned by an admin user.
|
||||
$definition = [
|
||||
'source' => [
|
||||
'plugin' => 'embedded_data',
|
||||
'data_rows' => [
|
||||
[
|
||||
'id' => 1,
|
||||
'uid' => $admin_user->id(),
|
||||
'body' => [
|
||||
'value' => 'foo',
|
||||
'format' => 'filter_test',
|
||||
],
|
||||
],
|
||||
[
|
||||
'id' => 2,
|
||||
'uid' => $normal_user->id(),
|
||||
'body' => [
|
||||
'value' => 'bar',
|
||||
'format' => 'filter_test',
|
||||
],
|
||||
],
|
||||
],
|
||||
'ids' => [
|
||||
'id' => ['type' => 'integer'],
|
||||
],
|
||||
],
|
||||
'process' => [
|
||||
'id' => 'id',
|
||||
'user_id' => 'uid',
|
||||
"$field_name/value" => 'body/value',
|
||||
"$field_name/format" => 'body/format',
|
||||
],
|
||||
'destination' => [
|
||||
'plugin' => 'entity:entity_test',
|
||||
'validate' => TRUE,
|
||||
],
|
||||
];
|
||||
$this->container->get('current_user')->setAccount($normal_user);
|
||||
$this->runImport($definition);
|
||||
|
||||
// The second user import should fail validation because they do not have
|
||||
// access to use "filter_test" filter.
|
||||
$this->assertSame(sprintf('2: [entity_test: 2]: user_id.0.target_id=This entity (<em class="placeholder">user</em>: <em class="placeholder">%s</em>) cannot be referenced.||%s.0.format=The value you selected is not a valid choice.', $normal_user->id(), $field_name), $this->messages[0]);
|
||||
$this->assertArrayNotHasKey(1, $this->messages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reacts to map message event.
|
||||
*
|
||||
* @param \Drupal\migrate\Event\MigrateIdMapMessageEvent $event
|
||||
* The migration event.
|
||||
*/
|
||||
public function mapMessageRecorder(MigrateIdMapMessageEvent $event): void {
|
||||
$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): void {
|
||||
// Reset the list of messages from a previous migration.
|
||||
$this->messages = [];
|
||||
|
||||
(new MigrateExecutable($this->container->get('plugin.manager.migration')->createStubMigration($definition)))->import();
|
||||
}
|
||||
|
||||
}
|
||||
217
web/core/modules/migrate/tests/src/Kernel/MigrateEventsTest.php
Normal file
217
web/core/modules/migrate/tests/src/Kernel/MigrateEventsTest.php
Normal file
@ -0,0 +1,217 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['migrate', 'migrate_events_test'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
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(): void {
|
||||
// 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->assertSame(MigrateEvents::PRE_IMPORT, $event['event_name']);
|
||||
$this->assertSame($migration->id(), $event['migration']->id());
|
||||
|
||||
$event = $this->state->get('migrate_events_test.post_import_event', []);
|
||||
$this->assertSame(MigrateEvents::POST_IMPORT, $event['event_name']);
|
||||
$this->assertSame($migration->id(), $event['migration']->id());
|
||||
|
||||
$event = $this->state->get('migrate_events_test.map_save_event', []);
|
||||
$this->assertSame(MigrateEvents::MAP_SAVE, $event['event_name']);
|
||||
// Validating the last row processed.
|
||||
$this->assertSame('dummy value', $event['fields']['sourceid1']);
|
||||
$this->assertSame('dummy value', $event['fields']['destid1']);
|
||||
$this->assertSame(0, $event['fields']['source_row_status']);
|
||||
|
||||
$event = $this->state->get('migrate_events_test.map_delete_event', []);
|
||||
$this->assertSame([], $event);
|
||||
|
||||
$event = $this->state->get('migrate_events_test.pre_row_save_event', []);
|
||||
$this->assertSame(MigrateEvents::PRE_ROW_SAVE, $event['event_name']);
|
||||
$this->assertSame($migration->id(), $event['migration']->id());
|
||||
// Validating the last row processed.
|
||||
$this->assertSame('dummy value', $event['row']->getSourceProperty('data'));
|
||||
|
||||
$event = $this->state->get('migrate_events_test.post_row_save_event', []);
|
||||
$this->assertSame(MigrateEvents::POST_ROW_SAVE, $event['event_name']);
|
||||
$this->assertSame($migration->id(), $event['migration']->id());
|
||||
// Validating the last row processed.
|
||||
$this->assertSame('dummy value', $event['row']->getSourceProperty('data'));
|
||||
$this->assertSame('dummy value', $event['destination_id_values']['value']);
|
||||
|
||||
// Generate a map delete event.
|
||||
$migration->getIdMap()->delete(['data' => 'dummy value']);
|
||||
$event = $this->state->get('migrate_events_test.map_delete_event', []);
|
||||
$this->assertSame(MigrateEvents::MAP_DELETE, $event['event_name']);
|
||||
$this->assertSame(['data' => 'dummy value'], $event['source_id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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): void {
|
||||
$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): void {
|
||||
$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): void {
|
||||
$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): void {
|
||||
$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): void {
|
||||
$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): void {
|
||||
$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,85 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel;
|
||||
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
|
||||
/**
|
||||
* Tests the MigrateExecutable class.
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class MigrateExecutableTest extends MigrateTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = [
|
||||
'entity_test',
|
||||
'user',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->installEntitySchema('user');
|
||||
$this->installEntitySchema('entity_test');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the MigrateExecutable class.
|
||||
*/
|
||||
public function testMigrateExecutable(): void {
|
||||
$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());
|
||||
|
||||
// Test the exception message when a process plugin throws a
|
||||
// MigrateSkipRowException. Change the definition to have one data row and a
|
||||
// process that will throw a MigrateSkipRowException on every row.
|
||||
$definition['source']['data_rows'] = [
|
||||
[
|
||||
'key' => '1',
|
||||
'field1' => 'f1value1',
|
||||
],
|
||||
];
|
||||
$definition['process'] = [
|
||||
'foo' => [
|
||||
'plugin' => 'skip_row_if_not_set',
|
||||
'index' => 'foo',
|
||||
'source' => 'field1',
|
||||
'message' => 'test message',
|
||||
],
|
||||
];
|
||||
|
||||
$migration = \Drupal::service('plugin.manager.migration')
|
||||
->createStubMigration($definition);
|
||||
$executable = new TestMigrateExecutable($migration);
|
||||
$executable->import();
|
||||
$messages = iterator_to_array($migration->getIdMap()->getMessages());
|
||||
$this->assertCount(1, $messages);
|
||||
$expected = $migration->getPluginId() . ':foo: test message';
|
||||
$this->assertEquals($expected, $messages[0]->message);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
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}
|
||||
*/
|
||||
protected static $modules = [
|
||||
'system',
|
||||
'user',
|
||||
'language',
|
||||
'node',
|
||||
'field',
|
||||
'migrate_external_translated_test',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
$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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests importing and rolling back our data.
|
||||
*/
|
||||
public function testMigrations(): void {
|
||||
/** @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('es - Cat', $node->getTranslation('es')->title->value);
|
||||
|
||||
$node = $storage->load(2);
|
||||
$this->assertEquals('en', $node->language()->getId());
|
||||
$this->assertEquals('Dog', $node->title->value);
|
||||
$this->assertEquals('fr - Dog', $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
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
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 {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['migrate', 'migrate_events_test'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
\Drupal::service('event_dispatcher')->addListener(MigrateEvents::POST_ROW_SAVE,
|
||||
[$this, 'postRowSaveEventRecorder']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests migration interruptions.
|
||||
*/
|
||||
public function testMigrateEvents(): void {
|
||||
// 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->assertEquals(MigrationInterface::RESULT_INCOMPLETE, $result);
|
||||
|
||||
// The status should have been reset to IDLE.
|
||||
$this->assertEquals(MigrationInterface::STATUS_IDLE, $migration->getStatus());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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): void {
|
||||
$event->getMigration()->interruptMigration(MigrationInterface::RESULT_INCOMPLETE);
|
||||
}
|
||||
|
||||
}
|
||||
172
web/core/modules/migrate/tests/src/Kernel/MigrateLookupTest.php
Normal file
172
web/core/modules/migrate/tests/src/Kernel/MigrateLookupTest.php
Normal file
@ -0,0 +1,172 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel;
|
||||
|
||||
use Drupal\migrate\MigrateException;
|
||||
use Drupal\Tests\node\Traits\ContentTypeCreationTrait;
|
||||
|
||||
/**
|
||||
* Tests the Migrate Lookup service.
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class MigrateLookupTest extends MigrateTestBase {
|
||||
|
||||
use ContentTypeCreationTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = [
|
||||
'system',
|
||||
'node',
|
||||
'field',
|
||||
'user',
|
||||
'text',
|
||||
'migrate_lookup_test',
|
||||
];
|
||||
|
||||
/**
|
||||
* The migration lookup service.
|
||||
*
|
||||
* @var \Drupal\migrate\MigrateLookupInterface
|
||||
*/
|
||||
protected $migrateLookup;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
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(): void {
|
||||
$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(): void {
|
||||
$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 lookups with multiple source ids.
|
||||
*/
|
||||
public function testMultipleSourceIds(): void {
|
||||
$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(): void {
|
||||
$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(): void {
|
||||
$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);
|
||||
}
|
||||
|
||||
}
|
||||
154
web/core/modules/migrate/tests/src/Kernel/MigrateMessageTest.php
Normal file
154
web/core/modules/migrate/tests/src/Kernel/MigrateMessageTest.php
Normal file
@ -0,0 +1,154 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
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 {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected 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(): void {
|
||||
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(): void {
|
||||
// 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(): void {
|
||||
// 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);
|
||||
$id = $this->migration->getPluginId();
|
||||
$this->assertSame("source_message: $id:message:concat: 'a message' is not an array", reset($this->messages));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(): void {
|
||||
$id = $this->migration->getPluginId();
|
||||
$expected_message = (object) [
|
||||
'src_name' => 'source_message',
|
||||
'dest_config_name' => NULL,
|
||||
'msgid' => '1',
|
||||
Sql::SOURCE_IDS_HASH => '170cde81762e22552d1b1578cf3804c89afefe9efbc7cc835185d7141060b032',
|
||||
'level' => '1',
|
||||
'message' => "$id:message:concat: '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->assertEquals($expected_message, $message);
|
||||
}
|
||||
$this->assertEquals(1, $count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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): void {
|
||||
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'): void {
|
||||
$this->messages[] = $message;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel;
|
||||
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\migrate\Exception\RequirementsException;
|
||||
use Drupal\migrate\Plugin\MigrateIdMapInterface;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
|
||||
/**
|
||||
* Tests that a SQL migration can be instantiated without a database connection.
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class MigrateMissingDatabaseTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['migrate', 'migrate_missing_database_test'];
|
||||
|
||||
/**
|
||||
* The migration plugin manager.
|
||||
*
|
||||
* @var \Drupal\migrate\Plugin\MigrationPluginManager
|
||||
*/
|
||||
protected $migrationPluginManager;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->migrationPluginManager = \Drupal::service('plugin.manager.migration');
|
||||
|
||||
// Set the 'migrate' database connection to use a missing database.
|
||||
$info = Database::getConnectionInfo('default')['default'];
|
||||
$info['database'] = 'godot';
|
||||
Database::addConnectionInfo('migrate', 'default', $info);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a SQL migration without the database connection.
|
||||
*
|
||||
* - The migration can be instantiated.
|
||||
* - The checkRequirements() method throws a RequirementsException.
|
||||
*/
|
||||
public function testMissingDatabase(): void {
|
||||
if (Database::getConnection()->driver() === 'sqlite') {
|
||||
$this->markTestSkipped('Not compatible with sqlite');
|
||||
}
|
||||
|
||||
$migration = $this->migrationPluginManager->createInstance('missing_database');
|
||||
$this->assertInstanceOf(MigrationInterface::class, $migration);
|
||||
$this->assertInstanceOf(MigrateIdMapInterface::class, $migration->getIdMap());
|
||||
$this->expectException(RequirementsException::class);
|
||||
$this->expectExceptionMessage('No database connection available for source plugin migrate_missing_database_test');
|
||||
$migration->checkRequirements();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,173 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel;
|
||||
|
||||
use Drupal\migrate\MigrateException;
|
||||
use Drupal\migrate\MigrateExecutable;
|
||||
use Drupal\migrate\Plugin\migrate\process\Get;
|
||||
use Drupal\migrate\Plugin\migrate\process\SubProcess;
|
||||
use Drupal\migrate\Plugin\MigrateIdMapInterface;
|
||||
use Drupal\migrate\Plugin\MigratePluginManagerInterface;
|
||||
use Drupal\migrate\Plugin\MigrateProcessInterface;
|
||||
use Drupal\migrate\Plugin\MigrationInterface;
|
||||
use Prophecy\Argument;
|
||||
use Prophecy\Prophecy\ObjectProphecy;
|
||||
|
||||
/**
|
||||
* Tests the format of messages from process plugin exceptions.
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class MigrateProcessErrorMessagesTest extends MigrateTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = [
|
||||
'system',
|
||||
'migrate_events_test',
|
||||
'migrate',
|
||||
];
|
||||
|
||||
/**
|
||||
* A mock Process Plugin Manager.
|
||||
*
|
||||
* @var \Prophecy\Prophecy\ObjectProphecy
|
||||
*/
|
||||
protected ObjectProphecy $processPluginManager;
|
||||
|
||||
/**
|
||||
* A mock ID Map Plugin Manager.
|
||||
*
|
||||
* @var \Prophecy\Prophecy\ObjectProphecy
|
||||
*/
|
||||
protected ObjectProphecy $idMapPluginManager;
|
||||
|
||||
/**
|
||||
* A mock ID Map.
|
||||
*
|
||||
* @var \Prophecy\Prophecy\ObjectProphecy
|
||||
*/
|
||||
protected ObjectProphecy $idMap;
|
||||
|
||||
/**
|
||||
* The default stub migration definition.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected array $definition = [
|
||||
'id' => 'process_errors_migration',
|
||||
'idMap' => [
|
||||
'plugin' => 'idmap_prophecy',
|
||||
],
|
||||
'source' => [
|
||||
'plugin' => 'embedded_data',
|
||||
'data_rows' => [
|
||||
[
|
||||
'id' => 1,
|
||||
'my_property' => [
|
||||
'subfield' => [
|
||||
42,
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
'ids' => ['id' => ['type' => 'integer']],
|
||||
],
|
||||
'process' => [
|
||||
'id' => 'id',
|
||||
],
|
||||
'destination' => [
|
||||
'plugin' => 'dummy',
|
||||
],
|
||||
'migration_dependencies' => [],
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->processPluginManager = $this->prophesize(MigratePluginManagerInterface::class);
|
||||
$this->idMapPluginManager = $this->prophesize(MigratePluginManagerInterface::class);
|
||||
$this->idMap = $this->prophesize(MigrateIdMapInterface::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests format of map messages saved from plugin exceptions.
|
||||
*/
|
||||
public function testProcessErrorMessage(): void {
|
||||
$this->definition['process']['error']['plugin'] = 'test_error';
|
||||
|
||||
$this->idMap->saveMessage(['id' => 1], "process_errors_migration:error:test_error: Process exception.", MigrationInterface::MESSAGE_ERROR)->shouldBeCalled();
|
||||
$this->setPluginManagers();
|
||||
|
||||
/** @var \Drupal\migrate\Plugin\MigrationInterface $migration */
|
||||
$migration = \Drupal::service('plugin.manager.migration')->createStubMigration($this->definition);
|
||||
|
||||
$executable = new MigrateExecutable($migration, $this);
|
||||
$executable->import();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests format of map messages saved from sub_process exceptions.
|
||||
*
|
||||
* This checks the format of messages that are thrown from normal process
|
||||
* plugins while being executed inside a sub_process pipeline as they
|
||||
* bubble up to the main migration.
|
||||
*/
|
||||
public function testSubProcessErrorMessage(): void {
|
||||
$this->definition['process']['subprocess_error'] = [
|
||||
'plugin' => 'sub_process',
|
||||
'source' => 'my_property',
|
||||
'process' => [
|
||||
'subfield' => [
|
||||
[
|
||||
'plugin' => 'test_error',
|
||||
'value' => 'subfield',
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$this->processPluginManager->createInstance('sub_process', Argument::cetera())
|
||||
->will(fn($x) => new SubProcess($x[1], 'sub_process', ['handle_multiples' => TRUE]));
|
||||
$this->idMap->saveMessage(['id' => 1], "process_errors_migration:subprocess_error:sub_process: test_error: Process exception.", MigrationInterface::MESSAGE_ERROR)->shouldBeCalled();
|
||||
$this->setPluginManagers();
|
||||
|
||||
/** @var \Drupal\migrate\Plugin\MigrationInterface $migration */
|
||||
$migration = \Drupal::service('plugin.manager.migration')->createStubMigration($this->definition);
|
||||
|
||||
$executable = new MigrateExecutable($migration, $this);
|
||||
$executable->import();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares and sets the prophesized plugin managers.
|
||||
*/
|
||||
protected function setPluginManagers(): void {
|
||||
$error_plugin_prophecy = $this->prophesize(MigrateProcessInterface::class);
|
||||
$error_plugin_prophecy->getPluginDefinition()->willReturn(['plugin_id' => 'test_error']);
|
||||
$error_plugin_prophecy->getPluginId()->willReturn('test_error');
|
||||
$error_plugin_prophecy->reset()->shouldBeCalled();
|
||||
$error_plugin_prophecy->transform(Argument::cetera())->willThrow(new MigrateException('Process exception.'));
|
||||
|
||||
$this->processPluginManager->createInstance('get', Argument::cetera())
|
||||
->will(fn($x) => new Get($x[1], 'get', ['handle_multiples' => TRUE]));
|
||||
$this->processPluginManager->createInstance('test_error', Argument::cetera())->willReturn($error_plugin_prophecy->reveal());
|
||||
|
||||
$this->idMap->setMessage(Argument::any())->willReturn();
|
||||
$this->idMap->getRowBySource(Argument::any())->willReturn([]);
|
||||
$this->idMap->delete(Argument::cetera())->willReturn();
|
||||
$this->idMap->saveIdMapping(Argument::cetera())->willReturn();
|
||||
|
||||
$this->idMapPluginManager->createInstance('idmap_prophecy', Argument::cetera())->willReturn($this->idMap->reveal());
|
||||
|
||||
$this->container->set('plugin.manager.migrate.process', $this->processPluginManager->reveal());
|
||||
$this->container->set('plugin.manager.migrate.id_map', $this->idMapPluginManager->reveal());
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,176 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
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 {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = [
|
||||
'field',
|
||||
'taxonomy',
|
||||
'text',
|
||||
'language',
|
||||
'config_translation',
|
||||
'user',
|
||||
'system',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->installEntitySchema('user');
|
||||
$this->installEntitySchema('taxonomy_term');
|
||||
$this->installConfig(['taxonomy']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests rolling back configuration entity translations.
|
||||
*/
|
||||
public function testConfigEntityRollback(): void {
|
||||
// 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,183 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
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 {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['field', 'taxonomy', 'text', 'user', 'system'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->installEntitySchema('user');
|
||||
$this->installEntitySchema('taxonomy_term');
|
||||
$this->installConfig(['taxonomy']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests rolling back configuration and content entities.
|
||||
*/
|
||||
public function testRollback(): void {
|
||||
// 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());
|
||||
}
|
||||
|
||||
}
|
||||
101
web/core/modules/migrate/tests/src/Kernel/MigrateSkipRowTest.php
Normal file
101
web/core/modules/migrate/tests/src/Kernel/MigrateSkipRowTest.php
Normal file
@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
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 {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['migrate', 'migrate_prepare_row_test'];
|
||||
|
||||
/**
|
||||
* Tests migration interruptions.
|
||||
*/
|
||||
public function testPrepareRowSkip(): void {
|
||||
// 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_do_not_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->assertEquals(MigrationInterface::RESULT_COMPLETED, $result);
|
||||
|
||||
/** @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->assertEquals(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_do_not_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_do_not_record (use plugin)'],
|
||||
];
|
||||
$migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition);
|
||||
|
||||
$executable = new MigrateExecutable($migration);
|
||||
$result = $executable->import();
|
||||
$this->assertEquals(MigrationInterface::RESULT_COMPLETED, $result);
|
||||
|
||||
$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,218 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
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.
|
||||
*
|
||||
* Implementing classes must declare a providerSource() method for this class
|
||||
* to work, defined as follows:
|
||||
*
|
||||
* @code
|
||||
* abstract public static function providerSource(): array;
|
||||
* @endcode
|
||||
*
|
||||
* The returned array should be as follows:
|
||||
*
|
||||
* @code
|
||||
* 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.
|
||||
* @endcode
|
||||
*
|
||||
* @see \Drupal\Tests\migrate\Kernel\MigrateSourceTestBase::testSource
|
||||
*/
|
||||
abstract class MigrateSourceTestBase extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['migrate', 'migrate_skip_all_rows_test'];
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
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
|
||||
* The fully qualified class name of the plugin to be tested.
|
||||
*/
|
||||
protected function getPluginClass() {
|
||||
$covers = $this->valueObjectForEvents()->metadata()->isCovers()->isClassLevel()->asArray();
|
||||
if (isset($covers[0])) {
|
||||
return $covers[0]->target();
|
||||
}
|
||||
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): void {
|
||||
$plugin = $this->getPlugin($configuration);
|
||||
$clone_plugin = clone $plugin;
|
||||
|
||||
// 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);
|
||||
|
||||
// Test that we can skip all rows.
|
||||
\Drupal::state()->set('migrate_skip_all_rows_test_migrate_prepare_row', TRUE);
|
||||
foreach ($clone_plugin as $row) {
|
||||
$this->fail('Row not skipped');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides source data for ::testSource.
|
||||
*
|
||||
* @return iterable
|
||||
* The source data.
|
||||
*/
|
||||
abstract public static function providerSource();
|
||||
|
||||
}
|
||||
@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel;
|
||||
|
||||
use Drupal\Core\Cache\MemoryCounterBackendFactory;
|
||||
use Drupal\sqlite\Driver\Database\sqlite\Connection;
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
/**
|
||||
* Base class for tests of Migrate source plugins that use a database.
|
||||
*/
|
||||
abstract class MigrateSqlSourceTestBase extends MigrateSourceTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function register(ContainerBuilder $container) {
|
||||
parent::register($container);
|
||||
$container
|
||||
->register('cache_factory', MemoryCounterBackendFactory::class)
|
||||
->addArgument(new Reference('datetime.time'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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\sqlite\Driver\Database\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);
|
||||
$pilot = $rows[array_key_last($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.
|
||||
* @param string|null $expected_cache_key
|
||||
* (optional) The expected cache key.
|
||||
*
|
||||
* @dataProvider providerSource
|
||||
*
|
||||
* @requires extension pdo_sqlite
|
||||
*/
|
||||
public function testSource(array $source_data, array $expected_data, $expected_count = NULL, array $configuration = [], $high_water = NULL, $expected_cache_key = NULL): void {
|
||||
$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->setValue($plugin, $this->getDatabase($source_data));
|
||||
|
||||
/** @var MemoryCounterBackend $cache **/
|
||||
$cache = \Drupal::cache('migrate');
|
||||
if ($expected_cache_key) {
|
||||
// Verify the computed cache key.
|
||||
$property = $reflector->getProperty('cacheKey');
|
||||
$this->assertSame($expected_cache_key, $property->getValue($plugin));
|
||||
|
||||
// Cache miss prior to calling ::count().
|
||||
$this->assertFalse($cache->get($expected_cache_key, 'cache'));
|
||||
|
||||
$this->assertSame([], $cache->getCounter('set'));
|
||||
$count = $plugin->count();
|
||||
$this->assertSame($expected_count, $count);
|
||||
$this->assertSame([$expected_cache_key => 1], $cache->getCounter('set'));
|
||||
|
||||
// Cache hit afterwards.
|
||||
$cache_item = $cache->get($expected_cache_key, 'cache');
|
||||
$this->assertNotSame(FALSE, $cache_item, 'This is not a cache hit.');
|
||||
$this->assertSame($expected_count, $cache_item->data);
|
||||
}
|
||||
else {
|
||||
$this->assertSame([], $cache->getCounter('set'));
|
||||
$plugin->count();
|
||||
$this->assertSame([], $cache->getCounter('set'));
|
||||
}
|
||||
|
||||
parent::testSource($source_data, $expected_data, $expected_count, $configuration, $high_water);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
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(): void {
|
||||
// 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->assertSame(MigrationInterface::STATUS_IDLE, $status);
|
||||
|
||||
// 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->assertSame($status, $migration->getStatus());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
142
web/core/modules/migrate/tests/src/Kernel/MigrateStubTest.php
Normal file
142
web/core/modules/migrate/tests/src/Kernel/MigrateStubTest.php
Normal file
@ -0,0 +1,142 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel;
|
||||
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\Tests\node\Traits\ContentTypeCreationTrait;
|
||||
|
||||
/**
|
||||
* Tests the migrate.stub Service.
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class MigrateStubTest extends MigrateTestBase {
|
||||
use ContentTypeCreationTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = [
|
||||
'system',
|
||||
'node',
|
||||
'field',
|
||||
'user',
|
||||
'text',
|
||||
'filter',
|
||||
'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(): void {
|
||||
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(): void {
|
||||
$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(): void {
|
||||
$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(): void {
|
||||
$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());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests stub creation with bundle fields.
|
||||
*/
|
||||
public function testStubWithBundleFields(): void {
|
||||
$this->createContentType(['type' => 'node_stub']);
|
||||
// Make "Body" field required to make stubbing populate field value.
|
||||
$body_field = FieldConfig::loadByName('node', 'node_stub', 'body');
|
||||
$body_field->setRequired(TRUE)->save();
|
||||
|
||||
$this->assertSame([], $this->migrateLookup->lookup('sample_stubbing_migration', [33]));
|
||||
$ids = $this->migrateStub->createStub('sample_stubbing_migration', [33], []);
|
||||
$this->assertSame([$ids], $this->migrateLookup->lookup('sample_stubbing_migration', [33]));
|
||||
$node = \Drupal::entityTypeManager()->getStorage('node')->load($ids['nid']);
|
||||
$this->assertNotNull($node);
|
||||
// Make sure the "Body" field value was populated.
|
||||
$this->assertNotEmpty($node->get('body')->value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests invalid source id count.
|
||||
*/
|
||||
public function testInvalidSourceIdCount(): void {
|
||||
$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(): void {
|
||||
$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]);
|
||||
}
|
||||
|
||||
}
|
||||
273
web/core/modules/migrate/tests/src/Kernel/MigrateTestBase.php
Normal file
273
web/core/modules/migrate/tests/src/Kernel/MigrateTestBase.php
Normal file
@ -0,0 +1,273 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['migrate'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->createMigrationConnection();
|
||||
$this->sourceDatabase = Database::getConnection('default', 'migrate');
|
||||
// Attach the original test prefix as a database, for SQLite to attach its
|
||||
// database file.
|
||||
$this->sourceDatabase->attachDatabase(substr($this->sourceDatabase->getConnectionOptions()['prefix'], 0, -1));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = $value['prefix'];
|
||||
// Tests use 7 character prefixes at most so this can't cause collisions.
|
||||
$connection_info[$target]['prefix'] = $prefix . '0';
|
||||
}
|
||||
Database::addConnectionInfo('migrate', 'default', $connection_info['default']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function tearDown(): void {
|
||||
$this->cleanupMigrateConnection();
|
||||
parent::tearDown();
|
||||
$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) {
|
||||
$this->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');
|
||||
$instances = $manager->createInstances($ids);
|
||||
array_walk($instances, [$this, 'executeMigration']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function display($message, $type = 'status') {
|
||||
if ($this->collectMessages) {
|
||||
$this->migrateMessages[$type][] = $message;
|
||||
}
|
||||
else {
|
||||
$this->assertEquals('status', $type, $message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 string $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,75 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel;
|
||||
|
||||
/**
|
||||
* Tests the migration plugin manager.
|
||||
*
|
||||
* @group migrate
|
||||
*
|
||||
* @coversDefaultClass \Drupal\migrate\Plugin\MigrationPluginManager
|
||||
*/
|
||||
class MigrationPluginManagerTest extends MigrateTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['migrate', 'migrate_tag_test'];
|
||||
|
||||
/**
|
||||
* The migration plugin manager.
|
||||
*
|
||||
* @var \Drupal\migrate\Plugin\MigrationPluginManager
|
||||
*/
|
||||
protected $migrationPluginManager;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->migrationPluginManager = \Drupal::service('plugin.manager.migration');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests Migration::createInstancesByTag().
|
||||
*
|
||||
* @covers ::createInstancesByTag
|
||||
*
|
||||
* @dataProvider providerCreateInstanceByTag
|
||||
*/
|
||||
public function testCreateInstancesByTag($tags, $expected): void {
|
||||
// The test module includes a migration that does not use the migration_tags
|
||||
// property. It is there to confirm that it is not included in the results.
|
||||
// We create it to ensure it is a valid migration.
|
||||
$migration = $this->migrationPluginManager->createInstances(['tag_test_no_tag']);
|
||||
$this->assertArrayHasKey('tag_test_no_tag', $migration);
|
||||
$migrations = $this->migrationPluginManager->createInstancesByTag($tags);
|
||||
$actual = array_keys($migrations);
|
||||
$this->assertSame($expected, $actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testCreateInstancesByTag.
|
||||
*/
|
||||
public static function providerCreateInstanceByTag() {
|
||||
return [
|
||||
'get test' => [
|
||||
'test',
|
||||
['tag_test_0', 'tag_test_1'],
|
||||
],
|
||||
'get tag_test_1' => [
|
||||
'tag_test_1',
|
||||
['tag_test_1'],
|
||||
],
|
||||
'get no tags' => [
|
||||
'',
|
||||
[],
|
||||
],
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
47
web/core/modules/migrate/tests/src/Kernel/MigrationTest.php
Normal file
47
web/core/modules/migrate/tests/src/Kernel/MigrationTest.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
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
|
||||
*/
|
||||
protected static $modules = ['migrate', 'field'];
|
||||
|
||||
/**
|
||||
* Tests Migration::set().
|
||||
*
|
||||
* @covers ::set
|
||||
*/
|
||||
public function testSetInvalidation(): void {
|
||||
$migration = \Drupal::service('plugin.manager.migration')->createStubMigration([
|
||||
'source' => ['plugin' => 'empty'],
|
||||
'destination' => ['plugin' => 'entity:entity_view_mode'],
|
||||
]);
|
||||
$this->assertEquals('empty', $migration->getSourcePlugin()->getPluginId());
|
||||
$this->assertEquals('entity:entity_view_mode', $migration->getDestinationPlugin()->getPluginId());
|
||||
|
||||
// Test the source plugin is invalidated.
|
||||
$migration->set('source', ['plugin' => 'embedded_data', 'data_rows' => [], 'ids' => []]);
|
||||
$this->assertEquals('embedded_data', $migration->getSourcePlugin()->getPluginId());
|
||||
|
||||
// Test the destination plugin is invalidated.
|
||||
$migration->set('destination', ['plugin' => 'null']);
|
||||
$this->assertEquals('null', $migration->getDestinationPlugin()->getPluginId());
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
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}
|
||||
*/
|
||||
protected static $modules = ['migrate', 'system', 'user'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->installEntitySchema('user');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the EntityExists plugin.
|
||||
*/
|
||||
public function testEntityExists(): void {
|
||||
$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,135 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
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;
|
||||
|
||||
// cspell:ignore tabarnak
|
||||
|
||||
/**
|
||||
* Tests the EntityRevision destination plugin.
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class EntityRevisionTest extends MigrateTestBase {
|
||||
|
||||
use ContentTypeCreationTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = [
|
||||
'content_translation',
|
||||
'field',
|
||||
'filter',
|
||||
'language',
|
||||
'node',
|
||||
'system',
|
||||
'text',
|
||||
'user',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
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(): void {
|
||||
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());
|
||||
}
|
||||
|
||||
}
|
||||
211
web/core/modules/migrate/tests/src/Kernel/Plugin/LogTest.php
Normal file
211
web/core/modules/migrate/tests/src/Kernel/Plugin/LogTest.php
Normal file
@ -0,0 +1,211 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\migrate\Kernel\Plugin;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\migrate\MigrateExecutable;
|
||||
use Drupal\migrate\Row;
|
||||
use Drupal\node\Entity\Node;
|
||||
|
||||
/**
|
||||
* Tests the Log process plugin.
|
||||
*
|
||||
* @group migrate
|
||||
*/
|
||||
class LogTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['node', 'migrate'];
|
||||
|
||||
/**
|
||||
* The Log process plugin.
|
||||
*
|
||||
* @var \Drupal\migrate\Plugin\migrate\process\Log
|
||||
*/
|
||||
protected $logPlugin;
|
||||
|
||||
/**
|
||||
* Migrate executable.
|
||||
*
|
||||
* @var \Drupal\Tests\migrate\Kernel\Plugin\TestMigrateExecutable
|
||||
*/
|
||||
protected $executable;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
$definition = [
|
||||
'source' => [
|
||||
'plugin' => 'embedded_data',
|
||||
'data_rows' => [
|
||||
['id' => '1'],
|
||||
],
|
||||
'ids' => [
|
||||
'id' => ['type' => 'integer'],
|
||||
],
|
||||
],
|
||||
'destination' => [
|
||||
'plugin' => 'null',
|
||||
],
|
||||
];
|
||||
|
||||
/** @var \Drupal\migrate\Plugin\migration $migration */
|
||||
$migration = \Drupal::service('plugin.manager.migration')
|
||||
->createStubMigration($definition);
|
||||
$this->executable = new TestMigrateExecutable($migration);
|
||||
|
||||
// Plugin being tested.
|
||||
$this->logPlugin = \Drupal::service('plugin.manager.migrate.process')
|
||||
->createInstance('log');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the Log plugin.
|
||||
*/
|
||||
public function testLog(): void {
|
||||
$values = [
|
||||
'nid' => 2,
|
||||
'type' => 'page',
|
||||
'title' => 'page',
|
||||
];
|
||||
|
||||
$node = new Node($values, 'node', 'test');
|
||||
$node_array = <<< NODE
|
||||
Array
|
||||
(
|
||||
[nid] => Array
|
||||
(
|
||||
)
|
||||
|
||||
[uuid] => Array
|
||||
(
|
||||
)
|
||||
|
||||
[vid] => Array
|
||||
(
|
||||
)
|
||||
|
||||
[langcode] => Array
|
||||
(
|
||||
)
|
||||
|
||||
[type] => Array
|
||||
(
|
||||
)
|
||||
|
||||
[revision_timestamp] => Array
|
||||
(
|
||||
)
|
||||
|
||||
[revision_uid] => Array
|
||||
(
|
||||
)
|
||||
|
||||
[revision_log] => Array
|
||||
(
|
||||
)
|
||||
|
||||
[status] => Array
|
||||
(
|
||||
)
|
||||
|
||||
[uid] => Array
|
||||
(
|
||||
)
|
||||
|
||||
[title] => Array
|
||||
(
|
||||
)
|
||||
|
||||
[created] => Array
|
||||
(
|
||||
)
|
||||
|
||||
[changed] => Array
|
||||
(
|
||||
)
|
||||
|
||||
[promote] => Array
|
||||
(
|
||||
)
|
||||
|
||||
[sticky] => Array
|
||||
(
|
||||
)
|
||||
|
||||
[default_langcode] => Array
|
||||
(
|
||||
)
|
||||
|
||||
[revision_default] => Array
|
||||
(
|
||||
)
|
||||
|
||||
[revision_translation_affected] => Array
|
||||
(
|
||||
)
|
||||
|
||||
)
|
||||
|
||||
NODE;
|
||||
|
||||
$data = [
|
||||
'node' => [
|
||||
'value' => $node,
|
||||
'expected_message' => "'foo' value is Drupal\\node\Entity\Node:\n'$node_array'",
|
||||
],
|
||||
'url' => [
|
||||
'value' => Url::fromUri('https://en.wikipedia.org/wiki/Drupal#Community'),
|
||||
'expected_message' => "'foo' value is Drupal\Core\Url:\n'https://en.wikipedia.org/wiki/Drupal#Community'",
|
||||
],
|
||||
];
|
||||
|
||||
$i = 1;
|
||||
foreach ($data as $datum) {
|
||||
$this->executable->sourceIdValues = ['id' => $i++];
|
||||
|
||||
// Test the input value is not altered.
|
||||
$new_value = $this->logPlugin->transform($datum['value'], $this->executable, new Row(), 'foo');
|
||||
$this->assertSame($datum['value'], $new_value);
|
||||
|
||||
// Test the stored message.
|
||||
$message = $this->executable->getIdMap()
|
||||
->getMessages($this->executable->sourceIdValues)
|
||||
->fetchAllAssoc('message');
|
||||
$actual_message = key($message);
|
||||
$this->assertSame($datum['expected_message'], $actual_message);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* MigrateExecutable test class.
|
||||
*/
|
||||
class TestMigrateExecutable extends MigrateExecutable {
|
||||
|
||||
/**
|
||||
* The configuration values of the source.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $sourceIdValues;
|
||||
|
||||
/**
|
||||
* Get the ID map from the current migration.
|
||||
*
|
||||
* @return \Drupal\migrate\Plugin\MigrateIdMapInterface
|
||||
* The ID map.
|
||||
*/
|
||||
public function getIdMap() {
|
||||
return parent::getIdMap();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
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}
|
||||
*/
|
||||
protected static $modules = [
|
||||
'migrate',
|
||||
'migrate_drupal',
|
||||
// Test with a simple migration.
|
||||
'migrate_plugin_config_test',
|
||||
'locale',
|
||||
];
|
||||
|
||||
/**
|
||||
* Tests merging configuration into a plugin through the plugin manager.
|
||||
*
|
||||
* @dataProvider mergeProvider
|
||||
*/
|
||||
public function testConfigurationMerge($id, $configuration, $expected): void {
|
||||
/** @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 static function mergeProvider() {
|
||||
return [
|
||||
// Tests adding new configuration to a migration.
|
||||
[
|
||||
// New configuration.
|
||||
'simple_migration',
|
||||
[
|
||||
'source' => [
|
||||
'constants' => [
|
||||
'added_setting' => 'Ban them all!',
|
||||
],
|
||||
],
|
||||
],
|
||||
// Expected final source configuration.
|
||||
[
|
||||
'plugin' => 'simple_source',
|
||||
'constants' => [
|
||||
'added_setting' => 'Ban them all!',
|
||||
],
|
||||
],
|
||||
],
|
||||
// Tests overriding pre-existing configuration in a migration.
|
||||
[
|
||||
// New configuration.
|
||||
'simple_migration',
|
||||
[
|
||||
'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,161 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
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\EntityReferenceFieldCreationTrait;
|
||||
|
||||
/**
|
||||
* Tests the migration plugin manager.
|
||||
*
|
||||
* @coversDefaultClass \Drupal\migrate\Plugin\MigratePluginManager
|
||||
* @group migrate
|
||||
*/
|
||||
class MigrationPluginListTest extends KernelTestBase {
|
||||
|
||||
use EntityReferenceFieldCreationTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = [
|
||||
'migrate',
|
||||
// Test with all modules containing Drupal migrations.
|
||||
// @todo Remove Ban in https://www.drupal.org/project/drupal/issues/3488827
|
||||
'ban',
|
||||
'block',
|
||||
'block_content',
|
||||
'comment',
|
||||
'contact',
|
||||
'content_translation',
|
||||
'dblog',
|
||||
'field',
|
||||
'file',
|
||||
'filter',
|
||||
'image',
|
||||
'language',
|
||||
'locale',
|
||||
'menu_link_content',
|
||||
'menu_ui',
|
||||
'node',
|
||||
'options',
|
||||
'path',
|
||||
'search',
|
||||
'shortcut',
|
||||
'syslog',
|
||||
'system',
|
||||
'taxonomy',
|
||||
'text',
|
||||
'update',
|
||||
'user',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('user');
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getDefinitions
|
||||
*/
|
||||
public function testGetDefinitions(): void {
|
||||
// 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']);
|
||||
$this->installConfig(['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) {
|
||||
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 = $value['prefix'];
|
||||
// Tests use 7 character prefixes at most so this can't cause collisions.
|
||||
$connection_info[$target]['prefix'] = $prefix . '0';
|
||||
}
|
||||
Database::addConnectionInfo('migrate', 'default', $connection_info['default']);
|
||||
|
||||
// Make sure source plugins can be serialized.
|
||||
foreach ($migration_plugins as $migration_plugin) {
|
||||
$source_plugin = $migration_plugin->getSourcePlugin();
|
||||
if ($source_plugin instanceof SqlBase) {
|
||||
$source_plugin->getDatabase();
|
||||
}
|
||||
$this->assertNotEmpty(serialize($source_plugin));
|
||||
}
|
||||
|
||||
$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);
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user