Update to Drupal 8.1.0. For more information, see https://www.drupal.org/drupal-8.1.0-release-notes
This commit is contained in:
parent
b11a755ba8
commit
c0a0d5a94c
6920 changed files with 64395 additions and 57312 deletions
|
|
@ -0,0 +1,82 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\FunctionalJavascriptTests;
|
||||
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Symfony\Component\CssSelector\CssSelector;
|
||||
use Zumba\Mink\Driver\PhantomJSDriver;
|
||||
|
||||
/**
|
||||
* Runs a browser test using PhantomJS.
|
||||
*
|
||||
* Base class for testing browser interaction implemented in JavaScript.
|
||||
*/
|
||||
abstract class JavascriptTestBase extends BrowserTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected $minkDefaultDriverClass = PhantomJSDriver::class;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function initMink() {
|
||||
// Set up the template cache used by the PhantomJS mink driver.
|
||||
$path = $this->tempFilesDirectory . DIRECTORY_SEPARATOR . 'browsertestbase-templatecache';
|
||||
$this->minkDefaultDriverArgs = [
|
||||
'http://127.0.0.1:8510',
|
||||
$path,
|
||||
];
|
||||
if (!file_exists($path)) {
|
||||
mkdir($path);
|
||||
}
|
||||
return parent::initMink();
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the element with the given CSS selector is visible.
|
||||
*
|
||||
* @param string $css_selector
|
||||
* The CSS selector identifying the element to check.
|
||||
* @param string $message
|
||||
* Optional message to show alongside the assertion.
|
||||
*/
|
||||
protected function assertElementVisible($css_selector, $message = '') {
|
||||
$this->assertTrue($this->getSession()->getDriver()->isVisible(CssSelector::toXPath($css_selector)), $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the element with the given CSS selector is not visible.
|
||||
*
|
||||
* @param string $css_selector
|
||||
* The CSS selector identifying the element to check.
|
||||
* @param string $message
|
||||
* Optional message to show alongside the assertion.
|
||||
*/
|
||||
protected function assertElementNotVisible($css_selector, $message = '') {
|
||||
$this->assertFalse($this->getSession()->getDriver()->isVisible(CssSelector::toXPath($css_selector)), $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for the given time or until the given JS condition becomes TRUE.
|
||||
*
|
||||
* @param string $condition
|
||||
* JS condition to wait until it becomes TRUE.
|
||||
* @param int $timeout
|
||||
* (Optional) Timeout in milliseconds, defaults to 1000.
|
||||
* @param string $message
|
||||
* (optional) A message to display with the assertion. If left blank, a
|
||||
* default message will be displayed.
|
||||
*
|
||||
* @throws \PHPUnit_Framework_AssertionFailedError
|
||||
*
|
||||
* @see \Behat\Mink\Driver\DriverInterface::evaluateScript()
|
||||
*/
|
||||
protected function assertJsCondition($condition, $timeout = 1000, $message = '') {
|
||||
$message = $message ?: "Javascript condition met:\n" . $condition;
|
||||
$result = $this->getSession()->getDriver()->wait($timeout, $condition);
|
||||
$this->assertTrue($result, $message);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,10 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\KernelTests\AssertConfigTrait.
|
||||
*/
|
||||
|
||||
namespace Drupal\KernelTests;
|
||||
|
||||
use Drupal\Component\Diff\Diff;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\KernelTests\AssertLegacyTrait.
|
||||
*/
|
||||
|
||||
namespace Drupal\KernelTests;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,10 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\KernelTests\Component\Utility\SafeMarkupKernelTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\KernelTests\Component\Utility;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
|
|
@ -29,7 +24,6 @@ class SafeMarkupKernelTest extends KernelTestBase {
|
|||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installSchema('system', 'router');
|
||||
$this->container->get('router.builder')->rebuild();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\KernelTests\Config\DefaultConfigTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\KernelTests\Config;
|
||||
|
||||
use Drupal\Core\Config\FileStorage;
|
||||
|
|
@ -46,8 +41,6 @@ class DefaultConfigTest extends KernelTestBase {
|
|||
// drupal_get_filename().
|
||||
// @todo Remove as part of https://www.drupal.org/node/2186491
|
||||
system_rebuild_module_data();
|
||||
|
||||
$this->installSchema('system', 'router');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -63,6 +56,11 @@ class DefaultConfigTest extends KernelTestBase {
|
|||
/** @var \Drupal\Core\Config\ConfigManagerInterface $config_manager */
|
||||
$config_manager = $this->container->get('config.manager');
|
||||
|
||||
// @todo https://www.drupal.org/node/2308745 Rest has an implicit dependency
|
||||
// on the Node module remove once solved.
|
||||
if (in_array($module, ['rest', 'hal'])) {
|
||||
$module_installer->install(['node']);
|
||||
}
|
||||
$module_installer->install([$module]);
|
||||
|
||||
// System and user are required in order to be able to install some of the
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
<?php
|
||||
/**
|
||||
* @file \Drupal\KernelTests\Core\Cache\CacheCollectorTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\KernelTests\Core\Cache;
|
||||
|
||||
|
|
@ -17,19 +14,6 @@ use Symfony\Component\DependencyInjection\Reference;
|
|||
*/
|
||||
class CacheCollectorTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['system'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installSchema('system', ['semaphore']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,10 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\KernelTests\Core\Common\DrupalSetMessageTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\KernelTests\Core\Common;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,89 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Config;
|
||||
|
||||
use Drupal\config_override_test\Cache\PirateDayCacheContext;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests if configuration overrides correctly affect cacheability metadata.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class CacheabilityMetadataConfigOverrideTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = [
|
||||
'block',
|
||||
'block_content',
|
||||
'config',
|
||||
'config_override_test',
|
||||
'system',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installEntitySchema('block_content');
|
||||
$this->installConfig(['config_override_test']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if config overrides correctly set cacheability metadata.
|
||||
*/
|
||||
public function testConfigOverride() {
|
||||
// It's pirate day today!
|
||||
$GLOBALS['it_is_pirate_day'] = TRUE;
|
||||
|
||||
$config_factory = $this->container->get('config.factory');
|
||||
$config = $config_factory->get('system.theme');
|
||||
|
||||
// Check that we are using the Pirate theme.
|
||||
$theme = $config->get('default');
|
||||
$this->assertEqual('pirate', $theme);
|
||||
|
||||
// Check that the cacheability metadata is correct.
|
||||
$this->assertEqual(['pirate_day'], $config->getCacheContexts());
|
||||
$this->assertEqual(['config:system.theme', 'pirate-day-tag'], $config->getCacheTags());
|
||||
$this->assertEqual(PirateDayCacheContext::PIRATE_DAY_MAX_AGE, $config->getCacheMaxAge());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if config overrides set cacheability metadata on config entities.
|
||||
*/
|
||||
public function testConfigEntityOverride() {
|
||||
// It's pirate day today!
|
||||
$GLOBALS['it_is_pirate_day'] = TRUE;
|
||||
|
||||
// Load the User login block and check that its cacheability metadata is
|
||||
// overridden correctly. This verifies that the metadata is correctly
|
||||
// applied to config entities.
|
||||
/** @var \Drupal\Core\Entity\EntityManagerInterface $entity_manager */
|
||||
$entity_manager = $this->container->get('entity.manager');
|
||||
$block = $entity_manager->getStorage('block')->load('call_to_action');
|
||||
|
||||
// Check that our call to action message is appealing to filibusters.
|
||||
$this->assertEqual($block->label(), 'Draw yer cutlasses!');
|
||||
|
||||
// Check that the cacheability metadata is correct.
|
||||
$this->assertEqual(['pirate_day'], $block->getCacheContexts());
|
||||
$this->assertEqual(['config:block.block.call_to_action', 'pirate-day-tag'], $block->getCacheTags());
|
||||
$this->assertEqual(PirateDayCacheContext::PIRATE_DAY_MAX_AGE, $block->getCacheMaxAge());
|
||||
|
||||
// Check that duplicating a config entity does not have the original config
|
||||
// entity's cache tag.
|
||||
$this->assertEqual(['config:block.block.', 'pirate-day-tag'], $block->createDuplicate()->getCacheTags());
|
||||
|
||||
// Check that renaming a config entity does not have the original config
|
||||
// entity's cache tag.
|
||||
$block->set('id', 'call_to_looting')->save();
|
||||
$this->assertEqual(['pirate_day'], $block->getCacheContexts());
|
||||
$this->assertEqual(['config:block.block.call_to_looting', 'pirate-day-tag'], $block->getCacheTags());
|
||||
$this->assertEqual(PirateDayCacheContext::PIRATE_DAY_MAX_AGE, $block->getCacheMaxAge());
|
||||
}
|
||||
|
||||
}
|
||||
318
core/tests/Drupal/KernelTests/Core/Config/ConfigCRUDTest.php
Normal file
318
core/tests/Drupal/KernelTests/Core/Config/ConfigCRUDTest.php
Normal file
|
|
@ -0,0 +1,318 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Config;
|
||||
|
||||
use Drupal\Component\Utility\Crypt;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Config\ConfigNameException;
|
||||
use Drupal\Core\Config\ConfigValueException;
|
||||
use Drupal\Core\Config\InstallStorage;
|
||||
use Drupal\Core\Config\DatabaseStorage;
|
||||
use Drupal\Core\Config\UnsupportedDataTypeConfigException;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests CRUD operations on configuration objects.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigCRUDTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Exempt from strict schema checking.
|
||||
*
|
||||
* @see \Drupal\Core\Config\Testing\ConfigSchemaChecker
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $strictConfigSchema = FALSE;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('system');
|
||||
|
||||
/**
|
||||
* Tests CRUD operations.
|
||||
*/
|
||||
function testCRUD() {
|
||||
$storage = $this->container->get('config.storage');
|
||||
$config_factory = $this->container->get('config.factory');
|
||||
$name = 'config_test.crud';
|
||||
|
||||
$config = $this->config($name);
|
||||
$this->assertIdentical($config->isNew(), TRUE);
|
||||
|
||||
// Create a new configuration object.
|
||||
$config->set('value', 'initial');
|
||||
$config->save();
|
||||
$this->assertIdentical($config->isNew(), FALSE);
|
||||
|
||||
// Verify the active configuration contains the saved value.
|
||||
$actual_data = $storage->read($name);
|
||||
$this->assertIdentical($actual_data, array('value' => 'initial'));
|
||||
|
||||
// Update the configuration object instance.
|
||||
$config->set('value', 'instance-update');
|
||||
$config->save();
|
||||
$this->assertIdentical($config->isNew(), FALSE);
|
||||
|
||||
// Verify the active configuration contains the updated value.
|
||||
$actual_data = $storage->read($name);
|
||||
$this->assertIdentical($actual_data, array('value' => 'instance-update'));
|
||||
|
||||
// Verify a call to $this->config() immediately returns the updated value.
|
||||
$new_config = $this->config($name);
|
||||
$this->assertIdentical($new_config->get(), $config->get());
|
||||
$this->assertIdentical($config->isNew(), FALSE);
|
||||
|
||||
// Pollute the config factory static cache.
|
||||
$config_factory->getEditable($name);
|
||||
|
||||
// Delete the configuration object.
|
||||
$config->delete();
|
||||
|
||||
// Verify the configuration object is empty.
|
||||
$this->assertIdentical($config->get(), array());
|
||||
$this->assertIdentical($config->isNew(), TRUE);
|
||||
|
||||
// Verify that all copies of the configuration has been removed from the
|
||||
// static cache.
|
||||
$this->assertIdentical($config_factory->getEditable($name)->isNew(), TRUE);
|
||||
|
||||
// Verify the active configuration contains no value.
|
||||
$actual_data = $storage->read($name);
|
||||
$this->assertIdentical($actual_data, FALSE);
|
||||
|
||||
// Verify $this->config() returns no data.
|
||||
$new_config = $this->config($name);
|
||||
$this->assertIdentical($new_config->get(), $config->get());
|
||||
$this->assertIdentical($config->isNew(), TRUE);
|
||||
|
||||
// Re-create the configuration object.
|
||||
$config->set('value', 're-created');
|
||||
$config->save();
|
||||
$this->assertIdentical($config->isNew(), FALSE);
|
||||
|
||||
// Verify the active configuration contains the updated value.
|
||||
$actual_data = $storage->read($name);
|
||||
$this->assertIdentical($actual_data, array('value' => 're-created'));
|
||||
|
||||
// Verify a call to $this->config() immediately returns the updated value.
|
||||
$new_config = $this->config($name);
|
||||
$this->assertIdentical($new_config->get(), $config->get());
|
||||
$this->assertIdentical($config->isNew(), FALSE);
|
||||
|
||||
// Rename the configuration object.
|
||||
$new_name = 'config_test.crud_rename';
|
||||
$this->container->get('config.factory')->rename($name, $new_name);
|
||||
$renamed_config = $this->config($new_name);
|
||||
$this->assertIdentical($renamed_config->get(), $config->get());
|
||||
$this->assertIdentical($renamed_config->isNew(), FALSE);
|
||||
|
||||
// Ensure that the old configuration object is removed from both the cache
|
||||
// and the configuration storage.
|
||||
$config = $this->config($name);
|
||||
$this->assertIdentical($config->get(), array());
|
||||
$this->assertIdentical($config->isNew(), TRUE);
|
||||
|
||||
// Test renaming when config.factory does not have the object in its static
|
||||
// cache.
|
||||
$name = 'config_test.crud_rename';
|
||||
// Pollute the non-overrides static cache.
|
||||
$config_factory->getEditable($name);
|
||||
// Pollute the overrides static cache.
|
||||
$config = $config_factory->get($name);
|
||||
// Rename and ensure that happened properly.
|
||||
$new_name = 'config_test.crud_rename_no_cache';
|
||||
$config_factory->rename($name, $new_name);
|
||||
$renamed_config = $config_factory->get($new_name);
|
||||
$this->assertIdentical($renamed_config->get(), $config->get());
|
||||
$this->assertIdentical($renamed_config->isNew(), FALSE);
|
||||
// Ensure the overrides static cache has been cleared.
|
||||
$this->assertIdentical($config_factory->get($name)->isNew(), TRUE);
|
||||
// Ensure the non-overrides static cache has been cleared.
|
||||
$this->assertIdentical($config_factory->getEditable($name)->isNew(), TRUE);
|
||||
|
||||
// Merge data into the configuration object.
|
||||
$new_config = $this->config($new_name);
|
||||
$expected_values = array(
|
||||
'value' => 'herp',
|
||||
'404' => 'derp',
|
||||
);
|
||||
$new_config->merge($expected_values);
|
||||
$new_config->save();
|
||||
$this->assertIdentical($new_config->get('value'), $expected_values['value']);
|
||||
$this->assertIdentical($new_config->get('404'), $expected_values['404']);
|
||||
|
||||
// Test that getMultiple() does not return new config objects that were
|
||||
// previously accessed with get()
|
||||
$new_config = $config_factory->get('non_existing_key');
|
||||
$this->assertTrue($new_config->isNew());
|
||||
$this->assertEqual(0, count($config_factory->loadMultiple(['non_existing_key'])), 'loadMultiple() does not return new objects');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the validation of configuration object names.
|
||||
*/
|
||||
function testNameValidation() {
|
||||
// Verify that an object name without namespace causes an exception.
|
||||
$name = 'nonamespace';
|
||||
$message = 'Expected ConfigNameException was thrown for a name without a namespace.';
|
||||
try {
|
||||
$this->config($name)->save();
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (ConfigNameException $e) {
|
||||
$this->pass($message);
|
||||
}
|
||||
|
||||
// Verify that a name longer than the maximum length causes an exception.
|
||||
$name = 'config_test.herman_melville.moby_dick_or_the_whale.harper_1851.now_small_fowls_flew_screaming_over_the_yet_yawning_gulf_a_sullen_white_surf_beat_against_its_steep_sides_then_all_collapsed_and_the_great_shroud_of_the_sea_rolled_on_as_it_rolled_five_thousand_years_ago';
|
||||
$message = 'Expected ConfigNameException was thrown for a name longer than Config::MAX_NAME_LENGTH.';
|
||||
try {
|
||||
$this->config($name)->save();
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (ConfigNameException $e) {
|
||||
$this->pass($message);
|
||||
}
|
||||
|
||||
// Verify that disallowed characters in the name cause an exception.
|
||||
$characters = $test_characters = array(':', '?', '*', '<', '>', '"', '\'', '/', '\\');
|
||||
foreach ($test_characters as $i => $c) {
|
||||
try {
|
||||
$name = 'namespace.object' . $c;
|
||||
$config = $this->config($name);
|
||||
$config->save();
|
||||
}
|
||||
catch (ConfigNameException $e) {
|
||||
unset($test_characters[$i]);
|
||||
}
|
||||
}
|
||||
$this->assertTrue(empty($test_characters), format_string('Expected ConfigNameException was thrown for all invalid name characters: @characters', array(
|
||||
'@characters' => implode(' ', $characters),
|
||||
)));
|
||||
|
||||
// Verify that a valid config object name can be saved.
|
||||
$name = 'namespace.object';
|
||||
$message = 'ConfigNameException was not thrown for a valid object name.';
|
||||
try {
|
||||
$config = $this->config($name);
|
||||
$config->save();
|
||||
$this->pass($message);
|
||||
}
|
||||
catch (ConfigNameException $e) {
|
||||
$this->fail($message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the validation of configuration object values.
|
||||
*/
|
||||
function testValueValidation() {
|
||||
// Verify that setData() will catch dotted keys.
|
||||
$message = 'Expected ConfigValueException was thrown from setData() for value with dotted keys.';
|
||||
try {
|
||||
$this->config('namespace.object')->setData(array('key.value' => 12))->save();
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (ConfigValueException $e) {
|
||||
$this->pass($message);
|
||||
}
|
||||
|
||||
// Verify that set() will catch dotted keys.
|
||||
$message = 'Expected ConfigValueException was thrown from set() for value with dotted keys.';
|
||||
try {
|
||||
$this->config('namespace.object')->set('foo', array('key.value' => 12))->save();
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (ConfigValueException $e) {
|
||||
$this->pass($message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests data type handling.
|
||||
*/
|
||||
public function testDataTypes() {
|
||||
\Drupal::service('module_installer')->install(array('config_test'));
|
||||
$storage = new DatabaseStorage($this->container->get('database'), 'config');
|
||||
$name = 'config_test.types';
|
||||
$config = $this->config($name);
|
||||
$original_content = file_get_contents(drupal_get_path('module', 'config_test') . '/' . InstallStorage::CONFIG_INSTALL_DIRECTORY . "/$name.yml");
|
||||
$this->verbose('<pre>' . $original_content . "\n" . var_export($storage->read($name), TRUE));
|
||||
|
||||
// Verify variable data types are intact.
|
||||
$data = array(
|
||||
'array' => array(),
|
||||
'boolean' => TRUE,
|
||||
'exp' => 1.2e+34,
|
||||
'float' => 3.14159,
|
||||
'float_as_integer' => (float) 1,
|
||||
'hex' => 0xC,
|
||||
'int' => 99,
|
||||
'octal' => 0775,
|
||||
'string' => 'string',
|
||||
'string_int' => '1',
|
||||
);
|
||||
$data['_core']['default_config_hash'] = Crypt::hashBase64(serialize($data));
|
||||
$this->assertIdentical($config->get(), $data);
|
||||
|
||||
// Re-set each key using Config::set().
|
||||
foreach($data as $key => $value) {
|
||||
$config->set($key, $value);
|
||||
}
|
||||
$config->save();
|
||||
$this->assertIdentical($config->get(), $data);
|
||||
// Assert the data against the file storage.
|
||||
$this->assertIdentical($storage->read($name), $data);
|
||||
$this->verbose('<pre>' . $name . var_export($storage->read($name), TRUE));
|
||||
|
||||
// Set data using config::setData().
|
||||
$config->setData($data)->save();
|
||||
$this->assertIdentical($config->get(), $data);
|
||||
$this->assertIdentical($storage->read($name), $data);
|
||||
|
||||
// Test that schema type enforcement can be overridden by trusting the data.
|
||||
$this->assertIdentical(99, $config->get('int'));
|
||||
$config->set('int', '99')->save(TRUE);
|
||||
$this->assertIdentical('99', $config->get('int'));
|
||||
// Test that re-saving without testing the data enforces the schema type.
|
||||
$config->save();
|
||||
$this->assertIdentical($data, $config->get());
|
||||
|
||||
// Test that setting an unsupported type for a config object with a schema
|
||||
// fails.
|
||||
try {
|
||||
$config->set('stream', fopen(__FILE__, 'r'))->save();
|
||||
$this->fail('No Exception thrown upon saving invalid data type.');
|
||||
}
|
||||
catch (UnsupportedDataTypeConfigException $e) {
|
||||
$this->pass(SafeMarkup::format('%class thrown upon saving invalid data type.', array(
|
||||
'%class' => get_class($e),
|
||||
)));
|
||||
}
|
||||
|
||||
// Test that setting an unsupported type for a config object with no schema
|
||||
// also fails.
|
||||
$typed_config_manager = $this->container->get('config.typed');
|
||||
$config_name = 'config_test.no_schema';
|
||||
$config = $this->config($config_name);
|
||||
$this->assertFalse($typed_config_manager->hasConfigSchema($config_name));
|
||||
|
||||
try {
|
||||
$config->set('stream', fopen(__FILE__, 'r'))->save();
|
||||
$this->fail('No Exception thrown upon saving invalid data type.');
|
||||
}
|
||||
catch (UnsupportedDataTypeConfigException $e) {
|
||||
$this->pass(SafeMarkup::format('%class thrown upon saving invalid data type.', array(
|
||||
'%class' => get_class($e),
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,481 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Config;
|
||||
|
||||
use Drupal\entity_test\Entity\EntityTest;
|
||||
use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests for configuration dependencies.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigDependencyTest extends EntityKernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* The entity_test module is enabled to provide content entity types.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('config_test', 'entity_test', 'user');
|
||||
|
||||
/**
|
||||
* Tests that calculating dependencies for system module.
|
||||
*/
|
||||
public function testNonEntity() {
|
||||
$this->installConfig(array('system'));
|
||||
$config_manager = \Drupal::service('config.manager');
|
||||
$dependents = $config_manager->findConfigEntityDependents('module', array('system'));
|
||||
$this->assertTrue(isset($dependents['system.site']), 'Simple configuration system.site has a UUID key even though it is not a configuration entity and therefore is found when looking for dependencies of the System module.');
|
||||
// Ensure that calling
|
||||
// \Drupal\Core\Config\ConfigManager::findConfigEntityDependentsAsEntities()
|
||||
// does not try to load system.site as an entity.
|
||||
$config_manager->findConfigEntityDependentsAsEntities('module', array('system'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests creating dependencies on configuration entities.
|
||||
*/
|
||||
public function testDependencyManagement() {
|
||||
/** @var \Drupal\Core\Config\ConfigManagerInterface $config_manager */
|
||||
$config_manager = \Drupal::service('config.manager');
|
||||
$storage = $this->container->get('entity.manager')->getStorage('config_test');
|
||||
// Test dependencies between modules.
|
||||
$entity1 = $storage->create(
|
||||
array(
|
||||
'id' => 'entity1',
|
||||
'dependencies' => array(
|
||||
'enforced' => array(
|
||||
'module' => array('node')
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
$entity1->save();
|
||||
|
||||
$dependents = $config_manager->findConfigEntityDependents('module', array('node'));
|
||||
$this->assertTrue(isset($dependents['config_test.dynamic.entity1']), 'config_test.dynamic.entity1 has a dependency on the Node module.');
|
||||
$dependents = $config_manager->findConfigEntityDependents('module', array('config_test'));
|
||||
$this->assertTrue(isset($dependents['config_test.dynamic.entity1']), 'config_test.dynamic.entity1 has a dependency on the config_test module.');
|
||||
$dependents = $config_manager->findConfigEntityDependents('module', array('views'));
|
||||
$this->assertFalse(isset($dependents['config_test.dynamic.entity1']), 'config_test.dynamic.entity1 does not have a dependency on the Views module.');
|
||||
// Ensure that the provider of the config entity is not actually written to
|
||||
// the dependencies array.
|
||||
$raw_config = $this->config('config_test.dynamic.entity1');
|
||||
$root_module_dependencies = $raw_config->get('dependencies.module');
|
||||
$this->assertTrue(empty($root_module_dependencies), 'Node module is not written to the root dependencies array as it is enforced.');
|
||||
|
||||
// Create additional entities to test dependencies on config entities.
|
||||
$entity2 = $storage->create(array('id' => 'entity2', 'dependencies' => array('enforced' => array('config' => array($entity1->getConfigDependencyName())))));
|
||||
$entity2->save();
|
||||
$entity3 = $storage->create(array('id' => 'entity3', 'dependencies' => array('enforced' => array('config' => array($entity2->getConfigDependencyName())))));
|
||||
$entity3->save();
|
||||
$entity4 = $storage->create(array('id' => 'entity4', 'dependencies' => array('enforced' => array('config' => array($entity3->getConfigDependencyName())))));
|
||||
$entity4->save();
|
||||
|
||||
// Test getting $entity1's dependencies as configuration dependency objects.
|
||||
$dependents = $config_manager->findConfigEntityDependents('config', array($entity1->getConfigDependencyName()));
|
||||
$this->assertFalse(isset($dependents['config_test.dynamic.entity1']), 'config_test.dynamic.entity1 does not have a dependency on itself.');
|
||||
$this->assertTrue(isset($dependents['config_test.dynamic.entity2']), 'config_test.dynamic.entity2 has a dependency on config_test.dynamic.entity1.');
|
||||
$this->assertTrue(isset($dependents['config_test.dynamic.entity3']), 'config_test.dynamic.entity3 has a dependency on config_test.dynamic.entity1.');
|
||||
$this->assertTrue(isset($dependents['config_test.dynamic.entity4']), 'config_test.dynamic.entity4 has a dependency on config_test.dynamic.entity1.');
|
||||
|
||||
// Test getting $entity2's dependencies as entities.
|
||||
$dependents = $config_manager->findConfigEntityDependentsAsEntities('config', array($entity2->getConfigDependencyName()));
|
||||
$dependent_ids = $this->getDependentIds($dependents);
|
||||
$this->assertFalse(in_array('config_test:entity1', $dependent_ids), 'config_test.dynamic.entity1 does not have a dependency on config_test.dynamic.entity1.');
|
||||
$this->assertFalse(in_array('config_test:entity2', $dependent_ids), 'config_test.dynamic.entity2 does not have a dependency on itself.');
|
||||
$this->assertTrue(in_array('config_test:entity3', $dependent_ids), 'config_test.dynamic.entity3 has a dependency on config_test.dynamic.entity2.');
|
||||
$this->assertTrue(in_array('config_test:entity4', $dependent_ids), 'config_test.dynamic.entity4 has a dependency on config_test.dynamic.entity2.');
|
||||
|
||||
// Test getting node module's dependencies as configuration dependency
|
||||
// objects.
|
||||
$dependents = $config_manager->findConfigEntityDependents('module', array('node'));
|
||||
$this->assertTrue(isset($dependents['config_test.dynamic.entity1']), 'config_test.dynamic.entity1 has a dependency on the Node module.');
|
||||
$this->assertTrue(isset($dependents['config_test.dynamic.entity2']), 'config_test.dynamic.entity2 has a dependency on the Node module.');
|
||||
$this->assertTrue(isset($dependents['config_test.dynamic.entity3']), 'config_test.dynamic.entity3 has a dependency on the Node module.');
|
||||
$this->assertTrue(isset($dependents['config_test.dynamic.entity4']), 'config_test.dynamic.entity4 has a dependency on the Node module.');
|
||||
|
||||
// Test getting node module's dependencies as configuration dependency
|
||||
// objects after making $entity3 also dependent on node module but $entity1
|
||||
// no longer depend on node module.
|
||||
$entity1->setEnforcedDependencies([])->save();
|
||||
$entity3->setEnforcedDependencies(['module' => ['node'], 'config' => [$entity2->getConfigDependencyName()]])->save();
|
||||
$dependents = $config_manager->findConfigEntityDependents('module', array('node'));
|
||||
$this->assertFalse(isset($dependents['config_test.dynamic.entity1']), 'config_test.dynamic.entity1 does not have a dependency on the Node module.');
|
||||
$this->assertFalse(isset($dependents['config_test.dynamic.entity2']), 'config_test.dynamic.entity2 does not have a dependency on the Node module.');
|
||||
$this->assertTrue(isset($dependents['config_test.dynamic.entity3']), 'config_test.dynamic.entity3 has a dependency on the Node module.');
|
||||
$this->assertTrue(isset($dependents['config_test.dynamic.entity4']), 'config_test.dynamic.entity4 has a dependency on the Node module.');
|
||||
|
||||
// Test dependency on a content entity.
|
||||
$entity_test = EntityTest::create(array(
|
||||
'name' => $this->randomString(),
|
||||
'type' => 'entity_test',
|
||||
));
|
||||
$entity_test->save();
|
||||
$entity2->setEnforcedDependencies(['config' => [$entity1->getConfigDependencyName()], 'content' => [$entity_test->getConfigDependencyName()]])->save();;
|
||||
$dependents = $config_manager->findConfigEntityDependents('content', array($entity_test->getConfigDependencyName()));
|
||||
$this->assertFalse(isset($dependents['config_test.dynamic.entity1']), 'config_test.dynamic.entity1 does not have a dependency on the content entity.');
|
||||
$this->assertTrue(isset($dependents['config_test.dynamic.entity2']), 'config_test.dynamic.entity2 has a dependency on the content entity.');
|
||||
$this->assertTrue(isset($dependents['config_test.dynamic.entity3']), 'config_test.dynamic.entity3 has a dependency on the content entity (via entity2).');
|
||||
$this->assertTrue(isset($dependents['config_test.dynamic.entity4']), 'config_test.dynamic.entity4 has a dependency on the content entity (via entity3).');
|
||||
|
||||
// Create a configuration entity of a different type with the same ID as one
|
||||
// of the entities already created.
|
||||
$alt_storage = $this->container->get('entity.manager')->getStorage('config_query_test');
|
||||
$alt_storage->create(array('id' => 'entity1', 'dependencies' => array('enforced' => array('config' => array($entity1->getConfigDependencyName())))))->save();
|
||||
$alt_storage->create(array('id' => 'entity2', 'dependencies' => array('enforced' => array('module' => array('views')))))->save();
|
||||
|
||||
$dependents = $config_manager->findConfigEntityDependentsAsEntities('config', array($entity1->getConfigDependencyName()));
|
||||
$dependent_ids = $this->getDependentIds($dependents);
|
||||
$this->assertFalse(in_array('config_test:entity1', $dependent_ids), 'config_test.dynamic.entity1 does not have a dependency on itself.');
|
||||
$this->assertTrue(in_array('config_test:entity2', $dependent_ids), 'config_test.dynamic.entity2 has a dependency on config_test.dynamic.entity1.');
|
||||
$this->assertTrue(in_array('config_test:entity3', $dependent_ids), 'config_test.dynamic.entity3 has a dependency on config_test.dynamic.entity1.');
|
||||
$this->assertTrue(in_array('config_test:entity4', $dependent_ids), 'config_test.dynamic.entity4 has a dependency on config_test.dynamic.entity1.');
|
||||
$this->assertTrue(in_array('config_query_test:entity1', $dependent_ids), 'config_query_test.dynamic.entity1 has a dependency on config_test.dynamic.entity1.');
|
||||
$this->assertFalse(in_array('config_query_test:entity2', $dependent_ids), 'config_query_test.dynamic.entity2 does not have a dependency on config_test.dynamic.entity1.');
|
||||
|
||||
$dependents = $config_manager->findConfigEntityDependentsAsEntities('module', array('node', 'views'));
|
||||
$dependent_ids = $this->getDependentIds($dependents);
|
||||
$this->assertFalse(in_array('config_test:entity1', $dependent_ids), 'config_test.dynamic.entity1 does not have a dependency on Views or Node.');
|
||||
$this->assertFalse(in_array('config_test:entity2', $dependent_ids), 'config_test.dynamic.entity2 does not have a dependency on Views or Node.');
|
||||
$this->assertTrue(in_array('config_test:entity3', $dependent_ids), 'config_test.dynamic.entity3 has a dependency on Views or Node.');
|
||||
$this->assertTrue(in_array('config_test:entity4', $dependent_ids), 'config_test.dynamic.entity4 has a dependency on Views or Node.');
|
||||
$this->assertFalse(in_array('config_query_test:entity1', $dependent_ids), 'config_test.query.entity1 does not have a dependency on Views or Node.');
|
||||
$this->assertTrue(in_array('config_query_test:entity2', $dependent_ids), 'config_test.query.entity2 has a dependency on Views or Node.');
|
||||
|
||||
$dependents = $config_manager->findConfigEntityDependentsAsEntities('module', array('config_test'));
|
||||
$dependent_ids = $this->getDependentIds($dependents);
|
||||
$this->assertTrue(in_array('config_test:entity1', $dependent_ids), 'config_test.dynamic.entity1 has a dependency on config_test module.');
|
||||
$this->assertTrue(in_array('config_test:entity2', $dependent_ids), 'config_test.dynamic.entity2 has a dependency on config_test module.');
|
||||
$this->assertTrue(in_array('config_test:entity3', $dependent_ids), 'config_test.dynamic.entity3 has a dependency on config_test module.');
|
||||
$this->assertTrue(in_array('config_test:entity4', $dependent_ids), 'config_test.dynamic.entity4 has a dependency on config_test module.');
|
||||
$this->assertTrue(in_array('config_query_test:entity1', $dependent_ids), 'config_test.query.entity1 has a dependency on config_test module.');
|
||||
$this->assertTrue(in_array('config_query_test:entity2', $dependent_ids), 'config_test.query.entity2 has a dependency on config_test module.');
|
||||
|
||||
// Test the ability to find missing content dependencies.
|
||||
$missing_dependencies = $config_manager->findMissingContentDependencies();
|
||||
$this->assertEqual([], $missing_dependencies);
|
||||
|
||||
$expected = [$entity_test->uuid() => [
|
||||
'entity_type' => 'entity_test',
|
||||
'bundle' => $entity_test->bundle(),
|
||||
'uuid' => $entity_test->uuid(),
|
||||
]];
|
||||
// Delete the content entity so that is it now missing.
|
||||
$entity_test->delete();
|
||||
$missing_dependencies = $config_manager->findMissingContentDependencies();
|
||||
$this->assertEqual($expected, $missing_dependencies);
|
||||
|
||||
// Add a fake missing dependency to ensure multiple missing dependencies
|
||||
// work.
|
||||
$entity1->setEnforcedDependencies(['content' => [$entity_test->getConfigDependencyName(), 'entity_test:bundle:uuid']])->save();;
|
||||
$expected['uuid'] = [
|
||||
'entity_type' => 'entity_test',
|
||||
'bundle' => 'bundle',
|
||||
'uuid' => 'uuid',
|
||||
];
|
||||
$missing_dependencies = $config_manager->findMissingContentDependencies();
|
||||
$this->assertEqual($expected, $missing_dependencies);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests ConfigManager::uninstall() and config entity dependency management.
|
||||
*/
|
||||
public function testConfigEntityUninstall() {
|
||||
/** @var \Drupal\Core\Config\ConfigManagerInterface $config_manager */
|
||||
$config_manager = \Drupal::service('config.manager');
|
||||
/** @var \Drupal\Core\Config\Entity\ConfigEntityStorage $storage */
|
||||
$storage = $this->container->get('entity.manager')->getStorage('config_test');
|
||||
// Test dependencies between modules.
|
||||
$entity1 = $storage->create(
|
||||
array(
|
||||
'id' => 'entity1',
|
||||
'dependencies' => array(
|
||||
'enforced' => array(
|
||||
'module' => array('node', 'config_test')
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
$entity1->save();
|
||||
$entity2 = $storage->create(
|
||||
array(
|
||||
'id' => 'entity2',
|
||||
'dependencies' => array(
|
||||
'enforced' => array(
|
||||
'config' => array($entity1->getConfigDependencyName()),
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
$entity2->save();
|
||||
// Perform a module rebuild so we can know where the node module is located
|
||||
// and uninstall it.
|
||||
// @todo Remove as part of https://www.drupal.org/node/2186491
|
||||
system_rebuild_module_data();
|
||||
// Test that doing a config uninstall of the node module deletes entity2
|
||||
// since it is dependent on entity1 which is dependent on the node module.
|
||||
$config_manager->uninstall('module', 'node');
|
||||
$this->assertFalse($storage->load('entity1'), 'Entity 1 deleted');
|
||||
$this->assertFalse($storage->load('entity2'), 'Entity 2 deleted');
|
||||
|
||||
// Set a more complicated test where dependencies will be fixed.
|
||||
\Drupal::state()->set('config_test.fix_dependencies', array($entity1->getConfigDependencyName()));
|
||||
\Drupal::state()->set('config_test.on_dependency_removal_called', []);
|
||||
// Entity1 will be deleted because it depends on node.
|
||||
$entity1 = $storage->create(
|
||||
array(
|
||||
'id' => 'entity1',
|
||||
'dependencies' => array(
|
||||
'enforced' => array(
|
||||
'module' => array('node', 'config_test')
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
$entity1->save();
|
||||
|
||||
// Entity2 has a dependency on Entity1 but it can be fixed because
|
||||
// \Drupal\config_test\Entity::onDependencyRemoval() will remove the
|
||||
// dependency before config entities are deleted.
|
||||
$entity2 = $storage->create(
|
||||
array(
|
||||
'id' => 'entity2',
|
||||
'dependencies' => array(
|
||||
'enforced' => array(
|
||||
'config' => array($entity1->getConfigDependencyName()),
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
$entity2->save();
|
||||
|
||||
// Entity3 will be unchanged because it is dependent on Entity2 which can
|
||||
// be fixed. The ConfigEntityInterface::onDependencyRemoval() method will
|
||||
// not be called for this entity.
|
||||
$entity3 = $storage->create(
|
||||
array(
|
||||
'id' => 'entity3',
|
||||
'dependencies' => array(
|
||||
'enforced' => array(
|
||||
'config' => array($entity2->getConfigDependencyName()),
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
$entity3->save();
|
||||
|
||||
// Entity4's config dependency will be fixed but it will still be deleted
|
||||
// because it also depends on the node module.
|
||||
$entity4 = $storage->create(
|
||||
array(
|
||||
'id' => 'entity4',
|
||||
'dependencies' => array(
|
||||
'enforced' => array(
|
||||
'config' => array($entity1->getConfigDependencyName()),
|
||||
'module' => array('node', 'config_test')
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
$entity4->save();
|
||||
|
||||
// Do a dry run using
|
||||
// \Drupal\Core\Config\ConfigManager::getConfigEntitiesToChangeOnDependencyRemoval().
|
||||
$config_entities = $config_manager->getConfigEntitiesToChangeOnDependencyRemoval('module', ['node']);
|
||||
$this->assertEqual($entity1->uuid(), $config_entities['delete'][0]->uuid(), 'Entity 1 will be deleted.');
|
||||
$this->assertEqual($entity2->uuid(), reset($config_entities['update'])->uuid(), 'Entity 2 will be updated.');
|
||||
$this->assertEqual($entity3->uuid(), reset($config_entities['unchanged'])->uuid(), 'Entity 3 is not changed.');
|
||||
$this->assertEqual($entity4->uuid(), $config_entities['delete'][1]->uuid(), 'Entity 4 will be deleted.');
|
||||
|
||||
$called = \Drupal::state()->get('config_test.on_dependency_removal_called', []);
|
||||
$this->assertFalse(in_array($entity3->id(), $called), 'ConfigEntityInterface::onDependencyRemoval() is not called for entity 3.');
|
||||
$this->assertIdentical(['entity1', 'entity2', 'entity4'], $called, 'The most dependent entites have ConfigEntityInterface::onDependencyRemoval() called first.');
|
||||
|
||||
// Perform the uninstall.
|
||||
$config_manager->uninstall('module', 'node');
|
||||
|
||||
// Test that expected actions have been performed.
|
||||
$this->assertFalse($storage->load('entity1'), 'Entity 1 deleted');
|
||||
$entity2 = $storage->load('entity2');
|
||||
$this->assertTrue($entity2, 'Entity 2 not deleted');
|
||||
$this->assertEqual($entity2->calculateDependencies()->getDependencies()['config'], array(), 'Entity 2 dependencies updated to remove dependency on Entity1.');
|
||||
$entity3 = $storage->load('entity3');
|
||||
$this->assertTrue($entity3, 'Entity 3 not deleted');
|
||||
$this->assertEqual($entity3->calculateDependencies()->getDependencies()['config'], [$entity2->getConfigDependencyName()], 'Entity 3 still depends on Entity 2.');
|
||||
$this->assertFalse($storage->load('entity4'), 'Entity 4 deleted');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests deleting a configuration entity and dependency management.
|
||||
*/
|
||||
public function testConfigEntityDelete() {
|
||||
/** @var \Drupal\Core\Config\ConfigManagerInterface $config_manager */
|
||||
$config_manager = \Drupal::service('config.manager');
|
||||
/** @var \Drupal\Core\Config\Entity\ConfigEntityStorage $storage */
|
||||
$storage = $this->container->get('entity.manager')->getStorage('config_test');
|
||||
// Test dependencies between configuration entities.
|
||||
$entity1 = $storage->create(
|
||||
array(
|
||||
'id' => 'entity1'
|
||||
)
|
||||
);
|
||||
$entity1->save();
|
||||
$entity2 = $storage->create(
|
||||
array(
|
||||
'id' => 'entity2',
|
||||
'dependencies' => array(
|
||||
'enforced' => array(
|
||||
'config' => array($entity1->getConfigDependencyName()),
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
$entity2->save();
|
||||
|
||||
// Do a dry run using
|
||||
// \Drupal\Core\Config\ConfigManager::getConfigEntitiesToChangeOnDependencyRemoval().
|
||||
$config_entities = $config_manager->getConfigEntitiesToChangeOnDependencyRemoval('config', [$entity1->getConfigDependencyName()]);
|
||||
$this->assertEqual($entity2->uuid(), reset($config_entities['delete'])->uuid(), 'Entity 2 will be deleted.');
|
||||
$this->assertTrue(empty($config_entities['update']), 'No dependent configuration entities will be updated.');
|
||||
$this->assertTrue(empty($config_entities['unchanged']), 'No dependent configuration entities will be unchanged.');
|
||||
|
||||
// Test that doing a delete of entity1 deletes entity2 since it is dependent
|
||||
// on entity1.
|
||||
$entity1->delete();
|
||||
$this->assertFalse($storage->load('entity1'), 'Entity 1 deleted');
|
||||
$this->assertFalse($storage->load('entity2'), 'Entity 2 deleted');
|
||||
|
||||
// Set a more complicated test where dependencies will be fixed.
|
||||
\Drupal::state()->set('config_test.fix_dependencies', array($entity1->getConfigDependencyName()));
|
||||
|
||||
// Entity1 will be deleted by the test.
|
||||
$entity1 = $storage->create(
|
||||
array(
|
||||
'id' => 'entity1',
|
||||
)
|
||||
);
|
||||
$entity1->save();
|
||||
|
||||
// Entity2 has a dependency on Entity1 but it can be fixed because
|
||||
// \Drupal\config_test\Entity::onDependencyRemoval() will remove the
|
||||
// dependency before config entities are deleted.
|
||||
$entity2 = $storage->create(
|
||||
array(
|
||||
'id' => 'entity2',
|
||||
'dependencies' => array(
|
||||
'enforced' => array(
|
||||
'config' => array($entity1->getConfigDependencyName()),
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
$entity2->save();
|
||||
|
||||
// Entity3 will be unchanged because it is dependent on Entity2 which can
|
||||
// be fixed.
|
||||
$entity3 = $storage->create(
|
||||
array(
|
||||
'id' => 'entity3',
|
||||
'dependencies' => array(
|
||||
'enforced' => array(
|
||||
'config' => array($entity2->getConfigDependencyName()),
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
$entity3->save();
|
||||
|
||||
// Do a dry run using
|
||||
// \Drupal\Core\Config\ConfigManager::getConfigEntitiesToChangeOnDependencyRemoval().
|
||||
$config_entities = $config_manager->getConfigEntitiesToChangeOnDependencyRemoval('config', [$entity1->getConfigDependencyName()]);
|
||||
$this->assertTrue(empty($config_entities['delete']), 'No dependent configuration entities will be deleted.');
|
||||
$this->assertEqual($entity2->uuid(), reset($config_entities['update'])->uuid(), 'Entity 2 will be updated.');
|
||||
$this->assertEqual($entity3->uuid(), reset($config_entities['unchanged'])->uuid(), 'Entity 3 is not changed.');
|
||||
|
||||
// Perform the uninstall.
|
||||
$entity1->delete();
|
||||
|
||||
// Test that expected actions have been performed.
|
||||
$this->assertFalse($storage->load('entity1'), 'Entity 1 deleted');
|
||||
$entity2 = $storage->load('entity2');
|
||||
$this->assertTrue($entity2, 'Entity 2 not deleted');
|
||||
$this->assertEqual($entity2->calculateDependencies()->getDependencies()['config'], array(), 'Entity 2 dependencies updated to remove dependency on Entity1.');
|
||||
$entity3 = $storage->load('entity3');
|
||||
$this->assertTrue($entity3, 'Entity 3 not deleted');
|
||||
$this->assertEqual($entity3->calculateDependencies()->getDependencies()['config'], [$entity2->getConfigDependencyName()], 'Entity 3 still depends on Entity 2.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests getConfigEntitiesToChangeOnDependencyRemoval() with content entities.
|
||||
*
|
||||
* At the moment there is no runtime code that calculates configuration
|
||||
* dependencies on content entity delete because this calculation is expensive
|
||||
* and all content dependencies are soft. This test ensures that the code
|
||||
* works for content entities.
|
||||
*
|
||||
* @see \Drupal\Core\Config\ConfigManager::getConfigEntitiesToChangeOnDependencyRemoval()
|
||||
*/
|
||||
public function testContentEntityDelete() {
|
||||
$this->installEntitySchema('entity_test');
|
||||
/** @var \Drupal\Core\Config\ConfigManagerInterface $config_manager */
|
||||
$config_manager = \Drupal::service('config.manager');
|
||||
|
||||
$content_entity = EntityTest::create();
|
||||
$content_entity->save();
|
||||
/** @var \Drupal\Core\Config\Entity\ConfigEntityStorage $storage */
|
||||
$storage = $this->container->get('entity.manager')->getStorage('config_test');
|
||||
$entity1 = $storage->create(
|
||||
array(
|
||||
'id' => 'entity1',
|
||||
'dependencies' => array(
|
||||
'enforced' => array(
|
||||
'content' => array($content_entity->getConfigDependencyName())
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
$entity1->save();
|
||||
$entity2 = $storage->create(
|
||||
array(
|
||||
'id' => 'entity2',
|
||||
'dependencies' => array(
|
||||
'enforced' => array(
|
||||
'config' => array($entity1->getConfigDependencyName())
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
$entity2->save();
|
||||
|
||||
// Create a configuration entity that is not in the dependency chain.
|
||||
$entity3 = $storage->create(array('id' => 'entity3'));
|
||||
$entity3->save();
|
||||
|
||||
$config_entities = $config_manager->getConfigEntitiesToChangeOnDependencyRemoval('content', [$content_entity->getConfigDependencyName()]);
|
||||
$this->assertEqual($entity1->uuid(), $config_entities['delete'][0]->uuid(), 'Entity 1 will be deleted.');
|
||||
$this->assertEqual($entity2->uuid(), $config_entities['delete'][1]->uuid(), 'Entity 2 will be deleted.');
|
||||
$this->assertTrue(empty($config_entities['update']), 'No dependencies of the content entity will be updated.');
|
||||
$this->assertTrue(empty($config_entities['unchanged']), 'No dependencies of the content entity will be unchanged.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of identifiers from an array of configuration entities.
|
||||
*
|
||||
* @param \Drupal\Core\Config\Entity\ConfigEntityInterface[] $dependents
|
||||
* An array of configuration entities.
|
||||
*
|
||||
* @return array
|
||||
* An array with values of entity_type_id:ID
|
||||
*/
|
||||
protected function getDependentIds(array $dependents) {
|
||||
$dependent_ids = array();
|
||||
foreach($dependents as $dependent) {
|
||||
$dependent_ids[] = $dependent->getEntityTypeId() . ':' . $dependent->id();
|
||||
}
|
||||
return $dependent_ids;
|
||||
}
|
||||
}
|
||||
142
core/tests/Drupal/KernelTests/Core/Config/ConfigDiffTest.php
Normal file
142
core/tests/Drupal/KernelTests/Core/Config/ConfigDiffTest.php
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Config;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Calculating the difference between two sets of configuration.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigDiffTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('config_test', 'system');
|
||||
|
||||
/**
|
||||
* Tests calculating the difference between two sets of configuration.
|
||||
*/
|
||||
function testDiff() {
|
||||
$active = $this->container->get('config.storage');
|
||||
$sync = $this->container->get('config.storage.sync');
|
||||
$config_name = 'config_test.system';
|
||||
$change_key = 'foo';
|
||||
$remove_key = '404';
|
||||
$add_key = 'biff';
|
||||
$add_data = 'bangpow';
|
||||
$change_data = 'foobar';
|
||||
|
||||
// Install the default config.
|
||||
$this->installConfig(array('config_test'));
|
||||
$original_data = \Drupal::config($config_name)->get();
|
||||
|
||||
// Change a configuration value in sync.
|
||||
$sync_data = $original_data;
|
||||
$sync_data[$change_key] = $change_data;
|
||||
$sync_data[$add_key] = $add_data;
|
||||
$sync->write($config_name, $sync_data);
|
||||
|
||||
// Verify that the diff reflects a change.
|
||||
$diff = \Drupal::service('config.manager')->diff($active, $sync, $config_name);
|
||||
$edits = $diff->getEdits();
|
||||
$this->assertEqual($edits[0]->type, 'change', 'The first item in the diff is a change.');
|
||||
$this->assertEqual($edits[0]->orig[0], $change_key . ': ' . $original_data[$change_key], format_string("The active value for key '%change_key' is '%original_data'.", array('%change_key' => $change_key, '%original_data' => $original_data[$change_key])));
|
||||
$this->assertEqual($edits[0]->closing[0], $change_key . ': ' . $change_data, format_string("The sync value for key '%change_key' is '%change_data'.", array('%change_key' => $change_key, '%change_data' => $change_data)));
|
||||
|
||||
// Reset data back to original, and remove a key
|
||||
$sync_data = $original_data;
|
||||
unset($sync_data[$remove_key]);
|
||||
$sync->write($config_name, $sync_data);
|
||||
|
||||
// Verify that the diff reflects a removed key.
|
||||
$diff = \Drupal::service('config.manager')->diff($active, $sync, $config_name);
|
||||
$edits = $diff->getEdits();
|
||||
$this->assertEqual($edits[0]->type, 'copy', 'The first item in the diff is a copy.');
|
||||
$this->assertEqual($edits[1]->type, 'delete', 'The second item in the diff is a delete.');
|
||||
$this->assertEqual($edits[1]->orig[0], $remove_key . ': ' . $original_data[$remove_key], format_string("The active value for key '%remove_key' is '%original_data'.", array('%remove_key' => $remove_key, '%original_data' => $original_data[$remove_key])));
|
||||
$this->assertFalse($edits[1]->closing, format_string("The key '%remove_key' does not exist in sync.", array('%remove_key' => $remove_key)));
|
||||
|
||||
// Reset data back to original and add a key
|
||||
$sync_data = $original_data;
|
||||
$sync_data[$add_key] = $add_data;
|
||||
$sync->write($config_name, $sync_data);
|
||||
|
||||
// Verify that the diff reflects an added key.
|
||||
$diff = \Drupal::service('config.manager')->diff($active, $sync, $config_name);
|
||||
$edits = $diff->getEdits();
|
||||
$this->assertEqual($edits[0]->type, 'copy', 'The first item in the diff is a copy.');
|
||||
$this->assertEqual($edits[1]->type, 'add', 'The second item in the diff is an add.');
|
||||
$this->assertFalse($edits[1]->orig, format_string("The key '%add_key' does not exist in active.", array('%add_key' => $add_key)));
|
||||
$this->assertEqual($edits[1]->closing[0], $add_key . ': ' . $add_data, format_string("The sync value for key '%add_key' is '%add_data'.", array('%add_key' => $add_key, '%add_data' => $add_data)));
|
||||
|
||||
// Test diffing a renamed config entity.
|
||||
$test_entity_id = $this->randomMachineName();
|
||||
$test_entity = entity_create('config_test', array(
|
||||
'id' => $test_entity_id,
|
||||
'label' => $this->randomMachineName(),
|
||||
));
|
||||
$test_entity->save();
|
||||
$data = $active->read('config_test.dynamic.' . $test_entity_id);
|
||||
$sync->write('config_test.dynamic.' . $test_entity_id, $data);
|
||||
$config_name = 'config_test.dynamic.' . $test_entity_id;
|
||||
$diff = \Drupal::service('config.manager')->diff($active, $sync, $config_name, $config_name);
|
||||
// Prove the fields match.
|
||||
$edits = $diff->getEdits();
|
||||
$this->assertEqual($edits[0]->type, 'copy', 'The first item in the diff is a copy.');
|
||||
$this->assertEqual(count($edits), 1, 'There is one item in the diff');
|
||||
|
||||
// Rename the entity.
|
||||
$new_test_entity_id = $this->randomMachineName();
|
||||
$test_entity->set('id', $new_test_entity_id);
|
||||
$test_entity->save();
|
||||
|
||||
$diff = \Drupal::service('config.manager')->diff($active, $sync, 'config_test.dynamic.' . $new_test_entity_id, $config_name);
|
||||
$edits = $diff->getEdits();
|
||||
$this->assertEqual($edits[0]->type, 'copy', 'The first item in the diff is a copy.');
|
||||
$this->assertEqual($edits[1]->type, 'change', 'The second item in the diff is a change.');
|
||||
$this->assertEqual($edits[1]->orig, array('id: ' . $new_test_entity_id));
|
||||
$this->assertEqual($edits[1]->closing, array('id: ' . $test_entity_id));
|
||||
$this->assertEqual($edits[2]->type, 'copy', 'The third item in the diff is a copy.');
|
||||
$this->assertEqual(count($edits), 3, 'There are three items in the diff.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests calculating the difference between two sets of config collections.
|
||||
*/
|
||||
function testCollectionDiff() {
|
||||
/** @var \Drupal\Core\Config\StorageInterface $active */
|
||||
$active = $this->container->get('config.storage');
|
||||
/** @var \Drupal\Core\Config\StorageInterface $sync */
|
||||
$sync = $this->container->get('config.storage.sync');
|
||||
$active_test_collection = $active->createCollection('test');
|
||||
$sync_test_collection = $sync->createCollection('test');
|
||||
|
||||
$config_name = 'config_test.test';
|
||||
$data = array('foo' => 'bar');
|
||||
|
||||
$active->write($config_name, $data);
|
||||
$sync->write($config_name, $data);
|
||||
$active_test_collection->write($config_name, $data);
|
||||
$sync_test_collection->write($config_name, array('foo' => 'baz'));
|
||||
|
||||
// Test the fields match in the default collection diff.
|
||||
$diff = \Drupal::service('config.manager')->diff($active, $sync, $config_name);
|
||||
$edits = $diff->getEdits();
|
||||
$this->assertEqual($edits[0]->type, 'copy', 'The first item in the diff is a copy.');
|
||||
$this->assertEqual(count($edits), 1, 'There is one item in the diff');
|
||||
|
||||
// Test that the differences are detected when diffing the collection.
|
||||
$diff = \Drupal::service('config.manager')->diff($active, $sync, $config_name, NULL, 'test');
|
||||
$edits = $diff->getEdits();
|
||||
$this->assertEqual($edits[0]->type, 'change', 'The second item in the diff is a copy.');
|
||||
$this->assertEqual($edits[0]->orig, array('foo: bar'));
|
||||
$this->assertEqual($edits[0]->closing, array('foo: baz'));
|
||||
$this->assertEqual($edits[1]->type, 'copy', 'The second item in the diff is a copy.');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Config;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests the listing of configuration entities.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigEntityNormalizeTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('config_test');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installConfig(static::$modules);
|
||||
}
|
||||
|
||||
public function testNormalize() {
|
||||
$config_entity = entity_create('config_test', array('id' => 'system', 'label' => 'foobar', 'weight' => 1));
|
||||
$config_entity->save();
|
||||
|
||||
// Modify stored config entity, this is comparable with a schema change.
|
||||
$config = $this->config('config_test.dynamic.system');
|
||||
$data = array(
|
||||
'label' => 'foobar',
|
||||
'additional_key' => TRUE
|
||||
) + $config->getRawData();
|
||||
$config->setData($data)->save();
|
||||
$this->assertNotIdentical($config_entity->toArray(), $config->getRawData(), 'Stored config entity is not is equivalent to config schema.');
|
||||
|
||||
$config_entity = entity_load('config_test', 'system', TRUE);
|
||||
$config_entity->save();
|
||||
|
||||
$config = $this->config('config_test.dynamic.system');
|
||||
$this->assertIdentical($config_entity->toArray(), $config->getRawData(), 'Stored config entity is equivalent to config schema.');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Config;
|
||||
|
||||
use Drupal\config_entity_static_cache_test\ConfigOverrider;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests the entity static cache when used by config entities.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigEntityStaticCacheTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('config_test', 'config_entity_static_cache_test');
|
||||
|
||||
/**
|
||||
* The type ID of the entity under test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $entityTypeId;
|
||||
|
||||
/**
|
||||
* The entity ID of the entity under test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $entityId;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->entityTypeId = 'config_test';
|
||||
$this->entityId = 'test_1';
|
||||
$this->container->get('entity_type.manager')
|
||||
->getStorage($this->entityTypeId)
|
||||
->create(array('id' => $this->entityId, 'label' => 'Original label'))
|
||||
->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the static cache is working.
|
||||
*/
|
||||
public function testCacheHit() {
|
||||
$entity_1 = entity_load($this->entityTypeId, $this->entityId);
|
||||
$entity_2 = entity_load($this->entityTypeId, $this->entityId);
|
||||
// config_entity_static_cache_test_config_test_load() sets _loadStamp to a
|
||||
// random string. If they match, it means $entity_2 was retrieved from the
|
||||
// static cache rather than going through a separate load sequence.
|
||||
$this->assertIdentical($entity_1->_loadStamp, $entity_2->_loadStamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the static cache is reset on entity save and delete.
|
||||
*/
|
||||
public function testReset() {
|
||||
$entity = entity_load($this->entityTypeId, $this->entityId);
|
||||
|
||||
// Ensure loading after a save retrieves the updated entity rather than an
|
||||
// obsolete cached one.
|
||||
$entity->label = 'New label';
|
||||
$entity->save();
|
||||
$entity = entity_load($this->entityTypeId, $this->entityId);
|
||||
$this->assertIdentical($entity->label, 'New label');
|
||||
|
||||
// Ensure loading after a delete retrieves NULL rather than an obsolete
|
||||
// cached one.
|
||||
$entity->delete();
|
||||
$this->assertNull(entity_load($this->entityTypeId, $this->entityId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the static cache is sensitive to config overrides.
|
||||
*/
|
||||
public function testConfigOverride() {
|
||||
/** @var \Drupal\Core\Config\Entity\ConfigEntityStorage $storage */
|
||||
$storage = \Drupal::entityManager()->getStorage($this->entityTypeId);
|
||||
// Prime the cache prior to adding a config override.
|
||||
$storage->load($this->entityId);
|
||||
|
||||
// Add the config override, and ensure that what is loaded is correct
|
||||
// despite the prior cache priming.
|
||||
\Drupal::configFactory()->addOverride(new ConfigOverrider());
|
||||
$entity_override = $storage->load($this->entityId);
|
||||
$this->assertIdentical($entity_override->label, 'Overridden label');
|
||||
|
||||
// Load override free to ensure that loading the config entity again does
|
||||
// not return the overridden value.
|
||||
$entity_no_override = $storage->loadOverrideFree($this->entityId);
|
||||
$this->assertNotIdentical($entity_no_override->label, 'Overridden label');
|
||||
$this->assertNotIdentical($entity_override->_loadStamp, $entity_no_override->_loadStamp);
|
||||
|
||||
// Reload the entity and ensure the cache is used.
|
||||
$this->assertIdentical($storage->loadOverrideFree($this->entityId)->_loadStamp, $entity_no_override->_loadStamp);
|
||||
|
||||
// Enable overrides and reload the entity and ensure the cache is used.
|
||||
$this->assertIdentical($storage->load($this->entityId)->_loadStamp, $entity_override->_loadStamp);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Config;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests configuration entity status functionality.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigEntityStatusTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('config_test');
|
||||
|
||||
/**
|
||||
* Tests the enabling/disabling of entities.
|
||||
*/
|
||||
function testCRUD() {
|
||||
$entity = entity_create('config_test', array(
|
||||
'id' => strtolower($this->randomMachineName()),
|
||||
));
|
||||
$this->assertTrue($entity->status(), 'Default status is enabled.');
|
||||
$entity->save();
|
||||
$this->assertTrue($entity->status(), 'Status is enabled after saving.');
|
||||
|
||||
$entity->disable()->save();
|
||||
$this->assertFalse($entity->status(), 'Entity is disabled after disabling.');
|
||||
|
||||
$entity->enable()->save();
|
||||
$this->assertTrue($entity->status(), 'Entity is enabled after enabling.');
|
||||
|
||||
$entity = entity_load('config_test', $entity->id());
|
||||
$this->assertTrue($entity->status(), 'Status is enabled after reload.');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Config;
|
||||
|
||||
use Drupal\Core\Config\ConfigDuplicateUUIDException;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests sync and importing config entities with IDs and UUIDs that match
|
||||
* existing config.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigEntityStorageTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('config_test');
|
||||
|
||||
/**
|
||||
* Tests creating configuration entities with changed UUIDs.
|
||||
*/
|
||||
public function testUUIDConflict() {
|
||||
$entity_type = 'config_test';
|
||||
$id = 'test_1';
|
||||
// Load the original configuration entity.
|
||||
$this->container->get('entity_type.manager')
|
||||
->getStorage($entity_type)
|
||||
->create(array('id' => $id))
|
||||
->save();
|
||||
$entity = entity_load($entity_type, $id);
|
||||
|
||||
$original_properties = $entity->toArray();
|
||||
|
||||
// Override with a new UUID and try to save.
|
||||
$new_uuid = $this->container->get('uuid')->generate();
|
||||
$entity->set('uuid', $new_uuid);
|
||||
|
||||
try {
|
||||
$entity->save();
|
||||
$this->fail('Exception thrown when attempting to save a configuration entity with a UUID that does not match the existing UUID.');
|
||||
}
|
||||
catch (ConfigDuplicateUUIDException $e) {
|
||||
$this->pass(format_string('Exception thrown when attempting to save a configuration entity with a UUID that does not match existing data: %e.', array('%e' => $e)));
|
||||
}
|
||||
|
||||
// Ensure that the config entity was not corrupted.
|
||||
$entity = entity_load('config_test', $entity->id(), TRUE);
|
||||
$this->assertIdentical($entity->toArray(), $original_properties);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Config;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Unit tests for configuration entity base methods.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigEntityUnitTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Exempt from strict schema checking.
|
||||
*
|
||||
* @see \Drupal\Core\Config\Testing\ConfigSchemaChecker
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $strictConfigSchema = FALSE;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('config_test');
|
||||
|
||||
/**
|
||||
* The config_test entity storage.
|
||||
*
|
||||
* @var \Drupal\Core\Config\Entity\ConfigEntityStorageInterface
|
||||
*/
|
||||
protected $storage;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->storage = $this->container->get('entity.manager')->getStorage('config_test');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests storage methods.
|
||||
*/
|
||||
public function testStorageMethods() {
|
||||
$entity_type = \Drupal::entityManager()->getDefinition('config_test');
|
||||
|
||||
// Test the static extractID() method.
|
||||
$expected_id = 'test_id';
|
||||
$config_name = $entity_type->getConfigPrefix() . '.' . $expected_id;
|
||||
$storage = $this->storage;
|
||||
$this->assertIdentical($storage::getIDFromConfigName($config_name, $entity_type->getConfigPrefix()), $expected_id);
|
||||
|
||||
// Create three entities, two with the same style.
|
||||
$style = $this->randomMachineName(8);
|
||||
for ($i = 0; $i < 2; $i++) {
|
||||
$entity = $this->storage->create(array(
|
||||
'id' => $this->randomMachineName(),
|
||||
'label' => $this->randomString(),
|
||||
'style' => $style,
|
||||
));
|
||||
$entity->save();
|
||||
}
|
||||
$entity = $this->storage->create(array(
|
||||
'id' => $this->randomMachineName(),
|
||||
'label' => $this->randomString(),
|
||||
// Use a different length for the entity to ensure uniqueness.
|
||||
'style' => $this->randomMachineName(9),
|
||||
));
|
||||
$entity->save();
|
||||
|
||||
// Ensure that the configuration entity can be loaded by UUID.
|
||||
$entity_loaded_by_uuid = \Drupal::entityManager()->loadEntityByUuid($entity_type->id(), $entity->uuid());
|
||||
if (!$entity_loaded_by_uuid) {
|
||||
$this->fail(sprintf("Failed to load '%s' entity ID '%s' by UUID '%s'.", $entity_type->id(), $entity->id(), $entity->uuid()));
|
||||
}
|
||||
// Compare UUIDs as the objects are not identical since
|
||||
// $entity->enforceIsNew is FALSE and $entity_loaded_by_uuid->enforceIsNew
|
||||
// is NULL.
|
||||
$this->assertIdentical($entity->uuid(), $entity_loaded_by_uuid->uuid());
|
||||
|
||||
$entities = $this->storage->loadByProperties();
|
||||
$this->assertEqual(count($entities), 3, 'Three entities are loaded when no properties are specified.');
|
||||
|
||||
$entities = $this->storage->loadByProperties(array('style' => $style));
|
||||
$this->assertEqual(count($entities), 2, 'Two entities are loaded when the style property is specified.');
|
||||
|
||||
// Assert that both returned entities have a matching style property.
|
||||
foreach ($entities as $entity) {
|
||||
$this->assertIdentical($entity->get('style'), $style, 'The loaded entity has the correct style value specified.');
|
||||
}
|
||||
|
||||
// Test that schema type enforcement can be overridden by trusting the data.
|
||||
$entity = $this->storage->create(array(
|
||||
'id' => $this->randomMachineName(),
|
||||
'label' => $this->randomString(),
|
||||
'style' => 999
|
||||
));
|
||||
$entity->save();
|
||||
$this->assertIdentical('999', $entity->style);
|
||||
$entity->style = 999;
|
||||
$entity->trustData()->save();
|
||||
$this->assertIdentical(999, $entity->style);
|
||||
$entity->save();
|
||||
$this->assertIdentical('999', $entity->style);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Config;
|
||||
|
||||
use Drupal\Core\Config\Config;
|
||||
use Drupal\Core\Config\ConfigEvents;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests events fired on configuration objects.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigEventsTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('config_events_test');
|
||||
|
||||
/**
|
||||
* Tests configuration events.
|
||||
*/
|
||||
function testConfigEvents() {
|
||||
$name = 'config_events_test.test';
|
||||
|
||||
$config = new Config($name, \Drupal::service('config.storage'), \Drupal::service('event_dispatcher'), \Drupal::service('config.typed'));
|
||||
$config->set('key', 'initial');
|
||||
$this->assertIdentical(\Drupal::state()->get('config_events_test.event', array()), array(), 'No events fired by creating a new configuration object');
|
||||
$config->save();
|
||||
|
||||
$event = \Drupal::state()->get('config_events_test.event', array());
|
||||
$this->assertIdentical($event['event_name'], ConfigEvents::SAVE);
|
||||
$this->assertIdentical($event['current_config_data'], array('key' => 'initial'));
|
||||
$this->assertIdentical($event['raw_config_data'], array('key' => 'initial'));
|
||||
$this->assertIdentical($event['original_config_data'], array());
|
||||
|
||||
$config->set('key', 'updated')->save();
|
||||
$event = \Drupal::state()->get('config_events_test.event', array());
|
||||
$this->assertIdentical($event['event_name'], ConfigEvents::SAVE);
|
||||
$this->assertIdentical($event['current_config_data'], array('key' => 'updated'));
|
||||
$this->assertIdentical($event['raw_config_data'], array('key' => 'updated'));
|
||||
$this->assertIdentical($event['original_config_data'], array('key' => 'initial'));
|
||||
|
||||
$config->delete();
|
||||
$event = \Drupal::state()->get('config_events_test.event', array());
|
||||
$this->assertIdentical($event['event_name'], ConfigEvents::DELETE);
|
||||
$this->assertIdentical($event['current_config_data'], array());
|
||||
$this->assertIdentical($event['raw_config_data'], array());
|
||||
$this->assertIdentical($event['original_config_data'], array('key' => 'updated'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests configuration rename event that is fired from the ConfigFactory.
|
||||
*/
|
||||
function testConfigRenameEvent() {
|
||||
$name = 'config_events_test.test';
|
||||
$new_name = 'config_events_test.test_rename';
|
||||
$GLOBALS['config'][$name] = array('key' => 'overridden');
|
||||
$GLOBALS['config'][$new_name] = array('key' => 'new overridden');
|
||||
|
||||
$config = $this->config($name);
|
||||
$config->set('key', 'initial')->save();
|
||||
$event = \Drupal::state()->get('config_events_test.event', array());
|
||||
$this->assertIdentical($event['event_name'], ConfigEvents::SAVE);
|
||||
$this->assertIdentical($event['current_config_data'], array('key' => 'initial'));
|
||||
|
||||
// Override applies when getting runtime config.
|
||||
$this->assertEqual($GLOBALS['config'][$name], \Drupal::config($name)->get());
|
||||
|
||||
\Drupal::configFactory()->rename($name, $new_name);
|
||||
$event = \Drupal::state()->get('config_events_test.event', array());
|
||||
$this->assertIdentical($event['event_name'], ConfigEvents::RENAME);
|
||||
$this->assertIdentical($event['current_config_data'], array('key' => 'new overridden'));
|
||||
$this->assertIdentical($event['raw_config_data'], array('key' => 'initial'));
|
||||
$this->assertIdentical($event['original_config_data'], array('key' => 'new overridden'));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,230 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Config;
|
||||
|
||||
use Drupal\Core\Config\FileStorage;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests reading and writing of configuration files.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigFileContentTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Exempt from strict schema checking.
|
||||
*
|
||||
* @see \Drupal\Core\Config\Testing\ConfigSchemaChecker
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $strictConfigSchema = FALSE;
|
||||
|
||||
/**
|
||||
* Tests setting, writing, and reading of a configuration setting.
|
||||
*/
|
||||
function testReadWriteConfig() {
|
||||
$storage = $this->container->get('config.storage');
|
||||
|
||||
$name = 'foo.bar';
|
||||
$key = 'foo';
|
||||
$value = 'bar';
|
||||
$nested_key = 'biff.bang';
|
||||
$nested_value = 'pow';
|
||||
$array_key = 'array';
|
||||
$array_value = array(
|
||||
'foo' => 'bar',
|
||||
'biff' => array(
|
||||
'bang' => 'pow',
|
||||
),
|
||||
);
|
||||
$casting_array_key = 'casting_array';
|
||||
$casting_array_false_value_key = 'casting_array.cast.false';
|
||||
$casting_array_value = array(
|
||||
'cast' => array(
|
||||
'false' => FALSE,
|
||||
),
|
||||
);
|
||||
$nested_array_key = 'nested.array';
|
||||
$true_key = 'true';
|
||||
$false_key = 'false';
|
||||
|
||||
// Attempt to read non-existing configuration.
|
||||
$config = $this->config($name);
|
||||
|
||||
// Verify a configuration object is returned.
|
||||
$this->assertEqual($config->getName(), $name);
|
||||
$this->assertTrue($config, 'Config object created.');
|
||||
|
||||
// Verify the configuration object is empty.
|
||||
$this->assertEqual($config->get(), array(), 'New config object is empty.');
|
||||
|
||||
// Verify nothing was saved.
|
||||
$data = $storage->read($name);
|
||||
$this->assertIdentical($data, FALSE);
|
||||
|
||||
// Add a top level value.
|
||||
$config = $this->config($name);
|
||||
$config->set($key, $value);
|
||||
|
||||
// Add a nested value.
|
||||
$config->set($nested_key, $nested_value);
|
||||
|
||||
// Add an array.
|
||||
$config->set($array_key, $array_value);
|
||||
|
||||
// Add a nested array.
|
||||
$config->set($nested_array_key, $array_value);
|
||||
|
||||
// Add a boolean false value. Should get cast to 0.
|
||||
$config->set($false_key, FALSE);
|
||||
|
||||
// Add a boolean true value. Should get cast to 1.
|
||||
$config->set($true_key, TRUE);
|
||||
|
||||
// Add a null value. Should get cast to an empty string.
|
||||
$config->set('null', NULL);
|
||||
|
||||
// Add an array with a nested boolean false that should get cast to 0.
|
||||
$config->set($casting_array_key, $casting_array_value);
|
||||
$config->save();
|
||||
|
||||
// Verify the database entry exists.
|
||||
$data = $storage->read($name);
|
||||
$this->assertTrue($data);
|
||||
|
||||
// Read top level value.
|
||||
$config = $this->config($name);
|
||||
$this->assertEqual($config->getName(), $name);
|
||||
$this->assertTrue($config, 'Config object created.');
|
||||
$this->assertEqual($config->get($key), 'bar', 'Top level configuration value found.');
|
||||
|
||||
// Read nested value.
|
||||
$this->assertEqual($config->get($nested_key), $nested_value, 'Nested configuration value found.');
|
||||
|
||||
// Read array.
|
||||
$this->assertEqual($config->get($array_key), $array_value, 'Top level array configuration value found.');
|
||||
|
||||
// Read nested array.
|
||||
$this->assertEqual($config->get($nested_array_key), $array_value, 'Nested array configuration value found.');
|
||||
|
||||
// Read a top level value that doesn't exist.
|
||||
$this->assertNull($config->get('i_dont_exist'), 'Non-existent top level value returned NULL.');
|
||||
|
||||
// Read a nested value that doesn't exist.
|
||||
$this->assertNull($config->get('i.dont.exist'), 'Non-existent nested value returned NULL.');
|
||||
|
||||
// Read false value.
|
||||
$this->assertFalse($config->get($false_key), "Boolean FALSE value returned the FALSE.");
|
||||
|
||||
// Read true value.
|
||||
$this->assertTrue($config->get($true_key), "Boolean TRUE value returned the TRUE.");
|
||||
|
||||
// Read null value.
|
||||
$this->assertIdentical($config->get('null'), NULL);
|
||||
|
||||
// Read false that had been nested in an array value.
|
||||
$this->assertSame($config->get($casting_array_false_value_key), FALSE, "Nested boolean FALSE value returned FALSE.");
|
||||
|
||||
// Unset a top level value.
|
||||
$config->clear($key);
|
||||
|
||||
// Unset a nested value.
|
||||
$config->clear($nested_key);
|
||||
$config->save();
|
||||
$config = $this->config($name);
|
||||
|
||||
// Read unset top level value.
|
||||
$this->assertNull($config->get($key), 'Top level value unset.');
|
||||
|
||||
// Read unset nested value.
|
||||
$this->assertNull($config->get($nested_key), 'Nested value unset.');
|
||||
|
||||
// Create two new configuration files to test listing.
|
||||
$config = $this->config('foo.baz');
|
||||
$config->set($key, $value);
|
||||
$config->save();
|
||||
|
||||
// Test chained set()->save().
|
||||
$chained_name = 'biff.bang';
|
||||
$config = $this->config($chained_name);
|
||||
$config->set($key, $value)->save();
|
||||
|
||||
// Verify the database entry exists from a chained save.
|
||||
$data = $storage->read($chained_name);
|
||||
$this->assertEqual($data, $config->get());
|
||||
|
||||
// Get file listing for all files starting with 'foo'. Should return
|
||||
// two elements.
|
||||
$files = $storage->listAll('foo');
|
||||
$this->assertEqual(count($files), 2, 'Two files listed with the prefix \'foo\'.');
|
||||
|
||||
// Get file listing for all files starting with 'biff'. Should return
|
||||
// one element.
|
||||
$files = $storage->listAll('biff');
|
||||
$this->assertEqual(count($files), 1, 'One file listed with the prefix \'biff\'.');
|
||||
|
||||
// Get file listing for all files starting with 'foo.bar'. Should return
|
||||
// one element.
|
||||
$files = $storage->listAll('foo.bar');
|
||||
$this->assertEqual(count($files), 1, 'One file listed with the prefix \'foo.bar\'.');
|
||||
|
||||
// Get file listing for all files starting with 'bar'. Should return
|
||||
// an empty array.
|
||||
$files = $storage->listAll('bar');
|
||||
$this->assertEqual($files, array(), 'No files listed with the prefix \'bar\'.');
|
||||
|
||||
// Delete the configuration.
|
||||
$config = $this->config($name);
|
||||
$config->delete();
|
||||
|
||||
// Verify the database entry no longer exists.
|
||||
$data = $storage->read($name);
|
||||
$this->assertIdentical($data, FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests serialization of configuration to file.
|
||||
*/
|
||||
function testSerialization() {
|
||||
$name = $this->randomMachineName(10) . '.' . $this->randomMachineName(10);
|
||||
$config_data = array(
|
||||
// Indexed arrays; the order of elements is essential.
|
||||
'numeric keys' => array('i', 'n', 'd', 'e', 'x', 'e', 'd'),
|
||||
// Infinitely nested keys using arbitrary element names.
|
||||
'nested keys' => array(
|
||||
// HTML/XML in values.
|
||||
'HTML' => '<strong> <bold> <em> <blockquote>',
|
||||
// UTF-8 in values.
|
||||
'UTF-8' => 'FrançAIS is ÜBER-åwesome',
|
||||
// Unicode in keys and values.
|
||||
'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΣὨ' => 'αβγδεζηθικλμνξοσὠ',
|
||||
),
|
||||
'invalid xml' => '</title><script type="text/javascript">alert("Title XSS!");</script> & < > " \' ',
|
||||
);
|
||||
|
||||
// Encode and write, and reload and decode the configuration data.
|
||||
$filestorage = new FileStorage(config_get_config_directory(CONFIG_SYNC_DIRECTORY));
|
||||
$filestorage->write($name, $config_data);
|
||||
$config_parsed = $filestorage->read($name);
|
||||
|
||||
$key = 'numeric keys';
|
||||
$this->assertIdentical($config_data[$key], $config_parsed[$key]);
|
||||
|
||||
$key = 'nested keys';
|
||||
$this->assertIdentical($config_data[$key], $config_parsed[$key]);
|
||||
|
||||
$key = 'HTML';
|
||||
$this->assertIdentical($config_data['nested keys'][$key], $config_parsed['nested keys'][$key]);
|
||||
|
||||
$key = 'UTF-8';
|
||||
$this->assertIdentical($config_data['nested keys'][$key], $config_parsed['nested keys'][$key]);
|
||||
|
||||
$key = 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΣὨ';
|
||||
$this->assertIdentical($config_data['nested keys'][$key], $config_parsed['nested keys'][$key]);
|
||||
|
||||
$key = 'invalid xml';
|
||||
$this->assertIdentical($config_data[$key], $config_parsed[$key]);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Config;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Core\Config\ConfigImporter;
|
||||
use Drupal\Core\Config\StorageComparer;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
|
||||
/**
|
||||
* Tests importing recreated configuration entities.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigImportRecreateTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Config Importer object used for testing.
|
||||
*
|
||||
* @var \Drupal\Core\Config\ConfigImporter
|
||||
*/
|
||||
protected $configImporter;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['system', 'field', 'text', 'user', 'node'];
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('node');
|
||||
$this->installConfig(array('field', 'node'));
|
||||
|
||||
$this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.sync'));
|
||||
|
||||
// Set up the ConfigImporter object for testing.
|
||||
$storage_comparer = new StorageComparer(
|
||||
$this->container->get('config.storage.sync'),
|
||||
$this->container->get('config.storage'),
|
||||
$this->container->get('config.manager')
|
||||
);
|
||||
$this->configImporter = new ConfigImporter(
|
||||
$storage_comparer->createChangelist(),
|
||||
$this->container->get('event_dispatcher'),
|
||||
$this->container->get('config.manager'),
|
||||
$this->container->get('lock'),
|
||||
$this->container->get('config.typed'),
|
||||
$this->container->get('module_handler'),
|
||||
$this->container->get('module_installer'),
|
||||
$this->container->get('theme_handler'),
|
||||
$this->container->get('string_translation')
|
||||
);
|
||||
}
|
||||
|
||||
public function testRecreateEntity() {
|
||||
$type_name = Unicode::strtolower($this->randomMachineName(16));
|
||||
$content_type = NodeType::create([
|
||||
'type' => $type_name,
|
||||
'name' => 'Node type one',
|
||||
]);
|
||||
$content_type->save();
|
||||
node_add_body_field($content_type);
|
||||
/** @var \Drupal\Core\Config\StorageInterface $active */
|
||||
$active = $this->container->get('config.storage');
|
||||
/** @var \Drupal\Core\Config\StorageInterface $sync */
|
||||
$sync = $this->container->get('config.storage.sync');
|
||||
|
||||
$config_name = $content_type->getEntityType()->getConfigPrefix() . '.' . $content_type->id();
|
||||
$this->copyConfig($active, $sync);
|
||||
|
||||
// Delete the content type. This will also delete a field storage, a field,
|
||||
// an entity view display and an entity form display.
|
||||
$content_type->delete();
|
||||
$this->assertFalse($active->exists($config_name), 'Content type\'s old name does not exist active store.');
|
||||
// Recreate with the same type - this will have a different UUID.
|
||||
$content_type = NodeType::create([
|
||||
'type' => $type_name,
|
||||
'name' => 'Node type two',
|
||||
]);
|
||||
$content_type->save();
|
||||
node_add_body_field($content_type);
|
||||
|
||||
$this->configImporter->reset();
|
||||
// A node type, a field, an entity view display and an entity form display
|
||||
// will be recreated.
|
||||
$creates = $this->configImporter->getUnprocessedConfiguration('create');
|
||||
$deletes = $this->configImporter->getUnprocessedConfiguration('delete');
|
||||
$this->assertEqual(5, count($creates), 'There are 5 configuration items to create.');
|
||||
$this->assertEqual(5, count($deletes), 'There are 5 configuration items to delete.');
|
||||
$this->assertEqual(0, count($this->configImporter->getUnprocessedConfiguration('update')), 'There are no configuration items to update.');
|
||||
$this->assertIdentical($creates, array_reverse($deletes), 'Deletes and creates contain the same configuration names in opposite orders due to dependencies.');
|
||||
|
||||
$this->configImporter->import();
|
||||
|
||||
// Verify that there is nothing more to import.
|
||||
$this->assertFalse($this->configImporter->reset()->hasUnprocessedConfigurationChanges());
|
||||
$content_type = NodeType::load($type_name);
|
||||
$this->assertEqual('Node type one', $content_type->label());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,158 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Config;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\Component\Uuid\Php;
|
||||
use Drupal\Core\Config\ConfigImporter;
|
||||
use Drupal\Core\Config\ConfigImporterException;
|
||||
use Drupal\Core\Config\StorageComparer;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests validating renamed configuration in a configuration import.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigImportRenameValidationTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Config Importer object used for testing.
|
||||
*
|
||||
* @var \Drupal\Core\Config\ConfigImporter
|
||||
*/
|
||||
protected $configImporter;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['system', 'user', 'node', 'field', 'text', 'config_test'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('user');
|
||||
$this->installEntitySchema('node');
|
||||
$this->installConfig(array('field'));
|
||||
|
||||
// Set up the ConfigImporter object for testing.
|
||||
$storage_comparer = new StorageComparer(
|
||||
$this->container->get('config.storage.sync'),
|
||||
$this->container->get('config.storage'),
|
||||
$this->container->get('config.manager')
|
||||
);
|
||||
$this->configImporter = new ConfigImporter(
|
||||
$storage_comparer->createChangelist(),
|
||||
$this->container->get('event_dispatcher'),
|
||||
$this->container->get('config.manager'),
|
||||
$this->container->get('lock.persistent'),
|
||||
$this->container->get('config.typed'),
|
||||
$this->container->get('module_handler'),
|
||||
$this->container->get('module_installer'),
|
||||
$this->container->get('theme_handler'),
|
||||
$this->container->get('string_translation')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests configuration renaming validation.
|
||||
*/
|
||||
public function testRenameValidation() {
|
||||
// Create a test entity.
|
||||
$test_entity_id = $this->randomMachineName();
|
||||
$test_entity = entity_create('config_test', array(
|
||||
'id' => $test_entity_id,
|
||||
'label' => $this->randomMachineName(),
|
||||
));
|
||||
$test_entity->save();
|
||||
$uuid = $test_entity->uuid();
|
||||
|
||||
// Stage the test entity and then delete it from the active storage.
|
||||
$active = $this->container->get('config.storage');
|
||||
$sync = $this->container->get('config.storage.sync');
|
||||
$this->copyConfig($active, $sync);
|
||||
$test_entity->delete();
|
||||
|
||||
// Create a content type with a matching UUID in the active storage.
|
||||
$content_type = NodeType::create([
|
||||
'type' => Unicode::strtolower($this->randomMachineName(16)),
|
||||
'name' => $this->randomMachineName(),
|
||||
'uuid' => $uuid,
|
||||
]);
|
||||
$content_type->save();
|
||||
|
||||
// Confirm that the staged configuration is detected as a rename since the
|
||||
// UUIDs match.
|
||||
$this->configImporter->reset();
|
||||
$expected = array(
|
||||
'node.type.' . $content_type->id() . '::config_test.dynamic.' . $test_entity_id,
|
||||
);
|
||||
$renames = $this->configImporter->getUnprocessedConfiguration('rename');
|
||||
$this->assertIdentical($expected, $renames);
|
||||
|
||||
// Try to import the configuration. We expect an exception to be thrown
|
||||
// because the staged entity is of a different type.
|
||||
try {
|
||||
$this->configImporter->import();
|
||||
$this->fail('Expected ConfigImporterException thrown when a renamed configuration entity does not match the existing entity type.');
|
||||
}
|
||||
catch (ConfigImporterException $e) {
|
||||
$this->pass('Expected ConfigImporterException thrown when a renamed configuration entity does not match the existing entity type.');
|
||||
$expected = array(
|
||||
SafeMarkup::format('Entity type mismatch on rename. @old_type not equal to @new_type for existing configuration @old_name and staged configuration @new_name.', array('@old_type' => 'node_type', '@new_type' => 'config_test', '@old_name' => 'node.type.' . $content_type->id(), '@new_name' => 'config_test.dynamic.' . $test_entity_id))
|
||||
);
|
||||
$this->assertEqual($expected, $this->configImporter->getErrors());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests configuration renaming validation for simple configuration.
|
||||
*/
|
||||
public function testRenameSimpleConfigValidation() {
|
||||
$uuid = new Php();
|
||||
// Create a simple configuration with a UUID.
|
||||
$config = $this->config('config_test.new');
|
||||
$uuid_value = $uuid->generate();
|
||||
$config->set('uuid', $uuid_value)->save();
|
||||
|
||||
$active = $this->container->get('config.storage');
|
||||
$sync = $this->container->get('config.storage.sync');
|
||||
$this->copyConfig($active, $sync);
|
||||
$config->delete();
|
||||
|
||||
// Create another simple configuration with the same UUID.
|
||||
$config = $this->config('config_test.old');
|
||||
$config->set('uuid', $uuid_value)->save();
|
||||
|
||||
// Confirm that the staged configuration is detected as a rename since the
|
||||
// UUIDs match.
|
||||
$this->configImporter->reset();
|
||||
$expected = array(
|
||||
'config_test.old::config_test.new'
|
||||
);
|
||||
$renames = $this->configImporter->getUnprocessedConfiguration('rename');
|
||||
$this->assertIdentical($expected, $renames);
|
||||
|
||||
// Try to import the configuration. We expect an exception to be thrown
|
||||
// because the rename is for simple configuration.
|
||||
try {
|
||||
$this->configImporter->import();
|
||||
$this->fail('Expected ConfigImporterException thrown when simple configuration is renamed.');
|
||||
}
|
||||
catch (ConfigImporterException $e) {
|
||||
$this->pass('Expected ConfigImporterException thrown when simple configuration is renamed.');
|
||||
$expected = array(
|
||||
SafeMarkup::format('Rename operation for simple configuration. Existing configuration @old_name and staged configuration @new_name.', array('@old_name' => 'config_test.old', '@new_name' => 'config_test.new'))
|
||||
);
|
||||
$this->assertEqual($expected, $this->configImporter->getErrors());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Config;
|
||||
|
||||
use Drupal\Core\Config\ConfigImporter;
|
||||
use Drupal\Core\Config\StorageComparer;
|
||||
use Drupal\entity_test\Entity\EntityTest;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests importing configuration which has missing content dependencies.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigImporterMissingContentTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Config Importer object used for testing.
|
||||
*
|
||||
* @var \Drupal\Core\Config\ConfigImporter
|
||||
*/
|
||||
protected $configImporter;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('system', 'user', 'entity_test', 'config_test', 'config_import_test');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installSchema('system', 'sequences');
|
||||
$this->installEntitySchema('entity_test');
|
||||
$this->installEntitySchema('user');
|
||||
$this->installConfig(array('config_test'));
|
||||
// Installing config_test's default configuration pollutes the global
|
||||
// variable being used for recording hook invocations by this test already,
|
||||
// so it has to be cleared out manually.
|
||||
unset($GLOBALS['hook_config_test']);
|
||||
|
||||
$this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.sync'));
|
||||
|
||||
// Set up the ConfigImporter object for testing.
|
||||
$storage_comparer = new StorageComparer(
|
||||
$this->container->get('config.storage.sync'),
|
||||
$this->container->get('config.storage'),
|
||||
$this->container->get('config.manager')
|
||||
);
|
||||
$this->configImporter = new ConfigImporter(
|
||||
$storage_comparer->createChangelist(),
|
||||
$this->container->get('event_dispatcher'),
|
||||
$this->container->get('config.manager'),
|
||||
$this->container->get('lock'),
|
||||
$this->container->get('config.typed'),
|
||||
$this->container->get('module_handler'),
|
||||
$this->container->get('module_installer'),
|
||||
$this->container->get('theme_handler'),
|
||||
$this->container->get('string_translation')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the missing content event is fired.
|
||||
*
|
||||
* @see \Drupal\Core\Config\ConfigImporter::processMissingContent()
|
||||
* @see \Drupal\config_import_test\EventSubscriber
|
||||
*/
|
||||
function testMissingContent() {
|
||||
\Drupal::state()->set('config_import_test.config_import_missing_content', TRUE);
|
||||
|
||||
// Update a configuration entity in the sync directory to have a dependency
|
||||
// on two content entities that do not exist.
|
||||
$storage = $this->container->get('config.storage');
|
||||
$sync = $this->container->get('config.storage.sync');
|
||||
$entity_one = EntityTest::create(array('name' => 'one'));
|
||||
$entity_two = EntityTest::create(array('name' => 'two'));
|
||||
$entity_three = EntityTest::create(array('name' => 'three'));
|
||||
$dynamic_name = 'config_test.dynamic.dotted.default';
|
||||
$original_dynamic_data = $storage->read($dynamic_name);
|
||||
// Entity one will be resolved by
|
||||
// \Drupal\config_import_test\EventSubscriber::onConfigImporterMissingContentOne().
|
||||
$original_dynamic_data['dependencies']['content'][] = $entity_one->getConfigDependencyName();
|
||||
// Entity two will be resolved by
|
||||
// \Drupal\config_import_test\EventSubscriber::onConfigImporterMissingContentTwo().
|
||||
$original_dynamic_data['dependencies']['content'][] = $entity_two->getConfigDependencyName();
|
||||
// Entity three will be resolved by
|
||||
// \Drupal\Core\Config\Importer\FinalMissingContentSubscriber.
|
||||
$original_dynamic_data['dependencies']['content'][] = $entity_three->getConfigDependencyName();
|
||||
$sync->write($dynamic_name, $original_dynamic_data);
|
||||
|
||||
// Import.
|
||||
$this->configImporter->reset()->import();
|
||||
$this->assertEqual([], $this->configImporter->getErrors(), 'There were no errors during the import.');
|
||||
$this->assertEqual($entity_one->uuid(), \Drupal::state()->get('config_import_test.config_import_missing_content_one'), 'The missing content event is fired during configuration import.');
|
||||
$this->assertEqual($entity_two->uuid(), \Drupal::state()->get('config_import_test.config_import_missing_content_two'), 'The missing content event is fired during configuration import.');
|
||||
$original_dynamic_data = $storage->read($dynamic_name);
|
||||
$this->assertEqual([$entity_one->getConfigDependencyName(), $entity_two->getConfigDependencyName(), $entity_three->getConfigDependencyName()], $original_dynamic_data['dependencies']['content'], 'The imported configuration entity has the missing content entity dependency.');
|
||||
}
|
||||
|
||||
}
|
||||
689
core/tests/Drupal/KernelTests/Core/Config/ConfigImporterTest.php
Normal file
689
core/tests/Drupal/KernelTests/Core/Config/ConfigImporterTest.php
Normal file
|
|
@ -0,0 +1,689 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Config;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Config\ConfigImporter;
|
||||
use Drupal\Core\Config\ConfigImporterException;
|
||||
use Drupal\Core\Config\StorageComparer;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests importing configuration from files into active configuration.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigImporterTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Config Importer object used for testing.
|
||||
*
|
||||
* @var \Drupal\Core\Config\ConfigImporter
|
||||
*/
|
||||
protected $configImporter;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('config_test', 'system', 'config_import_test');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installConfig(array('config_test'));
|
||||
// Installing config_test's default configuration pollutes the global
|
||||
// variable being used for recording hook invocations by this test already,
|
||||
// so it has to be cleared out manually.
|
||||
unset($GLOBALS['hook_config_test']);
|
||||
|
||||
$this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.sync'));
|
||||
|
||||
// Set up the ConfigImporter object for testing.
|
||||
$storage_comparer = new StorageComparer(
|
||||
$this->container->get('config.storage.sync'),
|
||||
$this->container->get('config.storage'),
|
||||
$this->container->get('config.manager')
|
||||
);
|
||||
$this->configImporter = new ConfigImporter(
|
||||
$storage_comparer->createChangelist(),
|
||||
$this->container->get('event_dispatcher'),
|
||||
$this->container->get('config.manager'),
|
||||
$this->container->get('lock'),
|
||||
$this->container->get('config.typed'),
|
||||
$this->container->get('module_handler'),
|
||||
$this->container->get('module_installer'),
|
||||
$this->container->get('theme_handler'),
|
||||
$this->container->get('string_translation')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests omission of module APIs for bare configuration operations.
|
||||
*/
|
||||
function testNoImport() {
|
||||
$dynamic_name = 'config_test.dynamic.dotted.default';
|
||||
|
||||
// Verify the default configuration values exist.
|
||||
$config = $this->config($dynamic_name);
|
||||
$this->assertIdentical($config->get('id'), 'dotted.default');
|
||||
|
||||
// Verify that a bare $this->config() does not involve module APIs.
|
||||
$this->assertFalse(isset($GLOBALS['hook_config_test']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that trying to import from an empty sync configuration directory
|
||||
* fails.
|
||||
*/
|
||||
function testEmptyImportFails() {
|
||||
try {
|
||||
$this->container->get('config.storage.sync')->deleteAll();
|
||||
$this->configImporter->reset()->import();
|
||||
$this->fail('ConfigImporterException thrown, successfully stopping an empty import.');
|
||||
}
|
||||
catch (ConfigImporterException $e) {
|
||||
$this->pass('ConfigImporterException thrown, successfully stopping an empty import.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests verification of site UUID before importing configuration.
|
||||
*/
|
||||
function testSiteUuidValidate() {
|
||||
$sync = \Drupal::service('config.storage.sync');
|
||||
// Create updated configuration object.
|
||||
$config_data = $this->config('system.site')->get();
|
||||
// Generate a new site UUID.
|
||||
$config_data['uuid'] = \Drupal::service('uuid')->generate();
|
||||
$sync->write('system.site', $config_data);
|
||||
try {
|
||||
$this->configImporter->reset()->import();
|
||||
$this->fail('ConfigImporterException not thrown, invalid import was not stopped due to mis-matching site UUID.');
|
||||
}
|
||||
catch (ConfigImporterException $e) {
|
||||
$this->assertEqual($e->getMessage(), 'There were errors validating the config synchronization.');
|
||||
$error_log = $this->configImporter->getErrors();
|
||||
$expected = array('Site UUID in source storage does not match the target storage.');
|
||||
$this->assertEqual($expected, $error_log);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests deletion of configuration during import.
|
||||
*/
|
||||
function testDeleted() {
|
||||
$dynamic_name = 'config_test.dynamic.dotted.default';
|
||||
$storage = $this->container->get('config.storage');
|
||||
$sync = $this->container->get('config.storage.sync');
|
||||
|
||||
// Verify the default configuration values exist.
|
||||
$config = $this->config($dynamic_name);
|
||||
$this->assertIdentical($config->get('id'), 'dotted.default');
|
||||
|
||||
// Delete the file from the sync directory.
|
||||
$sync->delete($dynamic_name);
|
||||
|
||||
// Import.
|
||||
$this->configImporter->reset()->import();
|
||||
|
||||
// Verify the file has been removed.
|
||||
$this->assertIdentical($storage->read($dynamic_name), FALSE);
|
||||
|
||||
$config = $this->config($dynamic_name);
|
||||
$this->assertIdentical($config->get('id'), NULL);
|
||||
|
||||
// Verify that appropriate module API hooks have been invoked.
|
||||
$this->assertTrue(isset($GLOBALS['hook_config_test']['load']));
|
||||
$this->assertFalse(isset($GLOBALS['hook_config_test']['presave']));
|
||||
$this->assertFalse(isset($GLOBALS['hook_config_test']['insert']));
|
||||
$this->assertFalse(isset($GLOBALS['hook_config_test']['update']));
|
||||
$this->assertTrue(isset($GLOBALS['hook_config_test']['predelete']));
|
||||
$this->assertTrue(isset($GLOBALS['hook_config_test']['delete']));
|
||||
|
||||
$this->assertFalse($this->configImporter->hasUnprocessedConfigurationChanges());
|
||||
$logs = $this->configImporter->getErrors();
|
||||
$this->assertEqual(count($logs), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests creation of configuration during import.
|
||||
*/
|
||||
function testNew() {
|
||||
$dynamic_name = 'config_test.dynamic.new';
|
||||
$storage = $this->container->get('config.storage');
|
||||
$sync = $this->container->get('config.storage.sync');
|
||||
|
||||
// Verify the configuration to create does not exist yet.
|
||||
$this->assertIdentical($storage->exists($dynamic_name), FALSE, $dynamic_name . ' not found.');
|
||||
|
||||
// Create new config entity.
|
||||
$original_dynamic_data = array(
|
||||
'uuid' => '30df59bd-7b03-4cf7-bb35-d42fc49f0651',
|
||||
'langcode' => \Drupal::languageManager()->getDefaultLanguage()->getId(),
|
||||
'status' => TRUE,
|
||||
'dependencies' => array(),
|
||||
'id' => 'new',
|
||||
'label' => 'New',
|
||||
'weight' => 0,
|
||||
'style' => '',
|
||||
'size' => '',
|
||||
'size_value' => '',
|
||||
'protected_property' => '',
|
||||
);
|
||||
$sync->write($dynamic_name, $original_dynamic_data);
|
||||
|
||||
$this->assertIdentical($sync->exists($dynamic_name), TRUE, $dynamic_name . ' found.');
|
||||
|
||||
// Import.
|
||||
$this->configImporter->reset()->import();
|
||||
|
||||
// Verify the values appeared.
|
||||
$config = $this->config($dynamic_name);
|
||||
$this->assertIdentical($config->get('label'), $original_dynamic_data['label']);
|
||||
|
||||
// Verify that appropriate module API hooks have been invoked.
|
||||
$this->assertFalse(isset($GLOBALS['hook_config_test']['load']));
|
||||
$this->assertTrue(isset($GLOBALS['hook_config_test']['presave']));
|
||||
$this->assertTrue(isset($GLOBALS['hook_config_test']['insert']));
|
||||
$this->assertFalse(isset($GLOBALS['hook_config_test']['update']));
|
||||
$this->assertFalse(isset($GLOBALS['hook_config_test']['predelete']));
|
||||
$this->assertFalse(isset($GLOBALS['hook_config_test']['delete']));
|
||||
|
||||
// Verify that hook_config_import_steps_alter() can add steps to
|
||||
// configuration synchronization.
|
||||
$this->assertTrue(isset($GLOBALS['hook_config_test']['config_import_steps_alter']));
|
||||
|
||||
// Verify that there is nothing more to import.
|
||||
$this->assertFalse($this->configImporter->hasUnprocessedConfigurationChanges());
|
||||
$logs = $this->configImporter->getErrors();
|
||||
$this->assertEqual(count($logs), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that secondary writes are overwritten.
|
||||
*/
|
||||
function testSecondaryWritePrimaryFirst() {
|
||||
$name_primary = 'config_test.dynamic.primary';
|
||||
$name_secondary = 'config_test.dynamic.secondary';
|
||||
$sync = $this->container->get('config.storage.sync');
|
||||
$uuid = $this->container->get('uuid');
|
||||
|
||||
$values_primary = array(
|
||||
'id' => 'primary',
|
||||
'label' => 'Primary',
|
||||
'weight' => 0,
|
||||
'uuid' => $uuid->generate(),
|
||||
);
|
||||
$sync->write($name_primary, $values_primary);
|
||||
$values_secondary = array(
|
||||
'id' => 'secondary',
|
||||
'label' => 'Secondary Sync',
|
||||
'weight' => 0,
|
||||
'uuid' => $uuid->generate(),
|
||||
// Add a dependency on primary, to ensure that is synced first.
|
||||
'dependencies' => array(
|
||||
'config' => array($name_primary),
|
||||
)
|
||||
);
|
||||
$sync->write($name_secondary, $values_secondary);
|
||||
|
||||
// Import.
|
||||
$this->configImporter->reset()->import();
|
||||
|
||||
$entity_storage = \Drupal::entityManager()->getStorage('config_test');
|
||||
$primary = $entity_storage->load('primary');
|
||||
$this->assertEqual($primary->id(), 'primary');
|
||||
$this->assertEqual($primary->uuid(), $values_primary['uuid']);
|
||||
$this->assertEqual($primary->label(), $values_primary['label']);
|
||||
$secondary = $entity_storage->load('secondary');
|
||||
$this->assertEqual($secondary->id(), 'secondary');
|
||||
$this->assertEqual($secondary->uuid(), $values_secondary['uuid']);
|
||||
$this->assertEqual($secondary->label(), $values_secondary['label']);
|
||||
|
||||
$logs = $this->configImporter->getErrors();
|
||||
$this->assertEqual(count($logs), 1);
|
||||
$this->assertEqual($logs[0], SafeMarkup::format('Deleted and replaced configuration entity "@name"', array('@name' => $name_secondary)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that secondary writes are overwritten.
|
||||
*/
|
||||
function testSecondaryWriteSecondaryFirst() {
|
||||
$name_primary = 'config_test.dynamic.primary';
|
||||
$name_secondary = 'config_test.dynamic.secondary';
|
||||
$sync = $this->container->get('config.storage.sync');
|
||||
$uuid = $this->container->get('uuid');
|
||||
|
||||
$values_primary = array(
|
||||
'id' => 'primary',
|
||||
'label' => 'Primary',
|
||||
'weight' => 0,
|
||||
'uuid' => $uuid->generate(),
|
||||
// Add a dependency on secondary, so that is synced first.
|
||||
'dependencies' => array(
|
||||
'config' => array($name_secondary),
|
||||
)
|
||||
);
|
||||
$sync->write($name_primary, $values_primary);
|
||||
$values_secondary = array(
|
||||
'id' => 'secondary',
|
||||
'label' => 'Secondary Sync',
|
||||
'weight' => 0,
|
||||
'uuid' => $uuid->generate(),
|
||||
);
|
||||
$sync->write($name_secondary, $values_secondary);
|
||||
|
||||
// Import.
|
||||
$this->configImporter->reset()->import();
|
||||
|
||||
$entity_storage = \Drupal::entityManager()->getStorage('config_test');
|
||||
$primary = $entity_storage->load('primary');
|
||||
$this->assertEqual($primary->id(), 'primary');
|
||||
$this->assertEqual($primary->uuid(), $values_primary['uuid']);
|
||||
$this->assertEqual($primary->label(), $values_primary['label']);
|
||||
$secondary = $entity_storage->load('secondary');
|
||||
$this->assertEqual($secondary->id(), 'secondary');
|
||||
$this->assertEqual($secondary->uuid(), $values_secondary['uuid']);
|
||||
$this->assertEqual($secondary->label(), $values_secondary['label']);
|
||||
|
||||
$logs = $this->configImporter->getErrors();
|
||||
$this->assertEqual(count($logs), 1);
|
||||
$this->assertEqual($logs[0], Html::escape("Unexpected error during import with operation create for $name_primary: 'config_test' entity with ID 'secondary' already exists."));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that secondary updates for deleted files work as expected.
|
||||
*/
|
||||
function testSecondaryUpdateDeletedDeleterFirst() {
|
||||
$name_deleter = 'config_test.dynamic.deleter';
|
||||
$name_deletee = 'config_test.dynamic.deletee';
|
||||
$name_other = 'config_test.dynamic.other';
|
||||
$storage = $this->container->get('config.storage');
|
||||
$sync = $this->container->get('config.storage.sync');
|
||||
$uuid = $this->container->get('uuid');
|
||||
|
||||
$values_deleter = array(
|
||||
'id' => 'deleter',
|
||||
'label' => 'Deleter',
|
||||
'weight' => 0,
|
||||
'uuid' => $uuid->generate(),
|
||||
);
|
||||
$storage->write($name_deleter, $values_deleter);
|
||||
$values_deleter['label'] = 'Updated Deleter';
|
||||
$sync->write($name_deleter, $values_deleter);
|
||||
$values_deletee = array(
|
||||
'id' => 'deletee',
|
||||
'label' => 'Deletee',
|
||||
'weight' => 0,
|
||||
'uuid' => $uuid->generate(),
|
||||
// Add a dependency on deleter, to make sure that is synced first.
|
||||
'dependencies' => array(
|
||||
'config' => array($name_deleter),
|
||||
)
|
||||
);
|
||||
$storage->write($name_deletee, $values_deletee);
|
||||
$values_deletee['label'] = 'Updated Deletee';
|
||||
$sync->write($name_deletee, $values_deletee);
|
||||
|
||||
// Ensure that import will continue after the error.
|
||||
$values_other = array(
|
||||
'id' => 'other',
|
||||
'label' => 'Other',
|
||||
'weight' => 0,
|
||||
'uuid' => $uuid->generate(),
|
||||
// Add a dependency on deleter, to make sure that is synced first. This
|
||||
// will also be synced after the deletee due to alphabetical ordering.
|
||||
'dependencies' => array(
|
||||
'config' => array($name_deleter),
|
||||
)
|
||||
);
|
||||
$storage->write($name_other, $values_other);
|
||||
$values_other['label'] = 'Updated other';
|
||||
$sync->write($name_other, $values_other);
|
||||
|
||||
// Check update changelist order.
|
||||
$updates = $this->configImporter->reset()->getStorageComparer()->getChangelist('update');
|
||||
$expected = array(
|
||||
$name_deleter,
|
||||
$name_deletee,
|
||||
$name_other,
|
||||
);
|
||||
$this->assertIdentical($expected, $updates);
|
||||
|
||||
// Import.
|
||||
$this->configImporter->import();
|
||||
|
||||
$entity_storage = \Drupal::entityManager()->getStorage('config_test');
|
||||
$deleter = $entity_storage->load('deleter');
|
||||
$this->assertEqual($deleter->id(), 'deleter');
|
||||
$this->assertEqual($deleter->uuid(), $values_deleter['uuid']);
|
||||
$this->assertEqual($deleter->label(), $values_deleter['label']);
|
||||
|
||||
// The deletee was deleted in
|
||||
// \Drupal\config_test\Entity\ConfigTest::postSave().
|
||||
$this->assertFalse($entity_storage->load('deletee'));
|
||||
|
||||
$other = $entity_storage->load('other');
|
||||
$this->assertEqual($other->id(), 'other');
|
||||
$this->assertEqual($other->uuid(), $values_other['uuid']);
|
||||
$this->assertEqual($other->label(), $values_other['label']);
|
||||
|
||||
$logs = $this->configImporter->getErrors();
|
||||
$this->assertEqual(count($logs), 1);
|
||||
$this->assertEqual($logs[0], SafeMarkup::format('Update target "@name" is missing.', array('@name' => $name_deletee)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that secondary updates for deleted files work as expected.
|
||||
*
|
||||
* This test is completely hypothetical since we only support full
|
||||
* configuration tree imports. Therefore, any configuration updates that cause
|
||||
* secondary deletes should be reflected already in the staged configuration.
|
||||
*/
|
||||
function testSecondaryUpdateDeletedDeleteeFirst() {
|
||||
$name_deleter = 'config_test.dynamic.deleter';
|
||||
$name_deletee = 'config_test.dynamic.deletee';
|
||||
$storage = $this->container->get('config.storage');
|
||||
$sync = $this->container->get('config.storage.sync');
|
||||
$uuid = $this->container->get('uuid');
|
||||
|
||||
$values_deleter = array(
|
||||
'id' => 'deleter',
|
||||
'label' => 'Deleter',
|
||||
'weight' => 0,
|
||||
'uuid' => $uuid->generate(),
|
||||
// Add a dependency on deletee, to make sure that is synced first.
|
||||
'dependencies' => array(
|
||||
'config' => array($name_deletee),
|
||||
),
|
||||
);
|
||||
$storage->write($name_deleter, $values_deleter);
|
||||
$values_deleter['label'] = 'Updated Deleter';
|
||||
$sync->write($name_deleter, $values_deleter);
|
||||
$values_deletee = array(
|
||||
'id' => 'deletee',
|
||||
'label' => 'Deletee',
|
||||
'weight' => 0,
|
||||
'uuid' => $uuid->generate(),
|
||||
);
|
||||
$storage->write($name_deletee, $values_deletee);
|
||||
$values_deletee['label'] = 'Updated Deletee';
|
||||
$sync->write($name_deletee, $values_deletee);
|
||||
|
||||
// Import.
|
||||
$this->configImporter->reset()->import();
|
||||
|
||||
$entity_storage = \Drupal::entityManager()->getStorage('config_test');
|
||||
// Both entities are deleted. ConfigTest::postSave() causes updates of the
|
||||
// deleter entity to delete the deletee entity. Since the deleter depends on
|
||||
// the deletee, removing the deletee causes the deleter to be removed.
|
||||
$this->assertFalse($entity_storage->load('deleter'));
|
||||
$this->assertFalse($entity_storage->load('deletee'));
|
||||
$logs = $this->configImporter->getErrors();
|
||||
$this->assertEqual(count($logs), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that secondary deletes for deleted files work as expected.
|
||||
*/
|
||||
function testSecondaryDeletedDeleteeSecond() {
|
||||
$name_deleter = 'config_test.dynamic.deleter';
|
||||
$name_deletee = 'config_test.dynamic.deletee';
|
||||
$storage = $this->container->get('config.storage');
|
||||
|
||||
$uuid = $this->container->get('uuid');
|
||||
|
||||
$values_deleter = array(
|
||||
'id' => 'deleter',
|
||||
'label' => 'Deleter',
|
||||
'weight' => 0,
|
||||
'uuid' => $uuid->generate(),
|
||||
// Add a dependency on deletee, to make sure this delete is synced first.
|
||||
'dependencies' => array(
|
||||
'config' => array($name_deletee),
|
||||
),
|
||||
);
|
||||
$storage->write($name_deleter, $values_deleter);
|
||||
$values_deletee = array(
|
||||
'id' => 'deletee',
|
||||
'label' => 'Deletee',
|
||||
'weight' => 0,
|
||||
'uuid' => $uuid->generate(),
|
||||
);
|
||||
$storage->write($name_deletee, $values_deletee);
|
||||
|
||||
// Import.
|
||||
$this->configImporter->reset()->import();
|
||||
|
||||
$entity_storage = \Drupal::entityManager()->getStorage('config_test');
|
||||
$this->assertFalse($entity_storage->load('deleter'));
|
||||
$this->assertFalse($entity_storage->load('deletee'));
|
||||
// The deletee entity does not exist as the delete worked and although the
|
||||
// delete occurred in \Drupal\config_test\Entity\ConfigTest::postDelete()
|
||||
// this does not matter.
|
||||
$logs = $this->configImporter->getErrors();
|
||||
$this->assertEqual(count($logs), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests updating of configuration during import.
|
||||
*/
|
||||
function testUpdated() {
|
||||
$name = 'config_test.system';
|
||||
$dynamic_name = 'config_test.dynamic.dotted.default';
|
||||
$storage = $this->container->get('config.storage');
|
||||
$sync = $this->container->get('config.storage.sync');
|
||||
|
||||
// Verify that the configuration objects to import exist.
|
||||
$this->assertIdentical($storage->exists($name), TRUE, $name . ' found.');
|
||||
$this->assertIdentical($storage->exists($dynamic_name), TRUE, $dynamic_name . ' found.');
|
||||
|
||||
// Replace the file content of the existing configuration objects in the
|
||||
// sync directory.
|
||||
$original_name_data = array(
|
||||
'foo' => 'beer',
|
||||
);
|
||||
$sync->write($name, $original_name_data);
|
||||
$original_dynamic_data = $storage->read($dynamic_name);
|
||||
$original_dynamic_data['label'] = 'Updated';
|
||||
$sync->write($dynamic_name, $original_dynamic_data);
|
||||
|
||||
// Verify the active configuration still returns the default values.
|
||||
$config = $this->config($name);
|
||||
$this->assertIdentical($config->get('foo'), 'bar');
|
||||
$config = $this->config($dynamic_name);
|
||||
$this->assertIdentical($config->get('label'), 'Default');
|
||||
|
||||
// Import.
|
||||
$this->configImporter->reset()->import();
|
||||
|
||||
// Verify the values were updated.
|
||||
\Drupal::configFactory()->reset($name);
|
||||
$config = $this->config($name);
|
||||
$this->assertIdentical($config->get('foo'), 'beer');
|
||||
$config = $this->config($dynamic_name);
|
||||
$this->assertIdentical($config->get('label'), 'Updated');
|
||||
|
||||
// Verify that the original file content is still the same.
|
||||
$this->assertIdentical($sync->read($name), $original_name_data);
|
||||
$this->assertIdentical($sync->read($dynamic_name), $original_dynamic_data);
|
||||
|
||||
// Verify that appropriate module API hooks have been invoked.
|
||||
$this->assertTrue(isset($GLOBALS['hook_config_test']['load']));
|
||||
$this->assertTrue(isset($GLOBALS['hook_config_test']['presave']));
|
||||
$this->assertFalse(isset($GLOBALS['hook_config_test']['insert']));
|
||||
$this->assertTrue(isset($GLOBALS['hook_config_test']['update']));
|
||||
$this->assertFalse(isset($GLOBALS['hook_config_test']['predelete']));
|
||||
$this->assertFalse(isset($GLOBALS['hook_config_test']['delete']));
|
||||
|
||||
// Verify that there is nothing more to import.
|
||||
$this->assertFalse($this->configImporter->hasUnprocessedConfigurationChanges());
|
||||
$logs = $this->configImporter->getErrors();
|
||||
$this->assertEqual(count($logs), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the isInstallable method()
|
||||
*/
|
||||
function testIsInstallable() {
|
||||
$config_name = 'config_test.dynamic.isinstallable';
|
||||
$this->assertFalse($this->container->get('config.storage')->exists($config_name));
|
||||
\Drupal::state()->set('config_test.isinstallable', TRUE);
|
||||
$this->installConfig(array('config_test'));
|
||||
$this->assertTrue($this->container->get('config.storage')->exists($config_name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests dependency validation during configuration import.
|
||||
*
|
||||
* @see \Drupal\Core\EventSubscriber\ConfigImportSubscriber
|
||||
* @see \Drupal\Core\Config\ConfigImporter::createExtensionChangelist()
|
||||
*/
|
||||
public function testUnmetDependency() {
|
||||
$storage = $this->container->get('config.storage');
|
||||
$sync = $this->container->get('config.storage.sync');
|
||||
|
||||
// Test an unknown configuration owner.
|
||||
$sync->write('unknown.config', ['test' => 'test']);
|
||||
|
||||
// Make a config entity have unmet dependencies.
|
||||
$config_entity_data = $sync->read('config_test.dynamic.dotted.default');
|
||||
$config_entity_data['dependencies'] = ['module' => ['unknown']];
|
||||
$sync->write('config_test.dynamic.dotted.module', $config_entity_data);
|
||||
$config_entity_data['dependencies'] = ['theme' => ['unknown']];
|
||||
$sync->write('config_test.dynamic.dotted.theme', $config_entity_data);
|
||||
$config_entity_data['dependencies'] = ['config' => ['unknown']];
|
||||
$sync->write('config_test.dynamic.dotted.config', $config_entity_data);
|
||||
|
||||
// Make an active config depend on something that is missing in sync.
|
||||
// The whole configuration needs to be consistent, not only the updated one.
|
||||
$config_entity_data['dependencies'] = [];
|
||||
$storage->write('config_test.dynamic.dotted.deleted', $config_entity_data);
|
||||
$config_entity_data['dependencies'] = ['config' => ['config_test.dynamic.dotted.deleted']];
|
||||
$storage->write('config_test.dynamic.dotted.existing', $config_entity_data);
|
||||
$sync->write('config_test.dynamic.dotted.existing', $config_entity_data);
|
||||
|
||||
$extensions = $sync->read('core.extension');
|
||||
// Add a module and a theme that do not exist.
|
||||
$extensions['module']['unknown_module'] = 0;
|
||||
$extensions['theme']['unknown_theme'] = 0;
|
||||
// Add a module and a theme that depend on uninstalled extensions.
|
||||
$extensions['module']['book'] = 0;
|
||||
$extensions['theme']['bartik'] = 0;
|
||||
|
||||
$sync->write('core.extension', $extensions);
|
||||
try {
|
||||
$this->configImporter->reset()->import();
|
||||
$this->fail('ConfigImporterException not thrown; an invalid import was not stopped due to missing dependencies.');
|
||||
}
|
||||
catch (ConfigImporterException $e) {
|
||||
$this->assertEqual($e->getMessage(), 'There were errors validating the config synchronization.');
|
||||
$error_log = $this->configImporter->getErrors();
|
||||
$expected = [
|
||||
'Unable to install the <em class="placeholder">unknown_module</em> module since it does not exist.',
|
||||
'Unable to install the <em class="placeholder">Book</em> module since it requires the <em class="placeholder">Node, Text, Field, Filter, User</em> modules.',
|
||||
'Unable to install the <em class="placeholder">unknown_theme</em> theme since it does not exist.',
|
||||
'Unable to install the <em class="placeholder">Bartik</em> theme since it requires the <em class="placeholder">Classy</em> theme.',
|
||||
'Configuration <em class="placeholder">config_test.dynamic.dotted.config</em> depends on the <em class="placeholder">unknown</em> configuration that will not exist after import.',
|
||||
'Configuration <em class="placeholder">config_test.dynamic.dotted.existing</em> depends on the <em class="placeholder">config_test.dynamic.dotted.deleted</em> configuration that will not exist after import.',
|
||||
'Configuration <em class="placeholder">config_test.dynamic.dotted.module</em> depends on the <em class="placeholder">unknown</em> module that will not be installed after import.',
|
||||
'Configuration <em class="placeholder">config_test.dynamic.dotted.theme</em> depends on the <em class="placeholder">unknown</em> theme that will not be installed after import.',
|
||||
'Configuration <em class="placeholder">unknown.config</em> depends on the <em class="placeholder">unknown</em> extension that will not be installed after import.',
|
||||
];
|
||||
foreach ($expected as $expected_message) {
|
||||
$this->assertTrue(in_array($expected_message, $error_log), $expected_message);
|
||||
}
|
||||
}
|
||||
|
||||
// Make a config entity have mulitple unmet dependencies.
|
||||
$config_entity_data = $sync->read('config_test.dynamic.dotted.default');
|
||||
$config_entity_data['dependencies'] = ['module' => ['unknown', 'dblog']];
|
||||
$sync->write('config_test.dynamic.dotted.module', $config_entity_data);
|
||||
$config_entity_data['dependencies'] = ['theme' => ['unknown', 'seven']];
|
||||
$sync->write('config_test.dynamic.dotted.theme', $config_entity_data);
|
||||
$config_entity_data['dependencies'] = ['config' => ['unknown', 'unknown2']];
|
||||
$sync->write('config_test.dynamic.dotted.config', $config_entity_data);
|
||||
try {
|
||||
$this->configImporter->reset()->import();
|
||||
$this->fail('ConfigImporterException not thrown, invalid import was not stopped due to missing dependencies.');
|
||||
}
|
||||
catch (ConfigImporterException $e) {
|
||||
$this->assertEqual($e->getMessage(), 'There were errors validating the config synchronization.');
|
||||
$error_log = $this->configImporter->getErrors();
|
||||
$expected = [
|
||||
'Configuration <em class="placeholder">config_test.dynamic.dotted.config</em> depends on configuration (<em class="placeholder">unknown, unknown2</em>) that will not exist after import.',
|
||||
'Configuration <em class="placeholder">config_test.dynamic.dotted.module</em> depends on modules (<em class="placeholder">unknown, Database Logging</em>) that will not be installed after import.',
|
||||
'Configuration <em class="placeholder">config_test.dynamic.dotted.theme</em> depends on themes (<em class="placeholder">unknown, Seven</em>) that will not be installed after import.',
|
||||
];
|
||||
foreach ($expected as $expected_message) {
|
||||
$this->assertTrue(in_array($expected_message, $error_log), $expected_message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests missing core.extension during configuration import.
|
||||
*
|
||||
* @see \Drupal\Core\EventSubscriber\ConfigImportSubscriber
|
||||
*/
|
||||
public function testMissingCoreExtension() {
|
||||
$sync = $this->container->get('config.storage.sync');
|
||||
$sync->delete('core.extension');
|
||||
try {
|
||||
$this->configImporter->reset()->import();
|
||||
$this->fail('ConfigImporterException not thrown, invalid import was not stopped due to missing dependencies.');
|
||||
}
|
||||
catch (ConfigImporterException $e) {
|
||||
$this->assertEqual($e->getMessage(), 'There were errors validating the config synchronization.');
|
||||
$error_log = $this->configImporter->getErrors();
|
||||
$this->assertEqual(['The core.extension configuration does not exist.'], $error_log);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests install profile validation during configuration import.
|
||||
*
|
||||
* @see \Drupal\Core\EventSubscriber\ConfigImportSubscriber
|
||||
*/
|
||||
public function testInstallProfile() {
|
||||
$sync = $this->container->get('config.storage.sync');
|
||||
|
||||
$extensions = $sync->read('core.extension');
|
||||
// Add an install profile.
|
||||
$extensions['module']['standard'] = 0;
|
||||
|
||||
$sync->write('core.extension', $extensions);
|
||||
try {
|
||||
$this->configImporter->reset()->import();
|
||||
$this->fail('ConfigImporterException not thrown; an invalid import was not stopped due to missing dependencies.');
|
||||
}
|
||||
catch (ConfigImporterException $e) {
|
||||
$this->assertEqual($e->getMessage(), 'There were errors validating the config synchronization.');
|
||||
$error_log = $this->configImporter->getErrors();
|
||||
// Install profiles should not even be scanned at this point.
|
||||
$this->assertEqual(['Unable to install the <em class="placeholder">standard</em> module since it does not exist.'], $error_log);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests config_get_config_directory().
|
||||
*/
|
||||
public function testConfigGetConfigDirectory() {
|
||||
global $config_directories;
|
||||
$directory = config_get_config_directory(CONFIG_SYNC_DIRECTORY);
|
||||
$this->assertEqual($config_directories[CONFIG_SYNC_DIRECTORY], $directory);
|
||||
|
||||
$message = 'Calling config_get_config_directory() with CONFIG_ACTIVE_DIRECTORY results in an exception.';
|
||||
try {
|
||||
config_get_config_directory(CONFIG_ACTIVE_DIRECTORY);
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$this->pass($message);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
248
core/tests/Drupal/KernelTests/Core/Config/ConfigInstallTest.php
Normal file
248
core/tests/Drupal/KernelTests/Core/Config/ConfigInstallTest.php
Normal file
|
|
@ -0,0 +1,248 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Config;
|
||||
|
||||
use Drupal\Core\Config\InstallStorage;
|
||||
use Drupal\Core\Config\PreExistingConfigException;
|
||||
use Drupal\Core\Config\UnmetDependenciesException;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests installation of configuration objects in installation functionality.
|
||||
*
|
||||
* @group config
|
||||
* @see \Drupal\Core\Config\ConfigInstaller
|
||||
*/
|
||||
class ConfigInstallTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['system'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Ensure the global variable being asserted by this test does not exist;
|
||||
// a previous test executed in this request/process might have set it.
|
||||
unset($GLOBALS['hook_config_test']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests module installation.
|
||||
*/
|
||||
function testModuleInstallation() {
|
||||
$default_config = 'config_test.system';
|
||||
$default_configuration_entity = 'config_test.dynamic.dotted.default';
|
||||
|
||||
// Verify that default module config does not exist before installation yet.
|
||||
$config = $this->config($default_config);
|
||||
$this->assertIdentical($config->isNew(), TRUE);
|
||||
$config = $this->config($default_configuration_entity);
|
||||
$this->assertIdentical($config->isNew(), TRUE);
|
||||
|
||||
// Ensure that schema provided by modules that are not installed is not
|
||||
// available.
|
||||
$this->assertFalse(\Drupal::service('config.typed')->hasConfigSchema('config_schema_test.someschema'), 'Configuration schema for config_schema_test.someschema does not exist.');
|
||||
|
||||
// Install the test module.
|
||||
$this->installModules(array('config_test'));
|
||||
|
||||
// Verify that default module config exists.
|
||||
\Drupal::configFactory()->reset($default_config);
|
||||
\Drupal::configFactory()->reset($default_configuration_entity);
|
||||
$config = $this->config($default_config);
|
||||
$this->assertIdentical($config->isNew(), FALSE);
|
||||
$config = $this->config($default_configuration_entity);
|
||||
$this->assertIdentical($config->isNew(), FALSE);
|
||||
|
||||
// Verify that config_test API hooks were invoked for the dynamic default
|
||||
// configuration entity.
|
||||
$this->assertFalse(isset($GLOBALS['hook_config_test']['load']));
|
||||
$this->assertTrue(isset($GLOBALS['hook_config_test']['presave']));
|
||||
$this->assertTrue(isset($GLOBALS['hook_config_test']['insert']));
|
||||
$this->assertFalse(isset($GLOBALS['hook_config_test']['update']));
|
||||
$this->assertFalse(isset($GLOBALS['hook_config_test']['predelete']));
|
||||
$this->assertFalse(isset($GLOBALS['hook_config_test']['delete']));
|
||||
|
||||
// Install the schema test module.
|
||||
$this->enableModules(array('config_schema_test'));
|
||||
$this->installConfig(array('config_schema_test'));
|
||||
|
||||
// After module installation the new schema should exist.
|
||||
$this->assertTrue(\Drupal::service('config.typed')->hasConfigSchema('config_schema_test.someschema'), 'Configuration schema for config_schema_test.someschema exists.');
|
||||
|
||||
// Test that uninstalling configuration removes configuration schema.
|
||||
$this->config('core.extension')->set('module', array())->save();
|
||||
\Drupal::service('config.manager')->uninstall('module', 'config_test');
|
||||
$this->assertFalse(\Drupal::service('config.typed')->hasConfigSchema('config_schema_test.someschema'), 'Configuration schema for config_schema_test.someschema does not exist.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that collections are ignored if the event does not return anything.
|
||||
*/
|
||||
public function testCollectionInstallationNoCollections() {
|
||||
// Install the test module.
|
||||
$this->enableModules(array('config_collection_install_test'));
|
||||
$this->installConfig(array('config_collection_install_test'));
|
||||
/** @var \Drupal\Core\Config\StorageInterface $active_storage */
|
||||
$active_storage = \Drupal::service('config.storage');
|
||||
$this->assertEqual(array(), $active_storage->getAllCollectionNames());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests config objects in collections are installed as expected.
|
||||
*/
|
||||
public function testCollectionInstallationCollections() {
|
||||
$collections = array(
|
||||
'another_collection',
|
||||
'collection.test1',
|
||||
'collection.test2',
|
||||
);
|
||||
// Set the event listener to return three possible collections.
|
||||
// @see \Drupal\config_collection_install_test\EventSubscriber
|
||||
\Drupal::state()->set('config_collection_install_test.collection_names', $collections);
|
||||
// Install the test module.
|
||||
$this->enableModules(array('config_collection_install_test'));
|
||||
$this->installConfig(array('config_collection_install_test'));
|
||||
/** @var \Drupal\Core\Config\StorageInterface $active_storage */
|
||||
$active_storage = \Drupal::service('config.storage');
|
||||
$this->assertEqual($collections, $active_storage->getAllCollectionNames());
|
||||
foreach ($collections as $collection) {
|
||||
$collection_storage = $active_storage->createCollection($collection);
|
||||
$data = $collection_storage->read('config_collection_install_test.test');
|
||||
$this->assertEqual($collection, $data['collection']);
|
||||
}
|
||||
|
||||
// Tests that clashing configuration in collections is detected.
|
||||
try {
|
||||
\Drupal::service('module_installer')->install(['config_collection_clash_install_test']);
|
||||
$this->fail('Expected PreExistingConfigException not thrown.');
|
||||
}
|
||||
catch (PreExistingConfigException $e) {
|
||||
$this->assertEqual($e->getExtension(), 'config_collection_clash_install_test');
|
||||
$this->assertEqual($e->getConfigObjects(), [
|
||||
'another_collection' => ['config_collection_install_test.test'],
|
||||
'collection.test1' => ['config_collection_install_test.test'],
|
||||
'collection.test2' => ['config_collection_install_test.test'],
|
||||
]);
|
||||
$this->assertEqual($e->getMessage(), 'Configuration objects (another_collection/config_collection_install_test.test, collection/test1/config_collection_install_test.test, collection/test2/config_collection_install_test.test) provided by config_collection_clash_install_test already exist in active configuration');
|
||||
}
|
||||
|
||||
// Test that the we can use the config installer to install all the
|
||||
// available default configuration in a particular collection for enabled
|
||||
// extensions.
|
||||
\Drupal::service('config.installer')->installCollectionDefaultConfig('entity');
|
||||
// The 'entity' collection will not exist because the 'config_test' module
|
||||
// is not enabled.
|
||||
$this->assertEqual($collections, $active_storage->getAllCollectionNames());
|
||||
// Enable the 'config_test' module and try again.
|
||||
$this->enableModules(array('config_test'));
|
||||
\Drupal::service('config.installer')->installCollectionDefaultConfig('entity');
|
||||
$collections[] = 'entity';
|
||||
$this->assertEqual($collections, $active_storage->getAllCollectionNames());
|
||||
$collection_storage = $active_storage->createCollection('entity');
|
||||
$data = $collection_storage->read('config_test.dynamic.dotted.default');
|
||||
$this->assertIdentical(array('label' => 'entity'), $data);
|
||||
|
||||
// Test that the config manager uninstalls configuration from collections
|
||||
// as expected.
|
||||
\Drupal::service('config.manager')->uninstall('module', 'config_collection_install_test');
|
||||
$this->assertEqual(array('entity'), $active_storage->getAllCollectionNames());
|
||||
\Drupal::service('config.manager')->uninstall('module', 'config_test');
|
||||
$this->assertEqual(array(), $active_storage->getAllCollectionNames());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests collections which do not support config entities install correctly.
|
||||
*
|
||||
* Config entity detection during config installation is done by matching
|
||||
* config name prefixes. If a collection provides a configuration with a
|
||||
* matching name but does not support config entities it should be created
|
||||
* using simple configuration.
|
||||
*/
|
||||
public function testCollectionInstallationCollectionConfigEntity() {
|
||||
$collections = array(
|
||||
'entity',
|
||||
);
|
||||
\Drupal::state()->set('config_collection_install_test.collection_names', $collections);
|
||||
// Install the test module.
|
||||
$this->installModules(array('config_test', 'config_collection_install_test'));
|
||||
/** @var \Drupal\Core\Config\StorageInterface $active_storage */
|
||||
$active_storage = \Drupal::service('config.storage');
|
||||
$this->assertEqual($collections, $active_storage->getAllCollectionNames());
|
||||
$collection_storage = $active_storage->createCollection('entity');
|
||||
|
||||
// The config_test.dynamic.dotted.default configuration object saved in the
|
||||
// active store should be a configuration entity complete with UUID. Because
|
||||
// the entity collection does not support configuration entities the
|
||||
// configuration object stored there with the same name should only contain
|
||||
// a label.
|
||||
$name = 'config_test.dynamic.dotted.default';
|
||||
$data = $active_storage->read($name);
|
||||
$this->assertTrue(isset($data['uuid']));
|
||||
$data = $collection_storage->read($name);
|
||||
$this->assertIdentical(array('label' => 'entity'), $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the configuration with unmet dependencies is not installed.
|
||||
*/
|
||||
public function testDependencyChecking() {
|
||||
$this->installModules(['config_test']);
|
||||
try {
|
||||
$this->installModules(['config_install_dependency_test']);
|
||||
$this->fail('Expected UnmetDependenciesException not thrown.');
|
||||
}
|
||||
catch (UnmetDependenciesException $e) {
|
||||
$this->assertEqual($e->getExtension(), 'config_install_dependency_test');
|
||||
$this->assertEqual($e->getConfigObjects(), ['config_test.dynamic.other_module_test_with_dependency']);
|
||||
$this->assertEqual($e->getMessage(), 'Configuration objects (config_test.dynamic.other_module_test_with_dependency) provided by config_install_dependency_test have unmet dependencies');
|
||||
}
|
||||
$this->installModules(['config_other_module_config_test']);
|
||||
$this->installModules(['config_install_dependency_test']);
|
||||
$entity = \Drupal::entityManager()->getStorage('config_test')->load('other_module_test_with_dependency');
|
||||
$this->assertTrue($entity, 'The config_test.dynamic.other_module_test_with_dependency configuration has been created during install.');
|
||||
// Ensure that dependencies can be added during module installation by
|
||||
// hooks.
|
||||
$this->assertIdentical('config_install_dependency_test', $entity->getDependencies()['module'][0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests imported configuration entities with and without language information.
|
||||
*/
|
||||
function testLanguage() {
|
||||
$this->installModules(['config_test_language']);
|
||||
// Test imported configuration with implicit language code.
|
||||
$storage = new InstallStorage();
|
||||
$data = $storage->read('config_test.dynamic.dotted.english');
|
||||
$this->assertTrue(!isset($data['langcode']));
|
||||
$this->assertEqual(
|
||||
$this->config('config_test.dynamic.dotted.english')->get('langcode'),
|
||||
'en'
|
||||
);
|
||||
|
||||
// Test imported configuration with explicit language code.
|
||||
$data = $storage->read('config_test.dynamic.dotted.french');
|
||||
$this->assertEqual($data['langcode'], 'fr');
|
||||
$this->assertEqual(
|
||||
$this->config('config_test.dynamic.dotted.french')->get('langcode'),
|
||||
'fr'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs a module.
|
||||
*
|
||||
* @param array $modules
|
||||
* The module names.
|
||||
*/
|
||||
protected function installModules(array $modules) {
|
||||
$this->container->get('module_installer')->install($modules);
|
||||
$this->container = \Drupal::getContainer();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Config;
|
||||
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Confirm that language overrides work.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigLanguageOverrideTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('user', 'language', 'config_test', 'system', 'field');
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installConfig(array('config_test'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests locale override based on language.
|
||||
*/
|
||||
function testConfigLanguageOverride() {
|
||||
// The language module implements a config factory override object that
|
||||
// overrides configuration when the Language module is enabled. This test ensures that
|
||||
// English overrides work.
|
||||
\Drupal::languageManager()->setConfigOverrideLanguage(\Drupal::languageManager()->getLanguage('en'));
|
||||
$config = \Drupal::config('config_test.system');
|
||||
$this->assertIdentical($config->get('foo'), 'en bar');
|
||||
|
||||
// Ensure that the raw data is not translated.
|
||||
$raw = $config->getRawData();
|
||||
$this->assertIdentical($raw['foo'], 'bar');
|
||||
|
||||
ConfigurableLanguage::createFromLangcode('fr')->save();
|
||||
ConfigurableLanguage::createFromLangcode('de')->save();
|
||||
|
||||
\Drupal::languageManager()->setConfigOverrideLanguage(\Drupal::languageManager()->getLanguage('fr'));
|
||||
$config = \Drupal::config('config_test.system');
|
||||
$this->assertIdentical($config->get('foo'), 'fr bar');
|
||||
|
||||
\Drupal::languageManager()->setConfigOverrideLanguage(\Drupal::languageManager()->getLanguage('de'));
|
||||
$config = \Drupal::config('config_test.system');
|
||||
$this->assertIdentical($config->get('foo'), 'de bar');
|
||||
|
||||
// Test overrides of completely new configuration objects. In normal runtime
|
||||
// this should only happen for configuration entities as we should not be
|
||||
// creating simple configuration objects on the fly.
|
||||
\Drupal::languageManager()
|
||||
->getLanguageConfigOverride('de', 'config_test.new')
|
||||
->set('language', 'override')
|
||||
->save();
|
||||
$config = \Drupal::config('config_test.new');
|
||||
$this->assertTrue($config->isNew(), 'The configuration object config_test.new is new');
|
||||
$this->assertIdentical($config->get('language'), 'override');
|
||||
$this->assertIdentical($config->getOriginal('language', FALSE), NULL);
|
||||
|
||||
// Test how overrides react to base configuration changes. Set up some base
|
||||
// values.
|
||||
\Drupal::configFactory()->getEditable('config_test.foo')
|
||||
->set('value', array('key' => 'original'))
|
||||
->set('label', 'Original')
|
||||
->save();
|
||||
\Drupal::languageManager()
|
||||
->getLanguageConfigOverride('de', 'config_test.foo')
|
||||
->set('value', array('key' => 'override'))
|
||||
->set('label', 'Override')
|
||||
->save();
|
||||
\Drupal::languageManager()
|
||||
->getLanguageConfigOverride('fr', 'config_test.foo')
|
||||
->set('value', array('key' => 'override'))
|
||||
->save();
|
||||
\Drupal::configFactory()->clearStaticCache();
|
||||
$config = \Drupal::config('config_test.foo');
|
||||
$this->assertIdentical($config->get('value'), array('key' => 'override'));
|
||||
|
||||
// Ensure renaming the config will rename the override.
|
||||
\Drupal::languageManager()->setConfigOverrideLanguage(\Drupal::languageManager()->getLanguage('en'));
|
||||
\Drupal::configFactory()->rename('config_test.foo', 'config_test.bar');
|
||||
$config = \Drupal::config('config_test.bar');
|
||||
$this->assertEqual($config->get('value'), array('key' => 'original'));
|
||||
$override = \Drupal::languageManager()->getLanguageConfigOverride('de', 'config_test.foo');
|
||||
$this->assertTrue($override->isNew());
|
||||
$this->assertEqual($override->get('value'), NULL);
|
||||
$override = \Drupal::languageManager()->getLanguageConfigOverride('de', 'config_test.bar');
|
||||
$this->assertFalse($override->isNew());
|
||||
$this->assertEqual($override->get('value'), array('key' => 'override'));
|
||||
$override = \Drupal::languageManager()->getLanguageConfigOverride('fr', 'config_test.bar');
|
||||
$this->assertFalse($override->isNew());
|
||||
$this->assertEqual($override->get('value'), array('key' => 'override'));
|
||||
|
||||
// Ensure changing data in the config will update the overrides.
|
||||
$config = \Drupal::configFactory()->getEditable('config_test.bar')->clear('value.key')->save();
|
||||
$this->assertEqual($config->get('value'), array());
|
||||
$override = \Drupal::languageManager()->getLanguageConfigOverride('de', 'config_test.bar');
|
||||
$this->assertFalse($override->isNew());
|
||||
$this->assertEqual($override->get('value'), NULL);
|
||||
// The French override will become empty and therefore removed.
|
||||
$override = \Drupal::languageManager()->getLanguageConfigOverride('fr', 'config_test.bar');
|
||||
$this->assertTrue($override->isNew());
|
||||
$this->assertEqual($override->get('value'), NULL);
|
||||
|
||||
// Ensure deleting the config will delete the override.
|
||||
\Drupal::configFactory()->getEditable('config_test.bar')->delete();
|
||||
$override = \Drupal::languageManager()->getLanguageConfigOverride('de', 'config_test.bar');
|
||||
$this->assertTrue($override->isNew());
|
||||
$this->assertEqual($override->get('value'), NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Config;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests module overrides of configuration using event subscribers.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigModuleOverridesTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('system', 'config', 'config_override_test');
|
||||
|
||||
public function testSimpleModuleOverrides() {
|
||||
$GLOBALS['config_test_run_module_overrides'] = TRUE;
|
||||
$name = 'system.site';
|
||||
$overridden_name = 'ZOMG overridden site name';
|
||||
$non_overridden_name = 'ZOMG this name is on disk mkay';
|
||||
$overridden_slogan = 'Yay for overrides!';
|
||||
$non_overridden_slogan = 'Yay for defaults!';
|
||||
$config_factory = $this->container->get('config.factory');
|
||||
$config_factory
|
||||
->getEditable($name)
|
||||
->set('name', $non_overridden_name)
|
||||
->set('slogan', $non_overridden_slogan)
|
||||
->save();
|
||||
|
||||
$this->assertEqual($non_overridden_name, $config_factory->get('system.site')->getOriginal('name', FALSE));
|
||||
$this->assertEqual($non_overridden_slogan, $config_factory->get('system.site')->getOriginal('slogan', FALSE));
|
||||
$this->assertEqual($overridden_name, $config_factory->get('system.site')->get('name'));
|
||||
$this->assertEqual($overridden_slogan, $config_factory->get('system.site')->get('slogan'));
|
||||
|
||||
// Test overrides of completely new configuration objects. In normal runtime
|
||||
// this should only happen for configuration entities as we should not be
|
||||
// creating simple configuration objects on the fly.
|
||||
$config = $config_factory->get('config_override_test.new');
|
||||
$this->assertTrue($config->isNew(), 'The configuration object config_override_test.new is new');
|
||||
$this->assertIdentical($config->get('module'), 'override');
|
||||
$this->assertIdentical($config->getOriginal('module', FALSE), NULL);
|
||||
|
||||
unset($GLOBALS['config_test_run_module_overrides']);
|
||||
}
|
||||
}
|
||||
138
core/tests/Drupal/KernelTests/Core/Config/ConfigOverrideTest.php
Normal file
138
core/tests/Drupal/KernelTests/Core/Config/ConfigOverrideTest.php
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Config;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests configuration overrides via $config in settings.php.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigOverrideTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('system', 'config_test');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.sync'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests configuration override.
|
||||
*/
|
||||
function testConfOverride() {
|
||||
$expected_original_data = array(
|
||||
'foo' => 'bar',
|
||||
'baz' => NULL,
|
||||
'404' => 'herp',
|
||||
);
|
||||
|
||||
// Set globals before installing to prove that the installed file does not
|
||||
// contain these values.
|
||||
$overrides['config_test.system']['foo'] = 'overridden';
|
||||
$overrides['config_test.system']['baz'] = 'injected';
|
||||
$overrides['config_test.system']['404'] = 'derp';
|
||||
$GLOBALS['config'] = $overrides;
|
||||
|
||||
$this->installConfig(array('config_test'));
|
||||
|
||||
// Verify that the original configuration data exists. Have to read storage
|
||||
// directly otherwise overrides will apply.
|
||||
$active = $this->container->get('config.storage');
|
||||
$data = $active->read('config_test.system');
|
||||
$this->assertIdentical($data['foo'], $expected_original_data['foo']);
|
||||
$this->assertFalse(isset($data['baz']));
|
||||
$this->assertIdentical($data['404'], $expected_original_data['404']);
|
||||
|
||||
// Get the configuration object with overrides.
|
||||
$config = \Drupal::configFactory()->get('config_test.system');
|
||||
|
||||
// Verify that it contains the overridden data from $config.
|
||||
$this->assertIdentical($config->get('foo'), $overrides['config_test.system']['foo']);
|
||||
$this->assertIdentical($config->get('baz'), $overrides['config_test.system']['baz']);
|
||||
$this->assertIdentical($config->get('404'), $overrides['config_test.system']['404']);
|
||||
|
||||
// Get the configuration object which does not have overrides.
|
||||
$config = \Drupal::configFactory()->getEditable('config_test.system');
|
||||
|
||||
// Verify that it does not contains the overridden data from $config.
|
||||
$this->assertIdentical($config->get('foo'), $expected_original_data['foo']);
|
||||
$this->assertIdentical($config->get('baz'), NULL);
|
||||
$this->assertIdentical($config->get('404'), $expected_original_data['404']);
|
||||
|
||||
// Set the value for 'baz' (on the original data).
|
||||
$expected_original_data['baz'] = 'original baz';
|
||||
$config->set('baz', $expected_original_data['baz']);
|
||||
|
||||
// Set the value for '404' (on the original data).
|
||||
$expected_original_data['404'] = 'original 404';
|
||||
$config->set('404', $expected_original_data['404']);
|
||||
|
||||
// Save the configuration object (having overrides applied).
|
||||
$config->save();
|
||||
|
||||
// Reload it and verify that it still contains overridden data from $config.
|
||||
$config = \Drupal::config('config_test.system');
|
||||
$this->assertIdentical($config->get('foo'), $overrides['config_test.system']['foo']);
|
||||
$this->assertIdentical($config->get('baz'), $overrides['config_test.system']['baz']);
|
||||
$this->assertIdentical($config->get('404'), $overrides['config_test.system']['404']);
|
||||
|
||||
// Verify that raw config data has changed.
|
||||
$this->assertIdentical($config->getOriginal('foo', FALSE), $expected_original_data['foo']);
|
||||
$this->assertIdentical($config->getOriginal('baz', FALSE), $expected_original_data['baz']);
|
||||
$this->assertIdentical($config->getOriginal('404', FALSE), $expected_original_data['404']);
|
||||
|
||||
// Write file to sync.
|
||||
$sync = $this->container->get('config.storage.sync');
|
||||
$expected_new_data = array(
|
||||
'foo' => 'barbar',
|
||||
'404' => 'herpderp',
|
||||
);
|
||||
$sync->write('config_test.system', $expected_new_data);
|
||||
|
||||
// Import changed data from sync to active.
|
||||
$this->configImporter()->import();
|
||||
$data = $active->read('config_test.system');
|
||||
|
||||
// Verify that the new configuration data exists. Have to read storage
|
||||
// directly otherwise overrides will apply.
|
||||
$this->assertIdentical($data['foo'], $expected_new_data['foo']);
|
||||
$this->assertFalse(isset($data['baz']));
|
||||
$this->assertIdentical($data['404'], $expected_new_data['404']);
|
||||
|
||||
// Verify that the overrides are still working.
|
||||
$config = \Drupal::config('config_test.system');
|
||||
$this->assertIdentical($config->get('foo'), $overrides['config_test.system']['foo']);
|
||||
$this->assertIdentical($config->get('baz'), $overrides['config_test.system']['baz']);
|
||||
$this->assertIdentical($config->get('404'), $overrides['config_test.system']['404']);
|
||||
|
||||
// Test overrides of completely new configuration objects. In normal runtime
|
||||
// this should only happen for configuration entities as we should not be
|
||||
// creating simple configuration objects on the fly.
|
||||
$GLOBALS['config']['config_test.new']['key'] = 'override';
|
||||
$config = \Drupal::config('config_test.new');
|
||||
$this->assertTrue($config->isNew(), 'The configuration object config_test.new is new');
|
||||
$this->assertIdentical($config->get('key'), 'override');
|
||||
$config_raw = \Drupal::configFactory()->getEditable('config_test.new');
|
||||
$this->assertIdentical($config_raw->get('key'), NULL);
|
||||
$config_raw
|
||||
->set('key', 'raw')
|
||||
->set('new_key', 'new_value')
|
||||
->save();
|
||||
// Ensure override is preserved but all other data has been updated
|
||||
// accordingly.
|
||||
$config = \Drupal::config('config_test.new');
|
||||
$this->assertFalse($config->isNew(), 'The configuration object config_test.new is not new');
|
||||
$this->assertIdentical($config->get('key'), 'override');
|
||||
$this->assertIdentical($config->get('new_key'), 'new_value');
|
||||
$raw_data = $config->getRawData();
|
||||
$this->assertIdentical($raw_data['key'], 'raw');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Config;
|
||||
|
||||
use Drupal\Core\Language\Language;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests that language, module and settings.php are applied in the correct
|
||||
* order.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigOverridesPriorityTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('system', 'config', 'config_override_test', 'language');
|
||||
|
||||
public function testOverridePriorities() {
|
||||
$GLOBALS['config_test_run_module_overrides'] = FALSE;
|
||||
|
||||
$non_overridden_mail = 'site@example.com';
|
||||
$language_overridden_mail = 'french@example.com';
|
||||
|
||||
$language_overridden_name = 'French site name';
|
||||
$module_overridden_name = 'ZOMG overridden site name';
|
||||
$non_overridden_name = 'ZOMG this name is on disk mkay';
|
||||
|
||||
$module_overridden_slogan = 'Yay for overrides!';
|
||||
$non_overridden_slogan = 'Yay for defaults!';
|
||||
|
||||
/** @var \Drupal\Core\Config\ConfigFactoryInterface $config_factory */
|
||||
$config_factory = $this->container->get('config.factory');
|
||||
$config_factory
|
||||
->getEditable('system.site')
|
||||
->set('name', $non_overridden_name)
|
||||
->set('slogan', $non_overridden_slogan)
|
||||
->set('mail', $non_overridden_mail)
|
||||
->set('weight_select_max', 50)
|
||||
->save();
|
||||
|
||||
// Ensure that no overrides are applying.
|
||||
$this->assertEqual($non_overridden_name, $config_factory->get('system.site')->get('name'));
|
||||
$this->assertEqual($non_overridden_slogan, $config_factory->get('system.site')->get('slogan'));
|
||||
$this->assertEqual($non_overridden_mail, $config_factory->get('system.site')->get('mail'));
|
||||
$this->assertEqual(50, $config_factory->get('system.site')->get('weight_select_max'));
|
||||
|
||||
// Override using language.
|
||||
$language = new Language(array(
|
||||
'name' => 'French',
|
||||
'id' => 'fr',
|
||||
));
|
||||
\Drupal::languageManager()->setConfigOverrideLanguage($language);
|
||||
\Drupal::languageManager()
|
||||
->getLanguageConfigOverride($language->getId(), 'system.site')
|
||||
->set('name', $language_overridden_name)
|
||||
->set('mail', $language_overridden_mail)
|
||||
->save();
|
||||
|
||||
$this->assertEqual($language_overridden_name, $config_factory->get('system.site')->get('name'));
|
||||
$this->assertEqual($non_overridden_slogan, $config_factory->get('system.site')->get('slogan'));
|
||||
$this->assertEqual($language_overridden_mail, $config_factory->get('system.site')->get('mail'));
|
||||
$this->assertEqual(50, $config_factory->get('system.site')->get('weight_select_max'));
|
||||
|
||||
// Enable module overrides. Do not override system.site:mail to prove that
|
||||
// the language override still applies.
|
||||
$GLOBALS['config_test_run_module_overrides'] = TRUE;
|
||||
$config_factory->reset('system.site');
|
||||
$this->assertEqual($module_overridden_name, $config_factory->get('system.site')->get('name'));
|
||||
$this->assertEqual($module_overridden_slogan, $config_factory->get('system.site')->get('slogan'));
|
||||
$this->assertEqual($language_overridden_mail, $config_factory->get('system.site')->get('mail'));
|
||||
$this->assertEqual(50, $config_factory->get('system.site')->get('weight_select_max'));
|
||||
|
||||
// Configure a global override to simulate overriding using settings.php. Do
|
||||
// not override system.site:mail or system.site:slogan to prove that the
|
||||
// language and module overrides still apply.
|
||||
$GLOBALS['config']['system.site']['name'] = 'Site name global conf override';
|
||||
$config_factory->reset('system.site');
|
||||
$this->assertEqual('Site name global conf override', $config_factory->get('system.site')->get('name'));
|
||||
$this->assertEqual($module_overridden_slogan, $config_factory->get('system.site')->get('slogan'));
|
||||
$this->assertEqual($language_overridden_mail, $config_factory->get('system.site')->get('mail'));
|
||||
$this->assertEqual(50, $config_factory->get('system.site')->get('weight_select_max'));
|
||||
|
||||
$this->assertEqual($non_overridden_name, $config_factory->get('system.site')->getOriginal('name', FALSE));
|
||||
$this->assertEqual($non_overridden_slogan, $config_factory->get('system.site')->getOriginal('slogan', FALSE));
|
||||
$this->assertEqual($non_overridden_mail, $config_factory->get('system.site')->getOriginal('mail', FALSE));
|
||||
$this->assertEqual(50, $config_factory->get('system.site')->getOriginal('weight_select_max', FALSE));
|
||||
|
||||
unset($GLOBALS['config_test_run_module_overrides']);
|
||||
}
|
||||
}
|
||||
636
core/tests/Drupal/KernelTests/Core/Config/ConfigSchemaTest.php
Normal file
636
core/tests/Drupal/KernelTests/Core/Config/ConfigSchemaTest.php
Normal file
|
|
@ -0,0 +1,636 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Config;
|
||||
|
||||
use Drupal\Core\Config\FileStorage;
|
||||
use Drupal\Core\Config\InstallStorage;
|
||||
use Drupal\Core\Config\Schema\ConfigSchemaAlterException;
|
||||
use Drupal\Core\TypedData\Type\IntegerInterface;
|
||||
use Drupal\Core\TypedData\Type\StringInterface;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests schema for configuration objects.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigSchemaTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('system', 'language', 'field', 'image', 'config_test', 'config_schema_test');
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installConfig(array('system', 'image', 'config_schema_test'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the basic metadata retrieval layer.
|
||||
*/
|
||||
function testSchemaMapping() {
|
||||
// Nonexistent configuration key will have Undefined as metadata.
|
||||
$this->assertIdentical(FALSE, \Drupal::service('config.typed')->hasConfigSchema('config_schema_test.no_such_key'));
|
||||
$definition = \Drupal::service('config.typed')->getDefinition('config_schema_test.no_such_key');
|
||||
$expected = array();
|
||||
$expected['label'] = 'Undefined';
|
||||
$expected['class'] = '\Drupal\Core\Config\Schema\Undefined';
|
||||
$expected['type'] = 'undefined';
|
||||
$expected['definition_class'] = '\Drupal\Core\TypedData\DataDefinition';
|
||||
$this->assertEqual($definition, $expected, 'Retrieved the right metadata for nonexistent configuration.');
|
||||
|
||||
// Configuration file without schema will return Undefined as well.
|
||||
$this->assertIdentical(FALSE, \Drupal::service('config.typed')->hasConfigSchema('config_schema_test.noschema'));
|
||||
$definition = \Drupal::service('config.typed')->getDefinition('config_schema_test.noschema');
|
||||
$this->assertEqual($definition, $expected, 'Retrieved the right metadata for configuration with no schema.');
|
||||
|
||||
// Configuration file with only some schema.
|
||||
$this->assertIdentical(TRUE, \Drupal::service('config.typed')->hasConfigSchema('config_schema_test.someschema'));
|
||||
$definition = \Drupal::service('config.typed')->getDefinition('config_schema_test.someschema');
|
||||
$expected = array();
|
||||
$expected['label'] = 'Schema test data';
|
||||
$expected['class'] = '\Drupal\Core\Config\Schema\Mapping';
|
||||
$expected['mapping']['langcode']['type'] = 'string';
|
||||
$expected['mapping']['langcode']['label'] = 'Language code';
|
||||
$expected['mapping']['_core']['type'] = '_core_config_info';
|
||||
$expected['mapping']['testitem'] = array('label' => 'Test item');
|
||||
$expected['mapping']['testlist'] = array('label' => 'Test list');
|
||||
$expected['type'] = 'config_schema_test.someschema';
|
||||
$expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition';
|
||||
$this->assertEqual($definition, $expected, 'Retrieved the right metadata for configuration with only some schema.');
|
||||
|
||||
// Check type detection on elements with undefined types.
|
||||
$config = \Drupal::service('config.typed')->get('config_schema_test.someschema');
|
||||
$definition = $config->get('testitem')->getDataDefinition()->toArray();
|
||||
$expected = array();
|
||||
$expected['label'] = 'Test item';
|
||||
$expected['class'] = '\Drupal\Core\Config\Schema\Undefined';
|
||||
$expected['type'] = 'undefined';
|
||||
$expected['definition_class'] = '\Drupal\Core\TypedData\DataDefinition';
|
||||
$this->assertEqual($definition, $expected, 'Automatic type detected for a scalar is undefined.');
|
||||
$definition = $config->get('testlist')->getDataDefinition()->toArray();
|
||||
$expected = array();
|
||||
$expected['label'] = 'Test list';
|
||||
$expected['class'] = '\Drupal\Core\Config\Schema\Undefined';
|
||||
$expected['type'] = 'undefined';
|
||||
$expected['definition_class'] = '\Drupal\Core\TypedData\DataDefinition';
|
||||
$this->assertEqual($definition, $expected, 'Automatic type detected for a list is undefined.');
|
||||
$definition = $config->get('testnoschema')->getDataDefinition()->toArray();
|
||||
$expected = array();
|
||||
$expected['label'] = 'Undefined';
|
||||
$expected['class'] = '\Drupal\Core\Config\Schema\Undefined';
|
||||
$expected['type'] = 'undefined';
|
||||
$expected['definition_class'] = '\Drupal\Core\TypedData\DataDefinition';
|
||||
$this->assertEqual($definition, $expected, 'Automatic type detected for an undefined integer is undefined.');
|
||||
|
||||
// Simple case, straight metadata.
|
||||
$definition = \Drupal::service('config.typed')->getDefinition('system.maintenance');
|
||||
$expected = array();
|
||||
$expected['label'] = 'Maintenance mode';
|
||||
$expected['class'] = '\Drupal\Core\Config\Schema\Mapping';
|
||||
$expected['mapping']['message'] = array(
|
||||
'label' => 'Message to display when in maintenance mode',
|
||||
'type' => 'text',
|
||||
);
|
||||
$expected['mapping']['langcode'] = array(
|
||||
'label' => 'Language code',
|
||||
'type' => 'string',
|
||||
);
|
||||
$expected['mapping']['_core']['type'] = '_core_config_info';
|
||||
$expected['type'] = 'system.maintenance';
|
||||
$expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition';
|
||||
$this->assertEqual($definition, $expected, 'Retrieved the right metadata for system.maintenance');
|
||||
|
||||
// Mixed schema with ignore elements.
|
||||
$definition = \Drupal::service('config.typed')->getDefinition('config_schema_test.ignore');
|
||||
$expected = array();
|
||||
$expected['label'] = 'Ignore test';
|
||||
$expected['class'] = '\Drupal\Core\Config\Schema\Mapping';
|
||||
$expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition';
|
||||
$expected['mapping']['langcode'] = array(
|
||||
'type' => 'string',
|
||||
'label' => 'Language code',
|
||||
);
|
||||
$expected['mapping']['_core']['type'] = '_core_config_info';
|
||||
$expected['mapping']['label'] = array(
|
||||
'label' => 'Label',
|
||||
'type' => 'label',
|
||||
);
|
||||
$expected['mapping']['irrelevant'] = array(
|
||||
'label' => 'Irrelevant',
|
||||
'type' => 'ignore',
|
||||
);
|
||||
$expected['mapping']['indescribable'] = array(
|
||||
'label' => 'Indescribable',
|
||||
'type' => 'ignore',
|
||||
);
|
||||
$expected['mapping']['weight'] = array(
|
||||
'label' => 'Weight',
|
||||
'type' => 'integer',
|
||||
);
|
||||
$expected['type'] = 'config_schema_test.ignore';
|
||||
|
||||
$this->assertEqual($definition, $expected);
|
||||
|
||||
// The ignore elements themselves.
|
||||
$definition = \Drupal::service('config.typed')->get('config_schema_test.ignore')->get('irrelevant')->getDataDefinition()->toArray();
|
||||
$expected = array();
|
||||
$expected['type'] = 'ignore';
|
||||
$expected['label'] = 'Irrelevant';
|
||||
$expected['class'] = '\Drupal\Core\Config\Schema\Ignore';
|
||||
$expected['definition_class'] = '\Drupal\Core\TypedData\DataDefinition';
|
||||
$this->assertEqual($definition, $expected);
|
||||
$definition = \Drupal::service('config.typed')->get('config_schema_test.ignore')->get('indescribable')->getDataDefinition()->toArray();
|
||||
$expected['label'] = 'Indescribable';
|
||||
$this->assertEqual($definition, $expected);
|
||||
|
||||
// More complex case, generic type. Metadata for image style.
|
||||
$definition = \Drupal::service('config.typed')->getDefinition('image.style.large');
|
||||
$expected = array();
|
||||
$expected['label'] = 'Image style';
|
||||
$expected['class'] = '\Drupal\Core\Config\Schema\Mapping';
|
||||
$expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition';
|
||||
$expected['mapping']['name']['type'] = 'string';
|
||||
$expected['mapping']['uuid']['type'] = 'string';
|
||||
$expected['mapping']['uuid']['label'] = 'UUID';
|
||||
$expected['mapping']['langcode']['type'] = 'string';
|
||||
$expected['mapping']['langcode']['label'] = 'Language code';
|
||||
$expected['mapping']['status']['type'] = 'boolean';
|
||||
$expected['mapping']['status']['label'] = 'Status';
|
||||
$expected['mapping']['dependencies']['type'] = 'config_dependencies';
|
||||
$expected['mapping']['dependencies']['label'] = 'Dependencies';
|
||||
$expected['mapping']['name']['type'] = 'string';
|
||||
$expected['mapping']['label']['type'] = 'label';
|
||||
$expected['mapping']['label']['label'] = 'Label';
|
||||
$expected['mapping']['effects']['type'] = 'sequence';
|
||||
$expected['mapping']['effects']['sequence']['type'] = 'mapping';
|
||||
$expected['mapping']['effects']['sequence']['mapping']['id']['type'] = 'string';
|
||||
$expected['mapping']['effects']['sequence']['mapping']['data']['type'] = 'image.effect.[%parent.id]';
|
||||
$expected['mapping']['effects']['sequence']['mapping']['weight']['type'] = 'integer';
|
||||
$expected['mapping']['effects']['sequence']['mapping']['uuid']['type'] = 'string';
|
||||
$expected['mapping']['third_party_settings']['type'] = 'sequence';
|
||||
$expected['mapping']['third_party_settings']['label'] = 'Third party settings';
|
||||
$expected['mapping']['third_party_settings']['sequence']['type'] = '[%parent.%parent.%type].third_party.[%key]';
|
||||
$expected['mapping']['_core']['type'] = '_core_config_info';
|
||||
$expected['type'] = 'image.style.*';
|
||||
|
||||
$this->assertEqual($definition, $expected);
|
||||
|
||||
// More complex, type based on a complex one.
|
||||
$definition = \Drupal::service('config.typed')->getDefinition('image.effect.image_scale');
|
||||
// This should be the schema for image.effect.image_scale.
|
||||
$expected = array();
|
||||
$expected['label'] = 'Image scale';
|
||||
$expected['class'] = '\Drupal\Core\Config\Schema\Mapping';
|
||||
$expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition';
|
||||
$expected['mapping']['width']['type'] = 'integer';
|
||||
$expected['mapping']['width']['label'] = 'Width';
|
||||
$expected['mapping']['height']['type'] = 'integer';
|
||||
$expected['mapping']['height']['label'] = 'Height';
|
||||
$expected['mapping']['upscale']['type'] = 'boolean';
|
||||
$expected['mapping']['upscale']['label'] = 'Upscale';
|
||||
$expected['type'] = 'image.effect.image_scale';
|
||||
|
||||
|
||||
$this->assertEqual($definition, $expected, 'Retrieved the right metadata for image.effect.image_scale');
|
||||
|
||||
// Most complex case, get metadata for actual configuration element.
|
||||
$effects = \Drupal::service('config.typed')->get('image.style.medium')->get('effects');
|
||||
$definition = $effects->get('bddf0d06-42f9-4c75-a700-a33cafa25ea0')->get('data')->getDataDefinition()->toArray();
|
||||
// This should be the schema for image.effect.image_scale, reuse previous one.
|
||||
$expected['type'] = 'image.effect.image_scale';
|
||||
|
||||
$this->assertEqual($definition, $expected, 'Retrieved the right metadata for the first effect of image.style.medium');
|
||||
|
||||
$a = \Drupal::config('config_test.dynamic.third_party');
|
||||
$test = \Drupal::service('config.typed')->get('config_test.dynamic.third_party')->get('third_party_settings.config_schema_test');
|
||||
$definition = $test->getDataDefinition()->toArray();
|
||||
$expected = array();
|
||||
$expected['type'] = 'config_test.dynamic.*.third_party.config_schema_test';
|
||||
$expected['label'] = 'Mapping';
|
||||
$expected['class'] = '\Drupal\Core\Config\Schema\Mapping';
|
||||
$expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition';
|
||||
$expected['mapping'] = [
|
||||
'integer' => ['type' => 'integer'],
|
||||
'string' => ['type' => 'string'],
|
||||
];
|
||||
$this->assertEqual($definition, $expected, 'Retrieved the right metadata for config_test.dynamic.third_party:third_party_settings.config_schema_test');
|
||||
|
||||
// More complex, several level deep test.
|
||||
$definition = \Drupal::service('config.typed')->getDefinition('config_schema_test.someschema.somemodule.section_one.subsection');
|
||||
// This should be the schema of config_schema_test.someschema.somemodule.*.*.
|
||||
$expected = array();
|
||||
$expected['label'] = 'Schema multiple filesystem marker test';
|
||||
$expected['class'] = '\Drupal\Core\Config\Schema\Mapping';
|
||||
$expected['mapping']['langcode']['type'] = 'string';
|
||||
$expected['mapping']['langcode']['label'] = 'Language code';
|
||||
$expected['mapping']['_core']['type'] = '_core_config_info';
|
||||
$expected['mapping']['testid']['type'] = 'string';
|
||||
$expected['mapping']['testid']['label'] = 'ID';
|
||||
$expected['mapping']['testdescription']['type'] = 'text';
|
||||
$expected['mapping']['testdescription']['label'] = 'Description';
|
||||
$expected['type'] = 'config_schema_test.someschema.somemodule.*.*';
|
||||
$expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition';
|
||||
|
||||
$this->assertEqual($definition, $expected, 'Retrieved the right metadata for config_schema_test.someschema.somemodule.section_one.subsection');
|
||||
|
||||
$definition = \Drupal::service('config.typed')->getDefinition('config_schema_test.someschema.somemodule.section_two.subsection');
|
||||
// The other file should have the same schema.
|
||||
$this->assertEqual($definition, $expected, 'Retrieved the right metadata for config_schema_test.someschema.somemodule.section_two.subsection');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests metadata retrieval with several levels of %parent indirection.
|
||||
*/
|
||||
function testSchemaMappingWithParents() {
|
||||
$config_data = \Drupal::service('config.typed')->get('config_schema_test.someschema.with_parents');
|
||||
|
||||
// Test fetching parent one level up.
|
||||
$entry = $config_data->get('one_level');
|
||||
$definition = $entry->get('testitem')->getDataDefinition()->toArray();
|
||||
$expected = array(
|
||||
'type' => 'config_schema_test.someschema.with_parents.key_1',
|
||||
'label' => 'Test item nested one level',
|
||||
'class' => '\Drupal\Core\TypedData\Plugin\DataType\StringData',
|
||||
'definition_class' => '\Drupal\Core\TypedData\DataDefinition',
|
||||
);
|
||||
$this->assertEqual($definition, $expected);
|
||||
|
||||
// Test fetching parent two levels up.
|
||||
$entry = $config_data->get('two_levels');
|
||||
$definition = $entry->get('wrapper')->get('testitem')->getDataDefinition()->toArray();
|
||||
$expected = array(
|
||||
'type' => 'config_schema_test.someschema.with_parents.key_2',
|
||||
'label' => 'Test item nested two levels',
|
||||
'class' => '\Drupal\Core\TypedData\Plugin\DataType\StringData',
|
||||
'definition_class' => '\Drupal\Core\TypedData\DataDefinition',
|
||||
);
|
||||
$this->assertEqual($definition, $expected);
|
||||
|
||||
// Test fetching parent three levels up.
|
||||
$entry = $config_data->get('three_levels');
|
||||
$definition = $entry->get('wrapper_1')->get('wrapper_2')->get('testitem')->getDataDefinition()->toArray();
|
||||
$expected = array(
|
||||
'type' => 'config_schema_test.someschema.with_parents.key_3',
|
||||
'label' => 'Test item nested three levels',
|
||||
'class' => '\Drupal\Core\TypedData\Plugin\DataType\StringData',
|
||||
'definition_class' => '\Drupal\Core\TypedData\DataDefinition',
|
||||
);
|
||||
$this->assertEqual($definition, $expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests metadata applied to configuration objects.
|
||||
*/
|
||||
function testSchemaData() {
|
||||
// Try a simple property.
|
||||
$meta = \Drupal::service('config.typed')->get('system.site');
|
||||
$property = $meta->get('page')->get('front');
|
||||
$this->assertTrue($property instanceof StringInterface, 'Got the right wrapper fo the page.front property.');
|
||||
$this->assertEqual($property->getValue(), '/user/login', 'Got the right value for page.front data.');
|
||||
$definition = $property->getDataDefinition();
|
||||
$this->assertTrue(empty($definition['translatable']), 'Got the right translatability setting for page.front data.');
|
||||
|
||||
// Check nested array of properties.
|
||||
$list = $meta->get('page')->getElements();
|
||||
$this->assertEqual(count($list), 3, 'Got a list with the right number of properties for site page data');
|
||||
$this->assertTrue(isset($list['front']) && isset($list['403']) && isset($list['404']), 'Got a list with the right properties for site page data.');
|
||||
$this->assertEqual($list['front']->getValue(), '/user/login', 'Got the right value for page.front data from the list.');
|
||||
|
||||
// And test some TypedConfigInterface methods.
|
||||
$properties = $list;
|
||||
$this->assertTrue(count($properties) == 3 && $properties['front'] == $list['front'], 'Got the right properties for site page.');
|
||||
$values = $meta->get('page')->toArray();
|
||||
$this->assertTrue(count($values) == 3 && $values['front'] == '/user/login', 'Got the right property values for site page.');
|
||||
|
||||
// Now let's try something more complex, with nested objects.
|
||||
$wrapper = \Drupal::service('config.typed')->get('image.style.large');
|
||||
$effects = $wrapper->get('effects');
|
||||
$this->assertTrue(count($effects->toArray()) == 1, 'Got an array with effects for image.style.large data');
|
||||
$uuid = key($effects->getValue());
|
||||
$effect = $effects->get($uuid)->getElements();
|
||||
$this->assertTrue(!$effect['data']->isEmpty() && $effect['id']->getValue() == 'image_scale', 'Got data for the image scale effect from metadata.');
|
||||
$this->assertTrue($effect['data']->get('width') instanceof IntegerInterface, 'Got the right type for the scale effect width.');
|
||||
$this->assertEqual($effect['data']->get('width')->getValue(), 480, 'Got the right value for the scale effect width.' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test configuration value data type enforcement using schemas.
|
||||
*/
|
||||
public function testConfigSaveWithSchema() {
|
||||
$untyped_values = array(
|
||||
'string' => 1,
|
||||
'empty_string' => '',
|
||||
'null_string' => NULL,
|
||||
'integer' => '100',
|
||||
'null_integer' => '',
|
||||
'boolean' => 1,
|
||||
// If the config schema doesn't have a type it shouldn't be casted.
|
||||
'no_type' => 1,
|
||||
'mapping' => array(
|
||||
'string' => 1
|
||||
),
|
||||
'float' => '3.14',
|
||||
'null_float' => '',
|
||||
'sequence' => array (1, 0, 1),
|
||||
'sequence_bc' => array(1, 0, 1),
|
||||
// Not in schema and therefore should be left untouched.
|
||||
'not_present_in_schema' => TRUE,
|
||||
// Test a custom type.
|
||||
'config_schema_test_integer' => '1',
|
||||
'config_schema_test_integer_empty_string' => '',
|
||||
);
|
||||
$untyped_to_typed = $untyped_values;
|
||||
|
||||
$typed_values = array(
|
||||
'string' => '1',
|
||||
'empty_string' => '',
|
||||
'null_string' => NULL,
|
||||
'integer' => 100,
|
||||
'null_integer' => NULL,
|
||||
'boolean' => TRUE,
|
||||
'no_type' => 1,
|
||||
'mapping' => array(
|
||||
'string' => '1'
|
||||
),
|
||||
'float' => 3.14,
|
||||
'null_float' => NULL,
|
||||
'sequence' => array (TRUE, FALSE, TRUE),
|
||||
'sequence_bc' => array(TRUE, FALSE, TRUE),
|
||||
'not_present_in_schema' => TRUE,
|
||||
'config_schema_test_integer' => 1,
|
||||
'config_schema_test_integer_empty_string' => NULL,
|
||||
);
|
||||
|
||||
// Save config which has a schema that enforces types.
|
||||
$this->config('config_schema_test.schema_data_types')
|
||||
->setData($untyped_to_typed)
|
||||
->save();
|
||||
$this->assertIdentical($this->config('config_schema_test.schema_data_types')->get(), $typed_values);
|
||||
|
||||
// Save config which does not have a schema that enforces types.
|
||||
$this->config('config_schema_test.no_schema_data_types')
|
||||
->setData($untyped_values)
|
||||
->save();
|
||||
$this->assertIdentical($this->config('config_schema_test.no_schema_data_types')->get(), $untyped_values);
|
||||
|
||||
// Ensure that configuration objects with keys marked as ignored are not
|
||||
// changed when saved. The 'config_schema_test.ignore' will have been saved
|
||||
// during the installation of configuration in the setUp method.
|
||||
$extension_path = __DIR__ . '/../../../../../modules/config/tests/config_schema_test/';
|
||||
$install_storage = new FileStorage($extension_path . InstallStorage::CONFIG_INSTALL_DIRECTORY);
|
||||
$original_data = $install_storage->read('config_schema_test.ignore');
|
||||
$installed_data = $this->config('config_schema_test.ignore')->get();
|
||||
unset($installed_data['_core']);
|
||||
$this->assertIdentical($installed_data, $original_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests fallback to a greedy wildcard.
|
||||
*/
|
||||
function testSchemaFallback() {
|
||||
$definition = \Drupal::service('config.typed')->getDefinition('config_schema_test.wildcard_fallback.something');
|
||||
// This should be the schema of config_schema_test.wildcard_fallback.*.
|
||||
$expected = array();
|
||||
$expected['label'] = 'Schema wildcard fallback test';
|
||||
$expected['class'] = '\Drupal\Core\Config\Schema\Mapping';
|
||||
$expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition';
|
||||
$expected['mapping']['langcode']['type'] = 'string';
|
||||
$expected['mapping']['langcode']['label'] = 'Language code';
|
||||
$expected['mapping']['_core']['type'] = '_core_config_info';
|
||||
$expected['mapping']['testid']['type'] = 'string';
|
||||
$expected['mapping']['testid']['label'] = 'ID';
|
||||
$expected['mapping']['testdescription']['type'] = 'text';
|
||||
$expected['mapping']['testdescription']['label'] = 'Description';
|
||||
$expected['type'] = 'config_schema_test.wildcard_fallback.*';
|
||||
|
||||
$this->assertEqual($definition, $expected, 'Retrieved the right metadata for config_schema_test.wildcard_fallback.something');
|
||||
|
||||
$definition2 = \Drupal::service('config.typed')->getDefinition('config_schema_test.wildcard_fallback.something.something');
|
||||
// This should be the schema of config_schema_test.wildcard_fallback.* as
|
||||
//well.
|
||||
$this->assertIdentical($definition, $definition2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests use of colons in schema type determination.
|
||||
*
|
||||
* @see \Drupal\Core\Config\TypedConfigManager::getFallbackName()
|
||||
*/
|
||||
function testColonsInSchemaTypeDetermination() {
|
||||
$tests = \Drupal::service('config.typed')->get('config_schema_test.plugin_types')->get('tests')->getElements();
|
||||
$definition = $tests[0]->getDataDefinition()->toArray();
|
||||
$this->assertEqual($definition['type'], 'test.plugin_types.boolean');
|
||||
|
||||
$definition = $tests[1]->getDataDefinition()->toArray();
|
||||
$this->assertEqual($definition['type'], 'test.plugin_types.boolean:*');
|
||||
|
||||
$definition = $tests[2]->getDataDefinition()->toArray();
|
||||
$this->assertEqual($definition['type'], 'test.plugin_types.*');
|
||||
|
||||
$definition = $tests[3]->getDataDefinition()->toArray();
|
||||
$this->assertEqual($definition['type'], 'test.plugin_types.*');
|
||||
|
||||
$tests = \Drupal::service('config.typed')->get('config_schema_test.plugin_types')->get('test_with_parents')->getElements();
|
||||
$definition = $tests[0]->get('settings')->getDataDefinition()->toArray();
|
||||
$this->assertEqual($definition['type'], 'test_with_parents.plugin_types.boolean');
|
||||
|
||||
$definition = $tests[1]->get('settings')->getDataDefinition()->toArray();
|
||||
$this->assertEqual($definition['type'], 'test_with_parents.plugin_types.boolean:*');
|
||||
|
||||
$definition = $tests[2]->get('settings')->getDataDefinition()->toArray();
|
||||
$this->assertEqual($definition['type'], 'test_with_parents.plugin_types.*');
|
||||
|
||||
$definition = $tests[3]->get('settings')->getDataDefinition()->toArray();
|
||||
$this->assertEqual($definition['type'], 'test_with_parents.plugin_types.*');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests hook_config_schema_info_alter().
|
||||
*/
|
||||
public function testConfigSchemaInfoAlter() {
|
||||
/** @var \Drupal\Core\Config\TypedConfigManagerInterface $typed_config */
|
||||
$typed_config = \Drupal::service('config.typed');
|
||||
$typed_config->clearCachedDefinitions();
|
||||
|
||||
// Ensure that keys can not be added or removed by
|
||||
// hook_config_schema_info_alter().
|
||||
\Drupal::state()->set('config_schema_test_exception_remove', TRUE);
|
||||
$message = 'Expected ConfigSchemaAlterException thrown.';
|
||||
try {
|
||||
$typed_config->getDefinitions();
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (ConfigSchemaAlterException $e) {
|
||||
$this->pass($message);
|
||||
$this->assertEqual($e->getMessage(), 'Invoking hook_config_schema_info_alter() has removed (config_schema_test.hook) schema definitions');
|
||||
}
|
||||
|
||||
\Drupal::state()->set('config_schema_test_exception_add', TRUE);
|
||||
$message = 'Expected ConfigSchemaAlterException thrown.';
|
||||
try {
|
||||
$typed_config->getDefinitions();
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (ConfigSchemaAlterException $e) {
|
||||
$this->pass($message);
|
||||
$this->assertEqual($e->getMessage(), 'Invoking hook_config_schema_info_alter() has added (config_schema_test.hook_added_defintion) and removed (config_schema_test.hook) schema definitions');
|
||||
}
|
||||
|
||||
\Drupal::state()->set('config_schema_test_exception_remove', FALSE);
|
||||
$message = 'Expected ConfigSchemaAlterException thrown.';
|
||||
try {
|
||||
$typed_config->getDefinitions();
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (ConfigSchemaAlterException $e) {
|
||||
$this->pass($message);
|
||||
$this->assertEqual($e->getMessage(), 'Invoking hook_config_schema_info_alter() has added (config_schema_test.hook_added_defintion) schema definitions');
|
||||
}
|
||||
|
||||
// Tests that hook_config_schema_info_alter() can add additional metadata to
|
||||
// existing configuration schema.
|
||||
\Drupal::state()->set('config_schema_test_exception_add', FALSE);
|
||||
$definitions = $typed_config->getDefinitions();
|
||||
$this->assertEqual($definitions['config_schema_test.hook']['additional_metadata'], 'new schema info');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests saving config when the type is wrapped by a dynamic type.
|
||||
*/
|
||||
public function testConfigSaveWithWrappingSchema() {
|
||||
$untyped_values = [
|
||||
'tests' => [
|
||||
[
|
||||
'wrapper_value' => 'foo',
|
||||
'plugin_id' => 'wrapper:foo',
|
||||
'internal_value' => 100,
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$typed_values = [
|
||||
'tests' => [
|
||||
[
|
||||
'wrapper_value' => 'foo',
|
||||
'plugin_id' => 'wrapper:foo',
|
||||
'internal_value' => '100',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
// Save config which has a schema that enforces types.
|
||||
\Drupal::configFactory()->getEditable('wrapping.config_schema_test.plugin_types')
|
||||
->setData($untyped_values)
|
||||
->save();
|
||||
$this->assertIdentical(\Drupal::config('wrapping.config_schema_test.plugin_types')
|
||||
->get(), $typed_values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests dynamic config schema type with multiple sub-key references.
|
||||
*/
|
||||
public function testConfigSaveWithWrappingSchemaDoubleBrackets() {
|
||||
$untyped_values = [
|
||||
'tests' => [
|
||||
[
|
||||
'wrapper_value' => 'foo',
|
||||
'foo' => 'turtle',
|
||||
'bar' => 'horse',
|
||||
// Converted to a string by 'test.double_brackets.turtle.horse'
|
||||
// schema.
|
||||
'another_key' => '100',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$typed_values = [
|
||||
'tests' => [
|
||||
[
|
||||
'wrapper_value' => 'foo',
|
||||
'foo' => 'turtle',
|
||||
'bar' => 'horse',
|
||||
'another_key' => 100,
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
// Save config which has a schema that enforces types.
|
||||
\Drupal::configFactory()->getEditable('wrapping.config_schema_test.double_brackets')
|
||||
->setData($untyped_values)
|
||||
->save();
|
||||
$this->assertIdentical(\Drupal::config('wrapping.config_schema_test.double_brackets')
|
||||
->get(), $typed_values);
|
||||
|
||||
$tests = \Drupal::service('config.typed')->get('wrapping.config_schema_test.double_brackets')->get('tests')->getElements();
|
||||
$definition = $tests[0]->getDataDefinition()->toArray();
|
||||
$this->assertEqual($definition['type'], 'wrapping.test.double_brackets.*||test.double_brackets.turtle.horse');
|
||||
|
||||
$untyped_values = [
|
||||
'tests' => [
|
||||
[
|
||||
'wrapper_value' => 'foo',
|
||||
'foo' => 'cat',
|
||||
'bar' => 'dog',
|
||||
// Converted to a string by 'test.double_brackets.cat.dog' schema.
|
||||
'another_key' => 100,
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$typed_values = [
|
||||
'tests' => [
|
||||
[
|
||||
'wrapper_value' => 'foo',
|
||||
'foo' => 'cat',
|
||||
'bar' => 'dog',
|
||||
'another_key' => '100',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
// Save config which has a schema that enforces types.
|
||||
\Drupal::configFactory()->getEditable('wrapping.config_schema_test.double_brackets')
|
||||
->setData($untyped_values)
|
||||
->save();
|
||||
$this->assertIdentical(\Drupal::config('wrapping.config_schema_test.double_brackets')
|
||||
->get(), $typed_values);
|
||||
|
||||
$tests = \Drupal::service('config.typed')->get('wrapping.config_schema_test.double_brackets')->get('tests')->getElements();
|
||||
$definition = $tests[0]->getDataDefinition()->toArray();
|
||||
$this->assertEqual($definition['type'], 'wrapping.test.double_brackets.*||test.double_brackets.cat.dog');
|
||||
|
||||
// Combine everything in a single save.
|
||||
$typed_values = [
|
||||
'tests' => [
|
||||
[
|
||||
'wrapper_value' => 'foo',
|
||||
'foo' => 'cat',
|
||||
'bar' => 'dog',
|
||||
'another_key' => 100,
|
||||
],
|
||||
[
|
||||
'wrapper_value' => 'foo',
|
||||
'foo' => 'turtle',
|
||||
'bar' => 'horse',
|
||||
'another_key' => '100',
|
||||
],
|
||||
],
|
||||
];
|
||||
\Drupal::configFactory()->getEditable('wrapping.config_schema_test.double_brackets')
|
||||
->setData($typed_values)
|
||||
->save();
|
||||
$tests = \Drupal::service('config.typed')->get('wrapping.config_schema_test.double_brackets')->get('tests')->getElements();
|
||||
$definition = $tests[0]->getDataDefinition()->toArray();
|
||||
$this->assertEqual($definition['type'], 'wrapping.test.double_brackets.*||test.double_brackets.cat.dog');
|
||||
$definition = $tests[1]->getDataDefinition()->toArray();
|
||||
$this->assertEqual($definition['type'], 'wrapping.test.double_brackets.*||test.double_brackets.turtle.horse');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Config;
|
||||
|
||||
use Drupal\Core\Config\StorageComparer;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests config snapshot creation and updating.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class ConfigSnapshotTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('config_test', 'system');
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
// Update the config snapshot. This allows the parent::setUp() to write
|
||||
// configuration files.
|
||||
\Drupal::service('config.manager')->createSnapshot(\Drupal::service('config.storage'), \Drupal::service('config.storage.snapshot'));
|
||||
$this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.sync'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests config snapshot creation and updating.
|
||||
*/
|
||||
function testSnapshot() {
|
||||
$active = $this->container->get('config.storage');
|
||||
$sync = $this->container->get('config.storage.sync');
|
||||
$snapshot = $this->container->get('config.storage.snapshot');
|
||||
$config_manager = $this->container->get('config.manager');
|
||||
$config_name = 'config_test.system';
|
||||
$config_key = 'foo';
|
||||
$new_data = 'foobar';
|
||||
|
||||
$active_snapshot_comparer = new StorageComparer($active, $snapshot, $config_manager);
|
||||
$sync_snapshot_comparer = new StorageComparer($sync, $snapshot, $config_manager);
|
||||
|
||||
// Verify that we have an initial snapshot that matches the active
|
||||
// configuration. This has to be true as no config should be installed.
|
||||
$this->assertFalse($active_snapshot_comparer->createChangelist()->hasChanges());
|
||||
|
||||
// Install the default config.
|
||||
$this->installConfig(array('config_test'));
|
||||
// Although we have imported config this has not affected the snapshot.
|
||||
$this->assertTrue($active_snapshot_comparer->reset()->hasChanges());
|
||||
|
||||
// Update the config snapshot.
|
||||
\Drupal::service('config.manager')->createSnapshot($active, $snapshot);
|
||||
|
||||
// The snapshot and active config should now contain the same config
|
||||
// objects.
|
||||
$this->assertFalse($active_snapshot_comparer->reset()->hasChanges());
|
||||
|
||||
// Change a configuration value in sync.
|
||||
$sync_data = $this->config($config_name)->get();
|
||||
$sync_data[$config_key] = $new_data;
|
||||
$sync->write($config_name, $sync_data);
|
||||
|
||||
// Verify that active and snapshot match, and that sync doesn't match
|
||||
// active.
|
||||
$this->assertFalse($active_snapshot_comparer->reset()->hasChanges());
|
||||
$this->assertTrue($sync_snapshot_comparer->createChangelist()->hasChanges());
|
||||
|
||||
// Import changed data from sync to active.
|
||||
$this->configImporter()->import();
|
||||
|
||||
// Verify changed config was properly imported.
|
||||
\Drupal::configFactory()->reset($config_name);
|
||||
$this->assertIdentical($this->config($config_name)->get($config_key), $new_data);
|
||||
|
||||
// Verify that a new snapshot was created which and that it matches
|
||||
// the active config.
|
||||
$this->assertFalse($active_snapshot_comparer->reset()->hasChanges());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Config;
|
||||
|
||||
use Drupal\config\Tests\SchemaCheckTestTrait;
|
||||
use Drupal\config_test\TestInstallStorage;
|
||||
use Drupal\Core\Config\InstallStorage;
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
/**
|
||||
* Tests that default configuration provided by all modules matches schema.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class DefaultConfigTest extends KernelTestBase {
|
||||
|
||||
use SchemaCheckTestTrait;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('system', 'config_test');
|
||||
|
||||
/**
|
||||
* Themes which provide default configuration and need enabling.
|
||||
*
|
||||
* If a theme provides default configuration but does not have a schema
|
||||
* because it can rely on schemas added by system_config_schema_info_alter()
|
||||
* then this test needs to enable it.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $themes = ['seven'];
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
\Drupal::service('theme_handler')->install($this->themes);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function register(ContainerBuilder $container) {
|
||||
parent::register($container);
|
||||
$container->register('default_config_test.schema_storage')
|
||||
->setClass('\Drupal\config_test\TestInstallStorage')
|
||||
->addArgument(InstallStorage::CONFIG_SCHEMA_DIRECTORY);
|
||||
|
||||
$definition = $container->getDefinition('config.typed');
|
||||
$definition->replaceArgument(1, new Reference('default_config_test.schema_storage'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests default configuration data type.
|
||||
*/
|
||||
public function testDefaultConfig() {
|
||||
$typed_config = \Drupal::service('config.typed');
|
||||
// Create a configuration storage with access to default configuration in
|
||||
// every module, profile and theme.
|
||||
$default_config_storage = new TestInstallStorage();
|
||||
|
||||
foreach ($default_config_storage->listAll() as $config_name) {
|
||||
// Skip files provided by the config_schema_test module since that module
|
||||
// is explicitly for testing schema.
|
||||
if (strpos($config_name, 'config_schema_test') === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$data = $default_config_storage->read($config_name);
|
||||
$this->assertConfigSchema($typed_config, $config_name, $data);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Config;
|
||||
|
||||
use Drupal\Core\Config\Schema\SchemaCheckTrait;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
|
||||
/**
|
||||
* Tests the functionality of SchemaCheckTrait.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class SchemaCheckTraitTest extends KernelTestBase {
|
||||
|
||||
use SchemaCheckTrait;
|
||||
|
||||
/**
|
||||
* The typed config manager.
|
||||
*
|
||||
* @var \Drupal\Core\Config\TypedConfigManagerInterface
|
||||
*/
|
||||
protected $typedConfig;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('config_test', 'config_schema_test');
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installConfig(array('config_test', 'config_schema_test'));
|
||||
$this->typedConfig = \Drupal::service('config.typed');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests \Drupal\Core\Config\Schema\SchemaCheckTrait.
|
||||
*/
|
||||
public function testTrait() {
|
||||
// Test a non existing schema.
|
||||
$ret = $this->checkConfigSchema($this->typedConfig, 'config_schema_test.noschema', $this->config('config_schema_test.noschema')->get());
|
||||
$this->assertIdentical($ret, FALSE);
|
||||
|
||||
// Test an existing schema with valid data.
|
||||
$config_data = $this->config('config_test.types')->get();
|
||||
$ret = $this->checkConfigSchema($this->typedConfig, 'config_test.types', $config_data);
|
||||
$this->assertIdentical($ret, TRUE);
|
||||
|
||||
// Add a new key, a new array and overwrite boolean with array to test the
|
||||
// error messages.
|
||||
$config_data = array('new_key' => 'new_value', 'new_array' => array()) + $config_data;
|
||||
$config_data['boolean'] = array();
|
||||
$ret = $this->checkConfigSchema($this->typedConfig, 'config_test.types', $config_data);
|
||||
$expected = array(
|
||||
'config_test.types:new_key' => 'missing schema',
|
||||
'config_test.types:new_array' => 'missing schema',
|
||||
'config_test.types:boolean' => 'non-scalar value but not defined as an array (such as mapping or sequence)',
|
||||
);
|
||||
$this->assertEqual($ret, $expected);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Config;
|
||||
|
||||
use Drupal\Core\Config\Schema\SchemaIncompleteException;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests the functionality of ConfigSchemaChecker in KernelTestBase tests.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class SchemaConfigListenerTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = array('config_test');
|
||||
|
||||
/**
|
||||
* Tests \Drupal\Core\Config\Testing\ConfigSchemaChecker.
|
||||
*/
|
||||
public function testConfigSchemaChecker() {
|
||||
// Test a non-existing schema.
|
||||
$message = 'Expected SchemaIncompleteException thrown';
|
||||
try {
|
||||
$this->config('config_schema_test.schemaless')->set('foo', 'bar')->save();
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (SchemaIncompleteException $e) {
|
||||
$this->pass($message);
|
||||
$this->assertEqual('No schema for config_schema_test.schemaless', $e->getMessage());
|
||||
}
|
||||
|
||||
// Test a valid schema.
|
||||
$message = 'Unexpected SchemaIncompleteException thrown';
|
||||
$config = $this->config('config_test.types')->set('int', 10);
|
||||
try {
|
||||
$config->save();
|
||||
$this->pass($message);
|
||||
}
|
||||
catch (SchemaIncompleteException $e) {
|
||||
$this->fail($message);
|
||||
}
|
||||
|
||||
// Test an invalid schema.
|
||||
$message = 'Expected SchemaIncompleteException thrown';
|
||||
$config = $this->config('config_test.types')
|
||||
->set('foo', 'bar')
|
||||
->set('array', 1);
|
||||
try {
|
||||
$config->save();
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (SchemaIncompleteException $e) {
|
||||
$this->pass($message);
|
||||
$this->assertEqual('Schema errors for config_test.types with the following errors: config_test.types:foo missing schema, config_test.types:array variable type is integer but applied schema class is Drupal\Core\Config\Schema\Sequence', $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Config\Storage;
|
||||
|
||||
use Drupal\Core\Config\FileStorage;
|
||||
use Drupal\Core\Config\CachedStorage;
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\Core\StreamWrapper\PublicStream;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
/**
|
||||
* Tests CachedStorage operations.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class CachedStorageTest extends ConfigStorageTestBase {
|
||||
|
||||
/**
|
||||
* The cache backend the cached storage is using.
|
||||
*
|
||||
* @var \Drupal\Core\Cache\CacheBackendInterface
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* The file storage the cached storage is using.
|
||||
*
|
||||
* @var \Drupal\Core\Config\FileStorage
|
||||
*/
|
||||
protected $fileStorage;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
// Create a directory.
|
||||
$dir = PublicStream::basePath() . '/config';
|
||||
$this->fileStorage = new FileStorage($dir);
|
||||
$this->storage = new CachedStorage($this->fileStorage, \Drupal::service('cache.config'));
|
||||
$this->cache = \Drupal::service('cache_factory')->get('config');
|
||||
// ::listAll() verifications require other configuration data to exist.
|
||||
$this->storage->write('system.performance', array());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function testInvalidStorage() {
|
||||
// No-op as this test does not make sense.
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function read($name) {
|
||||
$data = $this->cache->get($name);
|
||||
// Cache misses fall through to the underlying storage.
|
||||
return $data ? $data->data : $this->fileStorage->read($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function insert($name, $data) {
|
||||
$this->fileStorage->write($name, $data);
|
||||
$this->cache->set($name, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function update($name, $data) {
|
||||
$this->fileStorage->write($name, $data);
|
||||
$this->cache->set($name, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function delete($name) {
|
||||
$this->cache->delete($name);
|
||||
unlink($this->fileStorage->getFilePath($name));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function containerBuild(ContainerBuilder $container) {
|
||||
parent::containerBuild($container);
|
||||
// Use the regular database cache backend to aid testing.
|
||||
$container->register('cache_factory', 'Drupal\Core\Cache\DatabaseBackendFactory')
|
||||
->addArgument(new Reference('database'))
|
||||
->addArgument(new Reference('cache_tags.invalidator.checksum'));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,266 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Config\Storage;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Base class for testing storage operations.
|
||||
*
|
||||
* All configuration storages are expected to behave identically in
|
||||
* terms of reading, writing, listing, deleting, as well as error handling.
|
||||
*
|
||||
* Therefore, storage tests use an uncommon test case class structure;
|
||||
* the base class defines the test method(s) to execute, which are identical
|
||||
* for all storages. The storage specific test case classes supply the
|
||||
* necessary helper methods to interact with the raw/native storage
|
||||
* directly.
|
||||
*/
|
||||
abstract class ConfigStorageTestBase extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Config\StorageInterface
|
||||
*/
|
||||
protected $storage;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Config\StorageInterface
|
||||
*/
|
||||
protected $invalidStorage;
|
||||
|
||||
/**
|
||||
* Tests storage CRUD operations.
|
||||
*
|
||||
* @todo Coverage: Trigger PDOExceptions / Database exceptions.
|
||||
*/
|
||||
function testCRUD() {
|
||||
$name = 'config_test.storage';
|
||||
|
||||
// Checking whether a non-existing name exists returns FALSE.
|
||||
$this->assertIdentical($this->storage->exists($name), FALSE);
|
||||
|
||||
// Reading a non-existing name returns FALSE.
|
||||
$data = $this->storage->read($name);
|
||||
$this->assertIdentical($data, FALSE);
|
||||
|
||||
// Writing data returns TRUE and the data has been written.
|
||||
$data = array('foo' => 'bar');
|
||||
$result = $this->storage->write($name, $data);
|
||||
$this->assertIdentical($result, TRUE);
|
||||
|
||||
$raw_data = $this->read($name);
|
||||
$this->assertIdentical($raw_data, $data);
|
||||
|
||||
// Checking whether an existing name exists returns TRUE.
|
||||
$this->assertIdentical($this->storage->exists($name), TRUE);
|
||||
|
||||
// Writing the identical data again still returns TRUE.
|
||||
$result = $this->storage->write($name, $data);
|
||||
$this->assertIdentical($result, TRUE);
|
||||
|
||||
// Listing all names returns all.
|
||||
$names = $this->storage->listAll();
|
||||
$this->assertTrue(in_array('system.performance', $names));
|
||||
$this->assertTrue(in_array($name, $names));
|
||||
|
||||
// Listing all names with prefix returns names with that prefix only.
|
||||
$names = $this->storage->listAll('config_test.');
|
||||
$this->assertFalse(in_array('system.performance', $names));
|
||||
$this->assertTrue(in_array($name, $names));
|
||||
|
||||
// Rename the configuration storage object.
|
||||
$new_name = 'config_test.storage_rename';
|
||||
$this->storage->rename($name, $new_name);
|
||||
$raw_data = $this->read($new_name);
|
||||
$this->assertIdentical($raw_data, $data);
|
||||
// Rename it back so further tests work.
|
||||
$this->storage->rename($new_name, $name);
|
||||
|
||||
// Deleting an existing name returns TRUE.
|
||||
$result = $this->storage->delete($name);
|
||||
$this->assertIdentical($result, TRUE);
|
||||
|
||||
// Deleting a non-existing name returns FALSE.
|
||||
$result = $this->storage->delete($name);
|
||||
$this->assertIdentical($result, FALSE);
|
||||
|
||||
// Deleting all names with prefix deletes the appropriate data and returns
|
||||
// TRUE.
|
||||
$files = array(
|
||||
'config_test.test.biff',
|
||||
'config_test.test.bang',
|
||||
'config_test.test.pow',
|
||||
);
|
||||
foreach ($files as $name) {
|
||||
$this->storage->write($name, $data);
|
||||
}
|
||||
|
||||
$result = $this->storage->deleteAll('config_test.');
|
||||
$names = $this->storage->listAll('config_test.');
|
||||
$this->assertIdentical($result, TRUE);
|
||||
$this->assertIdentical($names, array());
|
||||
|
||||
// Test renaming an object that does not exist throws an exception.
|
||||
try {
|
||||
$this->storage->rename('config_test.storage_does_not_exist', 'config_test.storage_does_not_exist_rename');
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$class = get_class($e);
|
||||
$this->pass($class . ' thrown upon renaming a nonexistent storage bin.');
|
||||
}
|
||||
|
||||
// Test renaming to an object that already exists throws an exception.
|
||||
try {
|
||||
$this->storage->rename('system.cron', 'system.performance');
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$class = get_class($e);
|
||||
$this->pass($class . ' thrown upon renaming a nonexistent storage bin.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests an invalid storage.
|
||||
*/
|
||||
public function testInvalidStorage() {
|
||||
$name = 'config_test.storage';
|
||||
|
||||
// Write something to the valid storage to prove that the storages do not
|
||||
// pollute one another.
|
||||
$data = array('foo' => 'bar');
|
||||
$result = $this->storage->write($name, $data);
|
||||
$this->assertIdentical($result, TRUE);
|
||||
|
||||
$raw_data = $this->read($name);
|
||||
$this->assertIdentical($raw_data, $data);
|
||||
|
||||
// Reading from a non-existing storage bin returns FALSE.
|
||||
$result = $this->invalidStorage->read($name);
|
||||
$this->assertIdentical($result, FALSE);
|
||||
|
||||
// Deleting from a non-existing storage bin throws an exception.
|
||||
try {
|
||||
$this->invalidStorage->delete($name);
|
||||
$this->fail('Exception not thrown upon deleting from a non-existing storage bin.');
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$class = get_class($e);
|
||||
$this->pass($class . ' thrown upon deleting from a non-existing storage bin.');
|
||||
}
|
||||
|
||||
// Listing on a non-existing storage bin returns an empty array.
|
||||
$result = $this->invalidStorage->listAll();
|
||||
$this->assertIdentical($result, array());
|
||||
// Writing to a non-existing storage bin creates the bin.
|
||||
$this->invalidStorage->write($name, array('foo' => 'bar'));
|
||||
$result = $this->invalidStorage->read($name);
|
||||
$this->assertIdentical($result, array('foo' => 'bar'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests storage writing and reading data preserving data type.
|
||||
*/
|
||||
function testDataTypes() {
|
||||
$name = 'config_test.types';
|
||||
$data = array(
|
||||
'array' => array(),
|
||||
'boolean' => TRUE,
|
||||
'exp' => 1.2e+34,
|
||||
'float' => 3.14159,
|
||||
'hex' => 0xC,
|
||||
'int' => 99,
|
||||
'octal' => 0775,
|
||||
'string' => 'string',
|
||||
'string_int' => '1',
|
||||
);
|
||||
|
||||
$result = $this->storage->write($name, $data);
|
||||
$this->assertIdentical($result, TRUE);
|
||||
|
||||
$read_data = $this->storage->read($name);
|
||||
$this->assertIdentical($read_data, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the storage supports collections.
|
||||
*/
|
||||
public function testCollection() {
|
||||
$name = 'config_test.storage';
|
||||
$data = array('foo' => 'bar');
|
||||
$result = $this->storage->write($name, $data);
|
||||
$this->assertIdentical($result, TRUE);
|
||||
$this->assertIdentical($data, $this->storage->read($name));
|
||||
|
||||
// Create configuration in a new collection.
|
||||
$new_storage = $this->storage->createCollection('collection.sub.new');
|
||||
$this->assertFalse($new_storage->exists($name));
|
||||
$this->assertEqual(array(), $new_storage->listAll());
|
||||
$new_storage->write($name, $data);
|
||||
$this->assertIdentical($result, TRUE);
|
||||
$this->assertIdentical($data, $new_storage->read($name));
|
||||
$this->assertEqual(array($name), $new_storage->listAll());
|
||||
$this->assertTrue($new_storage->exists($name));
|
||||
$new_data = array('foo' => 'baz');
|
||||
$new_storage->write($name, $new_data);
|
||||
$this->assertIdentical($result, TRUE);
|
||||
$this->assertIdentical($new_data, $new_storage->read($name));
|
||||
|
||||
// Create configuration in another collection.
|
||||
$another_storage = $this->storage->createCollection('collection.sub.another');
|
||||
$this->assertFalse($another_storage->exists($name));
|
||||
$this->assertEqual(array(), $another_storage->listAll());
|
||||
$another_storage->write($name, $new_data);
|
||||
$this->assertIdentical($result, TRUE);
|
||||
$this->assertIdentical($new_data, $another_storage->read($name));
|
||||
$this->assertEqual(array($name), $another_storage->listAll());
|
||||
$this->assertTrue($another_storage->exists($name));
|
||||
|
||||
// Create configuration in yet another collection.
|
||||
$alt_storage = $this->storage->createCollection('alternate');
|
||||
$alt_storage->write($name, $new_data);
|
||||
$this->assertIdentical($result, TRUE);
|
||||
$this->assertIdentical($new_data, $alt_storage->read($name));
|
||||
|
||||
// Switch back to the collection-less mode and check the data still exists
|
||||
// add has not been touched.
|
||||
$this->assertIdentical($data, $this->storage->read($name));
|
||||
|
||||
// Check that the getAllCollectionNames() method works.
|
||||
$this->assertIdentical(array('alternate', 'collection.sub.another', 'collection.sub.new'), $this->storage->getAllCollectionNames());
|
||||
|
||||
// Check that the collections are removed when they are empty.
|
||||
$alt_storage->delete($name);
|
||||
$this->assertIdentical(array('collection.sub.another', 'collection.sub.new'), $this->storage->getAllCollectionNames());
|
||||
|
||||
// Create configuration in collection called 'collection'. This ensures that
|
||||
// FileStorage's collection storage works regardless of its use of
|
||||
// subdirectories.
|
||||
$parent_storage = $this->storage->createCollection('collection');
|
||||
$this->assertFalse($parent_storage->exists($name));
|
||||
$this->assertEqual(array(), $parent_storage->listAll());
|
||||
$parent_storage->write($name, $new_data);
|
||||
$this->assertIdentical($result, TRUE);
|
||||
$this->assertIdentical($new_data, $parent_storage->read($name));
|
||||
$this->assertEqual(array($name), $parent_storage->listAll());
|
||||
$this->assertTrue($parent_storage->exists($name));
|
||||
$this->assertIdentical(array('collection', 'collection.sub.another', 'collection.sub.new'), $this->storage->getAllCollectionNames());
|
||||
$parent_storage->deleteAll();
|
||||
$this->assertIdentical(array('collection.sub.another', 'collection.sub.new'), $this->storage->getAllCollectionNames());
|
||||
|
||||
// Check that the having an empty collection-less storage does not break
|
||||
// anything. Before deleting check that the previous delete did not affect
|
||||
// data in another collection.
|
||||
$this->assertIdentical($data, $this->storage->read($name));
|
||||
$this->storage->delete($name);
|
||||
$this->assertIdentical(array('collection.sub.another', 'collection.sub.new'), $this->storage->getAllCollectionNames());
|
||||
}
|
||||
|
||||
abstract protected function read($name);
|
||||
|
||||
abstract protected function insert($name, $data);
|
||||
|
||||
abstract protected function update($name, $data);
|
||||
|
||||
abstract protected function delete($name);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Config\Storage;
|
||||
|
||||
use Drupal\Core\Config\DatabaseStorage;
|
||||
|
||||
/**
|
||||
* Tests DatabaseStorage operations.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class DatabaseStorageTest extends ConfigStorageTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->storage = new DatabaseStorage($this->container->get('database'), 'config');
|
||||
$this->invalidStorage = new DatabaseStorage($this->container->get('database'), 'invalid');
|
||||
|
||||
// ::listAll() verifications require other configuration data to exist.
|
||||
$this->storage->write('system.performance', array());
|
||||
}
|
||||
|
||||
protected function read($name) {
|
||||
$data = db_query('SELECT data FROM {config} WHERE name = :name', array(':name' => $name))->fetchField();
|
||||
return unserialize($data);
|
||||
}
|
||||
|
||||
protected function insert($name, $data) {
|
||||
db_insert('config')->fields(array('name' => $name, 'data' => $data))->execute();
|
||||
}
|
||||
|
||||
protected function update($name, $data) {
|
||||
db_update('config')->fields(array('data' => $data))->condition('name', $name)->execute();
|
||||
}
|
||||
|
||||
protected function delete($name) {
|
||||
db_delete('config')->condition('name', $name)->execute();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Config\Storage;
|
||||
|
||||
use Drupal\Component\Serialization\Yaml;
|
||||
use Drupal\Core\Config\FileStorage;
|
||||
use Drupal\Core\Config\UnsupportedDataTypeConfigException;
|
||||
use Drupal\Core\StreamWrapper\PublicStream;
|
||||
|
||||
/**
|
||||
* Tests FileStorage operations.
|
||||
*
|
||||
* @group config
|
||||
*/
|
||||
class FileStorageTest extends ConfigStorageTestBase {
|
||||
|
||||
/**
|
||||
* A directory to store configuration in.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $directory;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
// Create a directory.
|
||||
$this->directory = PublicStream::basePath() . '/config';
|
||||
$this->storage = new FileStorage($this->directory);
|
||||
$this->invalidStorage = new FileStorage($this->directory . '/nonexisting');
|
||||
|
||||
// FileStorage::listAll() requires other configuration data to exist.
|
||||
$this->storage->write('system.performance', $this->config('system.performance')->get());
|
||||
$this->storage->write('core.extension', array('module' => array()));
|
||||
}
|
||||
|
||||
protected function read($name) {
|
||||
$data = file_get_contents($this->storage->getFilePath($name));
|
||||
return Yaml::decode($data);
|
||||
}
|
||||
|
||||
protected function insert($name, $data) {
|
||||
file_put_contents($this->storage->getFilePath($name), $data);
|
||||
}
|
||||
|
||||
protected function update($name, $data) {
|
||||
file_put_contents($this->storage->getFilePath($name), $data);
|
||||
}
|
||||
|
||||
protected function delete($name) {
|
||||
unlink($this->storage->getFilePath($name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the FileStorage::listAll method with a relative and absolute path.
|
||||
*/
|
||||
public function testlistAll() {
|
||||
$expected_files = array(
|
||||
'core.extension',
|
||||
'system.performance',
|
||||
);
|
||||
|
||||
$config_files = $this->storage->listAll();
|
||||
$this->assertIdentical($config_files, $expected_files, 'Relative path, two config files found.');
|
||||
|
||||
// @todo https://www.drupal.org/node/2666954 FileStorage::listAll() is
|
||||
// case-sensitive. However, \Drupal\Core\Config\DatabaseStorage::listAll()
|
||||
// is case-insensitive.
|
||||
$this->assertIdentical(['system.performance'], $this->storage->listAll('system'), 'The FileStorage::listAll() with prefix works.');
|
||||
$this->assertIdentical([], $this->storage->listAll('System'), 'The FileStorage::listAll() is case sensitive.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test UnsupportedDataTypeConfigException displays path of
|
||||
* erroneous file during read.
|
||||
*/
|
||||
public function testReadUnsupportedDataTypeConfigException() {
|
||||
file_put_contents($this->storage->getFilePath('core.extension'), PHP_EOL . 'foo : [bar}', FILE_APPEND);
|
||||
try {
|
||||
$config_parsed = $this->storage->read('core.extension');
|
||||
}
|
||||
catch (UnsupportedDataTypeConfigException $e) {
|
||||
$this->pass('Exception thrown when trying to read a field containing invalid data type.');
|
||||
$this->assertTrue((strpos($e->getMessage(), $this->storage->getFilePath('core.extension')) !== FALSE), 'Erroneous file path is displayed.');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
150
core/tests/Drupal/KernelTests/Core/Database/AlterTest.php
Normal file
150
core/tests/Drupal/KernelTests/Core/Database/AlterTest.php
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Database;
|
||||
|
||||
/**
|
||||
* Tests the hook_query_alter capabilities of the Select builder.
|
||||
*
|
||||
* @group Database
|
||||
* @see database_test_query_alter()
|
||||
*/
|
||||
class AlterTest extends DatabaseTestBase {
|
||||
|
||||
/**
|
||||
* Tests that we can do basic alters.
|
||||
*/
|
||||
function testSimpleAlter() {
|
||||
$query = db_select('test');
|
||||
$query->addField('test', 'name');
|
||||
$query->addField('test', 'age', 'age');
|
||||
$query->addTag('database_test_alter_add_range');
|
||||
|
||||
$result = $query->execute()->fetchAll();
|
||||
|
||||
$this->assertEqual(count($result), 2, 'Returned the correct number of rows.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that we can alter the joins on a query.
|
||||
*/
|
||||
function testAlterWithJoin() {
|
||||
$query = db_select('test_task');
|
||||
$tid_field = $query->addField('test_task', 'tid');
|
||||
$task_field = $query->addField('test_task', 'task');
|
||||
$query->orderBy($task_field);
|
||||
$query->addTag('database_test_alter_add_join');
|
||||
|
||||
$result = $query->execute();
|
||||
|
||||
$records = $result->fetchAll();
|
||||
|
||||
$this->assertEqual(count($records), 2, 'Returned the correct number of rows.');
|
||||
|
||||
$this->assertEqual($records[0]->name, 'George', 'Correct data retrieved.');
|
||||
$this->assertEqual($records[0]->$tid_field, 4, 'Correct data retrieved.');
|
||||
$this->assertEqual($records[0]->$task_field, 'sing', 'Correct data retrieved.');
|
||||
$this->assertEqual($records[1]->name, 'George', 'Correct data retrieved.');
|
||||
$this->assertEqual($records[1]->$tid_field, 5, 'Correct data retrieved.');
|
||||
$this->assertEqual($records[1]->$task_field, 'sleep', 'Correct data retrieved.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that we can alter a query's conditionals.
|
||||
*/
|
||||
function testAlterChangeConditional() {
|
||||
$query = db_select('test_task');
|
||||
$tid_field = $query->addField('test_task', 'tid');
|
||||
$pid_field = $query->addField('test_task', 'pid');
|
||||
$task_field = $query->addField('test_task', 'task');
|
||||
$people_alias = $query->join('test', 'people', "test_task.pid = people.id");
|
||||
$name_field = $query->addField($people_alias, 'name', 'name');
|
||||
$query->condition('test_task.tid', '1');
|
||||
$query->orderBy($tid_field);
|
||||
$query->addTag('database_test_alter_change_conditional');
|
||||
|
||||
$result = $query->execute();
|
||||
|
||||
$records = $result->fetchAll();
|
||||
|
||||
$this->assertEqual(count($records), 1, 'Returned the correct number of rows.');
|
||||
$this->assertEqual($records[0]->$name_field, 'John', 'Correct data retrieved.');
|
||||
$this->assertEqual($records[0]->$tid_field, 2, 'Correct data retrieved.');
|
||||
$this->assertEqual($records[0]->$pid_field, 1, 'Correct data retrieved.');
|
||||
$this->assertEqual($records[0]->$task_field, 'sleep', 'Correct data retrieved.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that we can alter the fields of a query.
|
||||
*/
|
||||
function testAlterChangeFields() {
|
||||
$query = db_select('test');
|
||||
$name_field = $query->addField('test', 'name');
|
||||
$age_field = $query->addField('test', 'age', 'age');
|
||||
$query->orderBy('name');
|
||||
$query->addTag('database_test_alter_change_fields');
|
||||
|
||||
$record = $query->execute()->fetch();
|
||||
$this->assertEqual($record->$name_field, 'George', 'Correct data retrieved.');
|
||||
$this->assertFalse(isset($record->$age_field), 'Age field not found, as intended.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that we can alter expressions in the query.
|
||||
*/
|
||||
function testAlterExpression() {
|
||||
$query = db_select('test');
|
||||
$name_field = $query->addField('test', 'name');
|
||||
$age_field = $query->addExpression("age*2", 'double_age');
|
||||
$query->condition('age', 27);
|
||||
$query->addTag('database_test_alter_change_expressions');
|
||||
$result = $query->execute();
|
||||
|
||||
// Ensure that we got the right record.
|
||||
$record = $result->fetch();
|
||||
|
||||
$this->assertEqual($record->$name_field, 'George', 'Fetched name is correct.');
|
||||
$this->assertEqual($record->$age_field, 27*3, 'Fetched age expression is correct.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that we can remove a range() value from a query.
|
||||
*
|
||||
* This also tests hook_query_TAG_alter().
|
||||
*/
|
||||
function testAlterRemoveRange() {
|
||||
$query = db_select('test');
|
||||
$query->addField('test', 'name');
|
||||
$query->addField('test', 'age', 'age');
|
||||
$query->range(0, 2);
|
||||
$query->addTag('database_test_alter_remove_range');
|
||||
|
||||
$num_records = count($query->execute()->fetchAll());
|
||||
|
||||
$this->assertEqual($num_records, 4, 'Returned the correct number of rows.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that we can do basic alters on subqueries.
|
||||
*/
|
||||
function testSimpleAlterSubquery() {
|
||||
// Create a sub-query with an alter tag.
|
||||
$subquery = db_select('test', 'p');
|
||||
$subquery->addField('p', 'name');
|
||||
$subquery->addField('p', 'id');
|
||||
// Pick out George.
|
||||
$subquery->condition('age', 27);
|
||||
$subquery->addExpression("age*2", 'double_age');
|
||||
// This query alter should change it to age * 3.
|
||||
$subquery->addTag('database_test_alter_change_expressions');
|
||||
|
||||
// Create a main query and join to sub-query.
|
||||
$query = db_select('test_task', 'tt');
|
||||
$query->join($subquery, 'pq', 'pq.id = tt.pid');
|
||||
$age_field = $query->addField('pq', 'double_age');
|
||||
$name_field = $query->addField('pq', 'name');
|
||||
|
||||
$record = $query->execute()->fetch();
|
||||
$this->assertEqual($record->$name_field, 'George', 'Fetched name is correct.');
|
||||
$this->assertEqual($record->$age_field, 27*3, 'Fetched age expression is correct.');
|
||||
}
|
||||
}
|
||||
136
core/tests/Drupal/KernelTests/Core/Database/BasicSyntaxTest.php
Normal file
136
core/tests/Drupal/KernelTests/Core/Database/BasicSyntaxTest.php
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Database;
|
||||
|
||||
/**
|
||||
* Tests SQL syntax interpretation.
|
||||
*
|
||||
* In order to ensure consistent SQL handling throughout Drupal
|
||||
* across multiple kinds of database systems, we test that the
|
||||
* database system interprets SQL syntax in an expected fashion.
|
||||
*
|
||||
* @group Database
|
||||
*/
|
||||
class BasicSyntaxTest extends DatabaseTestBase {
|
||||
/**
|
||||
* Tests string concatenation.
|
||||
*/
|
||||
function testConcatLiterals() {
|
||||
$result = db_query('SELECT CONCAT(:a1, CONCAT(:a2, CONCAT(:a3, CONCAT(:a4, :a5))))', array(
|
||||
':a1' => 'This',
|
||||
':a2' => ' ',
|
||||
':a3' => 'is',
|
||||
':a4' => ' a ',
|
||||
':a5' => 'test.',
|
||||
));
|
||||
$this->assertIdentical($result->fetchField(), 'This is a test.', 'Basic CONCAT works.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests string concatenation with field values.
|
||||
*/
|
||||
function testConcatFields() {
|
||||
$result = db_query('SELECT CONCAT(:a1, CONCAT(name, CONCAT(:a2, CONCAT(age, :a3)))) FROM {test} WHERE age = :age', array(
|
||||
':a1' => 'The age of ',
|
||||
':a2' => ' is ',
|
||||
':a3' => '.',
|
||||
':age' => 25,
|
||||
));
|
||||
$this->assertIdentical($result->fetchField(), 'The age of John is 25.', 'Field CONCAT works.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests string concatenation with separator.
|
||||
*/
|
||||
function testConcatWsLiterals() {
|
||||
$result = db_query("SELECT CONCAT_WS(', ', :a1, NULL, :a2, :a3, :a4)", array(
|
||||
':a1' => 'Hello',
|
||||
':a2' => NULL,
|
||||
':a3' => '',
|
||||
':a4' => 'world.',
|
||||
));
|
||||
$this->assertIdentical($result->fetchField(), 'Hello, , world.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests string concatenation with separator, with field values.
|
||||
*/
|
||||
function testConcatWsFields() {
|
||||
$result = db_query("SELECT CONCAT_WS('-', :a1, name, :a2, age) FROM {test} WHERE age = :age", array(
|
||||
':a1' => 'name',
|
||||
':a2' => 'age',
|
||||
':age' => 25,
|
||||
));
|
||||
$this->assertIdentical($result->fetchField(), 'name-John-age-25');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests escaping of LIKE wildcards.
|
||||
*/
|
||||
function testLikeEscape() {
|
||||
db_insert('test')
|
||||
->fields(array(
|
||||
'name' => 'Ring_',
|
||||
))
|
||||
->execute();
|
||||
|
||||
// Match both "Ringo" and "Ring_".
|
||||
$num_matches = db_select('test', 't')
|
||||
->condition('name', 'Ring_', 'LIKE')
|
||||
->countQuery()
|
||||
->execute()
|
||||
->fetchField();
|
||||
$this->assertIdentical($num_matches, '2', 'Found 2 records.');
|
||||
// Match only "Ring_" using a LIKE expression with no wildcards.
|
||||
$num_matches = db_select('test', 't')
|
||||
->condition('name', db_like('Ring_'), 'LIKE')
|
||||
->countQuery()
|
||||
->execute()
|
||||
->fetchField();
|
||||
$this->assertIdentical($num_matches, '1', 'Found 1 record.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a LIKE query containing a backslash.
|
||||
*/
|
||||
function testLikeBackslash() {
|
||||
db_insert('test')
|
||||
->fields(array('name'))
|
||||
->values(array(
|
||||
'name' => 'abcde\f',
|
||||
))
|
||||
->values(array(
|
||||
'name' => 'abc%\_',
|
||||
))
|
||||
->execute();
|
||||
|
||||
// Match both rows using a LIKE expression with two wildcards and a verbatim
|
||||
// backslash.
|
||||
$num_matches = db_select('test', 't')
|
||||
->condition('name', 'abc%\\\\_', 'LIKE')
|
||||
->countQuery()
|
||||
->execute()
|
||||
->fetchField();
|
||||
$this->assertIdentical($num_matches, '2', 'Found 2 records.');
|
||||
// Match only the former using a LIKE expression with no wildcards.
|
||||
$num_matches = db_select('test', 't')
|
||||
->condition('name', db_like('abc%\_'), 'LIKE')
|
||||
->countQuery()
|
||||
->execute()
|
||||
->fetchField();
|
||||
$this->assertIdentical($num_matches, '1', 'Found 1 record.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests \Drupal\Core\Database\Connection::getFullQualifiedTableName().
|
||||
*/
|
||||
public function testGetFullQualifiedTableName() {
|
||||
$database = \Drupal::database();
|
||||
$num_matches = $database->select($database->getFullQualifiedTableName('test'), 't')
|
||||
->countQuery()
|
||||
->execute()
|
||||
->fetchField();
|
||||
$this->assertIdentical($num_matches, '4', 'Found 4 records.');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Database;
|
||||
|
||||
/**
|
||||
* Tests handling case sensitive collation.
|
||||
*
|
||||
* @group Database
|
||||
*/
|
||||
class CaseSensitivityTest extends DatabaseTestBase {
|
||||
/**
|
||||
* Tests BINARY collation in MySQL.
|
||||
*/
|
||||
function testCaseSensitiveInsert() {
|
||||
$num_records_before = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
|
||||
|
||||
db_insert('test')
|
||||
->fields(array(
|
||||
'name' => 'john', // <- A record already exists with name 'John'.
|
||||
'age' => 2,
|
||||
'job' => 'Baby',
|
||||
))
|
||||
->execute();
|
||||
|
||||
$num_records_after = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
|
||||
$this->assertIdentical($num_records_before + 1, (int) $num_records_after, 'Record inserts correctly.');
|
||||
$saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'john'))->fetchField();
|
||||
$this->assertIdentical($saved_age, '2', 'Can retrieve after inserting.');
|
||||
}
|
||||
}
|
||||
175
core/tests/Drupal/KernelTests/Core/Database/ConnectionTest.php
Normal file
175
core/tests/Drupal/KernelTests/Core/Database/ConnectionTest.php
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Database;
|
||||
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\Database\DatabaseExceptionWrapper;
|
||||
|
||||
/**
|
||||
* Tests of the core database system.
|
||||
*
|
||||
* @group Database
|
||||
*/
|
||||
class ConnectionTest extends DatabaseTestBase {
|
||||
|
||||
/**
|
||||
* Tests that connections return appropriate connection objects.
|
||||
*/
|
||||
function testConnectionRouting() {
|
||||
// Clone the primary credentials to a replica connection.
|
||||
// Note this will result in two independent connection objects that happen
|
||||
// to point to the same place.
|
||||
$connection_info = Database::getConnectionInfo('default');
|
||||
Database::addConnectionInfo('default', 'replica', $connection_info['default']);
|
||||
|
||||
$db1 = Database::getConnection('default', 'default');
|
||||
$db2 = Database::getConnection('replica', 'default');
|
||||
|
||||
$this->assertNotNull($db1, 'default connection is a real connection object.');
|
||||
$this->assertNotNull($db2, 'replica connection is a real connection object.');
|
||||
$this->assertNotIdentical($db1, $db2, 'Each target refers to a different connection.');
|
||||
|
||||
// Try to open those targets another time, that should return the same objects.
|
||||
$db1b = Database::getConnection('default', 'default');
|
||||
$db2b = Database::getConnection('replica', 'default');
|
||||
$this->assertIdentical($db1, $db1b, 'A second call to getConnection() returns the same object.');
|
||||
$this->assertIdentical($db2, $db2b, 'A second call to getConnection() returns the same object.');
|
||||
|
||||
// Try to open an unknown target.
|
||||
$unknown_target = $this->randomMachineName();
|
||||
$db3 = Database::getConnection($unknown_target, 'default');
|
||||
$this->assertNotNull($db3, 'Opening an unknown target returns a real connection object.');
|
||||
$this->assertIdentical($db1, $db3, 'An unknown target opens the default connection.');
|
||||
|
||||
// Try to open that unknown target another time, that should return the same object.
|
||||
$db3b = Database::getConnection($unknown_target, 'default');
|
||||
$this->assertIdentical($db3, $db3b, 'A second call to getConnection() returns the same object.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that connections return appropriate connection objects.
|
||||
*/
|
||||
function testConnectionRoutingOverride() {
|
||||
// Clone the primary credentials to a replica connection.
|
||||
// Note this will result in two independent connection objects that happen
|
||||
// to point to the same place.
|
||||
$connection_info = Database::getConnectionInfo('default');
|
||||
Database::addConnectionInfo('default', 'replica', $connection_info['default']);
|
||||
|
||||
Database::ignoreTarget('default', 'replica');
|
||||
|
||||
$db1 = Database::getConnection('default', 'default');
|
||||
$db2 = Database::getConnection('replica', 'default');
|
||||
|
||||
$this->assertIdentical($db1, $db2, 'Both targets refer to the same connection.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the closing of a database connection.
|
||||
*/
|
||||
function testConnectionClosing() {
|
||||
// Open the default target so we have an object to compare.
|
||||
$db1 = Database::getConnection('default', 'default');
|
||||
|
||||
// Try to close the default connection, then open a new one.
|
||||
Database::closeConnection('default', 'default');
|
||||
$db2 = Database::getConnection('default', 'default');
|
||||
|
||||
// Opening a connection after closing it should yield an object different than the original.
|
||||
$this->assertNotIdentical($db1, $db2, 'Opening the default connection after it is closed returns a new object.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the connection options of the active database.
|
||||
*/
|
||||
function testConnectionOptions() {
|
||||
$connection_info = Database::getConnectionInfo('default');
|
||||
|
||||
// Be sure we're connected to the default database.
|
||||
$db = Database::getConnection('default', 'default');
|
||||
$connectionOptions = $db->getConnectionOptions();
|
||||
|
||||
// In the MySQL driver, the port can be different, so check individual
|
||||
// options.
|
||||
$this->assertEqual($connection_info['default']['driver'], $connectionOptions['driver'], 'The default connection info driver matches the current connection options driver.');
|
||||
$this->assertEqual($connection_info['default']['database'], $connectionOptions['database'], 'The default connection info database matches the current connection options database.');
|
||||
|
||||
// Set up identical replica and confirm connection options are identical.
|
||||
Database::addConnectionInfo('default', 'replica', $connection_info['default']);
|
||||
$db2 = Database::getConnection('replica', 'default');
|
||||
$connectionOptions2 = $db2->getConnectionOptions();
|
||||
|
||||
// Get a fresh copy of the default connection options.
|
||||
$connectionOptions = $db->getConnectionOptions();
|
||||
$this->assertIdentical($connectionOptions, $connectionOptions2, 'The default and replica connection options are identical.');
|
||||
|
||||
// Set up a new connection with different connection info.
|
||||
$test = $connection_info['default'];
|
||||
$test['database'] .= 'test';
|
||||
Database::addConnectionInfo('test', 'default', $test);
|
||||
$connection_info = Database::getConnectionInfo('test');
|
||||
|
||||
// Get a fresh copy of the default connection options.
|
||||
$connectionOptions = $db->getConnectionOptions();
|
||||
$this->assertNotEqual($connection_info['default']['database'], $connectionOptions['database'], 'The test connection info database does not match the current connection options database.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that you cannot execute multiple statements on phpversion() > 5.5.21 or > 5.6.5.
|
||||
*/
|
||||
public function testMultipleStatementsForNewPhp() {
|
||||
// This just tests mysql, as other PDO integrations don't allow disabling
|
||||
// multiple statements.
|
||||
if (Database::getConnection()->databaseType() !== 'mysql' || !defined('\PDO::MYSQL_ATTR_MULTI_STATEMENTS')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$db = Database::getConnection('default', 'default');
|
||||
// Disable the protection at the PHP level.
|
||||
try {
|
||||
$db->query('SELECT * FROM {test}; SELECT * FROM {test_people}',
|
||||
[],
|
||||
[ 'allow_delimiter_in_query' => TRUE ]
|
||||
);
|
||||
$this->fail('No PDO exception thrown for multiple statements.');
|
||||
}
|
||||
catch (DatabaseExceptionWrapper $e) {
|
||||
$this->pass('PDO exception thrown for multiple statements.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that you cannot execute multiple statements.
|
||||
*/
|
||||
public function testMultipleStatements() {
|
||||
|
||||
$db = Database::getConnection('default', 'default');
|
||||
try {
|
||||
$db->query('SELECT * FROM {test}; SELECT * FROM {test_people}');
|
||||
$this->fail('No exception thrown for multiple statements.');
|
||||
}
|
||||
catch (\InvalidArgumentException $e) {
|
||||
$this->pass('Exception thrown for multiple statements.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the escapeTable(), escapeField() and escapeAlias() methods with all possible reserved words in PostgreSQL.
|
||||
*/
|
||||
public function testPostgresqlReservedWords() {
|
||||
if (Database::getConnection()->databaseType() !== 'pgsql') {
|
||||
return;
|
||||
}
|
||||
|
||||
$db = Database::getConnection('default', 'default');
|
||||
$stmt = $db->query("SELECT word FROM pg_get_keywords() WHERE catcode IN ('R', 'T')");
|
||||
$stmt->execute();
|
||||
foreach ($stmt->fetchAllAssoc('word') as $word => $row) {
|
||||
$expected = '"' . $word . '"';
|
||||
$this->assertIdentical($db->escapeTable($word), $expected, format_string('The reserved word %word was correctly escaped when used as a table name.', array('%word' => $word)));
|
||||
$this->assertIdentical($db->escapeField($word), $expected, format_string('The reserved word %word was correctly escaped when used as a column name.', array('%word' => $word)));
|
||||
$this->assertIdentical($db->escapeAlias($word), $expected, format_string('The reserved word %word was correctly escaped when used as an alias.', array('%word' => $word)));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,243 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Database;
|
||||
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests management of database connections.
|
||||
*
|
||||
* @group Database
|
||||
*/
|
||||
class ConnectionUnitTest extends KernelTestBase {
|
||||
|
||||
protected $key;
|
||||
protected $target;
|
||||
|
||||
protected $monitor;
|
||||
protected $originalCount;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->key = 'default';
|
||||
$this->originalTarget = 'default';
|
||||
$this->target = 'DatabaseConnectionUnitTest';
|
||||
|
||||
// Determine whether the database driver is MySQL. If it is not, the test
|
||||
// methods will not be executed.
|
||||
// @todo Make this test driver-agnostic, or find a proper way to skip it.
|
||||
// See https://www.drupal.org/node/1273478.
|
||||
$connection_info = Database::getConnectionInfo('default');
|
||||
$this->skipTest = (bool) ($connection_info['default']['driver'] != 'mysql');
|
||||
if ($this->skipTest) {
|
||||
// Insert an assertion to prevent Simpletest from interpreting the test
|
||||
// as failure.
|
||||
$this->pass('This test is only compatible with MySQL.');
|
||||
}
|
||||
|
||||
// Create an additional connection to monitor the connections being opened
|
||||
// and closed in this test.
|
||||
// @see TestBase::changeDatabasePrefix()
|
||||
Database::addConnectionInfo('default', 'monitor', $connection_info['default']);
|
||||
$this->monitor = Database::getConnection('monitor');
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new database connection info to Database.
|
||||
*/
|
||||
protected function addConnection() {
|
||||
// Add a new target to the connection, by cloning the current connection.
|
||||
$connection_info = Database::getConnectionInfo($this->key);
|
||||
Database::addConnectionInfo($this->key, $this->target, $connection_info[$this->originalTarget]);
|
||||
|
||||
// Verify that the new target exists.
|
||||
$info = Database::getConnectionInfo($this->key);
|
||||
// Note: Custom assertion message to not expose database credentials.
|
||||
$this->assertIdentical($info[$this->target], $connection_info[$this->key], 'New connection info found.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the connection ID of the current test connection.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function getConnectionID() {
|
||||
return (int) Database::getConnection($this->target, $this->key)->query('SELECT CONNECTION_ID()')->fetchField();
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that a connection ID exists.
|
||||
*
|
||||
* @param int $id
|
||||
* The connection ID to verify.
|
||||
*/
|
||||
protected function assertConnection($id) {
|
||||
$list = $this->monitor->query('SHOW PROCESSLIST')->fetchAllKeyed(0, 0);
|
||||
return $this->assertTrue(isset($list[$id]), format_string('Connection ID @id found.', array('@id' => $id)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that a connection ID does not exist.
|
||||
*
|
||||
* @param int $id
|
||||
* The connection ID to verify.
|
||||
*/
|
||||
protected function assertNoConnection($id) {
|
||||
$list = $this->monitor->query('SHOW PROCESSLIST')->fetchAllKeyed(0, 0);
|
||||
return $this->assertFalse(isset($list[$id]), format_string('Connection ID @id not found.', array('@id' => $id)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests Database::closeConnection() without query.
|
||||
*
|
||||
* @todo getConnectionID() executes a query.
|
||||
*/
|
||||
function testOpenClose() {
|
||||
if ($this->skipTest) {
|
||||
return;
|
||||
}
|
||||
// Add and open a new connection.
|
||||
$this->addConnection();
|
||||
$id = $this->getConnectionID();
|
||||
Database::getConnection($this->target, $this->key);
|
||||
|
||||
// Verify that there is a new connection.
|
||||
$this->assertConnection($id);
|
||||
|
||||
// Close the connection.
|
||||
Database::closeConnection($this->target, $this->key);
|
||||
// Wait 20ms to give the database engine sufficient time to react.
|
||||
usleep(20000);
|
||||
|
||||
// Verify that we are back to the original connection count.
|
||||
$this->assertNoConnection($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests Database::closeConnection() with a query.
|
||||
*/
|
||||
function testOpenQueryClose() {
|
||||
if ($this->skipTest) {
|
||||
return;
|
||||
}
|
||||
// Add and open a new connection.
|
||||
$this->addConnection();
|
||||
$id = $this->getConnectionID();
|
||||
Database::getConnection($this->target, $this->key);
|
||||
|
||||
// Verify that there is a new connection.
|
||||
$this->assertConnection($id);
|
||||
|
||||
// Execute a query.
|
||||
Database::getConnection($this->target, $this->key)->query('SHOW TABLES');
|
||||
|
||||
// Close the connection.
|
||||
Database::closeConnection($this->target, $this->key);
|
||||
// Wait 20ms to give the database engine sufficient time to react.
|
||||
usleep(20000);
|
||||
|
||||
// Verify that we are back to the original connection count.
|
||||
$this->assertNoConnection($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests Database::closeConnection() with a query and custom prefetch method.
|
||||
*/
|
||||
function testOpenQueryPrefetchClose() {
|
||||
if ($this->skipTest) {
|
||||
return;
|
||||
}
|
||||
// Add and open a new connection.
|
||||
$this->addConnection();
|
||||
$id = $this->getConnectionID();
|
||||
Database::getConnection($this->target, $this->key);
|
||||
|
||||
// Verify that there is a new connection.
|
||||
$this->assertConnection($id);
|
||||
|
||||
// Execute a query.
|
||||
Database::getConnection($this->target, $this->key)->query('SHOW TABLES')->fetchCol();
|
||||
|
||||
// Close the connection.
|
||||
Database::closeConnection($this->target, $this->key);
|
||||
// Wait 20ms to give the database engine sufficient time to react.
|
||||
usleep(20000);
|
||||
|
||||
// Verify that we are back to the original connection count.
|
||||
$this->assertNoConnection($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests Database::closeConnection() with a select query.
|
||||
*/
|
||||
function testOpenSelectQueryClose() {
|
||||
if ($this->skipTest) {
|
||||
return;
|
||||
}
|
||||
// Add and open a new connection.
|
||||
$this->addConnection();
|
||||
$id = $this->getConnectionID();
|
||||
Database::getConnection($this->target, $this->key);
|
||||
|
||||
// Verify that there is a new connection.
|
||||
$this->assertConnection($id);
|
||||
|
||||
// Create a table.
|
||||
$name = 'foo';
|
||||
Database::getConnection($this->target, $this->key)->schema()->createTable($name, array(
|
||||
'fields' => array(
|
||||
'name' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
// Execute a query.
|
||||
Database::getConnection($this->target, $this->key)->select('foo', 'f')
|
||||
->fields('f', array('name'))
|
||||
->execute()
|
||||
->fetchAll();
|
||||
|
||||
// Drop the table.
|
||||
Database::getConnection($this->target, $this->key)->schema()->dropTable($name);
|
||||
|
||||
// Close the connection.
|
||||
Database::closeConnection($this->target, $this->key);
|
||||
// Wait 20ms to give the database engine sufficient time to react.
|
||||
usleep(20000);
|
||||
|
||||
// Verify that we are back to the original connection count.
|
||||
$this->assertNoConnection($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests pdo options override.
|
||||
*/
|
||||
public function testConnectionOpen() {
|
||||
$connection = Database::getConnection('default');
|
||||
$reflection = new \ReflectionObject($connection);
|
||||
$connection_property = $reflection->getProperty('connection');
|
||||
$connection_property->setAccessible(TRUE);
|
||||
$error_mode = $connection_property->getValue($connection)
|
||||
->getAttribute(\PDO::ATTR_ERRMODE);
|
||||
$this->assertEqual($error_mode, \PDO::ERRMODE_EXCEPTION, 'Ensure the default error mode is set to exception.');
|
||||
|
||||
$connection = Database::getConnectionInfo('default');
|
||||
$connection['default']['pdo'][\PDO::ATTR_ERRMODE] = \PDO::ERRMODE_SILENT;
|
||||
Database::addConnectionInfo('test', 'default', $connection['default']);
|
||||
$connection = Database::getConnection('default', 'test');
|
||||
|
||||
$reflection = new \ReflectionObject($connection);
|
||||
$connection_property = $reflection->getProperty('connection');
|
||||
$connection_property->setAccessible(TRUE);
|
||||
$error_mode = $connection_property->getValue($connection)
|
||||
->getAttribute(\PDO::ATTR_ERRMODE);
|
||||
$this->assertEqual($error_mode, \PDO::ERRMODE_SILENT, 'Ensure PDO connection options can be overridden.');
|
||||
|
||||
Database::removeConnection('test');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Database;
|
||||
|
||||
use Drupal\Core\Database\DatabaseExceptionWrapper;
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests exceptions thrown by queries.
|
||||
*
|
||||
* @group Database
|
||||
*/
|
||||
class DatabaseExceptionWrapperTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Tests the expected database exception thrown for prepared statements.
|
||||
*/
|
||||
public function testPreparedStatement() {
|
||||
$connection = Database::getConnection();
|
||||
try {
|
||||
// SQLite validates the syntax upon preparing a statement already.
|
||||
// @throws \PDOException
|
||||
$query = $connection->prepare('bananas');
|
||||
|
||||
// MySQL only validates the syntax upon trying to execute a query.
|
||||
// @throws \Drupal\Core\Database\DatabaseExceptionWrapper
|
||||
$connection->query($query);
|
||||
|
||||
$this->fail('Expected PDOException or DatabaseExceptionWrapper, none was thrown.');
|
||||
}
|
||||
catch (\PDOException $e) {
|
||||
$this->pass('Expected PDOException was thrown.');
|
||||
}
|
||||
catch (DatabaseExceptionWrapper $e) {
|
||||
$this->pass('Expected DatabaseExceptionWrapper was thrown.');
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$this->fail("Thrown exception is not a PDOException:\n" . (string) $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the expected database exception thrown for inexistent tables.
|
||||
*/
|
||||
public function testQueryThrowsDatabaseExceptionWrapperException() {
|
||||
$connection = Database::getConnection();
|
||||
try {
|
||||
$connection->query('SELECT * FROM {does_not_exist}');
|
||||
$this->fail('Expected PDOException, none was thrown.');
|
||||
}
|
||||
catch (DatabaseExceptionWrapper $e) {
|
||||
$this->pass('Expected DatabaseExceptionWrapper was thrown.');
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$this->fail("Thrown exception is not a DatabaseExceptionWrapper:\n" . (string) $e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
146
core/tests/Drupal/KernelTests/Core/Database/DatabaseTestBase.php
Normal file
146
core/tests/Drupal/KernelTests/Core/Database/DatabaseTestBase.php
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Database;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Base class for databases database tests.
|
||||
*
|
||||
* Because all database tests share the same test data, we can centralize that
|
||||
* here.
|
||||
*/
|
||||
abstract class DatabaseTestBase extends KernelTestBase {
|
||||
|
||||
public static $modules = array('database_test');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installSchema('database_test', array(
|
||||
'test',
|
||||
'test_people',
|
||||
'test_people_copy',
|
||||
'test_one_blob',
|
||||
'test_two_blobs',
|
||||
'test_task',
|
||||
'test_null',
|
||||
'test_serialized',
|
||||
'test_special_columns',
|
||||
'TEST_UPPERCASE',
|
||||
));
|
||||
self::addSampleData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up tables for NULL handling.
|
||||
*/
|
||||
function ensureSampleDataNull() {
|
||||
db_insert('test_null')
|
||||
->fields(array('name', 'age'))
|
||||
->values(array(
|
||||
'name' => 'Kermit',
|
||||
'age' => 25,
|
||||
))
|
||||
->values(array(
|
||||
'name' => 'Fozzie',
|
||||
'age' => NULL,
|
||||
))
|
||||
->values(array(
|
||||
'name' => 'Gonzo',
|
||||
'age' => 27,
|
||||
))
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up our sample data.
|
||||
*/
|
||||
static function addSampleData() {
|
||||
// We need the IDs, so we can't use a multi-insert here.
|
||||
$john = db_insert('test')
|
||||
->fields(array(
|
||||
'name' => 'John',
|
||||
'age' => 25,
|
||||
'job' => 'Singer',
|
||||
))
|
||||
->execute();
|
||||
|
||||
$george = db_insert('test')
|
||||
->fields(array(
|
||||
'name' => 'George',
|
||||
'age' => 27,
|
||||
'job' => 'Singer',
|
||||
))
|
||||
->execute();
|
||||
|
||||
db_insert('test')
|
||||
->fields(array(
|
||||
'name' => 'Ringo',
|
||||
'age' => 28,
|
||||
'job' => 'Drummer',
|
||||
))
|
||||
->execute();
|
||||
|
||||
$paul = db_insert('test')
|
||||
->fields(array(
|
||||
'name' => 'Paul',
|
||||
'age' => 26,
|
||||
'job' => 'Songwriter',
|
||||
))
|
||||
->execute();
|
||||
|
||||
db_insert('test_people')
|
||||
->fields(array(
|
||||
'name' => 'Meredith',
|
||||
'age' => 30,
|
||||
'job' => 'Speaker',
|
||||
))
|
||||
->execute();
|
||||
|
||||
db_insert('test_task')
|
||||
->fields(array('pid', 'task', 'priority'))
|
||||
->values(array(
|
||||
'pid' => $john,
|
||||
'task' => 'eat',
|
||||
'priority' => 3,
|
||||
))
|
||||
->values(array(
|
||||
'pid' => $john,
|
||||
'task' => 'sleep',
|
||||
'priority' => 4,
|
||||
))
|
||||
->values(array(
|
||||
'pid' => $john,
|
||||
'task' => 'code',
|
||||
'priority' => 1,
|
||||
))
|
||||
->values(array(
|
||||
'pid' => $george,
|
||||
'task' => 'sing',
|
||||
'priority' => 2,
|
||||
))
|
||||
->values(array(
|
||||
'pid' => $george,
|
||||
'task' => 'sleep',
|
||||
'priority' => 2,
|
||||
))
|
||||
->values(array(
|
||||
'pid' => $paul,
|
||||
'task' => 'found new band',
|
||||
'priority' => 1,
|
||||
))
|
||||
->values(array(
|
||||
'pid' => $paul,
|
||||
'task' => 'perform at superbowl',
|
||||
'priority' => 3,
|
||||
))
|
||||
->execute();
|
||||
|
||||
db_insert('test_special_columns')
|
||||
->fields(array(
|
||||
'id' => 1,
|
||||
'offset' => 'Offset value 1',
|
||||
))
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Database;
|
||||
|
||||
/**
|
||||
* Tests delete and truncate queries.
|
||||
*
|
||||
* The DELETE tests are not as extensive, as all of the interesting code for
|
||||
* DELETE queries is in the conditional which is identical to the UPDATE and
|
||||
* SELECT conditional handling.
|
||||
*
|
||||
* The TRUNCATE tests are not extensive either, because the behavior of
|
||||
* TRUNCATE queries is not consistent across database engines. We only test
|
||||
* that a TRUNCATE query actually deletes all rows from the target table.
|
||||
*
|
||||
* @group Database
|
||||
*/
|
||||
class DeleteTruncateTest extends DatabaseTestBase {
|
||||
|
||||
/**
|
||||
* Confirms that we can use a subselect in a delete successfully.
|
||||
*/
|
||||
function testSubselectDelete() {
|
||||
$num_records_before = db_query('SELECT COUNT(*) FROM {test_task}')->fetchField();
|
||||
$pid_to_delete = db_query("SELECT * FROM {test_task} WHERE task = 'sleep'")->fetchField();
|
||||
|
||||
$subquery = db_select('test', 't')
|
||||
->fields('t', array('id'))
|
||||
->condition('t.id', array($pid_to_delete), 'IN');
|
||||
$delete = db_delete('test_task')
|
||||
->condition('task', 'sleep')
|
||||
->condition('pid', $subquery, 'IN');
|
||||
|
||||
$num_deleted = $delete->execute();
|
||||
$this->assertEqual($num_deleted, 1, 'Deleted 1 record.');
|
||||
|
||||
$num_records_after = db_query('SELECT COUNT(*) FROM {test_task}')->fetchField();
|
||||
$this->assertEqual($num_records_before, $num_records_after + $num_deleted, 'Deletion adds up.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms that we can delete a single record successfully.
|
||||
*/
|
||||
function testSimpleDelete() {
|
||||
$num_records_before = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
|
||||
|
||||
$num_deleted = db_delete('test')
|
||||
->condition('id', 1)
|
||||
->execute();
|
||||
$this->assertIdentical($num_deleted, 1, 'Deleted 1 record.');
|
||||
|
||||
$num_records_after = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
|
||||
$this->assertEqual($num_records_before, $num_records_after + $num_deleted, 'Deletion adds up.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms that we can truncate a whole table successfully.
|
||||
*/
|
||||
function testTruncate() {
|
||||
$num_records_before = db_query("SELECT COUNT(*) FROM {test}")->fetchField();
|
||||
$this->assertTrue($num_records_before > 0, 'The table is not empty.');
|
||||
|
||||
db_truncate('test')->execute();
|
||||
|
||||
$num_records_after = db_query("SELECT COUNT(*) FROM {test}")->fetchField();
|
||||
$this->assertEqual(0, $num_records_after, 'Truncate really deletes everything.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms that we can delete a single special column name record successfully.
|
||||
*/
|
||||
function testSpecialColumnDelete() {
|
||||
$num_records_before = db_query('SELECT COUNT(*) FROM {test_special_columns}')->fetchField();
|
||||
|
||||
$num_deleted = db_delete('test_special_columns')
|
||||
->condition('id', 1)
|
||||
->execute();
|
||||
$this->assertIdentical($num_deleted, 1, 'Deleted 1 special column record.');
|
||||
|
||||
$num_records_after = db_query('SELECT COUNT(*) FROM {test_special_columns}')->fetchField();
|
||||
$this->assertEqual($num_records_before, $num_records_after + $num_deleted, 'Deletion adds up.');
|
||||
}
|
||||
}
|
||||
146
core/tests/Drupal/KernelTests/Core/Database/FetchTest.php
Normal file
146
core/tests/Drupal/KernelTests/Core/Database/FetchTest.php
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Database;
|
||||
|
||||
use Drupal\Core\Database\RowCountException;
|
||||
use Drupal\Core\Database\StatementInterface;
|
||||
use Drupal\system\Tests\Database\FakeRecord;
|
||||
|
||||
/**
|
||||
* Tests the Database system's various fetch capabilities.
|
||||
*
|
||||
* We get timeout errors if we try to run too many tests at once.
|
||||
*
|
||||
* @group Database
|
||||
*/
|
||||
class FetchTest extends DatabaseTestBase {
|
||||
|
||||
/**
|
||||
* Confirms that we can fetch a record properly in default object mode.
|
||||
*/
|
||||
function testQueryFetchDefault() {
|
||||
$records = array();
|
||||
$result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25));
|
||||
$this->assertTrue($result instanceof StatementInterface, 'Result set is a Drupal statement object.');
|
||||
foreach ($result as $record) {
|
||||
$records[] = $record;
|
||||
$this->assertTrue(is_object($record), 'Record is an object.');
|
||||
$this->assertIdentical($record->name, 'John', '25 year old is John.');
|
||||
}
|
||||
|
||||
$this->assertIdentical(count($records), 1, 'There is only one record.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms that we can fetch a record to an object explicitly.
|
||||
*/
|
||||
function testQueryFetchObject() {
|
||||
$records = array();
|
||||
$result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => \PDO::FETCH_OBJ));
|
||||
foreach ($result as $record) {
|
||||
$records[] = $record;
|
||||
$this->assertTrue(is_object($record), 'Record is an object.');
|
||||
$this->assertIdentical($record->name, 'John', '25 year old is John.');
|
||||
}
|
||||
|
||||
$this->assertIdentical(count($records), 1, 'There is only one record.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms that we can fetch a record to an associative array explicitly.
|
||||
*/
|
||||
function testQueryFetchArray() {
|
||||
$records = array();
|
||||
$result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => \PDO::FETCH_ASSOC));
|
||||
foreach ($result as $record) {
|
||||
$records[] = $record;
|
||||
if ($this->assertTrue(is_array($record), 'Record is an array.')) {
|
||||
$this->assertIdentical($record['name'], 'John', 'Record can be accessed associatively.');
|
||||
}
|
||||
}
|
||||
|
||||
$this->assertIdentical(count($records), 1, 'There is only one record.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms that we can fetch a record into a new instance of a custom class.
|
||||
*
|
||||
* @see \Drupal\system\Tests\Database\FakeRecord
|
||||
*/
|
||||
function testQueryFetchClass() {
|
||||
$records = array();
|
||||
$result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => 'Drupal\system\Tests\Database\FakeRecord'));
|
||||
foreach ($result as $record) {
|
||||
$records[] = $record;
|
||||
if ($this->assertTrue($record instanceof FakeRecord, 'Record is an object of class FakeRecord.')) {
|
||||
$this->assertIdentical($record->name, 'John', '25 year old is John.');
|
||||
}
|
||||
}
|
||||
|
||||
$this->assertIdentical(count($records), 1, 'There is only one record.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms that we can fetch a record into an indexed array explicitly.
|
||||
*/
|
||||
function testQueryFetchNum() {
|
||||
$records = array();
|
||||
$result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => \PDO::FETCH_NUM));
|
||||
foreach ($result as $record) {
|
||||
$records[] = $record;
|
||||
if ($this->assertTrue(is_array($record), 'Record is an array.')) {
|
||||
$this->assertIdentical($record[0], 'John', 'Record can be accessed numerically.');
|
||||
}
|
||||
}
|
||||
|
||||
$this->assertIdentical(count($records), 1, 'There is only one record');
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms that we can fetch a record into a doubly-keyed array explicitly.
|
||||
*/
|
||||
function testQueryFetchBoth() {
|
||||
$records = array();
|
||||
$result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => \PDO::FETCH_BOTH));
|
||||
foreach ($result as $record) {
|
||||
$records[] = $record;
|
||||
if ($this->assertTrue(is_array($record), 'Record is an array.')) {
|
||||
$this->assertIdentical($record[0], 'John', 'Record can be accessed numerically.');
|
||||
$this->assertIdentical($record['name'], 'John', 'Record can be accessed associatively.');
|
||||
}
|
||||
}
|
||||
|
||||
$this->assertIdentical(count($records), 1, 'There is only one record.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms that we can fetch an entire column of a result set at once.
|
||||
*/
|
||||
function testQueryFetchCol() {
|
||||
$result = db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25));
|
||||
$column = $result->fetchCol();
|
||||
$this->assertIdentical(count($column), 3, 'fetchCol() returns the right number of records.');
|
||||
|
||||
$result = db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25));
|
||||
$i = 0;
|
||||
foreach ($result as $record) {
|
||||
$this->assertIdentical($record->name, $column[$i++], 'Column matches direct access.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that rowCount() throws exception on SELECT query.
|
||||
*/
|
||||
public function testRowCount() {
|
||||
$result = db_query('SELECT name FROM {test}');
|
||||
try {
|
||||
$result->rowCount();
|
||||
$exception = FALSE;
|
||||
}
|
||||
catch (RowCountException $e) {
|
||||
$exception = TRUE;
|
||||
}
|
||||
$this->assertTrue($exception, 'Exception was thrown');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Database;
|
||||
|
||||
use Drupal\Core\Database\Query\NoFieldsException;
|
||||
|
||||
/**
|
||||
* Tests the Insert query builder with default values.
|
||||
*
|
||||
* @group Database
|
||||
*/
|
||||
class InsertDefaultsTest extends DatabaseTestBase {
|
||||
|
||||
/**
|
||||
* Tests that we can run a query that uses default values for everything.
|
||||
*/
|
||||
function testDefaultInsert() {
|
||||
$query = db_insert('test')->useDefaults(array('job'));
|
||||
$id = $query->execute();
|
||||
|
||||
$schema = drupal_get_module_schema('database_test', 'test');
|
||||
|
||||
$job = db_query('SELECT job FROM {test} WHERE id = :id', array(':id' => $id))->fetchField();
|
||||
$this->assertEqual($job, $schema['fields']['job']['default'], 'Default field value is set.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that no action will be preformed if no fields are specified.
|
||||
*/
|
||||
function testDefaultEmptyInsert() {
|
||||
$num_records_before = (int) db_query('SELECT COUNT(*) FROM {test}')->fetchField();
|
||||
|
||||
try {
|
||||
db_insert('test')->execute();
|
||||
// This is only executed if no exception has been thrown.
|
||||
$this->fail('Expected exception NoFieldsException has not been thrown.');
|
||||
} catch (NoFieldsException $e) {
|
||||
$this->pass('Expected exception NoFieldsException has been thrown.');
|
||||
}
|
||||
|
||||
$num_records_after = (int) db_query('SELECT COUNT(*) FROM {test}')->fetchField();
|
||||
$this->assertIdentical($num_records_before, $num_records_after, 'Do nothing as no fields are specified.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that we can insert fields with values and defaults in the same query.
|
||||
*/
|
||||
function testDefaultInsertWithFields() {
|
||||
$query = db_insert('test')
|
||||
->fields(array('name' => 'Bob'))
|
||||
->useDefaults(array('job'));
|
||||
$id = $query->execute();
|
||||
|
||||
$schema = drupal_get_module_schema('database_test', 'test');
|
||||
|
||||
$job = db_query('SELECT job FROM {test} WHERE id = :id', array(':id' => $id))->fetchField();
|
||||
$this->assertEqual($job, $schema['fields']['job']['default'], 'Default field value is set.');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Database;
|
||||
|
||||
/**
|
||||
* Tests the Insert query builder with LOB fields.
|
||||
*
|
||||
* @group Database
|
||||
*/
|
||||
class InsertLobTest extends DatabaseTestBase {
|
||||
|
||||
/**
|
||||
* Tests that we can insert a single blob field successfully.
|
||||
*/
|
||||
function testInsertOneBlob() {
|
||||
$data = "This is\000a test.";
|
||||
$this->assertTrue(strlen($data) === 15, 'Test data contains a NULL.');
|
||||
$id = db_insert('test_one_blob')
|
||||
->fields(array('blob1' => $data))
|
||||
->execute();
|
||||
$r = db_query('SELECT * FROM {test_one_blob} WHERE id = :id', array(':id' => $id))->fetchAssoc();
|
||||
$this->assertTrue($r['blob1'] === $data, format_string('Can insert a blob: id @id, @data.', array('@id' => $id, '@data' => serialize($r))));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that we can insert multiple blob fields in the same query.
|
||||
*/
|
||||
function testInsertMultipleBlob() {
|
||||
$id = db_insert('test_two_blobs')
|
||||
->fields(array(
|
||||
'blob1' => 'This is',
|
||||
'blob2' => 'a test',
|
||||
))
|
||||
->execute();
|
||||
$r = db_query('SELECT * FROM {test_two_blobs} WHERE id = :id', array(':id' => $id))->fetchAssoc();
|
||||
$this->assertTrue($r['blob1'] === 'This is' && $r['blob2'] === 'a test', 'Can insert multiple blobs per row.');
|
||||
}
|
||||
}
|
||||
209
core/tests/Drupal/KernelTests/Core/Database/InsertTest.php
Normal file
209
core/tests/Drupal/KernelTests/Core/Database/InsertTest.php
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Database;
|
||||
|
||||
/**
|
||||
* Tests the insert builder.
|
||||
*
|
||||
* @group Database
|
||||
*/
|
||||
class InsertTest extends DatabaseTestBase {
|
||||
|
||||
/**
|
||||
* Tests very basic insert functionality.
|
||||
*/
|
||||
function testSimpleInsert() {
|
||||
$num_records_before = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
|
||||
|
||||
$query = db_insert('test');
|
||||
$query->fields(array(
|
||||
'name' => 'Yoko',
|
||||
'age' => '29',
|
||||
));
|
||||
|
||||
// Check how many records are queued for insertion.
|
||||
$this->assertIdentical($query->count(), 1, 'One record is queued for insertion.');
|
||||
$query->execute();
|
||||
|
||||
$num_records_after = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
|
||||
$this->assertIdentical($num_records_before + 1, (int) $num_records_after, 'Record inserts correctly.');
|
||||
$saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Yoko'))->fetchField();
|
||||
$this->assertIdentical($saved_age, '29', 'Can retrieve after inserting.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that we can insert multiple records in one query object.
|
||||
*/
|
||||
function testMultiInsert() {
|
||||
$num_records_before = (int) db_query('SELECT COUNT(*) FROM {test}')->fetchField();
|
||||
|
||||
$query = db_insert('test');
|
||||
$query->fields(array(
|
||||
'name' => 'Larry',
|
||||
'age' => '30',
|
||||
));
|
||||
|
||||
// We should be able to specify values in any order if named.
|
||||
$query->values(array(
|
||||
'age' => '31',
|
||||
'name' => 'Curly',
|
||||
));
|
||||
|
||||
// Check how many records are queued for insertion.
|
||||
$this->assertIdentical($query->count(), 2, 'Two records are queued for insertion.');
|
||||
|
||||
// We should be able to say "use the field order".
|
||||
// This is not the recommended mechanism for most cases, but it should work.
|
||||
$query->values(array('Moe', '32'));
|
||||
|
||||
// Check how many records are queued for insertion.
|
||||
$this->assertIdentical($query->count(), 3, 'Three records are queued for insertion.');
|
||||
$query->execute();
|
||||
|
||||
$num_records_after = (int) db_query('SELECT COUNT(*) FROM {test}')->fetchField();
|
||||
$this->assertIdentical($num_records_before + 3, $num_records_after, 'Record inserts correctly.');
|
||||
$saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Larry'))->fetchField();
|
||||
$this->assertIdentical($saved_age, '30', 'Can retrieve after inserting.');
|
||||
$saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Curly'))->fetchField();
|
||||
$this->assertIdentical($saved_age, '31', 'Can retrieve after inserting.');
|
||||
$saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Moe'))->fetchField();
|
||||
$this->assertIdentical($saved_age, '32', 'Can retrieve after inserting.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that an insert object can be reused with new data after it executes.
|
||||
*/
|
||||
function testRepeatedInsert() {
|
||||
$num_records_before = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
|
||||
|
||||
$query = db_insert('test');
|
||||
|
||||
$query->fields(array(
|
||||
'name' => 'Larry',
|
||||
'age' => '30',
|
||||
));
|
||||
// Check how many records are queued for insertion.
|
||||
$this->assertIdentical($query->count(), 1, 'One record is queued for insertion.');
|
||||
$query->execute(); // This should run the insert, but leave the fields intact.
|
||||
|
||||
// We should be able to specify values in any order if named.
|
||||
$query->values(array(
|
||||
'age' => '31',
|
||||
'name' => 'Curly',
|
||||
));
|
||||
// Check how many records are queued for insertion.
|
||||
$this->assertIdentical($query->count(), 1, 'One record is queued for insertion.');
|
||||
$query->execute();
|
||||
|
||||
// We should be able to say "use the field order".
|
||||
$query->values(array('Moe', '32'));
|
||||
|
||||
// Check how many records are queued for insertion.
|
||||
$this->assertIdentical($query->count(), 1, 'One record is queued for insertion.');
|
||||
$query->execute();
|
||||
|
||||
$num_records_after = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
|
||||
$this->assertIdentical((int) $num_records_before + 3, (int) $num_records_after, 'Record inserts correctly.');
|
||||
$saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Larry'))->fetchField();
|
||||
$this->assertIdentical($saved_age, '30', 'Can retrieve after inserting.');
|
||||
$saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Curly'))->fetchField();
|
||||
$this->assertIdentical($saved_age, '31', 'Can retrieve after inserting.');
|
||||
$saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Moe'))->fetchField();
|
||||
$this->assertIdentical($saved_age, '32', 'Can retrieve after inserting.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that we can specify fields without values and specify values later.
|
||||
*/
|
||||
function testInsertFieldOnlyDefinition() {
|
||||
// This is useful for importers, when we want to create a query and define
|
||||
// its fields once, then loop over a multi-insert execution.
|
||||
db_insert('test')
|
||||
->fields(array('name', 'age'))
|
||||
->values(array('Larry', '30'))
|
||||
->values(array('Curly', '31'))
|
||||
->values(array('Moe', '32'))
|
||||
->execute();
|
||||
$saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Larry'))->fetchField();
|
||||
$this->assertIdentical($saved_age, '30', 'Can retrieve after inserting.');
|
||||
$saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Curly'))->fetchField();
|
||||
$this->assertIdentical($saved_age, '31', 'Can retrieve after inserting.');
|
||||
$saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Moe'))->fetchField();
|
||||
$this->assertIdentical($saved_age, '32', 'Can retrieve after inserting.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that inserts return the proper auto-increment ID.
|
||||
*/
|
||||
function testInsertLastInsertID() {
|
||||
$id = db_insert('test')
|
||||
->fields(array(
|
||||
'name' => 'Larry',
|
||||
'age' => '30',
|
||||
))
|
||||
->execute();
|
||||
|
||||
$this->assertIdentical($id, '5', 'Auto-increment ID returned successfully.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the INSERT INTO ... SELECT (fields) ... syntax works.
|
||||
*/
|
||||
function testInsertSelectFields() {
|
||||
$query = db_select('test_people', 'tp');
|
||||
// The query builder will always append expressions after fields.
|
||||
// Add the expression first to test that the insert fields are correctly
|
||||
// re-ordered.
|
||||
$query->addExpression('tp.age', 'age');
|
||||
$query
|
||||
->fields('tp', array('name', 'job'))
|
||||
->condition('tp.name', 'Meredith');
|
||||
|
||||
// The resulting query should be equivalent to:
|
||||
// INSERT INTO test (age, name, job)
|
||||
// SELECT tp.age AS age, tp.name AS name, tp.job AS job
|
||||
// FROM test_people tp
|
||||
// WHERE tp.name = 'Meredith'
|
||||
db_insert('test')
|
||||
->from($query)
|
||||
->execute();
|
||||
|
||||
$saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Meredith'))->fetchField();
|
||||
$this->assertIdentical($saved_age, '30', 'Can retrieve after inserting.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the INSERT INTO ... SELECT * ... syntax works.
|
||||
*/
|
||||
function testInsertSelectAll() {
|
||||
$query = db_select('test_people', 'tp')
|
||||
->fields('tp')
|
||||
->condition('tp.name', 'Meredith');
|
||||
|
||||
// The resulting query should be equivalent to:
|
||||
// INSERT INTO test_people_copy
|
||||
// SELECT *
|
||||
// FROM test_people tp
|
||||
// WHERE tp.name = 'Meredith'
|
||||
db_insert('test_people_copy')
|
||||
->from($query)
|
||||
->execute();
|
||||
|
||||
$saved_age = db_query('SELECT age FROM {test_people_copy} WHERE name = :name', array(':name' => 'Meredith'))->fetchField();
|
||||
$this->assertIdentical($saved_age, '30', 'Can retrieve after inserting.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that we can INSERT INTO a special named column.
|
||||
*/
|
||||
function testSpecialColumnInsert() {
|
||||
$id = db_insert('test_special_columns')
|
||||
->fields(array(
|
||||
'id' => 2,
|
||||
'offset' => 'Offset value 2',
|
||||
))
|
||||
->execute();
|
||||
$saved_value = db_query('SELECT "offset" FROM {test_special_columns} WHERE id = :id', array(':id' => 2))->fetchField();
|
||||
$this->assertIdentical($saved_value, 'Offset value 2', 'Can retrieve special column name value after inserting.');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Database;
|
||||
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\Database\IntegrityConstraintViolationException;
|
||||
|
||||
/**
|
||||
* Tests handling of some invalid data.
|
||||
*
|
||||
* @group Database
|
||||
*/
|
||||
class InvalidDataTest extends DatabaseTestBase {
|
||||
/**
|
||||
* Tests aborting of traditional SQL database systems with invalid data.
|
||||
*/
|
||||
function testInsertDuplicateData() {
|
||||
// Try to insert multiple records where at least one has bad data.
|
||||
try {
|
||||
db_insert('test')
|
||||
->fields(array('name', 'age', 'job'))
|
||||
->values(array(
|
||||
'name' => 'Elvis',
|
||||
'age' => 63,
|
||||
'job' => 'Singer',
|
||||
))->values(array(
|
||||
'name' => 'John', // <-- Duplicate value on unique field.
|
||||
'age' => 17,
|
||||
'job' => 'Consultant',
|
||||
))
|
||||
->values(array(
|
||||
'name' => 'Frank',
|
||||
'age' => 75,
|
||||
'job' => 'Singer',
|
||||
))
|
||||
->execute();
|
||||
$this->fail('Insert succeeded when it should not have.');
|
||||
}
|
||||
catch (IntegrityConstraintViolationException $e) {
|
||||
// Check if the first record was inserted.
|
||||
$name = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 63))->fetchField();
|
||||
|
||||
if ($name == 'Elvis') {
|
||||
if (!Database::getConnection()->supportsTransactions()) {
|
||||
// This is an expected fail.
|
||||
// Database engines that don't support transactions can leave partial
|
||||
// inserts in place when an error occurs. This is the case for MySQL
|
||||
// when running on a MyISAM table.
|
||||
$this->pass("The whole transaction has not been rolled-back when a duplicate key insert occurs, this is expected because the database doesn't support transactions");
|
||||
}
|
||||
else {
|
||||
$this->fail('The whole transaction is rolled back when a duplicate key insert occurs.');
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->pass('The whole transaction is rolled back when a duplicate key insert occurs.');
|
||||
}
|
||||
|
||||
// Ensure the other values were not inserted.
|
||||
$record = db_select('test')
|
||||
->fields('test', array('name', 'age'))
|
||||
->condition('age', array(17, 75), 'IN')
|
||||
->execute()->fetchObject();
|
||||
|
||||
$this->assertFalse($record, 'The rest of the insert aborted as expected.');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Database;
|
||||
|
||||
use Drupal\Component\Utility\Environment;
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\Database\DatabaseException;
|
||||
|
||||
/**
|
||||
* Tests handling of large queries.
|
||||
*
|
||||
* @group Database
|
||||
*/
|
||||
class LargeQueryTest extends DatabaseTestBase {
|
||||
|
||||
/**
|
||||
* Tests truncation of messages when max_allowed_packet exception occurs.
|
||||
*/
|
||||
function testMaxAllowedPacketQueryTruncating() {
|
||||
// This test only makes sense if we are running on a MySQL database.
|
||||
// Test if we are.
|
||||
$database = Database::getConnectionInfo('default');
|
||||
if ($database['default']['driver'] == 'mysql') {
|
||||
// The max_allowed_packet value is configured per database instance.
|
||||
// Retrieve the max_allowed_packet value from the current instance and
|
||||
// check if PHP is configured with sufficient allowed memory to be able
|
||||
// to generate a query larger than max_allowed_packet.
|
||||
$max_allowed_packet = db_query('SELECT @@global.max_allowed_packet')->fetchField();
|
||||
if (Environment::checkMemoryLimit($max_allowed_packet + (16 * 1024 * 1024))) {
|
||||
$long_name = str_repeat('a', $max_allowed_packet + 1);
|
||||
try {
|
||||
db_query('SELECT name FROM {test} WHERE name = :name', array(':name' => $long_name));
|
||||
$this->fail("An exception should be thrown for queries larger than 'max_allowed_packet'");
|
||||
} catch (DatabaseException $e) {
|
||||
// Close and re-open the connection. Otherwise we will run into error
|
||||
// 2006 "MySQL server had gone away" afterwards.
|
||||
Database::closeConnection();
|
||||
Database::getConnection();
|
||||
$this->assertEqual($e->getPrevious()->errorInfo[1], 1153, "Got a packet bigger than 'max_allowed_packet' bytes exception thrown.");
|
||||
// Use strlen() to count the bytes exactly, not the unicode chars.
|
||||
$this->assertTrue(strlen($e->getMessage()) <= $max_allowed_packet, "'max_allowed_packet' exception message truncated.");
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->verbose('The configured max_allowed_packet exceeds the php memory limit. Therefore the test is skipped.');
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->verbose('The test requires MySQL. Therefore the test is skipped.');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
128
core/tests/Drupal/KernelTests/Core/Database/LoggingTest.php
Normal file
128
core/tests/Drupal/KernelTests/Core/Database/LoggingTest.php
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Database;
|
||||
|
||||
use Drupal\Core\Database\Database;
|
||||
|
||||
/**
|
||||
* Tests the query logging facility.
|
||||
*
|
||||
* @group Database
|
||||
*/
|
||||
class LoggingTest extends DatabaseTestBase {
|
||||
|
||||
/**
|
||||
* Tests that we can log the existence of a query.
|
||||
*/
|
||||
function testEnableLogging() {
|
||||
Database::startLog('testing');
|
||||
|
||||
db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25))->fetchCol();
|
||||
db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchCol();
|
||||
|
||||
// Trigger a call that does not have file in the backtrace.
|
||||
call_user_func_array('db_query', array('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo')))->fetchCol();
|
||||
|
||||
$queries = Database::getLog('testing', 'default');
|
||||
|
||||
$this->assertEqual(count($queries), 3, 'Correct number of queries recorded.');
|
||||
|
||||
foreach ($queries as $query) {
|
||||
$this->assertEqual($query['caller']['function'], __FUNCTION__, 'Correct function in query log.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that we can run two logs in parallel.
|
||||
*/
|
||||
function testEnableMultiLogging() {
|
||||
Database::startLog('testing1');
|
||||
|
||||
db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25))->fetchCol();
|
||||
|
||||
Database::startLog('testing2');
|
||||
|
||||
db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchCol();
|
||||
|
||||
$queries1 = Database::getLog('testing1');
|
||||
$queries2 = Database::getLog('testing2');
|
||||
|
||||
$this->assertEqual(count($queries1), 2, 'Correct number of queries recorded for log 1.');
|
||||
$this->assertEqual(count($queries2), 1, 'Correct number of queries recorded for log 2.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests logging queries against multiple targets on the same connection.
|
||||
*/
|
||||
function testEnableTargetLogging() {
|
||||
// Clone the primary credentials to a replica connection and to another fake
|
||||
// connection.
|
||||
$connection_info = Database::getConnectionInfo('default');
|
||||
Database::addConnectionInfo('default', 'replica', $connection_info['default']);
|
||||
|
||||
Database::startLog('testing1');
|
||||
|
||||
db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25))->fetchCol();
|
||||
|
||||
db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'), array('target' => 'replica'));//->fetchCol();
|
||||
|
||||
$queries1 = Database::getLog('testing1');
|
||||
|
||||
$this->assertEqual(count($queries1), 2, 'Recorded queries from all targets.');
|
||||
$this->assertEqual($queries1[0]['target'], 'default', 'First query used default target.');
|
||||
$this->assertEqual($queries1[1]['target'], 'replica', 'Second query used replica target.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that logs to separate targets use the same connection properly.
|
||||
*
|
||||
* This test is identical to the one above, except that it doesn't create
|
||||
* a fake target so the query should fall back to running on the default
|
||||
* target.
|
||||
*/
|
||||
function testEnableTargetLoggingNoTarget() {
|
||||
Database::startLog('testing1');
|
||||
|
||||
db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25))->fetchCol();
|
||||
|
||||
// We use "fake" here as a target because any non-existent target will do.
|
||||
// However, because all of the tests in this class share a single page
|
||||
// request there is likely to be a target of "replica" from one of the other
|
||||
// unit tests, so we use a target here that we know with absolute certainty
|
||||
// does not exist.
|
||||
db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'), array('target' => 'fake'))->fetchCol();
|
||||
|
||||
$queries1 = Database::getLog('testing1');
|
||||
|
||||
$this->assertEqual(count($queries1), 2, 'Recorded queries from all targets.');
|
||||
$this->assertEqual($queries1[0]['target'], 'default', 'First query used default target.');
|
||||
$this->assertEqual($queries1[1]['target'], 'default', 'Second query used default target as fallback.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that we can log queries separately on different connections.
|
||||
*/
|
||||
function testEnableMultiConnectionLogging() {
|
||||
// Clone the primary credentials to a fake connection.
|
||||
// That both connections point to the same physical database is irrelevant.
|
||||
$connection_info = Database::getConnectionInfo('default');
|
||||
Database::addConnectionInfo('test2', 'default', $connection_info['default']);
|
||||
|
||||
Database::startLog('testing1');
|
||||
Database::startLog('testing1', 'test2');
|
||||
|
||||
db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25))->fetchCol();
|
||||
|
||||
$old_key = db_set_active('test2');
|
||||
|
||||
db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'), array('target' => 'replica'))->fetchCol();
|
||||
|
||||
db_set_active($old_key);
|
||||
|
||||
$queries1 = Database::getLog('testing1');
|
||||
$queries2 = Database::getLog('testing1', 'test2');
|
||||
|
||||
$this->assertEqual(count($queries1), 1, 'Correct number of queries recorded for first connection.');
|
||||
$this->assertEqual(count($queries2), 1, 'Correct number of queries recorded for second connection.');
|
||||
}
|
||||
}
|
||||
232
core/tests/Drupal/KernelTests/Core/Database/MergeTest.php
Normal file
232
core/tests/Drupal/KernelTests/Core/Database/MergeTest.php
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Database;
|
||||
|
||||
use Drupal\Core\Database\Query\Merge;
|
||||
use Drupal\Core\Database\Query\InvalidMergeQueryException;
|
||||
|
||||
/**
|
||||
* Tests the MERGE query builder.
|
||||
*
|
||||
* @group Database
|
||||
*/
|
||||
class MergeTest extends DatabaseTestBase {
|
||||
|
||||
/**
|
||||
* Confirms that we can merge-insert a record successfully.
|
||||
*/
|
||||
function testMergeInsert() {
|
||||
$num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
|
||||
|
||||
$result = db_merge('test_people')
|
||||
->key('job', 'Presenter')
|
||||
->fields(array(
|
||||
'age' => 31,
|
||||
'name' => 'Tiffany',
|
||||
))
|
||||
->execute();
|
||||
|
||||
$this->assertEqual($result, Merge::STATUS_INSERT, 'Insert status returned.');
|
||||
|
||||
$num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
|
||||
$this->assertEqual($num_records_before + 1, $num_records_after, 'Merge inserted properly.');
|
||||
|
||||
$person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Presenter'))->fetch();
|
||||
$this->assertEqual($person->name, 'Tiffany', 'Name set correctly.');
|
||||
$this->assertEqual($person->age, 31, 'Age set correctly.');
|
||||
$this->assertEqual($person->job, 'Presenter', 'Job set correctly.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms that we can merge-update a record successfully.
|
||||
*/
|
||||
function testMergeUpdate() {
|
||||
$num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
|
||||
|
||||
$result = db_merge('test_people')
|
||||
->key('job', 'Speaker')
|
||||
->fields(array(
|
||||
'age' => 31,
|
||||
'name' => 'Tiffany',
|
||||
))
|
||||
->execute();
|
||||
|
||||
$this->assertEqual($result, Merge::STATUS_UPDATE, 'Update status returned.');
|
||||
|
||||
$num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
|
||||
$this->assertEqual($num_records_before, $num_records_after, 'Merge updated properly.');
|
||||
|
||||
$person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch();
|
||||
$this->assertEqual($person->name, 'Tiffany', 'Name set correctly.');
|
||||
$this->assertEqual($person->age, 31, 'Age set correctly.');
|
||||
$this->assertEqual($person->job, 'Speaker', 'Job set correctly.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms that we can merge-update a record successfully.
|
||||
*
|
||||
* This test varies from the previous test because it manually defines which
|
||||
* fields are inserted, and which fields are updated.
|
||||
*/
|
||||
function testMergeUpdateExcept() {
|
||||
$num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
|
||||
|
||||
db_merge('test_people')
|
||||
->key('job', 'Speaker')
|
||||
->insertFields(array('age' => 31))
|
||||
->updateFields(array('name' => 'Tiffany'))
|
||||
->execute();
|
||||
|
||||
$num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
|
||||
$this->assertEqual($num_records_before, $num_records_after, 'Merge updated properly.');
|
||||
|
||||
$person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch();
|
||||
$this->assertEqual($person->name, 'Tiffany', 'Name set correctly.');
|
||||
$this->assertEqual($person->age, 30, 'Age skipped correctly.');
|
||||
$this->assertEqual($person->job, 'Speaker', 'Job set correctly.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms that we can merge-update a record, with alternate replacement.
|
||||
*/
|
||||
function testMergeUpdateExplicit() {
|
||||
$num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
|
||||
|
||||
db_merge('test_people')
|
||||
->key('job', 'Speaker')
|
||||
->insertFields(array(
|
||||
'age' => 31,
|
||||
'name' => 'Tiffany',
|
||||
))
|
||||
->updateFields(array(
|
||||
'name' => 'Joe',
|
||||
))
|
||||
->execute();
|
||||
|
||||
$num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
|
||||
$this->assertEqual($num_records_before, $num_records_after, 'Merge updated properly.');
|
||||
|
||||
$person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch();
|
||||
$this->assertEqual($person->name, 'Joe', 'Name set correctly.');
|
||||
$this->assertEqual($person->age, 30, 'Age skipped correctly.');
|
||||
$this->assertEqual($person->job, 'Speaker', 'Job set correctly.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms that we can merge-update a record successfully, with expressions.
|
||||
*/
|
||||
function testMergeUpdateExpression() {
|
||||
$num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
|
||||
|
||||
$age_before = db_query('SELECT age FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetchField();
|
||||
|
||||
// This is a very contrived example, as I have no idea why you'd want to
|
||||
// change age this way, but that's beside the point.
|
||||
// Note that we are also double-setting age here, once as a literal and
|
||||
// once as an expression. This test will only pass if the expression wins,
|
||||
// which is what is supposed to happen.
|
||||
db_merge('test_people')
|
||||
->key('job', 'Speaker')
|
||||
->fields(array('name' => 'Tiffany'))
|
||||
->insertFields(array('age' => 31))
|
||||
->expression('age', 'age + :age', array(':age' => 4))
|
||||
->execute();
|
||||
|
||||
$num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
|
||||
$this->assertEqual($num_records_before, $num_records_after, 'Merge updated properly.');
|
||||
|
||||
$person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch();
|
||||
$this->assertEqual($person->name, 'Tiffany', 'Name set correctly.');
|
||||
$this->assertEqual($person->age, $age_before + 4, 'Age updated correctly.');
|
||||
$this->assertEqual($person->job, 'Speaker', 'Job set correctly.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that we can merge-insert without any update fields.
|
||||
*/
|
||||
function testMergeInsertWithoutUpdate() {
|
||||
$num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
|
||||
|
||||
db_merge('test_people')
|
||||
->key('job', 'Presenter')
|
||||
->execute();
|
||||
|
||||
$num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
|
||||
$this->assertEqual($num_records_before + 1, $num_records_after, 'Merge inserted properly.');
|
||||
|
||||
$person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Presenter'))->fetch();
|
||||
$this->assertEqual($person->name, '', 'Name set correctly.');
|
||||
$this->assertEqual($person->age, 0, 'Age set correctly.');
|
||||
$this->assertEqual($person->job, 'Presenter', 'Job set correctly.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms that we can merge-update without any update fields.
|
||||
*/
|
||||
function testMergeUpdateWithoutUpdate() {
|
||||
$num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
|
||||
|
||||
db_merge('test_people')
|
||||
->key('job', 'Speaker')
|
||||
->execute();
|
||||
|
||||
$num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
|
||||
$this->assertEqual($num_records_before, $num_records_after, 'Merge skipped properly.');
|
||||
|
||||
$person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch();
|
||||
$this->assertEqual($person->name, 'Meredith', 'Name skipped correctly.');
|
||||
$this->assertEqual($person->age, 30, 'Age skipped correctly.');
|
||||
$this->assertEqual($person->job, 'Speaker', 'Job skipped correctly.');
|
||||
|
||||
db_merge('test_people')
|
||||
->key('job', 'Speaker')
|
||||
->insertFields(array('age' => 31))
|
||||
->execute();
|
||||
|
||||
$num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
|
||||
$this->assertEqual($num_records_before, $num_records_after, 'Merge skipped properly.');
|
||||
|
||||
$person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch();
|
||||
$this->assertEqual($person->name, 'Meredith', 'Name skipped correctly.');
|
||||
$this->assertEqual($person->age, 30, 'Age skipped correctly.');
|
||||
$this->assertEqual($person->job, 'Speaker', 'Job skipped correctly.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that an invalid merge query throws an exception.
|
||||
*/
|
||||
function testInvalidMerge() {
|
||||
try {
|
||||
// This query will fail because there is no key field specified.
|
||||
// Normally it would throw an exception but we are suppressing it with
|
||||
// the throw_exception option.
|
||||
$options['throw_exception'] = FALSE;
|
||||
db_merge('test_people', $options)
|
||||
->fields(array(
|
||||
'age' => 31,
|
||||
'name' => 'Tiffany',
|
||||
))
|
||||
->execute();
|
||||
$this->pass('$options[\'throw_exception\'] is FALSE, no InvalidMergeQueryException thrown.');
|
||||
}
|
||||
catch (InvalidMergeQueryException $e) {
|
||||
$this->fail('$options[\'throw_exception\'] is FALSE, but InvalidMergeQueryException thrown for invalid query.');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// This query will fail because there is no key field specified.
|
||||
db_merge('test_people')
|
||||
->fields(array(
|
||||
'age' => 31,
|
||||
'name' => 'Tiffany',
|
||||
))
|
||||
->execute();
|
||||
}
|
||||
catch (InvalidMergeQueryException $e) {
|
||||
$this->pass('InvalidMergeQueryException thrown for invalid query.');
|
||||
return;
|
||||
}
|
||||
$this->fail('No InvalidMergeQueryException thrown');
|
||||
}
|
||||
}
|
||||
38
core/tests/Drupal/KernelTests/Core/Database/NextIdTest.php
Normal file
38
core/tests/Drupal/KernelTests/Core/Database/NextIdTest.php
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Database;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests the sequences API.
|
||||
*
|
||||
* @group Database
|
||||
*/
|
||||
class NextIdTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* The modules to enable.
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('system');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installSchema('system', 'sequences');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the sequences API works.
|
||||
*/
|
||||
function testDbNextId() {
|
||||
$first = db_next_id();
|
||||
$second = db_next_id();
|
||||
// We can test for exact increase in here because we know there is no
|
||||
// other process operating on these tables -- normally we could only
|
||||
// expect $second > $first.
|
||||
$this->assertEqual($first + 1, $second, 'The second call from a sequence provides a number increased by one.');
|
||||
$result = db_next_id(1000);
|
||||
$this->assertEqual($result, 1001, 'Sequence provides a larger number than the existing ID.');
|
||||
}
|
||||
}
|
||||
148
core/tests/Drupal/KernelTests/Core/Database/QueryTest.php
Normal file
148
core/tests/Drupal/KernelTests/Core/Database/QueryTest.php
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Database;
|
||||
|
||||
/**
|
||||
* Tests Drupal's extended prepared statement syntax..
|
||||
*
|
||||
* @group Database
|
||||
*/
|
||||
class QueryTest extends DatabaseTestBase {
|
||||
|
||||
/**
|
||||
* Tests that we can pass an array of values directly in the query.
|
||||
*/
|
||||
function testArraySubstitution() {
|
||||
$names = db_query('SELECT name FROM {test} WHERE age IN ( :ages[] ) ORDER BY age', array(':ages[]' => array(25, 26, 27)))->fetchAll();
|
||||
$this->assertEqual(count($names), 3, 'Correct number of names returned');
|
||||
|
||||
$names = db_query('SELECT name FROM {test} WHERE age IN ( :ages[] ) ORDER BY age', array(':ages[]' => array(25)))->fetchAll();
|
||||
$this->assertEqual(count($names), 1, 'Correct number of names returned');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that we can not pass a scalar value when an array is expected.
|
||||
*/
|
||||
function testScalarSubstitution() {
|
||||
try {
|
||||
$names = db_query('SELECT name FROM {test} WHERE age IN ( :ages[] ) ORDER BY age', array(':ages[]' => 25))->fetchAll();
|
||||
$this->fail('Array placeholder with scalar argument should result in an exception.');
|
||||
}
|
||||
catch (\InvalidArgumentException $e) {
|
||||
$this->pass('Array placeholder with scalar argument should result in an exception.');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests SQL injection via database query array arguments.
|
||||
*/
|
||||
public function testArrayArgumentsSQLInjection() {
|
||||
// Attempt SQL injection and verify that it does not work.
|
||||
$condition = array(
|
||||
"1 ;INSERT INTO {test} (name) VALUES ('test12345678'); -- " => '',
|
||||
'1' => '',
|
||||
);
|
||||
try {
|
||||
db_query("SELECT * FROM {test} WHERE name = :name", array(':name' => $condition))->fetchObject();
|
||||
$this->fail('SQL injection attempt via array arguments should result in a database exception.');
|
||||
}
|
||||
catch (\InvalidArgumentException $e) {
|
||||
$this->pass('SQL injection attempt via array arguments should result in a database exception.');
|
||||
}
|
||||
|
||||
// Test that the insert query that was used in the SQL injection attempt did
|
||||
// not result in a row being inserted in the database.
|
||||
$result = db_select('test')
|
||||
->condition('name', 'test12345678')
|
||||
->countQuery()
|
||||
->execute()
|
||||
->fetchField();
|
||||
$this->assertFalse($result, 'SQL injection attempt did not result in a row being inserted in the database table.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests SQL injection via condition operator.
|
||||
*/
|
||||
public function testConditionOperatorArgumentsSQLInjection() {
|
||||
$injection = "IS NOT NULL) ;INSERT INTO {test} (name) VALUES ('test12345678'); -- ";
|
||||
|
||||
// Convert errors to exceptions for testing purposes below.
|
||||
set_error_handler(function ($severity, $message, $filename, $lineno) {
|
||||
throw new \ErrorException($message, 0, $severity, $filename, $lineno);
|
||||
});
|
||||
try {
|
||||
$result = db_select('test', 't')
|
||||
->fields('t')
|
||||
->condition('name', 1, $injection)
|
||||
->execute();
|
||||
$this->fail('Should not be able to attempt SQL injection via condition operator.');
|
||||
}
|
||||
catch (\ErrorException $e) {
|
||||
$this->pass('SQL injection attempt via condition arguments should result in a database exception.');
|
||||
}
|
||||
|
||||
// Test that the insert query that was used in the SQL injection attempt did
|
||||
// not result in a row being inserted in the database.
|
||||
$result = db_select('test')
|
||||
->condition('name', 'test12345678')
|
||||
->countQuery()
|
||||
->execute()
|
||||
->fetchField();
|
||||
$this->assertFalse($result, 'SQL injection attempt did not result in a row being inserted in the database table.');
|
||||
|
||||
// Attempt SQLi via union query with no unsafe characters.
|
||||
$this->enableModules(['user']);
|
||||
$this->installEntitySchema('user');
|
||||
db_insert('test')
|
||||
->fields(['name' => '123456'])
|
||||
->execute();
|
||||
$injection = "= 1 UNION ALL SELECT password FROM user WHERE uid =";
|
||||
|
||||
try {
|
||||
$result = db_select('test', 't')
|
||||
->fields('t', array('name', 'name'))
|
||||
->condition('name', 1, $injection)
|
||||
->execute();
|
||||
$this->fail('Should not be able to attempt SQL injection via operator.');
|
||||
}
|
||||
catch (\ErrorException $e) {
|
||||
$this->pass('SQL injection attempt via condition arguments should result in a database exception.');
|
||||
}
|
||||
|
||||
// Attempt SQLi via union query - uppercase tablename.
|
||||
db_insert('TEST_UPPERCASE')
|
||||
->fields(['name' => 'secrets'])
|
||||
->execute();
|
||||
$injection = "IS NOT NULL) UNION ALL SELECT name FROM {TEST_UPPERCASE} -- ";
|
||||
|
||||
try {
|
||||
$result = db_select('test', 't')
|
||||
->fields('t', array('name'))
|
||||
->condition('name', 1, $injection)
|
||||
->execute();
|
||||
$this->fail('Should not be able to attempt SQL injection via operator.');
|
||||
}
|
||||
catch (\ErrorException $e) {
|
||||
$this->pass('SQL injection attempt via condition arguments should result in a database exception.');
|
||||
}
|
||||
restore_error_handler();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests numeric query parameter expansion in expressions.
|
||||
*
|
||||
* @see \Drupal\Core\Database\Driver\sqlite\Statement::getStatement()
|
||||
* @see http://bugs.php.net/bug.php?id=45259
|
||||
*/
|
||||
public function testNumericExpressionSubstitution() {
|
||||
$count = db_query('SELECT COUNT(*) >= 3 FROM {test}')->fetchField();
|
||||
$this->assertEqual((bool) $count, TRUE);
|
||||
|
||||
$count = db_query('SELECT COUNT(*) >= :count FROM {test}', array(
|
||||
':count' => 3,
|
||||
))->fetchField();
|
||||
$this->assertEqual((bool) $count, TRUE);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Database;
|
||||
|
||||
/**
|
||||
* Tests the Range query functionality.
|
||||
*
|
||||
* @group Database
|
||||
*/
|
||||
class RangeQueryTest extends DatabaseTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('database_test');
|
||||
|
||||
/**
|
||||
* Confirms that range queries work and return the correct result.
|
||||
*/
|
||||
function testRangeQuery() {
|
||||
// Test if return correct number of rows.
|
||||
$range_rows = db_query_range("SELECT name FROM {test} ORDER BY name", 1, 3)->fetchAll();
|
||||
$this->assertEqual(count($range_rows), 3, 'Range query work and return correct number of rows.');
|
||||
|
||||
// Test if return target data.
|
||||
$raw_rows = db_query('SELECT name FROM {test} ORDER BY name')->fetchAll();
|
||||
$raw_rows = array_slice($raw_rows, 1, 3);
|
||||
$this->assertEqual($range_rows, $raw_rows);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Database;
|
||||
|
||||
/**
|
||||
* Regression tests cases for the database layer.
|
||||
*
|
||||
* @group Database
|
||||
*/
|
||||
class RegressionTest extends DatabaseTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('node', 'user');
|
||||
|
||||
/**
|
||||
* Ensures that non-ASCII UTF-8 data is stored in the database properly.
|
||||
*/
|
||||
function testRegression_310447() {
|
||||
// That's a 255 character UTF-8 string.
|
||||
$job = str_repeat("é", 255);
|
||||
db_insert('test')
|
||||
->fields(array(
|
||||
'name' => $this->randomMachineName(),
|
||||
'age' => 20,
|
||||
'job' => $job,
|
||||
))->execute();
|
||||
|
||||
$from_database = db_query('SELECT job FROM {test} WHERE job = :job', array(':job' => $job))->fetchField();
|
||||
$this->assertIdentical($job, $from_database, 'The database handles UTF-8 characters cleanly.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the db_table_exists() function.
|
||||
*/
|
||||
function testDBTableExists() {
|
||||
$this->assertIdentical(TRUE, db_table_exists('test'), 'Returns true for existent table.');
|
||||
$this->assertIdentical(FALSE, db_table_exists('nosuchtable'), 'Returns false for nonexistent table.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the db_field_exists() function.
|
||||
*/
|
||||
function testDBFieldExists() {
|
||||
$this->assertIdentical(TRUE, db_field_exists('test', 'name'), 'Returns true for existent column.');
|
||||
$this->assertIdentical(FALSE, db_field_exists('test', 'nosuchcolumn'), 'Returns false for nonexistent column.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the db_index_exists() function.
|
||||
*/
|
||||
function testDBIndexExists() {
|
||||
$this->assertIdentical(TRUE, db_index_exists('test', 'ages'), 'Returns true for existent index.');
|
||||
$this->assertIdentical(FALSE, db_index_exists('test', 'nosuchindex'), 'Returns false for nonexistent index.');
|
||||
}
|
||||
}
|
||||
791
core/tests/Drupal/KernelTests/Core/Database/SchemaTest.php
Normal file
791
core/tests/Drupal/KernelTests/Core/Database/SchemaTest.php
Normal file
|
|
@ -0,0 +1,791 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Database;
|
||||
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\Database\SchemaException;
|
||||
use Drupal\Core\Database\SchemaObjectDoesNotExistException;
|
||||
use Drupal\Core\Database\SchemaObjectExistsException;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
|
||||
/**
|
||||
* Tests table creation and modification via the schema API.
|
||||
*
|
||||
* @group Database
|
||||
*/
|
||||
class SchemaTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* A global counter for table and field creation.
|
||||
*/
|
||||
protected $counter;
|
||||
|
||||
/**
|
||||
* Tests database interactions.
|
||||
*/
|
||||
function testSchema() {
|
||||
// Try creating a table.
|
||||
$table_specification = array(
|
||||
'description' => 'Schema table description may contain "quotes" and could be long—very long indeed.',
|
||||
'fields' => array(
|
||||
'id' => array(
|
||||
'type' => 'int',
|
||||
'default' => NULL,
|
||||
),
|
||||
'test_field' => array(
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'description' => 'Schema table description may contain "quotes" and could be long—very long indeed. There could be "multiple quoted regions".',
|
||||
),
|
||||
'test_field_string' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 20,
|
||||
'not null' => TRUE,
|
||||
'default' => "'\"funky default'\"",
|
||||
'description' => 'Schema column description for string.',
|
||||
),
|
||||
'test_field_string_ascii' => array(
|
||||
'type' => 'varchar_ascii',
|
||||
'length' => 255,
|
||||
'description' => 'Schema column description for ASCII string.',
|
||||
),
|
||||
),
|
||||
);
|
||||
db_create_table('test_table', $table_specification);
|
||||
|
||||
// Assert that the table exists.
|
||||
$this->assertTrue(db_table_exists('test_table'), 'The table exists.');
|
||||
|
||||
// Assert that the table comment has been set.
|
||||
$this->checkSchemaComment($table_specification['description'], 'test_table');
|
||||
|
||||
// Assert that the column comment has been set.
|
||||
$this->checkSchemaComment($table_specification['fields']['test_field']['description'], 'test_table', 'test_field');
|
||||
|
||||
if (Database::getConnection()->databaseType() == 'mysql') {
|
||||
// Make sure that varchar fields have the correct collation.
|
||||
$columns = db_query('SHOW FULL COLUMNS FROM {test_table}');
|
||||
foreach ($columns as $column) {
|
||||
if ($column->Field == 'test_field_string') {
|
||||
$string_check = ($column->Collation == 'utf8mb4_general_ci');
|
||||
}
|
||||
if ($column->Field == 'test_field_string_ascii') {
|
||||
$string_ascii_check = ($column->Collation == 'ascii_general_ci');
|
||||
}
|
||||
}
|
||||
$this->assertTrue(!empty($string_check), 'string field has the right collation.');
|
||||
$this->assertTrue(!empty($string_ascii_check), 'ASCII string field has the right collation.');
|
||||
}
|
||||
|
||||
// An insert without a value for the column 'test_table' should fail.
|
||||
$this->assertFalse($this->tryInsert(), 'Insert without a default failed.');
|
||||
|
||||
// Add a default value to the column.
|
||||
db_field_set_default('test_table', 'test_field', 0);
|
||||
// The insert should now succeed.
|
||||
$this->assertTrue($this->tryInsert(), 'Insert with a default succeeded.');
|
||||
|
||||
// Remove the default.
|
||||
db_field_set_no_default('test_table', 'test_field');
|
||||
// The insert should fail again.
|
||||
$this->assertFalse($this->tryInsert(), 'Insert without a default failed.');
|
||||
|
||||
// Test for fake index and test for the boolean result of indexExists().
|
||||
$index_exists = Database::getConnection()->schema()->indexExists('test_table', 'test_field');
|
||||
$this->assertIdentical($index_exists, FALSE, 'Fake index does not exists');
|
||||
// Add index.
|
||||
db_add_index('test_table', 'test_field', array('test_field'), $table_specification);
|
||||
// Test for created index and test for the boolean result of indexExists().
|
||||
$index_exists = Database::getConnection()->schema()->indexExists('test_table', 'test_field');
|
||||
$this->assertIdentical($index_exists, TRUE, 'Index created.');
|
||||
|
||||
// Rename the table.
|
||||
db_rename_table('test_table', 'test_table2');
|
||||
|
||||
// Index should be renamed.
|
||||
$index_exists = Database::getConnection()->schema()->indexExists('test_table2', 'test_field');
|
||||
$this->assertTrue($index_exists, 'Index was renamed.');
|
||||
|
||||
// We need the default so that we can insert after the rename.
|
||||
db_field_set_default('test_table2', 'test_field', 0);
|
||||
$this->assertFalse($this->tryInsert(), 'Insert into the old table failed.');
|
||||
$this->assertTrue($this->tryInsert('test_table2'), 'Insert into the new table succeeded.');
|
||||
|
||||
// We should have successfully inserted exactly two rows.
|
||||
$count = db_query('SELECT COUNT(*) FROM {test_table2}')->fetchField();
|
||||
$this->assertEqual($count, 2, 'Two fields were successfully inserted.');
|
||||
|
||||
// Try to drop the table.
|
||||
db_drop_table('test_table2');
|
||||
$this->assertFalse(db_table_exists('test_table2'), 'The dropped table does not exist.');
|
||||
|
||||
// Recreate the table.
|
||||
db_create_table('test_table', $table_specification);
|
||||
db_field_set_default('test_table', 'test_field', 0);
|
||||
db_add_field('test_table', 'test_serial', array('type' => 'int', 'not null' => TRUE, 'default' => 0, 'description' => 'Added column description.'));
|
||||
|
||||
// Assert that the column comment has been set.
|
||||
$this->checkSchemaComment('Added column description.', 'test_table', 'test_serial');
|
||||
|
||||
// Change the new field to a serial column.
|
||||
db_change_field('test_table', 'test_serial', 'test_serial', array('type' => 'serial', 'not null' => TRUE, 'description' => 'Changed column description.'), array('primary key' => array('test_serial')));
|
||||
|
||||
// Assert that the column comment has been set.
|
||||
$this->checkSchemaComment('Changed column description.', 'test_table', 'test_serial');
|
||||
|
||||
$this->assertTrue($this->tryInsert(), 'Insert with a serial succeeded.');
|
||||
$max1 = db_query('SELECT MAX(test_serial) FROM {test_table}')->fetchField();
|
||||
$this->assertTrue($this->tryInsert(), 'Insert with a serial succeeded.');
|
||||
$max2 = db_query('SELECT MAX(test_serial) FROM {test_table}')->fetchField();
|
||||
$this->assertTrue($max2 > $max1, 'The serial is monotone.');
|
||||
|
||||
$count = db_query('SELECT COUNT(*) FROM {test_table}')->fetchField();
|
||||
$this->assertEqual($count, 2, 'There were two rows.');
|
||||
|
||||
// Test renaming of keys and constraints.
|
||||
db_drop_table('test_table');
|
||||
$table_specification = array(
|
||||
'fields' => array(
|
||||
'id' => array(
|
||||
'type' => 'serial',
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'test_field' => array(
|
||||
'type' => 'int',
|
||||
'default' => 0,
|
||||
),
|
||||
),
|
||||
'primary key' => array('id'),
|
||||
'unique keys' => array(
|
||||
'test_field' => array('test_field'),
|
||||
),
|
||||
);
|
||||
db_create_table('test_table', $table_specification);
|
||||
|
||||
// Tests for indexes are Database specific.
|
||||
$db_type = Database::getConnection()->databaseType();
|
||||
|
||||
// Test for existing primary and unique keys.
|
||||
switch ($db_type) {
|
||||
case 'pgsql':
|
||||
$primary_key_exists = Database::getConnection()->schema()->constraintExists('test_table', '__pkey');
|
||||
$unique_key_exists = Database::getConnection()->schema()->constraintExists('test_table', 'test_field' . '__key');
|
||||
break;
|
||||
case 'sqlite':
|
||||
// SQLite does not create a standalone index for primary keys.
|
||||
$primary_key_exists = TRUE;
|
||||
$unique_key_exists = Database::getConnection()->schema()->indexExists('test_table', 'test_field');
|
||||
break;
|
||||
default:
|
||||
$primary_key_exists = Database::getConnection()->schema()->indexExists('test_table', 'PRIMARY');
|
||||
$unique_key_exists = Database::getConnection()->schema()->indexExists('test_table', 'test_field');
|
||||
break;
|
||||
}
|
||||
$this->assertIdentical($primary_key_exists, TRUE, 'Primary key created.');
|
||||
$this->assertIdentical($unique_key_exists, TRUE, 'Unique key created.');
|
||||
|
||||
db_rename_table('test_table', 'test_table2');
|
||||
|
||||
// Test for renamed primary and unique keys.
|
||||
switch ($db_type) {
|
||||
case 'pgsql':
|
||||
$renamed_primary_key_exists = Database::getConnection()->schema()->constraintExists('test_table2', '__pkey');
|
||||
$renamed_unique_key_exists = Database::getConnection()->schema()->constraintExists('test_table2', 'test_field' . '__key');
|
||||
break;
|
||||
case 'sqlite':
|
||||
// SQLite does not create a standalone index for primary keys.
|
||||
$renamed_primary_key_exists = TRUE;
|
||||
$renamed_unique_key_exists = Database::getConnection()->schema()->indexExists('test_table2', 'test_field');
|
||||
break;
|
||||
default:
|
||||
$renamed_primary_key_exists = Database::getConnection()->schema()->indexExists('test_table2', 'PRIMARY');
|
||||
$renamed_unique_key_exists = Database::getConnection()->schema()->indexExists('test_table2', 'test_field');
|
||||
break;
|
||||
}
|
||||
$this->assertIdentical($renamed_primary_key_exists, TRUE, 'Primary key was renamed.');
|
||||
$this->assertIdentical($renamed_unique_key_exists, TRUE, 'Unique key was renamed.');
|
||||
|
||||
// For PostgreSQL check in addition that sequence was renamed.
|
||||
if ($db_type == 'pgsql') {
|
||||
// Get information about new table.
|
||||
$info = Database::getConnection()->schema()->queryTableInformation('test_table2');
|
||||
$sequence_name = Database::getConnection()->schema()->prefixNonTable('test_table2', 'id', 'seq');
|
||||
$this->assertEqual($sequence_name, current($info->sequences), 'Sequence was renamed.');
|
||||
}
|
||||
|
||||
// Use database specific data type and ensure that table is created.
|
||||
$table_specification = array(
|
||||
'description' => 'Schema table description.',
|
||||
'fields' => array(
|
||||
'timestamp' => array(
|
||||
'mysql_type' => 'timestamp',
|
||||
'pgsql_type' => 'timestamp',
|
||||
'sqlite_type' => 'datetime',
|
||||
'not null' => FALSE,
|
||||
'default' => NULL,
|
||||
),
|
||||
),
|
||||
);
|
||||
try {
|
||||
db_create_table('test_timestamp', $table_specification);
|
||||
}
|
||||
catch (\Exception $e) {}
|
||||
$this->assertTrue(db_table_exists('test_timestamp'), 'Table with database specific datatype was created.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that indexes on string fields are limited to 191 characters on MySQL.
|
||||
*
|
||||
* @see \Drupal\Core\Database\Driver\mysql\Schema::getNormalizedIndexes()
|
||||
*/
|
||||
function testIndexLength() {
|
||||
if (Database::getConnection()->databaseType() != 'mysql') {
|
||||
return;
|
||||
}
|
||||
$table_specification = array(
|
||||
'fields' => array(
|
||||
'id' => array(
|
||||
'type' => 'int',
|
||||
'default' => NULL,
|
||||
),
|
||||
'test_field_text' => array(
|
||||
'type' => 'text',
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'test_field_string_long' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
'test_field_string_ascii_long' => array(
|
||||
'type' => 'varchar_ascii',
|
||||
'length' => 255,
|
||||
),
|
||||
'test_field_string_short' => array(
|
||||
'type' => 'varchar',
|
||||
'length' => 128,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
),
|
||||
'indexes' => array(
|
||||
'test_regular' => array(
|
||||
'test_field_text',
|
||||
'test_field_string_long',
|
||||
'test_field_string_ascii_long',
|
||||
'test_field_string_short',
|
||||
),
|
||||
'test_length' => array(
|
||||
array('test_field_text', 128),
|
||||
array('test_field_string_long', 128),
|
||||
array('test_field_string_ascii_long', 128),
|
||||
array('test_field_string_short', 128),
|
||||
),
|
||||
'test_mixed' => array(
|
||||
array('test_field_text', 200),
|
||||
'test_field_string_long',
|
||||
array('test_field_string_ascii_long', 200),
|
||||
'test_field_string_short',
|
||||
),
|
||||
),
|
||||
);
|
||||
db_create_table('test_table_index_length', $table_specification);
|
||||
|
||||
$schema_object = Database::getConnection()->schema();
|
||||
|
||||
// Ensure expected exception thrown when adding index with missing info.
|
||||
$expected_exception_message = "MySQL needs the 'test_field_text' field specification in order to normalize the 'test_regular' index";
|
||||
$missing_field_spec = $table_specification;
|
||||
unset($missing_field_spec['fields']['test_field_text']);
|
||||
try {
|
||||
$schema_object->addIndex('test_table_index_length', 'test_separate', [['test_field_text', 200]], $missing_field_spec);
|
||||
$this->fail('SchemaException not thrown when adding index with missing information.');
|
||||
}
|
||||
catch (SchemaException $e) {
|
||||
$this->assertEqual($expected_exception_message, $e->getMessage());
|
||||
}
|
||||
|
||||
// Add a separate index.
|
||||
$schema_object->addIndex('test_table_index_length', 'test_separate', [['test_field_text', 200]], $table_specification);
|
||||
$table_specification_with_new_index = $table_specification;
|
||||
$table_specification_with_new_index['indexes']['test_separate'] = [['test_field_text', 200]];
|
||||
|
||||
// Ensure that the exceptions of addIndex are thrown as expected.
|
||||
|
||||
try {
|
||||
$schema_object->addIndex('test_table_index_length', 'test_separate', [['test_field_text', 200]], $table_specification);
|
||||
$this->fail('\Drupal\Core\Database\SchemaObjectExistsException exception missed.');
|
||||
}
|
||||
catch (SchemaObjectExistsException $e) {
|
||||
$this->pass('\Drupal\Core\Database\SchemaObjectExistsException thrown when index already exists.');
|
||||
}
|
||||
|
||||
try {
|
||||
$schema_object->addIndex('test_table_non_existing', 'test_separate', [['test_field_text', 200]], $table_specification);
|
||||
$this->fail('\Drupal\Core\Database\SchemaObjectDoesNotExistException exception missed.');
|
||||
}
|
||||
catch (SchemaObjectDoesNotExistException $e) {
|
||||
$this->pass('\Drupal\Core\Database\SchemaObjectDoesNotExistException thrown when index already exists.');
|
||||
}
|
||||
|
||||
// Get index information.
|
||||
$results = db_query('SHOW INDEX FROM {test_table_index_length}');
|
||||
$expected_lengths = array(
|
||||
'test_regular' => array(
|
||||
'test_field_text' => 191,
|
||||
'test_field_string_long' => 191,
|
||||
'test_field_string_ascii_long' => NULL,
|
||||
'test_field_string_short' => NULL,
|
||||
),
|
||||
'test_length' => array(
|
||||
'test_field_text' => 128,
|
||||
'test_field_string_long' => 128,
|
||||
'test_field_string_ascii_long' => 128,
|
||||
'test_field_string_short' => NULL,
|
||||
),
|
||||
'test_mixed' => array(
|
||||
'test_field_text' => 191,
|
||||
'test_field_string_long' => 191,
|
||||
'test_field_string_ascii_long' => 200,
|
||||
'test_field_string_short' => NULL,
|
||||
),
|
||||
'test_separate' => array(
|
||||
'test_field_text' => 191,
|
||||
),
|
||||
);
|
||||
|
||||
// Count the number of columns defined in the indexes.
|
||||
$column_count = 0;
|
||||
foreach ($table_specification_with_new_index['indexes'] as $index) {
|
||||
foreach ($index as $field) {
|
||||
$column_count++;
|
||||
}
|
||||
}
|
||||
$test_count = 0;
|
||||
foreach ($results as $result) {
|
||||
$this->assertEqual($result->Sub_part, $expected_lengths[$result->Key_name][$result->Column_name], 'Index length matches expected value.');
|
||||
$test_count++;
|
||||
}
|
||||
$this->assertEqual($test_count, $column_count, 'Number of tests matches expected value.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests inserting data into an existing table.
|
||||
*
|
||||
* @param $table
|
||||
* The database table to insert data into.
|
||||
*
|
||||
* @return
|
||||
* TRUE if the insert succeeded, FALSE otherwise.
|
||||
*/
|
||||
function tryInsert($table = 'test_table') {
|
||||
try {
|
||||
db_insert($table)
|
||||
->fields(array('id' => mt_rand(10, 20)))
|
||||
->execute();
|
||||
return TRUE;
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that a table or column comment matches a given description.
|
||||
*
|
||||
* @param $description
|
||||
* The asserted description.
|
||||
* @param $table
|
||||
* The table to test.
|
||||
* @param $column
|
||||
* Optional column to test.
|
||||
*/
|
||||
function checkSchemaComment($description, $table, $column = NULL) {
|
||||
if (method_exists(Database::getConnection()->schema(), 'getComment')) {
|
||||
$comment = Database::getConnection()->schema()->getComment($table, $column);
|
||||
// The schema comment truncation for mysql is different.
|
||||
if (Database::getConnection()->databaseType() == 'mysql') {
|
||||
$max_length = $column ? 255 : 60;
|
||||
$description = Unicode::truncate($description, $max_length, TRUE, TRUE);
|
||||
}
|
||||
$this->assertEqual($comment, $description, 'The comment matches the schema description.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests creating unsigned columns and data integrity thereof.
|
||||
*/
|
||||
function testUnsignedColumns() {
|
||||
// First create the table with just a serial column.
|
||||
$table_name = 'unsigned_table';
|
||||
$table_spec = array(
|
||||
'fields' => array('serial_column' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE)),
|
||||
'primary key' => array('serial_column'),
|
||||
);
|
||||
db_create_table($table_name, $table_spec);
|
||||
|
||||
// Now set up columns for the other types.
|
||||
$types = array('int', 'float', 'numeric');
|
||||
foreach ($types as $type) {
|
||||
$column_spec = array('type' => $type, 'unsigned'=> TRUE);
|
||||
if ($type == 'numeric') {
|
||||
$column_spec += array('precision' => 10, 'scale' => 0);
|
||||
}
|
||||
$column_name = $type . '_column';
|
||||
$table_spec['fields'][$column_name] = $column_spec;
|
||||
db_add_field($table_name, $column_name, $column_spec);
|
||||
}
|
||||
|
||||
// Finally, check each column and try to insert invalid values into them.
|
||||
foreach ($table_spec['fields'] as $column_name => $column_spec) {
|
||||
$this->assertTrue(db_field_exists($table_name, $column_name), format_string('Unsigned @type column was created.', array('@type' => $column_spec['type'])));
|
||||
$this->assertFalse($this->tryUnsignedInsert($table_name, $column_name), format_string('Unsigned @type column rejected a negative value.', array('@type' => $column_spec['type'])));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to insert a negative value into columns defined as unsigned.
|
||||
*
|
||||
* @param $table_name
|
||||
* The table to insert.
|
||||
* @param $column_name
|
||||
* The column to insert.
|
||||
*
|
||||
* @return
|
||||
* TRUE if the insert succeeded, FALSE otherwise.
|
||||
*/
|
||||
function tryUnsignedInsert($table_name, $column_name) {
|
||||
try {
|
||||
db_insert($table_name)
|
||||
->fields(array($column_name => -1))
|
||||
->execute();
|
||||
return TRUE;
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests adding columns to an existing table.
|
||||
*/
|
||||
function testSchemaAddField() {
|
||||
// Test varchar types.
|
||||
foreach (array(1, 32, 128, 256, 512) as $length) {
|
||||
$base_field_spec = array(
|
||||
'type' => 'varchar',
|
||||
'length' => $length,
|
||||
);
|
||||
$variations = array(
|
||||
array('not null' => FALSE),
|
||||
array('not null' => FALSE, 'default' => '7'),
|
||||
array('not null' => FALSE, 'default' => substr('"thing"', 0, $length)),
|
||||
array('not null' => FALSE, 'default' => substr("\"'hing", 0, $length)),
|
||||
array('not null' => TRUE, 'initial' => 'd'),
|
||||
array('not null' => FALSE, 'default' => NULL),
|
||||
array('not null' => TRUE, 'initial' => 'd', 'default' => '7'),
|
||||
);
|
||||
|
||||
foreach ($variations as $variation) {
|
||||
$field_spec = $variation + $base_field_spec;
|
||||
$this->assertFieldAdditionRemoval($field_spec);
|
||||
}
|
||||
}
|
||||
|
||||
// Test int and float types.
|
||||
foreach (array('int', 'float') as $type) {
|
||||
foreach (array('tiny', 'small', 'medium', 'normal', 'big') as $size) {
|
||||
$base_field_spec = array(
|
||||
'type' => $type,
|
||||
'size' => $size,
|
||||
);
|
||||
$variations = array(
|
||||
array('not null' => FALSE),
|
||||
array('not null' => FALSE, 'default' => 7),
|
||||
array('not null' => TRUE, 'initial' => 1),
|
||||
array('not null' => TRUE, 'initial' => 1, 'default' => 7),
|
||||
);
|
||||
|
||||
foreach ($variations as $variation) {
|
||||
$field_spec = $variation + $base_field_spec;
|
||||
$this->assertFieldAdditionRemoval($field_spec);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test numeric types.
|
||||
foreach (array(1, 5, 10, 40, 65) as $precision) {
|
||||
foreach (array(0, 2, 10, 30) as $scale) {
|
||||
// Skip combinations where precision is smaller than scale.
|
||||
if ($precision <= $scale) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$base_field_spec = array(
|
||||
'type' => 'numeric',
|
||||
'scale' => $scale,
|
||||
'precision' => $precision,
|
||||
);
|
||||
$variations = array(
|
||||
array('not null' => FALSE),
|
||||
array('not null' => FALSE, 'default' => 7),
|
||||
array('not null' => TRUE, 'initial' => 1),
|
||||
array('not null' => TRUE, 'initial' => 1, 'default' => 7),
|
||||
);
|
||||
|
||||
foreach ($variations as $variation) {
|
||||
$field_spec = $variation + $base_field_spec;
|
||||
$this->assertFieldAdditionRemoval($field_spec);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that a given field can be added and removed from a table.
|
||||
*
|
||||
* The addition test covers both defining a field of a given specification
|
||||
* when initially creating at table and extending an existing table.
|
||||
*
|
||||
* @param $field_spec
|
||||
* The schema specification of the field.
|
||||
*/
|
||||
protected function assertFieldAdditionRemoval($field_spec) {
|
||||
// Try creating the field on a new table.
|
||||
$table_name = 'test_table_' . ($this->counter++);
|
||||
$table_spec = array(
|
||||
'fields' => array(
|
||||
'serial_column' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE),
|
||||
'test_field' => $field_spec,
|
||||
),
|
||||
'primary key' => array('serial_column'),
|
||||
);
|
||||
db_create_table($table_name, $table_spec);
|
||||
$this->pass(format_string('Table %table created.', array('%table' => $table_name)));
|
||||
|
||||
// Check the characteristics of the field.
|
||||
$this->assertFieldCharacteristics($table_name, 'test_field', $field_spec);
|
||||
|
||||
// Clean-up.
|
||||
db_drop_table($table_name);
|
||||
|
||||
// Try adding a field to an existing table.
|
||||
$table_name = 'test_table_' . ($this->counter++);
|
||||
$table_spec = array(
|
||||
'fields' => array(
|
||||
'serial_column' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE),
|
||||
),
|
||||
'primary key' => array('serial_column'),
|
||||
);
|
||||
db_create_table($table_name, $table_spec);
|
||||
$this->pass(format_string('Table %table created.', array('%table' => $table_name)));
|
||||
|
||||
// Insert some rows to the table to test the handling of initial values.
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
db_insert($table_name)
|
||||
->useDefaults(array('serial_column'))
|
||||
->execute();
|
||||
}
|
||||
|
||||
db_add_field($table_name, 'test_field', $field_spec);
|
||||
$this->pass(format_string('Column %column created.', array('%column' => 'test_field')));
|
||||
|
||||
// Check the characteristics of the field.
|
||||
$this->assertFieldCharacteristics($table_name, 'test_field', $field_spec);
|
||||
|
||||
// Clean-up.
|
||||
db_drop_field($table_name, 'test_field');
|
||||
|
||||
// Add back the field and then try to delete a field which is also a primary
|
||||
// key.
|
||||
db_add_field($table_name, 'test_field', $field_spec);
|
||||
db_drop_field($table_name, 'serial_column');
|
||||
db_drop_table($table_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that a newly added field has the correct characteristics.
|
||||
*/
|
||||
protected function assertFieldCharacteristics($table_name, $field_name, $field_spec) {
|
||||
// Check that the initial value has been registered.
|
||||
if (isset($field_spec['initial'])) {
|
||||
// There should be no row with a value different then $field_spec['initial'].
|
||||
$count = db_select($table_name)
|
||||
->fields($table_name, array('serial_column'))
|
||||
->condition($field_name, $field_spec['initial'], '<>')
|
||||
->countQuery()
|
||||
->execute()
|
||||
->fetchField();
|
||||
$this->assertEqual($count, 0, 'Initial values filled out.');
|
||||
}
|
||||
|
||||
// Check that the default value has been registered.
|
||||
if (isset($field_spec['default'])) {
|
||||
// Try inserting a row, and check the resulting value of the new column.
|
||||
$id = db_insert($table_name)
|
||||
->useDefaults(array('serial_column'))
|
||||
->execute();
|
||||
$field_value = db_select($table_name)
|
||||
->fields($table_name, array($field_name))
|
||||
->condition('serial_column', $id)
|
||||
->execute()
|
||||
->fetchField();
|
||||
$this->assertEqual($field_value, $field_spec['default'], 'Default value registered.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests changing columns between types.
|
||||
*/
|
||||
function testSchemaChangeField() {
|
||||
$field_specs = array(
|
||||
array('type' => 'int', 'size' => 'normal', 'not null' => FALSE),
|
||||
array('type' => 'int', 'size' => 'normal', 'not null' => TRUE, 'initial' => 1, 'default' => 17),
|
||||
array('type' => 'float', 'size' => 'normal', 'not null' => FALSE),
|
||||
array('type' => 'float', 'size' => 'normal', 'not null' => TRUE, 'initial' => 1, 'default' => 7.3),
|
||||
array('type' => 'numeric', 'scale' => 2, 'precision' => 10, 'not null' => FALSE),
|
||||
array('type' => 'numeric', 'scale' => 2, 'precision' => 10, 'not null' => TRUE, 'initial' => 1, 'default' => 7),
|
||||
);
|
||||
|
||||
foreach ($field_specs as $i => $old_spec) {
|
||||
foreach ($field_specs as $j => $new_spec) {
|
||||
if ($i === $j) {
|
||||
// Do not change a field into itself.
|
||||
continue;
|
||||
}
|
||||
$this->assertFieldChange($old_spec, $new_spec);
|
||||
}
|
||||
}
|
||||
|
||||
$field_specs = array(
|
||||
array('type' => 'varchar_ascii', 'length' => '255'),
|
||||
array('type' => 'varchar', 'length' => '255'),
|
||||
array('type' => 'text'),
|
||||
array('type' => 'blob', 'size' => 'big'),
|
||||
);
|
||||
|
||||
foreach ($field_specs as $i => $old_spec) {
|
||||
foreach ($field_specs as $j => $new_spec) {
|
||||
if ($i === $j) {
|
||||
// Do not change a field into itself.
|
||||
continue;
|
||||
}
|
||||
// Note if the serialized data contained an object this would fail on
|
||||
// Postgres.
|
||||
// @see https://www.drupal.org/node/1031122
|
||||
$this->assertFieldChange($old_spec, $new_spec, serialize(['string' => "This \n has \\\\ some backslash \"*string action.\\n"]));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that a field can be changed from one spec to another.
|
||||
*
|
||||
* @param $old_spec
|
||||
* The beginning field specification.
|
||||
* @param $new_spec
|
||||
* The ending field specification.
|
||||
*/
|
||||
protected function assertFieldChange($old_spec, $new_spec, $test_data = NULL) {
|
||||
$table_name = 'test_table_' . ($this->counter++);
|
||||
$table_spec = array(
|
||||
'fields' => array(
|
||||
'serial_column' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE),
|
||||
'test_field' => $old_spec,
|
||||
),
|
||||
'primary key' => array('serial_column'),
|
||||
);
|
||||
db_create_table($table_name, $table_spec);
|
||||
$this->pass(format_string('Table %table created.', array('%table' => $table_name)));
|
||||
|
||||
// Check the characteristics of the field.
|
||||
$this->assertFieldCharacteristics($table_name, 'test_field', $old_spec);
|
||||
|
||||
// Remove inserted rows.
|
||||
db_truncate($table_name)->execute();
|
||||
|
||||
if ($test_data) {
|
||||
$id = db_insert($table_name)
|
||||
->fields(['test_field'], [$test_data])
|
||||
->execute();
|
||||
}
|
||||
|
||||
// Change the field.
|
||||
db_change_field($table_name, 'test_field', 'test_field', $new_spec);
|
||||
|
||||
if ($test_data) {
|
||||
$field_value = db_select($table_name)
|
||||
->fields($table_name, ['test_field'])
|
||||
->condition('serial_column', $id)
|
||||
->execute()
|
||||
->fetchField();
|
||||
$this->assertIdentical($field_value, $test_data);
|
||||
}
|
||||
|
||||
// Check the field was changed.
|
||||
$this->assertFieldCharacteristics($table_name, 'test_field', $new_spec);
|
||||
|
||||
// Clean-up.
|
||||
db_drop_table($table_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the findTables() method.
|
||||
*/
|
||||
public function testFindTables() {
|
||||
// We will be testing with three tables, two of them using the default
|
||||
// prefix and the third one with an individually specified prefix.
|
||||
|
||||
// Set up a new connection with different connection info.
|
||||
$connection_info = Database::getConnectionInfo();
|
||||
|
||||
// Add per-table prefix to the second table.
|
||||
$new_connection_info = $connection_info['default'];
|
||||
$new_connection_info['prefix']['test_2_table'] = $new_connection_info['prefix']['default'] . '_shared_';
|
||||
Database::addConnectionInfo('test', 'default', $new_connection_info);
|
||||
|
||||
Database::setActiveConnection('test');
|
||||
|
||||
// Create the tables.
|
||||
$table_specification = [
|
||||
'description' => 'Test table.',
|
||||
'fields' => [
|
||||
'id' => [
|
||||
'type' => 'int',
|
||||
'default' => NULL,
|
||||
],
|
||||
],
|
||||
];
|
||||
Database::getConnection()->schema()->createTable('test_1_table', $table_specification);
|
||||
Database::getConnection()->schema()->createTable('test_2_table', $table_specification);
|
||||
Database::getConnection()->schema()->createTable('the_third_table', $table_specification);
|
||||
|
||||
// Check the "all tables" syntax.
|
||||
$tables = Database::getConnection()->schema()->findTables('%');
|
||||
sort($tables);
|
||||
$expected = [
|
||||
// The 'config' table is added by
|
||||
// \Drupal\KernelTests\KernelTestBase::containerBuild().
|
||||
'config',
|
||||
'test_1_table',
|
||||
// This table uses a per-table prefix, yet it is returned as un-prefixed.
|
||||
'test_2_table',
|
||||
'the_third_table',
|
||||
];
|
||||
$this->assertEqual($tables, $expected, 'All tables were found.');
|
||||
|
||||
// Check the restrictive syntax.
|
||||
$tables = Database::getConnection()->schema()->findTables('test_%');
|
||||
sort($tables);
|
||||
$expected = [
|
||||
'test_1_table',
|
||||
'test_2_table',
|
||||
];
|
||||
$this->assertEqual($tables, $expected, 'Two tables were found.');
|
||||
|
||||
// Go back to the initial connection.
|
||||
Database::setActiveConnection('default');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Database;
|
||||
|
||||
/**
|
||||
* Tests cloning Select queries.
|
||||
*
|
||||
* @group Database
|
||||
*/
|
||||
class SelectCloneTest extends DatabaseTestBase {
|
||||
|
||||
/**
|
||||
* Test that subqueries as value within conditions are cloned properly.
|
||||
*/
|
||||
function testSelectConditionSubQueryCloning() {
|
||||
$subquery = db_select('test', 't');
|
||||
$subquery->addField('t', 'id', 'id');
|
||||
$subquery->condition('age', 28, '<');
|
||||
|
||||
$query = db_select('test', 't');
|
||||
$query->addField('t', 'name', 'name');
|
||||
$query->condition('id', $subquery, 'IN');
|
||||
|
||||
$clone = clone $query;
|
||||
// Cloned query should not be altered by the following modification
|
||||
// happening on original query.
|
||||
$subquery->condition('age', 25, '>');
|
||||
|
||||
$clone_result = $clone->countQuery()->execute()->fetchField();
|
||||
$query_result = $query->countQuery()->execute()->fetchField();
|
||||
|
||||
// Make sure the cloned query has not been modified
|
||||
$this->assertEqual(3, $clone_result, 'The cloned query returns the expected number of rows');
|
||||
$this->assertEqual(2, $query_result, 'The query returns the expected number of rows');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,378 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Database;
|
||||
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\Database\RowCountException;
|
||||
use Drupal\user\Entity\User;
|
||||
|
||||
/**
|
||||
* Tests the Select query builder with more complex queries.
|
||||
*
|
||||
* @group Database
|
||||
*/
|
||||
class SelectComplexTest extends DatabaseTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('system', 'user', 'node_access_test', 'field');
|
||||
|
||||
/**
|
||||
* Tests simple JOIN statements.
|
||||
*/
|
||||
function testDefaultJoin() {
|
||||
$query = db_select('test_task', 't');
|
||||
$people_alias = $query->join('test', 'p', 't.pid = p.id');
|
||||
$name_field = $query->addField($people_alias, 'name', 'name');
|
||||
$query->addField('t', 'task', 'task');
|
||||
$priority_field = $query->addField('t', 'priority', 'priority');
|
||||
|
||||
$query->orderBy($priority_field);
|
||||
$result = $query->execute();
|
||||
|
||||
$num_records = 0;
|
||||
$last_priority = 0;
|
||||
foreach ($result as $record) {
|
||||
$num_records++;
|
||||
$this->assertTrue($record->$priority_field >= $last_priority, 'Results returned in correct order.');
|
||||
$this->assertNotEqual($record->$name_field, 'Ringo', 'Taskless person not selected.');
|
||||
$last_priority = $record->$priority_field;
|
||||
}
|
||||
|
||||
$this->assertEqual($num_records, 7, 'Returned the correct number of rows.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests LEFT OUTER joins.
|
||||
*/
|
||||
function testLeftOuterJoin() {
|
||||
$query = db_select('test', 'p');
|
||||
$people_alias = $query->leftJoin('test_task', 't', 't.pid = p.id');
|
||||
$name_field = $query->addField('p', 'name', 'name');
|
||||
$query->addField($people_alias, 'task', 'task');
|
||||
$query->addField($people_alias, 'priority', 'priority');
|
||||
|
||||
$query->orderBy($name_field);
|
||||
$result = $query->execute();
|
||||
|
||||
$num_records = 0;
|
||||
$last_name = 0;
|
||||
|
||||
foreach ($result as $record) {
|
||||
$num_records++;
|
||||
$this->assertTrue(strcmp($record->$name_field, $last_name) >= 0, 'Results returned in correct order.');
|
||||
}
|
||||
|
||||
$this->assertEqual($num_records, 8, 'Returned the correct number of rows.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests GROUP BY clauses.
|
||||
*/
|
||||
function testGroupBy() {
|
||||
$query = db_select('test_task', 't');
|
||||
$count_field = $query->addExpression('COUNT(task)', 'num');
|
||||
$task_field = $query->addField('t', 'task');
|
||||
$query->orderBy($count_field);
|
||||
$query->groupBy($task_field);
|
||||
$result = $query->execute();
|
||||
|
||||
$num_records = 0;
|
||||
$last_count = 0;
|
||||
$records = array();
|
||||
foreach ($result as $record) {
|
||||
$num_records++;
|
||||
$this->assertTrue($record->$count_field >= $last_count, 'Results returned in correct order.');
|
||||
$last_count = $record->$count_field;
|
||||
$records[$record->$task_field] = $record->$count_field;
|
||||
}
|
||||
|
||||
$correct_results = array(
|
||||
'eat' => 1,
|
||||
'sleep' => 2,
|
||||
'code' => 1,
|
||||
'found new band' => 1,
|
||||
'perform at superbowl' => 1,
|
||||
);
|
||||
|
||||
foreach ($correct_results as $task => $count) {
|
||||
$this->assertEqual($records[$task], $count, format_string("Correct number of '@task' records found.", array('@task' => $task)));
|
||||
}
|
||||
|
||||
$this->assertEqual($num_records, 6, 'Returned the correct number of total rows.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests GROUP BY and HAVING clauses together.
|
||||
*/
|
||||
function testGroupByAndHaving() {
|
||||
$query = db_select('test_task', 't');
|
||||
$count_field = $query->addExpression('COUNT(task)', 'num');
|
||||
$task_field = $query->addField('t', 'task');
|
||||
$query->orderBy($count_field);
|
||||
$query->groupBy($task_field);
|
||||
$query->having('COUNT(task) >= 2');
|
||||
$result = $query->execute();
|
||||
|
||||
$num_records = 0;
|
||||
$last_count = 0;
|
||||
$records = array();
|
||||
foreach ($result as $record) {
|
||||
$num_records++;
|
||||
$this->assertTrue($record->$count_field >= 2, 'Record has the minimum count.');
|
||||
$this->assertTrue($record->$count_field >= $last_count, 'Results returned in correct order.');
|
||||
$last_count = $record->$count_field;
|
||||
$records[$record->$task_field] = $record->$count_field;
|
||||
}
|
||||
|
||||
$correct_results = array(
|
||||
'sleep' => 2,
|
||||
);
|
||||
|
||||
foreach ($correct_results as $task => $count) {
|
||||
$this->assertEqual($records[$task], $count, format_string("Correct number of '@task' records found.", array('@task' => $task)));
|
||||
}
|
||||
|
||||
$this->assertEqual($num_records, 1, 'Returned the correct number of total rows.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests range queries.
|
||||
*
|
||||
* The SQL clause varies with the database.
|
||||
*/
|
||||
function testRange() {
|
||||
$query = db_select('test');
|
||||
$query->addField('test', 'name');
|
||||
$query->addField('test', 'age', 'age');
|
||||
$query->range(0, 2);
|
||||
$query_result = $query->countQuery()->execute()->fetchField();
|
||||
|
||||
$this->assertEqual($query_result, 2, 'Returned the correct number of rows.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests distinct queries.
|
||||
*/
|
||||
function testDistinct() {
|
||||
$query = db_select('test_task');
|
||||
$query->addField('test_task', 'task');
|
||||
$query->distinct();
|
||||
$query_result = $query->countQuery()->execute()->fetchField();
|
||||
|
||||
$this->assertEqual($query_result, 6, 'Returned the correct number of rows.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that we can generate a count query from a built query.
|
||||
*/
|
||||
function testCountQuery() {
|
||||
$query = db_select('test');
|
||||
$name_field = $query->addField('test', 'name');
|
||||
$age_field = $query->addField('test', 'age', 'age');
|
||||
$query->orderBy('name');
|
||||
|
||||
$count = $query->countQuery()->execute()->fetchField();
|
||||
|
||||
$this->assertEqual($count, 4, 'Counted the correct number of records.');
|
||||
|
||||
// Now make sure we didn't break the original query! We should still have
|
||||
// all of the fields we asked for.
|
||||
$record = $query->execute()->fetch();
|
||||
$this->assertEqual($record->$name_field, 'George', 'Correct data retrieved.');
|
||||
$this->assertEqual($record->$age_field, 27, 'Correct data retrieved.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests having queries.
|
||||
*/
|
||||
function testHavingCountQuery() {
|
||||
$query = db_select('test')
|
||||
->extend('Drupal\Core\Database\Query\PagerSelectExtender')
|
||||
->groupBy('age')
|
||||
->having('age + 1 > 0');
|
||||
$query->addField('test', 'age');
|
||||
$query->addExpression('age + 1');
|
||||
$count = count($query->execute()->fetchCol());
|
||||
$this->assertEqual($count, 4, 'Counted the correct number of records.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that countQuery removes 'all_fields' statements and ordering clauses.
|
||||
*/
|
||||
function testCountQueryRemovals() {
|
||||
$query = db_select('test');
|
||||
$query->fields('test');
|
||||
$query->orderBy('name');
|
||||
$count = $query->countQuery();
|
||||
|
||||
// Check that the 'all_fields' statement is handled properly.
|
||||
$tables = $query->getTables();
|
||||
$this->assertEqual($tables['test']['all_fields'], 1, 'Query correctly sets \'all_fields\' statement.');
|
||||
$tables = $count->getTables();
|
||||
$this->assertFalse(isset($tables['test']['all_fields']), 'Count query correctly unsets \'all_fields\' statement.');
|
||||
|
||||
// Check that the ordering clause is handled properly.
|
||||
$orderby = $query->getOrderBy();
|
||||
// The orderby string is different for PostgreSQL.
|
||||
// @see Drupal\Core\Database\Driver\pgsql\Select::orderBy()
|
||||
$db_type = Database::getConnection()->databaseType();
|
||||
$this->assertEqual($orderby['name'], ($db_type == 'pgsql' ? 'ASC NULLS FIRST' : 'ASC'), 'Query correctly sets ordering clause.');
|
||||
$orderby = $count->getOrderBy();
|
||||
$this->assertFalse(isset($orderby['name']), 'Count query correctly unsets ordering clause.');
|
||||
|
||||
// Make sure that the count query works.
|
||||
$count = $count->execute()->fetchField();
|
||||
|
||||
$this->assertEqual($count, 4, 'Counted the correct number of records.');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tests that countQuery properly removes fields and expressions.
|
||||
*/
|
||||
function testCountQueryFieldRemovals() {
|
||||
// countQuery should remove all fields and expressions, so this can be
|
||||
// tested by adding a non-existent field and expression: if it ends
|
||||
// up in the query, an error will be thrown. If not, it will return the
|
||||
// number of records, which in this case happens to be 4 (there are four
|
||||
// records in the {test} table).
|
||||
$query = db_select('test');
|
||||
$query->fields('test', array('fail'));
|
||||
$this->assertEqual(4, $query->countQuery()->execute()->fetchField(), 'Count Query removed fields');
|
||||
|
||||
$query = db_select('test');
|
||||
$query->addExpression('fail');
|
||||
$this->assertEqual(4, $query->countQuery()->execute()->fetchField(), 'Count Query removed expressions');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that we can generate a count query from a query with distinct.
|
||||
*/
|
||||
function testCountQueryDistinct() {
|
||||
$query = db_select('test_task');
|
||||
$query->addField('test_task', 'task');
|
||||
$query->distinct();
|
||||
|
||||
$count = $query->countQuery()->execute()->fetchField();
|
||||
|
||||
$this->assertEqual($count, 6, 'Counted the correct number of records.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that we can generate a count query from a query with GROUP BY.
|
||||
*/
|
||||
function testCountQueryGroupBy() {
|
||||
$query = db_select('test_task');
|
||||
$query->addField('test_task', 'pid');
|
||||
$query->groupBy('pid');
|
||||
|
||||
$count = $query->countQuery()->execute()->fetchField();
|
||||
|
||||
$this->assertEqual($count, 3, 'Counted the correct number of records.');
|
||||
|
||||
// Use a column alias as, without one, the query can succeed for the wrong
|
||||
// reason.
|
||||
$query = db_select('test_task');
|
||||
$query->addField('test_task', 'pid', 'pid_alias');
|
||||
$query->addExpression('COUNT(test_task.task)', 'count');
|
||||
$query->groupBy('pid_alias');
|
||||
$query->orderBy('pid_alias', 'asc');
|
||||
|
||||
$count = $query->countQuery()->execute()->fetchField();
|
||||
|
||||
$this->assertEqual($count, 3, 'Counted the correct number of records.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms that we can properly nest conditional clauses.
|
||||
*/
|
||||
function testNestedConditions() {
|
||||
// This query should translate to:
|
||||
// "SELECT job FROM {test} WHERE name = 'Paul' AND (age = 26 OR age = 27)"
|
||||
// That should find only one record. Yes it's a non-optimal way of writing
|
||||
// that query but that's not the point!
|
||||
$query = db_select('test');
|
||||
$query->addField('test', 'job');
|
||||
$query->condition('name', 'Paul');
|
||||
$query->condition(db_or()->condition('age', 26)->condition('age', 27));
|
||||
|
||||
$job = $query->execute()->fetchField();
|
||||
$this->assertEqual($job, 'Songwriter', 'Correct data retrieved.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms we can join on a single table twice with a dynamic alias.
|
||||
*/
|
||||
function testJoinTwice() {
|
||||
$query = db_select('test')->fields('test');
|
||||
$alias = $query->join('test', 'test', 'test.job = %alias.job');
|
||||
$query->addField($alias, 'name', 'othername');
|
||||
$query->addField($alias, 'job', 'otherjob');
|
||||
$query->where("$alias.name <> test.name");
|
||||
$crowded_job = $query->execute()->fetch();
|
||||
$this->assertEqual($crowded_job->job, $crowded_job->otherjob, 'Correctly joined same table twice.');
|
||||
$this->assertNotEqual($crowded_job->name, $crowded_job->othername, 'Correctly joined same table twice.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that we can join on a query.
|
||||
*/
|
||||
function testJoinSubquery() {
|
||||
$this->installSchema('system', 'sequences');
|
||||
|
||||
$account = User::create([
|
||||
'name' => $this->randomMachineName(),
|
||||
'mail' => $this->randomMachineName() . '@example.com',
|
||||
]);
|
||||
|
||||
$query = db_select('test_task', 'tt', array('target' => 'replica'));
|
||||
$query->addExpression('tt.pid + 1', 'abc');
|
||||
$query->condition('priority', 1, '>');
|
||||
$query->condition('priority', 100, '<');
|
||||
|
||||
$subquery = db_select('test', 'tp');
|
||||
$subquery->join('test_one_blob', 'tpb', 'tp.id = tpb.id');
|
||||
$subquery->join('node', 'n', 'tp.id = n.nid');
|
||||
$subquery->addTag('node_access');
|
||||
$subquery->addMetaData('account', $account);
|
||||
$subquery->addField('tp', 'id');
|
||||
$subquery->condition('age', 5, '>');
|
||||
$subquery->condition('age', 500, '<');
|
||||
|
||||
$query->leftJoin($subquery, 'sq', 'tt.pid = sq.id');
|
||||
$query->join('test_one_blob', 'tb3', 'tt.pid = tb3.id');
|
||||
|
||||
// Construct the query string.
|
||||
// This is the same sequence that SelectQuery::execute() goes through.
|
||||
$query->preExecute();
|
||||
$query->getArguments();
|
||||
$str = (string) $query;
|
||||
|
||||
// Verify that the string only has one copy of condition placeholder 0.
|
||||
$pos = strpos($str, 'db_condition_placeholder_0', 0);
|
||||
$pos2 = strpos($str, 'db_condition_placeholder_0', $pos + 1);
|
||||
$this->assertFalse($pos2, 'Condition placeholder is not repeated.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that rowCount() throws exception on SELECT query.
|
||||
*/
|
||||
function testSelectWithRowCount() {
|
||||
$query = db_select('test');
|
||||
$query->addField('test', 'name');
|
||||
$result = $query->execute();
|
||||
try {
|
||||
$result->rowCount();
|
||||
$exception = FALSE;
|
||||
}
|
||||
catch (RowCountException $e) {
|
||||
$exception = TRUE;
|
||||
}
|
||||
$this->assertTrue($exception, 'Exception was thrown');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Database;
|
||||
|
||||
/**
|
||||
* Tests the Select query builder.
|
||||
*
|
||||
* @group Database
|
||||
*/
|
||||
class SelectOrderedTest extends DatabaseTestBase {
|
||||
|
||||
/**
|
||||
* Tests basic ORDER BY.
|
||||
*/
|
||||
function testSimpleSelectOrdered() {
|
||||
$query = db_select('test');
|
||||
$query->addField('test', 'name');
|
||||
$age_field = $query->addField('test', 'age', 'age');
|
||||
$query->orderBy($age_field);
|
||||
$result = $query->execute();
|
||||
|
||||
$num_records = 0;
|
||||
$last_age = 0;
|
||||
foreach ($result as $record) {
|
||||
$num_records++;
|
||||
$this->assertTrue($record->age >= $last_age, 'Results returned in correct order.');
|
||||
$last_age = $record->age;
|
||||
}
|
||||
|
||||
$this->assertEqual($num_records, 4, 'Returned the correct number of rows.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests multiple ORDER BY.
|
||||
*/
|
||||
function testSimpleSelectMultiOrdered() {
|
||||
$query = db_select('test');
|
||||
$query->addField('test', 'name');
|
||||
$age_field = $query->addField('test', 'age', 'age');
|
||||
$job_field = $query->addField('test', 'job');
|
||||
$query->orderBy($job_field);
|
||||
$query->orderBy($age_field);
|
||||
$result = $query->execute();
|
||||
|
||||
$num_records = 0;
|
||||
$expected = array(
|
||||
array('Ringo', 28, 'Drummer'),
|
||||
array('John', 25, 'Singer'),
|
||||
array('George', 27, 'Singer'),
|
||||
array('Paul', 26, 'Songwriter'),
|
||||
);
|
||||
$results = $result->fetchAll(\PDO::FETCH_NUM);
|
||||
foreach ($expected as $k => $record) {
|
||||
$num_records++;
|
||||
foreach ($record as $kk => $col) {
|
||||
if ($expected[$k][$kk] != $results[$k][$kk]) {
|
||||
$this->assertTrue(FALSE, 'Results returned in correct order.');
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->assertEqual($num_records, 4, 'Returned the correct number of rows.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests ORDER BY descending.
|
||||
*/
|
||||
function testSimpleSelectOrderedDesc() {
|
||||
$query = db_select('test');
|
||||
$query->addField('test', 'name');
|
||||
$age_field = $query->addField('test', 'age', 'age');
|
||||
$query->orderBy($age_field, 'DESC');
|
||||
$result = $query->execute();
|
||||
|
||||
$num_records = 0;
|
||||
$last_age = 100000000;
|
||||
foreach ($result as $record) {
|
||||
$num_records++;
|
||||
$this->assertTrue($record->age <= $last_age, 'Results returned in correct order.');
|
||||
$last_age = $record->age;
|
||||
}
|
||||
|
||||
$this->assertEqual($num_records, 4, 'Returned the correct number of rows.');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,178 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Database;
|
||||
|
||||
/**
|
||||
* Tests the Select query builder.
|
||||
*
|
||||
* @group Database
|
||||
*/
|
||||
class SelectSubqueryTest extends DatabaseTestBase {
|
||||
|
||||
/**
|
||||
* Tests that we can use a subquery in a FROM clause.
|
||||
*/
|
||||
function testFromSubquerySelect() {
|
||||
// Create a subquery, which is just a normal query object.
|
||||
$subquery = db_select('test_task', 'tt');
|
||||
$subquery->addField('tt', 'pid', 'pid');
|
||||
$subquery->addField('tt', 'task', 'task');
|
||||
$subquery->condition('priority', 1);
|
||||
|
||||
for ($i = 0; $i < 2; $i++) {
|
||||
// Create another query that joins against the virtual table resulting
|
||||
// from the subquery.
|
||||
$select = db_select($subquery, 'tt2');
|
||||
$select->join('test', 't', 't.id=tt2.pid');
|
||||
$select->addField('t', 'name');
|
||||
if ($i) {
|
||||
// Use a different number of conditions here to confuse the subquery
|
||||
// placeholder counter, testing https://www.drupal.org/node/1112854.
|
||||
$select->condition('name', 'John');
|
||||
}
|
||||
$select->condition('task', 'code');
|
||||
|
||||
// The resulting query should be equivalent to:
|
||||
// SELECT t.name
|
||||
// FROM (SELECT tt.pid AS pid, tt.task AS task FROM test_task tt WHERE priority=1) tt
|
||||
// INNER JOIN test t ON t.id=tt.pid
|
||||
// WHERE tt.task = 'code'
|
||||
$people = $select->execute()->fetchCol();
|
||||
|
||||
$this->assertEqual(count($people), 1, 'Returned the correct number of rows.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that we can use a subquery in a FROM clause with a LIMIT.
|
||||
*/
|
||||
function testFromSubquerySelectWithLimit() {
|
||||
// Create a subquery, which is just a normal query object.
|
||||
$subquery = db_select('test_task', 'tt');
|
||||
$subquery->addField('tt', 'pid', 'pid');
|
||||
$subquery->addField('tt', 'task', 'task');
|
||||
$subquery->orderBy('priority', 'DESC');
|
||||
$subquery->range(0, 1);
|
||||
|
||||
// Create another query that joins against the virtual table resulting
|
||||
// from the subquery.
|
||||
$select = db_select($subquery, 'tt2');
|
||||
$select->join('test', 't', 't.id=tt2.pid');
|
||||
$select->addField('t', 'name');
|
||||
|
||||
// The resulting query should be equivalent to:
|
||||
// SELECT t.name
|
||||
// FROM (SELECT tt.pid AS pid, tt.task AS task FROM test_task tt ORDER BY priority DESC LIMIT 1 OFFSET 0) tt
|
||||
// INNER JOIN test t ON t.id=tt.pid
|
||||
$people = $select->execute()->fetchCol();
|
||||
|
||||
$this->assertEqual(count($people), 1, 'Returned the correct number of rows.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that we can use a subquery in a WHERE clause.
|
||||
*/
|
||||
function testConditionSubquerySelect() {
|
||||
// Create a subquery, which is just a normal query object.
|
||||
$subquery = db_select('test_task', 'tt');
|
||||
$subquery->addField('tt', 'pid', 'pid');
|
||||
$subquery->condition('tt.priority', 1);
|
||||
|
||||
// Create another query that joins against the virtual table resulting
|
||||
// from the subquery.
|
||||
$select = db_select('test_task', 'tt2');
|
||||
$select->addField('tt2', 'task');
|
||||
$select->condition('tt2.pid', $subquery, 'IN');
|
||||
|
||||
// The resulting query should be equivalent to:
|
||||
// SELECT tt2.name
|
||||
// FROM test tt2
|
||||
// WHERE tt2.pid IN (SELECT tt.pid AS pid FROM test_task tt WHERE tt.priority=1)
|
||||
$people = $select->execute()->fetchCol();
|
||||
$this->assertEqual(count($people), 5, 'Returned the correct number of rows.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that we can use a subquery in a JOIN clause.
|
||||
*/
|
||||
function testJoinSubquerySelect() {
|
||||
// Create a subquery, which is just a normal query object.
|
||||
$subquery = db_select('test_task', 'tt');
|
||||
$subquery->addField('tt', 'pid', 'pid');
|
||||
$subquery->condition('priority', 1);
|
||||
|
||||
// Create another query that joins against the virtual table resulting
|
||||
// from the subquery.
|
||||
$select = db_select('test', 't');
|
||||
$select->join($subquery, 'tt', 't.id=tt.pid');
|
||||
$select->addField('t', 'name');
|
||||
|
||||
// The resulting query should be equivalent to:
|
||||
// SELECT t.name
|
||||
// FROM test t
|
||||
// INNER JOIN (SELECT tt.pid AS pid FROM test_task tt WHERE priority=1) tt ON t.id=tt.pid
|
||||
$people = $select->execute()->fetchCol();
|
||||
|
||||
$this->assertEqual(count($people), 2, 'Returned the correct number of rows.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests EXISTS subquery conditionals on SELECT statements.
|
||||
*
|
||||
* We essentially select all rows from the {test} table that have matching
|
||||
* rows in the {test_people} table based on the shared name column.
|
||||
*/
|
||||
function testExistsSubquerySelect() {
|
||||
// Put George into {test_people}.
|
||||
db_insert('test_people')
|
||||
->fields(array(
|
||||
'name' => 'George',
|
||||
'age' => 27,
|
||||
'job' => 'Singer',
|
||||
))
|
||||
->execute();
|
||||
// Base query to {test}.
|
||||
$query = db_select('test', 't')
|
||||
->fields('t', array('name'));
|
||||
// Subquery to {test_people}.
|
||||
$subquery = db_select('test_people', 'tp')
|
||||
->fields('tp', array('name'))
|
||||
->where('tp.name = t.name');
|
||||
$query->exists($subquery);
|
||||
$result = $query->execute();
|
||||
|
||||
// Ensure that we got the right record.
|
||||
$record = $result->fetch();
|
||||
$this->assertEqual($record->name, 'George', 'Fetched name is correct using EXISTS query.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests NOT EXISTS subquery conditionals on SELECT statements.
|
||||
*
|
||||
* We essentially select all rows from the {test} table that don't have
|
||||
* matching rows in the {test_people} table based on the shared name column.
|
||||
*/
|
||||
function testNotExistsSubquerySelect() {
|
||||
// Put George into {test_people}.
|
||||
db_insert('test_people')
|
||||
->fields(array(
|
||||
'name' => 'George',
|
||||
'age' => 27,
|
||||
'job' => 'Singer',
|
||||
))
|
||||
->execute();
|
||||
|
||||
// Base query to {test}.
|
||||
$query = db_select('test', 't')
|
||||
->fields('t', array('name'));
|
||||
// Subquery to {test_people}.
|
||||
$subquery = db_select('test_people', 'tp')
|
||||
->fields('tp', array('name'))
|
||||
->where('tp.name = t.name');
|
||||
$query->notExists($subquery);
|
||||
|
||||
// Ensure that we got the right number of records.
|
||||
$people = $query->execute()->fetchCol();
|
||||
$this->assertEqual(count($people), 3, 'NOT EXISTS query returned the correct results.');
|
||||
}
|
||||
}
|
||||
530
core/tests/Drupal/KernelTests/Core/Database/SelectTest.php
Normal file
530
core/tests/Drupal/KernelTests/Core/Database/SelectTest.php
Normal file
|
|
@ -0,0 +1,530 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Database;
|
||||
use Drupal\Core\Database\InvalidQueryException;
|
||||
use Drupal\Core\Database\Database;
|
||||
|
||||
/**
|
||||
* Tests the Select query builder.
|
||||
*
|
||||
* @group Database
|
||||
*/
|
||||
class SelectTest extends DatabaseTestBase {
|
||||
|
||||
/**
|
||||
* Tests rudimentary SELECT statements.
|
||||
*/
|
||||
function testSimpleSelect() {
|
||||
$query = db_select('test');
|
||||
$query->addField('test', 'name');
|
||||
$query->addField('test', 'age', 'age');
|
||||
$num_records = $query->countQuery()->execute()->fetchField();
|
||||
|
||||
$this->assertEqual($num_records, 4, 'Returned the correct number of rows.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests rudimentary SELECT statement with a COMMENT.
|
||||
*/
|
||||
function testSimpleComment() {
|
||||
$query = db_select('test')->comment('Testing query comments');
|
||||
$query->addField('test', 'name');
|
||||
$query->addField('test', 'age', 'age');
|
||||
$result = $query->execute();
|
||||
|
||||
$records = $result->fetchAll();
|
||||
|
||||
$query = (string) $query;
|
||||
$expected = "/* Testing query comments */";
|
||||
|
||||
$this->assertEqual(count($records), 4, 'Returned the correct number of rows.');
|
||||
$this->assertNotIdentical(FALSE, strpos($query, $expected), 'The flattened query contains the comment string.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests query COMMENT system against vulnerabilities.
|
||||
*/
|
||||
function testVulnerableComment() {
|
||||
$query = db_select('test')->comment('Testing query comments */ SELECT nid FROM {node}; --');
|
||||
$query->addField('test', 'name');
|
||||
$query->addField('test', 'age', 'age');
|
||||
$result = $query->execute();
|
||||
|
||||
$records = $result->fetchAll();
|
||||
|
||||
$query = (string) $query;
|
||||
$expected = "/* Testing query comments * / SELECT nid FROM {node}. -- */ SELECT test.name AS name, test.age AS age\nFROM \n{test} test";
|
||||
|
||||
$this->assertEqual(count($records), 4, 'Returned the correct number of rows.');
|
||||
$this->assertNotIdentical(FALSE, strpos($query, $expected), 'The flattened query contains the sanitised comment string.');
|
||||
|
||||
$connection = Database::getConnection();
|
||||
foreach ($this->makeCommentsProvider() as $test_set) {
|
||||
list($expected, $comments) = $test_set;
|
||||
$this->assertEqual($expected, $connection->makeComment($comments));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides expected and input values for testVulnerableComment().
|
||||
*/
|
||||
function makeCommentsProvider() {
|
||||
return [
|
||||
[
|
||||
'/* */ ',
|
||||
[''],
|
||||
],
|
||||
// Try and close the comment early.
|
||||
[
|
||||
'/* Exploit * / DROP TABLE node. -- */ ',
|
||||
['Exploit */ DROP TABLE node; --'],
|
||||
],
|
||||
// Variations on comment closing.
|
||||
[
|
||||
'/* Exploit * / * / DROP TABLE node. -- */ ',
|
||||
['Exploit */*/ DROP TABLE node; --'],
|
||||
],
|
||||
[
|
||||
'/* Exploit * * // DROP TABLE node. -- */ ',
|
||||
['Exploit **// DROP TABLE node; --'],
|
||||
],
|
||||
// Try closing the comment in the second string which is appended.
|
||||
[
|
||||
'/* Exploit * / DROP TABLE node. --. Another try * / DROP TABLE node. -- */ ',
|
||||
['Exploit */ DROP TABLE node; --', 'Another try */ DROP TABLE node; --'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests basic conditionals on SELECT statements.
|
||||
*/
|
||||
function testSimpleSelectConditional() {
|
||||
$query = db_select('test');
|
||||
$name_field = $query->addField('test', 'name');
|
||||
$age_field = $query->addField('test', 'age', 'age');
|
||||
$query->condition('age', 27);
|
||||
$result = $query->execute();
|
||||
|
||||
// Check that the aliases are being created the way we want.
|
||||
$this->assertEqual($name_field, 'name', 'Name field alias is correct.');
|
||||
$this->assertEqual($age_field, 'age', 'Age field alias is correct.');
|
||||
|
||||
// Ensure that we got the right record.
|
||||
$record = $result->fetch();
|
||||
$this->assertEqual($record->$name_field, 'George', 'Fetched name is correct.');
|
||||
$this->assertEqual($record->$age_field, 27, 'Fetched age is correct.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests SELECT statements with expressions.
|
||||
*/
|
||||
function testSimpleSelectExpression() {
|
||||
$query = db_select('test');
|
||||
$name_field = $query->addField('test', 'name');
|
||||
$age_field = $query->addExpression("age*2", 'double_age');
|
||||
$query->condition('age', 27);
|
||||
$result = $query->execute();
|
||||
|
||||
// Check that the aliases are being created the way we want.
|
||||
$this->assertEqual($name_field, 'name', 'Name field alias is correct.');
|
||||
$this->assertEqual($age_field, 'double_age', 'Age field alias is correct.');
|
||||
|
||||
// Ensure that we got the right record.
|
||||
$record = $result->fetch();
|
||||
$this->assertEqual($record->$name_field, 'George', 'Fetched name is correct.');
|
||||
$this->assertEqual($record->$age_field, 27*2, 'Fetched age expression is correct.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests SELECT statements with multiple expressions.
|
||||
*/
|
||||
function testSimpleSelectExpressionMultiple() {
|
||||
$query = db_select('test');
|
||||
$name_field = $query->addField('test', 'name');
|
||||
$age_double_field = $query->addExpression("age*2");
|
||||
$age_triple_field = $query->addExpression("age*3");
|
||||
$query->condition('age', 27);
|
||||
$result = $query->execute();
|
||||
|
||||
// Check that the aliases are being created the way we want.
|
||||
$this->assertEqual($age_double_field, 'expression', 'Double age field alias is correct.');
|
||||
$this->assertEqual($age_triple_field, 'expression_2', 'Triple age field alias is correct.');
|
||||
|
||||
// Ensure that we got the right record.
|
||||
$record = $result->fetch();
|
||||
$this->assertEqual($record->$name_field, 'George', 'Fetched name is correct.');
|
||||
$this->assertEqual($record->$age_double_field, 27*2, 'Fetched double age expression is correct.');
|
||||
$this->assertEqual($record->$age_triple_field, 27*3, 'Fetched triple age expression is correct.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests adding multiple fields to a SELECT statement at the same time.
|
||||
*/
|
||||
function testSimpleSelectMultipleFields() {
|
||||
$record = db_select('test')
|
||||
->fields('test', array('id', 'name', 'age', 'job'))
|
||||
->condition('age', 27)
|
||||
->execute()->fetchObject();
|
||||
|
||||
// Check that all fields we asked for are present.
|
||||
$this->assertNotNull($record->id, 'ID field is present.');
|
||||
$this->assertNotNull($record->name, 'Name field is present.');
|
||||
$this->assertNotNull($record->age, 'Age field is present.');
|
||||
$this->assertNotNull($record->job, 'Job field is present.');
|
||||
|
||||
// Ensure that we got the right record.
|
||||
// Check that all fields we asked for are present.
|
||||
$this->assertEqual($record->id, 2, 'ID field has the correct value.');
|
||||
$this->assertEqual($record->name, 'George', 'Name field has the correct value.');
|
||||
$this->assertEqual($record->age, 27, 'Age field has the correct value.');
|
||||
$this->assertEqual($record->job, 'Singer', 'Job field has the correct value.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests adding all fields from a given table to a SELECT statement.
|
||||
*/
|
||||
function testSimpleSelectAllFields() {
|
||||
$record = db_select('test')
|
||||
->fields('test')
|
||||
->condition('age', 27)
|
||||
->execute()->fetchObject();
|
||||
|
||||
// Check that all fields we asked for are present.
|
||||
$this->assertNotNull($record->id, 'ID field is present.');
|
||||
$this->assertNotNull($record->name, 'Name field is present.');
|
||||
$this->assertNotNull($record->age, 'Age field is present.');
|
||||
$this->assertNotNull($record->job, 'Job field is present.');
|
||||
|
||||
// Ensure that we got the right record.
|
||||
// Check that all fields we asked for are present.
|
||||
$this->assertEqual($record->id, 2, 'ID field has the correct value.');
|
||||
$this->assertEqual($record->name, 'George', 'Name field has the correct value.');
|
||||
$this->assertEqual($record->age, 27, 'Age field has the correct value.');
|
||||
$this->assertEqual($record->job, 'Singer', 'Job field has the correct value.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that a comparison with NULL is always FALSE.
|
||||
*/
|
||||
function testNullCondition() {
|
||||
$this->ensureSampleDataNull();
|
||||
|
||||
$names = db_select('test_null', 'tn')
|
||||
->fields('tn', array('name'))
|
||||
->condition('age', NULL)
|
||||
->execute()->fetchCol();
|
||||
|
||||
$this->assertEqual(count($names), 0, 'No records found when comparing to NULL.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that we can find a record with a NULL value.
|
||||
*/
|
||||
function testIsNullCondition() {
|
||||
$this->ensureSampleDataNull();
|
||||
|
||||
$names = db_select('test_null', 'tn')
|
||||
->fields('tn', array('name'))
|
||||
->isNull('age')
|
||||
->execute()->fetchCol();
|
||||
|
||||
$this->assertEqual(count($names), 1, 'Correct number of records found with NULL age.');
|
||||
$this->assertEqual($names[0], 'Fozzie', 'Correct record returned for NULL age.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that we can find a record without a NULL value.
|
||||
*/
|
||||
function testIsNotNullCondition() {
|
||||
$this->ensureSampleDataNull();
|
||||
|
||||
$names = db_select('test_null', 'tn')
|
||||
->fields('tn', array('name'))
|
||||
->isNotNull('tn.age')
|
||||
->orderBy('name')
|
||||
->execute()->fetchCol();
|
||||
|
||||
$this->assertEqual(count($names), 2, 'Correct number of records found withNOT NULL age.');
|
||||
$this->assertEqual($names[0], 'Gonzo', 'Correct record returned for NOT NULL age.');
|
||||
$this->assertEqual($names[1], 'Kermit', 'Correct record returned for NOT NULL age.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that we can UNION multiple Select queries together.
|
||||
*
|
||||
* This is semantically equal to UNION DISTINCT, so we don't explicitly test
|
||||
* that.
|
||||
*/
|
||||
function testUnion() {
|
||||
$query_1 = db_select('test', 't')
|
||||
->fields('t', array('name'))
|
||||
->condition('age', array(27, 28), 'IN');
|
||||
|
||||
$query_2 = db_select('test', 't')
|
||||
->fields('t', array('name'))
|
||||
->condition('age', 28);
|
||||
|
||||
$query_1->union($query_2);
|
||||
|
||||
$names = $query_1->execute()->fetchCol();
|
||||
|
||||
// Ensure we only get 2 records.
|
||||
$this->assertEqual(count($names), 2, 'UNION correctly discarded duplicates.');
|
||||
|
||||
$this->assertEqual($names[0], 'George', 'First query returned correct name.');
|
||||
$this->assertEqual($names[1], 'Ringo', 'Second query returned correct name.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that we can UNION ALL multiple SELECT queries together.
|
||||
*/
|
||||
function testUnionAll() {
|
||||
$query_1 = db_select('test', 't')
|
||||
->fields('t', array('name'))
|
||||
->condition('age', array(27, 28), 'IN');
|
||||
|
||||
$query_2 = db_select('test', 't')
|
||||
->fields('t', array('name'))
|
||||
->condition('age', 28);
|
||||
|
||||
$query_1->union($query_2, 'ALL');
|
||||
|
||||
$names = $query_1->execute()->fetchCol();
|
||||
|
||||
// Ensure we get all 3 records.
|
||||
$this->assertEqual(count($names), 3, 'UNION ALL correctly preserved duplicates.');
|
||||
|
||||
$this->assertEqual($names[0], 'George', 'First query returned correct first name.');
|
||||
$this->assertEqual($names[1], 'Ringo', 'Second query returned correct second name.');
|
||||
$this->assertEqual($names[2], 'Ringo', 'Third query returned correct name.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that we can get a count query for a UNION Select query.
|
||||
*/
|
||||
function testUnionCount() {
|
||||
$query_1 = db_select('test', 't')
|
||||
->fields('t', array('name', 'age'))
|
||||
->condition('age', array(27, 28), 'IN');
|
||||
|
||||
$query_2 = db_select('test', 't')
|
||||
->fields('t', array('name', 'age'))
|
||||
->condition('age', 28);
|
||||
|
||||
$query_1->union($query_2, 'ALL');
|
||||
$names = $query_1->execute()->fetchCol();
|
||||
|
||||
$query_3 = $query_1->countQuery();
|
||||
$count = $query_3->execute()->fetchField();
|
||||
|
||||
// Ensure the counts match.
|
||||
$this->assertEqual(count($names), $count, "The count query's result matched the number of rows in the UNION query.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that random ordering of queries works.
|
||||
*
|
||||
* We take the approach of testing the Drupal layer only, rather than trying
|
||||
* to test that the database's random number generator actually produces
|
||||
* random queries (which is very difficult to do without an unacceptable risk
|
||||
* of the test failing by accident).
|
||||
*
|
||||
* Therefore, in this test we simply run the same query twice and assert that
|
||||
* the two results are reordered versions of each other (as well as of the
|
||||
* same query without the random ordering). It is reasonable to assume that
|
||||
* if we run the same select query twice and the results are in a different
|
||||
* order each time, the only way this could happen is if we have successfully
|
||||
* triggered the database's random ordering functionality.
|
||||
*/
|
||||
function testRandomOrder() {
|
||||
// Use 52 items, so the chance that this test fails by accident will be the
|
||||
// same as the chance that a deck of cards will come out in the same order
|
||||
// after shuffling it (in other words, nearly impossible).
|
||||
$number_of_items = 52;
|
||||
while (db_query("SELECT MAX(id) FROM {test}")->fetchField() < $number_of_items) {
|
||||
db_insert('test')->fields(array('name' => $this->randomMachineName()))->execute();
|
||||
}
|
||||
|
||||
// First select the items in order and make sure we get an ordered list.
|
||||
$expected_ids = range(1, $number_of_items);
|
||||
$ordered_ids = db_select('test', 't')
|
||||
->fields('t', array('id'))
|
||||
->range(0, $number_of_items)
|
||||
->orderBy('id')
|
||||
->execute()
|
||||
->fetchCol();
|
||||
$this->assertEqual($ordered_ids, $expected_ids, 'A query without random ordering returns IDs in the correct order.');
|
||||
|
||||
// Now perform the same query, but instead choose a random ordering. We
|
||||
// expect this to contain a differently ordered version of the original
|
||||
// result.
|
||||
$randomized_ids = db_select('test', 't')
|
||||
->fields('t', array('id'))
|
||||
->range(0, $number_of_items)
|
||||
->orderRandom()
|
||||
->execute()
|
||||
->fetchCol();
|
||||
$this->assertNotEqual($randomized_ids, $ordered_ids, 'A query with random ordering returns an unordered set of IDs.');
|
||||
$sorted_ids = $randomized_ids;
|
||||
sort($sorted_ids);
|
||||
$this->assertEqual($sorted_ids, $ordered_ids, 'After sorting the random list, the result matches the original query.');
|
||||
|
||||
// Now perform the exact same query again, and make sure the order is
|
||||
// different.
|
||||
$randomized_ids_second_set = db_select('test', 't')
|
||||
->fields('t', array('id'))
|
||||
->range(0, $number_of_items)
|
||||
->orderRandom()
|
||||
->execute()
|
||||
->fetchCol();
|
||||
$this->assertNotEqual($randomized_ids_second_set, $randomized_ids, 'Performing the query with random ordering a second time returns IDs in a different order.');
|
||||
$sorted_ids_second_set = $randomized_ids_second_set;
|
||||
sort($sorted_ids_second_set);
|
||||
$this->assertEqual($sorted_ids_second_set, $sorted_ids, 'After sorting the second random list, the result matches the sorted version of the first random list.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that filter by a regular expression works as expected.
|
||||
*/
|
||||
public function testRegexCondition() {
|
||||
|
||||
$test_groups[] = array(
|
||||
'regex' => 'hn$',
|
||||
'expected' => array(
|
||||
'John',
|
||||
),
|
||||
);
|
||||
$test_groups[] = array(
|
||||
'regex' => '^Pau',
|
||||
'expected' => array(
|
||||
'Paul',
|
||||
),
|
||||
);
|
||||
$test_groups[] = array(
|
||||
'regex' => 'Ringo|George',
|
||||
'expected' => array(
|
||||
'Ringo', 'George',
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
$database = $this->container->get('database');
|
||||
foreach ($test_groups as $test_group) {
|
||||
$query = $database->select('test', 't');
|
||||
$query->addField('t', 'name');
|
||||
$query->condition('t.name', $test_group['regex'], 'REGEXP');
|
||||
$result = $query->execute()->fetchCol();
|
||||
|
||||
$this->assertEqual(count($result), count($test_group['expected']), 'Returns the expected number of rows.');
|
||||
$this->assertEqual(sort($result), sort($test_group['expected']), 'Returns the expected rows.');
|
||||
}
|
||||
|
||||
// Ensure that filter by "#" still works due to the quoting.
|
||||
$database->insert('test')
|
||||
->fields(array(
|
||||
'name' => 'Pete',
|
||||
'age' => 26,
|
||||
'job' => '#Drummer',
|
||||
))
|
||||
->execute();
|
||||
|
||||
$test_groups = array();
|
||||
$test_groups[] = array(
|
||||
'regex' => '#Drummer',
|
||||
'expected' => array(
|
||||
'Pete',
|
||||
),
|
||||
);
|
||||
$test_groups[] = array(
|
||||
'regex' => '#Singer',
|
||||
'expected' => array(
|
||||
),
|
||||
);
|
||||
|
||||
foreach ($test_groups as $test_group) {
|
||||
$query = $database->select('test', 't');
|
||||
$query->addField('t', 'name');
|
||||
$query->condition('t.job', $test_group['regex'], 'REGEXP');
|
||||
$result = $query->execute()->fetchCol();
|
||||
|
||||
$this->assertEqual(count($result), count($test_group['expected']), 'Returns the expected number of rows.');
|
||||
$this->assertEqual(sort($result), sort($test_group['expected']), 'Returns the expected rows.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that aliases are renamed when they are duplicates.
|
||||
*/
|
||||
function testSelectDuplicateAlias() {
|
||||
$query = db_select('test', 't');
|
||||
$alias1 = $query->addField('t', 'name', 'the_alias');
|
||||
$alias2 = $query->addField('t', 'age', 'the_alias');
|
||||
$this->assertNotIdentical($alias1, $alias2, 'Duplicate aliases are renamed.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that an invalid merge query throws an exception.
|
||||
*/
|
||||
function testInvalidSelectCount() {
|
||||
try {
|
||||
// This query will fail because the table does not exist.
|
||||
// Normally it would throw an exception but we are suppressing
|
||||
// it with the throw_exception option.
|
||||
$options['throw_exception'] = FALSE;
|
||||
db_select('some_table_that_doesnt_exist', 't', $options)
|
||||
->fields('t')
|
||||
->countQuery()
|
||||
->execute();
|
||||
|
||||
$this->pass('$options[\'throw_exception\'] is FALSE, no Exception thrown.');
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$this->fail('$options[\'throw_exception\'] is FALSE, but Exception thrown for invalid query.');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// This query will fail because the table does not exist.
|
||||
db_select('some_table_that_doesnt_exist', 't')
|
||||
->fields('t')
|
||||
->countQuery()
|
||||
->execute();
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$this->pass('Exception thrown for invalid query.');
|
||||
return;
|
||||
}
|
||||
$this->fail('No Exception thrown.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests thrown exception for IN query conditions with an empty array.
|
||||
*/
|
||||
function testEmptyInCondition() {
|
||||
try {
|
||||
db_select('test', 't')
|
||||
->fields('t')
|
||||
->condition('age', array(), 'IN')
|
||||
->execute();
|
||||
|
||||
$this->fail('Expected exception not thrown');
|
||||
}
|
||||
catch (InvalidQueryException $e) {
|
||||
$this->assertEqual("Query condition 'age IN ()' cannot be empty.", $e->getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
db_select('test', 't')
|
||||
->fields('t')
|
||||
->condition('age', array(), 'NOT IN')
|
||||
->execute();
|
||||
|
||||
$this->fail('Expected exception not thrown');
|
||||
}
|
||||
catch (InvalidQueryException $e) {
|
||||
$this->assertEqual("Query condition 'age NOT IN ()' cannot be empty.", $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Database;
|
||||
|
||||
/**
|
||||
* Tests serializing and unserializing a query.
|
||||
*
|
||||
* @group Database
|
||||
*/
|
||||
class SerializeQueryTest extends DatabaseTestBase {
|
||||
/**
|
||||
* Confirms that a query can be serialized and unserialized.
|
||||
*/
|
||||
function testSerializeQuery() {
|
||||
$query = db_select('test');
|
||||
$query->addField('test', 'age');
|
||||
$query->condition('name', 'Ringo');
|
||||
// If this doesn't work, it will throw an exception, so no need for an
|
||||
// assertion.
|
||||
$query = unserialize(serialize($query));
|
||||
$results = $query->execute()->fetchCol();
|
||||
$this->assertEqual($results[0], 28, 'Query properly executed after unserialization.');
|
||||
}
|
||||
}
|
||||
127
core/tests/Drupal/KernelTests/Core/Database/TaggingTest.php
Normal file
127
core/tests/Drupal/KernelTests/Core/Database/TaggingTest.php
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Database;
|
||||
|
||||
/**
|
||||
* Tests the tagging capabilities of the Select builder.
|
||||
*
|
||||
* Tags are a way to flag queries for alter hooks so they know
|
||||
* what type of query it is, such as "node_access".
|
||||
*
|
||||
* @group Database
|
||||
*/
|
||||
class TaggingTest extends DatabaseTestBase {
|
||||
|
||||
/**
|
||||
* Confirms that a query has a tag added to it.
|
||||
*/
|
||||
function testHasTag() {
|
||||
$query = db_select('test');
|
||||
$query->addField('test', 'name');
|
||||
$query->addField('test', 'age', 'age');
|
||||
|
||||
$query->addTag('test');
|
||||
|
||||
$this->assertTrue($query->hasTag('test'), 'hasTag() returned true.');
|
||||
$this->assertFalse($query->hasTag('other'), 'hasTag() returned false.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests query tagging "has all of these tags" functionality.
|
||||
*/
|
||||
function testHasAllTags() {
|
||||
$query = db_select('test');
|
||||
$query->addField('test', 'name');
|
||||
$query->addField('test', 'age', 'age');
|
||||
|
||||
$query->addTag('test');
|
||||
$query->addTag('other');
|
||||
|
||||
$this->assertTrue($query->hasAllTags('test', 'other'), 'hasAllTags() returned true.');
|
||||
$this->assertFalse($query->hasAllTags('test', 'stuff'), 'hasAllTags() returned false.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests query tagging "has at least one of these tags" functionality.
|
||||
*/
|
||||
function testHasAnyTag() {
|
||||
$query = db_select('test');
|
||||
$query->addField('test', 'name');
|
||||
$query->addField('test', 'age', 'age');
|
||||
|
||||
$query->addTag('test');
|
||||
|
||||
$this->assertTrue($query->hasAnyTag('test', 'other'), 'hasAnyTag() returned true.');
|
||||
$this->assertFalse($query->hasAnyTag('other', 'stuff'), 'hasAnyTag() returned false.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms that an extended query has a tag added to it.
|
||||
*/
|
||||
function testExtenderHasTag() {
|
||||
$query = db_select('test')
|
||||
->extend('Drupal\Core\Database\Query\SelectExtender');
|
||||
$query->addField('test', 'name');
|
||||
$query->addField('test', 'age', 'age');
|
||||
|
||||
$query->addTag('test');
|
||||
|
||||
$this->assertTrue($query->hasTag('test'), 'hasTag() returned true.');
|
||||
$this->assertFalse($query->hasTag('other'), 'hasTag() returned false.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests extended query tagging "has all of these tags" functionality.
|
||||
*/
|
||||
function testExtenderHasAllTags() {
|
||||
$query = db_select('test')
|
||||
->extend('Drupal\Core\Database\Query\SelectExtender');
|
||||
$query->addField('test', 'name');
|
||||
$query->addField('test', 'age', 'age');
|
||||
|
||||
$query->addTag('test');
|
||||
$query->addTag('other');
|
||||
|
||||
$this->assertTrue($query->hasAllTags('test', 'other'), 'hasAllTags() returned true.');
|
||||
$this->assertFalse($query->hasAllTags('test', 'stuff'), 'hasAllTags() returned false.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests extended query tagging "has at least one of these tags" functionality.
|
||||
*/
|
||||
function testExtenderHasAnyTag() {
|
||||
$query = db_select('test')
|
||||
->extend('Drupal\Core\Database\Query\SelectExtender');
|
||||
$query->addField('test', 'name');
|
||||
$query->addField('test', 'age', 'age');
|
||||
|
||||
$query->addTag('test');
|
||||
|
||||
$this->assertTrue($query->hasAnyTag('test', 'other'), 'hasAnyTag() returned true.');
|
||||
$this->assertFalse($query->hasAnyTag('other', 'stuff'), 'hasAnyTag() returned false.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that we can attach metadata to a query object.
|
||||
*
|
||||
* This is how we pass additional context to alter hooks.
|
||||
*/
|
||||
function testMetaData() {
|
||||
$query = db_select('test');
|
||||
$query->addField('test', 'name');
|
||||
$query->addField('test', 'age', 'age');
|
||||
|
||||
$data = array(
|
||||
'a' => 'A',
|
||||
'b' => 'B',
|
||||
);
|
||||
|
||||
$query->addMetaData('test', $data);
|
||||
|
||||
$return = $query->getMetaData('test');
|
||||
$this->assertEqual($data, $return, 'Correct metadata returned.');
|
||||
|
||||
$return = $query->getMetaData('nothere');
|
||||
$this->assertNull($return, 'Non-existent key returned NULL.');
|
||||
}
|
||||
}
|
||||
610
core/tests/Drupal/KernelTests/Core/Database/TransactionTest.php
Normal file
610
core/tests/Drupal/KernelTests/Core/Database/TransactionTest.php
Normal file
|
|
@ -0,0 +1,610 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Database;
|
||||
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\Database\TransactionOutOfOrderException;
|
||||
use Drupal\Core\Database\TransactionNoActiveException;
|
||||
|
||||
/**
|
||||
* Tests the transaction abstraction system.
|
||||
*
|
||||
* We test nesting by having two transaction layers, an outer and inner. The
|
||||
* outer layer encapsulates the inner layer. Our transaction nesting abstraction
|
||||
* should allow the outer layer function to call any function it wants,
|
||||
* especially the inner layer that starts its own transaction, and be
|
||||
* confident that, when the function it calls returns, its own transaction
|
||||
* is still "alive."
|
||||
*
|
||||
* Call structure:
|
||||
* transactionOuterLayer()
|
||||
* Start transaction
|
||||
* transactionInnerLayer()
|
||||
* Start transaction (does nothing in database)
|
||||
* [Maybe decide to roll back]
|
||||
* Do more stuff
|
||||
* Should still be in transaction A
|
||||
*
|
||||
* @group Database
|
||||
*/
|
||||
class TransactionTest extends DatabaseTestBase {
|
||||
|
||||
/**
|
||||
* Encapsulates a transaction's "inner layer" with an "outer layer".
|
||||
*
|
||||
* This "outer layer" transaction starts and then encapsulates the "inner
|
||||
* layer" transaction. This nesting is used to evaluate whether the database
|
||||
* transaction API properly supports nesting. By "properly supports," we mean
|
||||
* the outer transaction continues to exist regardless of what functions are
|
||||
* called and whether those functions start their own transactions.
|
||||
*
|
||||
* In contrast, a typical database would commit the outer transaction, start
|
||||
* a new transaction for the inner layer, commit the inner layer transaction,
|
||||
* and then be confused when the outer layer transaction tries to commit its
|
||||
* transaction (which was already committed when the inner transaction
|
||||
* started).
|
||||
*
|
||||
* @param $suffix
|
||||
* Suffix to add to field values to differentiate tests.
|
||||
* @param $rollback
|
||||
* Whether or not to try rolling back the transaction when we're done.
|
||||
* @param $ddl_statement
|
||||
* Whether to execute a DDL statement during the inner transaction.
|
||||
*/
|
||||
protected function transactionOuterLayer($suffix, $rollback = FALSE, $ddl_statement = FALSE) {
|
||||
$connection = Database::getConnection();
|
||||
$depth = $connection->transactionDepth();
|
||||
$txn = db_transaction();
|
||||
|
||||
// Insert a single row into the testing table.
|
||||
db_insert('test')
|
||||
->fields(array(
|
||||
'name' => 'David' . $suffix,
|
||||
'age' => '24',
|
||||
))
|
||||
->execute();
|
||||
|
||||
$this->assertTrue($connection->inTransaction(), 'In transaction before calling nested transaction.');
|
||||
|
||||
// We're already in a transaction, but we call ->transactionInnerLayer
|
||||
// to nest another transaction inside the current one.
|
||||
$this->transactionInnerLayer($suffix, $rollback, $ddl_statement);
|
||||
|
||||
$this->assertTrue($connection->inTransaction(), 'In transaction after calling nested transaction.');
|
||||
|
||||
if ($rollback) {
|
||||
// Roll back the transaction, if requested.
|
||||
// This rollback should propagate to the last savepoint.
|
||||
$txn->rollback();
|
||||
$this->assertTrue(($connection->transactionDepth() == $depth), 'Transaction has rolled back to the last savepoint after calling rollback().');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an "inner layer" transaction.
|
||||
*
|
||||
* This "inner layer" transaction is either used alone or nested inside of the
|
||||
* "outer layer" transaction.
|
||||
*
|
||||
* @param $suffix
|
||||
* Suffix to add to field values to differentiate tests.
|
||||
* @param $rollback
|
||||
* Whether or not to try rolling back the transaction when we're done.
|
||||
* @param $ddl_statement
|
||||
* Whether to execute a DDL statement during the transaction.
|
||||
*/
|
||||
protected function transactionInnerLayer($suffix, $rollback = FALSE, $ddl_statement = FALSE) {
|
||||
$connection = Database::getConnection();
|
||||
|
||||
$depth = $connection->transactionDepth();
|
||||
// Start a transaction. If we're being called from ->transactionOuterLayer,
|
||||
// then we're already in a transaction. Normally, that would make starting
|
||||
// a transaction here dangerous, but the database API handles this problem
|
||||
// for us by tracking the nesting and avoiding the danger.
|
||||
$txn = db_transaction();
|
||||
|
||||
$depth2 = $connection->transactionDepth();
|
||||
$this->assertTrue($depth < $depth2, 'Transaction depth is has increased with new transaction.');
|
||||
|
||||
// Insert a single row into the testing table.
|
||||
db_insert('test')
|
||||
->fields(array(
|
||||
'name' => 'Daniel' . $suffix,
|
||||
'age' => '19',
|
||||
))
|
||||
->execute();
|
||||
|
||||
$this->assertTrue($connection->inTransaction(), 'In transaction inside nested transaction.');
|
||||
|
||||
if ($ddl_statement) {
|
||||
$table = array(
|
||||
'fields' => array(
|
||||
'id' => array(
|
||||
'type' => 'serial',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
),
|
||||
'primary key' => array('id'),
|
||||
);
|
||||
db_create_table('database_test_1', $table);
|
||||
|
||||
$this->assertTrue($connection->inTransaction(), 'In transaction inside nested transaction.');
|
||||
}
|
||||
|
||||
if ($rollback) {
|
||||
// Roll back the transaction, if requested.
|
||||
// This rollback should propagate to the last savepoint.
|
||||
$txn->rollback();
|
||||
$this->assertTrue(($connection->transactionDepth() == $depth), 'Transaction has rolled back to the last savepoint after calling rollback().');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests transaction rollback on a database that supports transactions.
|
||||
*
|
||||
* If the active connection does not support transactions, this test does
|
||||
* nothing.
|
||||
*/
|
||||
function testTransactionRollBackSupported() {
|
||||
// This test won't work right if transactions are not supported.
|
||||
if (!Database::getConnection()->supportsTransactions()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// Create two nested transactions. Roll back from the inner one.
|
||||
$this->transactionOuterLayer('B', TRUE);
|
||||
|
||||
// Neither of the rows we inserted in the two transaction layers
|
||||
// should be present in the tables post-rollback.
|
||||
$saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DavidB'))->fetchField();
|
||||
$this->assertNotIdentical($saved_age, '24', 'Cannot retrieve DavidB row after commit.');
|
||||
$saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DanielB'))->fetchField();
|
||||
$this->assertNotIdentical($saved_age, '19', 'Cannot retrieve DanielB row after commit.');
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$this->fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests transaction rollback on a database that doesn't support transactions.
|
||||
*
|
||||
* If the active driver supports transactions, this test does nothing.
|
||||
*/
|
||||
function testTransactionRollBackNotSupported() {
|
||||
// This test won't work right if transactions are supported.
|
||||
if (Database::getConnection()->supportsTransactions()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// Create two nested transactions. Attempt to roll back from the inner one.
|
||||
$this->transactionOuterLayer('B', TRUE);
|
||||
|
||||
// Because our current database claims to not support transactions,
|
||||
// the inserted rows should be present despite the attempt to roll back.
|
||||
$saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DavidB'))->fetchField();
|
||||
$this->assertIdentical($saved_age, '24', 'DavidB not rolled back, since transactions are not supported.');
|
||||
$saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DanielB'))->fetchField();
|
||||
$this->assertIdentical($saved_age, '19', 'DanielB not rolled back, since transactions are not supported.');
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$this->fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a committed transaction.
|
||||
*
|
||||
* The behavior of this test should be identical for connections that support
|
||||
* transactions and those that do not.
|
||||
*/
|
||||
function testCommittedTransaction() {
|
||||
try {
|
||||
// Create two nested transactions. The changes should be committed.
|
||||
$this->transactionOuterLayer('A');
|
||||
|
||||
// Because we committed, both of the inserted rows should be present.
|
||||
$saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DavidA'))->fetchField();
|
||||
$this->assertIdentical($saved_age, '24', 'Can retrieve DavidA row after commit.');
|
||||
$saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DanielA'))->fetchField();
|
||||
$this->assertIdentical($saved_age, '19', 'Can retrieve DanielA row after commit.');
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$this->fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the compatibility of transactions with DDL statements.
|
||||
*/
|
||||
function testTransactionWithDdlStatement() {
|
||||
// First, test that a commit works normally, even with DDL statements.
|
||||
$transaction = db_transaction();
|
||||
$this->insertRow('row');
|
||||
$this->executeDDLStatement();
|
||||
unset($transaction);
|
||||
$this->assertRowPresent('row');
|
||||
|
||||
// Even in different order.
|
||||
$this->cleanUp();
|
||||
$transaction = db_transaction();
|
||||
$this->executeDDLStatement();
|
||||
$this->insertRow('row');
|
||||
unset($transaction);
|
||||
$this->assertRowPresent('row');
|
||||
|
||||
// Even with stacking.
|
||||
$this->cleanUp();
|
||||
$transaction = db_transaction();
|
||||
$transaction2 = db_transaction();
|
||||
$this->executeDDLStatement();
|
||||
unset($transaction2);
|
||||
$transaction3 = db_transaction();
|
||||
$this->insertRow('row');
|
||||
unset($transaction3);
|
||||
unset($transaction);
|
||||
$this->assertRowPresent('row');
|
||||
|
||||
// A transaction after a DDL statement should still work the same.
|
||||
$this->cleanUp();
|
||||
$transaction = db_transaction();
|
||||
$transaction2 = db_transaction();
|
||||
$this->executeDDLStatement();
|
||||
unset($transaction2);
|
||||
$transaction3 = db_transaction();
|
||||
$this->insertRow('row');
|
||||
$transaction3->rollback();
|
||||
unset($transaction3);
|
||||
unset($transaction);
|
||||
$this->assertRowAbsent('row');
|
||||
|
||||
// The behavior of a rollback depends on the type of database server.
|
||||
if (Database::getConnection()->supportsTransactionalDDL()) {
|
||||
// For database servers that support transactional DDL, a rollback
|
||||
// of a transaction including DDL statements should be possible.
|
||||
$this->cleanUp();
|
||||
$transaction = db_transaction();
|
||||
$this->insertRow('row');
|
||||
$this->executeDDLStatement();
|
||||
$transaction->rollback();
|
||||
unset($transaction);
|
||||
$this->assertRowAbsent('row');
|
||||
|
||||
// Including with stacking.
|
||||
$this->cleanUp();
|
||||
$transaction = db_transaction();
|
||||
$transaction2 = db_transaction();
|
||||
$this->executeDDLStatement();
|
||||
unset($transaction2);
|
||||
$transaction3 = db_transaction();
|
||||
$this->insertRow('row');
|
||||
unset($transaction3);
|
||||
$transaction->rollback();
|
||||
unset($transaction);
|
||||
$this->assertRowAbsent('row');
|
||||
}
|
||||
else {
|
||||
// For database servers that do not support transactional DDL,
|
||||
// the DDL statement should commit the transaction stack.
|
||||
$this->cleanUp();
|
||||
$transaction = db_transaction();
|
||||
$this->insertRow('row');
|
||||
$this->executeDDLStatement();
|
||||
// Rollback the outer transaction.
|
||||
try {
|
||||
$transaction->rollback();
|
||||
unset($transaction);
|
||||
// @TODO: an exception should be triggered here, but is not, because
|
||||
// "ROLLBACK" fails silently in MySQL if there is no transaction active.
|
||||
// $this->fail(t('Rolling back a transaction containing DDL should fail.'));
|
||||
}
|
||||
catch (TransactionNoActiveException $e) {
|
||||
$this->pass('Rolling back a transaction containing DDL should fail.');
|
||||
}
|
||||
$this->assertRowPresent('row');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a single row into the testing table.
|
||||
*/
|
||||
protected function insertRow($name) {
|
||||
db_insert('test')
|
||||
->fields(array(
|
||||
'name' => $name,
|
||||
))
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a DDL statement.
|
||||
*/
|
||||
protected function executeDDLStatement() {
|
||||
static $count = 0;
|
||||
$table = array(
|
||||
'fields' => array(
|
||||
'id' => array(
|
||||
'type' => 'serial',
|
||||
'unsigned' => TRUE,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
),
|
||||
'primary key' => array('id'),
|
||||
);
|
||||
db_create_table('database_test_' . ++$count, $table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts over for a new test.
|
||||
*/
|
||||
protected function cleanUp() {
|
||||
db_truncate('test')
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that a given row is present in the test table.
|
||||
*
|
||||
* @param $name
|
||||
* The name of the row.
|
||||
* @param $message
|
||||
* The message to log for the assertion.
|
||||
*/
|
||||
function assertRowPresent($name, $message = NULL) {
|
||||
if (!isset($message)) {
|
||||
$message = format_string('Row %name is present.', array('%name' => $name));
|
||||
}
|
||||
$present = (boolean) db_query('SELECT 1 FROM {test} WHERE name = :name', array(':name' => $name))->fetchField();
|
||||
return $this->assertTrue($present, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that a given row is absent from the test table.
|
||||
*
|
||||
* @param $name
|
||||
* The name of the row.
|
||||
* @param $message
|
||||
* The message to log for the assertion.
|
||||
*/
|
||||
function assertRowAbsent($name, $message = NULL) {
|
||||
if (!isset($message)) {
|
||||
$message = format_string('Row %name is absent.', array('%name' => $name));
|
||||
}
|
||||
$present = (boolean) db_query('SELECT 1 FROM {test} WHERE name = :name', array(':name' => $name))->fetchField();
|
||||
return $this->assertFalse($present, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests transaction stacking, commit, and rollback.
|
||||
*/
|
||||
function testTransactionStacking() {
|
||||
// This test won't work right if transactions are not supported.
|
||||
if (!Database::getConnection()->supportsTransactions()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$database = Database::getConnection();
|
||||
|
||||
// Standard case: pop the inner transaction before the outer transaction.
|
||||
$transaction = db_transaction();
|
||||
$this->insertRow('outer');
|
||||
$transaction2 = db_transaction();
|
||||
$this->insertRow('inner');
|
||||
// Pop the inner transaction.
|
||||
unset($transaction2);
|
||||
$this->assertTrue($database->inTransaction(), 'Still in a transaction after popping the inner transaction');
|
||||
// Pop the outer transaction.
|
||||
unset($transaction);
|
||||
$this->assertFalse($database->inTransaction(), 'Transaction closed after popping the outer transaction');
|
||||
$this->assertRowPresent('outer');
|
||||
$this->assertRowPresent('inner');
|
||||
|
||||
// Pop the transaction in a different order they have been pushed.
|
||||
$this->cleanUp();
|
||||
$transaction = db_transaction();
|
||||
$this->insertRow('outer');
|
||||
$transaction2 = db_transaction();
|
||||
$this->insertRow('inner');
|
||||
// Pop the outer transaction, nothing should happen.
|
||||
unset($transaction);
|
||||
$this->insertRow('inner-after-outer-commit');
|
||||
$this->assertTrue($database->inTransaction(), 'Still in a transaction after popping the outer transaction');
|
||||
// Pop the inner transaction, the whole transaction should commit.
|
||||
unset($transaction2);
|
||||
$this->assertFalse($database->inTransaction(), 'Transaction closed after popping the inner transaction');
|
||||
$this->assertRowPresent('outer');
|
||||
$this->assertRowPresent('inner');
|
||||
$this->assertRowPresent('inner-after-outer-commit');
|
||||
|
||||
// Rollback the inner transaction.
|
||||
$this->cleanUp();
|
||||
$transaction = db_transaction();
|
||||
$this->insertRow('outer');
|
||||
$transaction2 = db_transaction();
|
||||
$this->insertRow('inner');
|
||||
// Now rollback the inner transaction.
|
||||
$transaction2->rollback();
|
||||
unset($transaction2);
|
||||
$this->assertTrue($database->inTransaction(), 'Still in a transaction after popping the outer transaction');
|
||||
// Pop the outer transaction, it should commit.
|
||||
$this->insertRow('outer-after-inner-rollback');
|
||||
unset($transaction);
|
||||
$this->assertFalse($database->inTransaction(), 'Transaction closed after popping the inner transaction');
|
||||
$this->assertRowPresent('outer');
|
||||
$this->assertRowAbsent('inner');
|
||||
$this->assertRowPresent('outer-after-inner-rollback');
|
||||
|
||||
// Rollback the inner transaction after committing the outer one.
|
||||
$this->cleanUp();
|
||||
$transaction = db_transaction();
|
||||
$this->insertRow('outer');
|
||||
$transaction2 = db_transaction();
|
||||
$this->insertRow('inner');
|
||||
// Pop the outer transaction, nothing should happen.
|
||||
unset($transaction);
|
||||
$this->assertTrue($database->inTransaction(), 'Still in a transaction after popping the outer transaction');
|
||||
// Now rollback the inner transaction, it should rollback.
|
||||
$transaction2->rollback();
|
||||
unset($transaction2);
|
||||
$this->assertFalse($database->inTransaction(), 'Transaction closed after popping the inner transaction');
|
||||
$this->assertRowPresent('outer');
|
||||
$this->assertRowAbsent('inner');
|
||||
|
||||
// Rollback the outer transaction while the inner transaction is active.
|
||||
// In that case, an exception will be triggered because we cannot
|
||||
// ensure that the final result will have any meaning.
|
||||
$this->cleanUp();
|
||||
$transaction = db_transaction();
|
||||
$this->insertRow('outer');
|
||||
$transaction2 = db_transaction();
|
||||
$this->insertRow('inner');
|
||||
$transaction3 = db_transaction();
|
||||
$this->insertRow('inner2');
|
||||
// Rollback the outer transaction.
|
||||
try {
|
||||
$transaction->rollback();
|
||||
unset($transaction);
|
||||
$this->fail('Rolling back the outer transaction while the inner transaction is active resulted in an exception.');
|
||||
}
|
||||
catch (TransactionOutOfOrderException $e) {
|
||||
$this->pass('Rolling back the outer transaction while the inner transaction is active resulted in an exception.');
|
||||
}
|
||||
$this->assertFalse($database->inTransaction(), 'No more in a transaction after rolling back the outer transaction');
|
||||
// Try to commit one inner transaction.
|
||||
unset($transaction3);
|
||||
$this->pass('Trying to commit an inner transaction resulted in an exception.');
|
||||
// Try to rollback one inner transaction.
|
||||
try {
|
||||
$transaction->rollback();
|
||||
unset($transaction2);
|
||||
$this->fail('Trying to commit an inner transaction resulted in an exception.');
|
||||
}
|
||||
catch (TransactionNoActiveException $e) {
|
||||
$this->pass('Trying to commit an inner transaction resulted in an exception.');
|
||||
}
|
||||
$this->assertRowAbsent('outer');
|
||||
$this->assertRowAbsent('inner');
|
||||
$this->assertRowAbsent('inner2');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that transactions can continue to be used if a query fails.
|
||||
*/
|
||||
public function testQueryFailureInTransaction() {
|
||||
$connection = Database::getConnection();
|
||||
$transaction = $connection->startTransaction('test_transaction');
|
||||
$connection->schema()->dropTable('test');
|
||||
|
||||
// Test a failed query using the query() method.
|
||||
try {
|
||||
$connection->query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'David'))->fetchField();
|
||||
$this->fail('Using the query method failed.');
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$this->pass('Using the query method failed.');
|
||||
}
|
||||
|
||||
// Test a failed select query.
|
||||
try {
|
||||
$connection->select('test')
|
||||
->fields('test', ['name'])
|
||||
->execute();
|
||||
|
||||
$this->fail('Select query failed.');
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$this->pass('Select query failed.');
|
||||
}
|
||||
|
||||
// Test a failed insert query.
|
||||
try {
|
||||
$connection->insert('test')
|
||||
->fields([
|
||||
'name' => 'David',
|
||||
'age' => '24',
|
||||
])
|
||||
->execute();
|
||||
|
||||
$this->fail('Insert query failed.');
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$this->pass('Insert query failed.');
|
||||
}
|
||||
|
||||
// Test a failed update query.
|
||||
try {
|
||||
$connection->update('test')
|
||||
->fields(['name' => 'Tiffany'])
|
||||
->condition('id', 1)
|
||||
->execute();
|
||||
|
||||
$this->fail('Update query failed.');
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$this->pass('Update query failed.');
|
||||
}
|
||||
|
||||
// Test a failed delete query.
|
||||
try {
|
||||
$connection->delete('test')
|
||||
->condition('id', 1)
|
||||
->execute();
|
||||
|
||||
$this->fail('Delete query failed.');
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$this->pass('Delete query failed.');
|
||||
}
|
||||
|
||||
// Test a failed merge query.
|
||||
try {
|
||||
$connection->merge('test')
|
||||
->key('job', 'Presenter')
|
||||
->fields([
|
||||
'age' => '31',
|
||||
'name' => 'Tiffany',
|
||||
])
|
||||
->execute();
|
||||
|
||||
$this->fail('Merge query failed.');
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$this->pass('Merge query failed.');
|
||||
}
|
||||
|
||||
// Test a failed upsert query.
|
||||
try {
|
||||
$connection->upsert('test')
|
||||
->key('job')
|
||||
->fields(['job', 'age', 'name'])
|
||||
->values([
|
||||
'job' => 'Presenter',
|
||||
'age' => 31,
|
||||
'name' => 'Tiffany',
|
||||
])
|
||||
->execute();
|
||||
|
||||
$this->fail('Upset query failed.');
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$this->pass('Upset query failed.');
|
||||
}
|
||||
|
||||
// Create the missing schema and insert a row.
|
||||
$this->installSchema('database_test', ['test']);
|
||||
$connection->insert('test')
|
||||
->fields(array(
|
||||
'name' => 'David',
|
||||
'age' => '24',
|
||||
))
|
||||
->execute();
|
||||
|
||||
// Commit the transaction.
|
||||
unset($transaction);
|
||||
|
||||
$saved_age = $connection->query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'David'))->fetchField();
|
||||
$this->assertEqual('24', $saved_age);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Database;
|
||||
|
||||
/**
|
||||
* Tests the Update query builder, complex queries.
|
||||
*
|
||||
* @group Database
|
||||
*/
|
||||
class UpdateComplexTest extends DatabaseTestBase {
|
||||
|
||||
/**
|
||||
* Tests updates with OR conditionals.
|
||||
*/
|
||||
function testOrConditionUpdate() {
|
||||
$update = db_update('test')
|
||||
->fields(array('job' => 'Musician'))
|
||||
->condition(db_or()
|
||||
->condition('name', 'John')
|
||||
->condition('name', 'Paul')
|
||||
);
|
||||
$num_updated = $update->execute();
|
||||
$this->assertIdentical($num_updated, 2, 'Updated 2 records.');
|
||||
|
||||
$num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
|
||||
$this->assertIdentical($num_matches, '2', 'Updated fields successfully.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests WHERE IN clauses.
|
||||
*/
|
||||
function testInConditionUpdate() {
|
||||
$num_updated = db_update('test')
|
||||
->fields(array('job' => 'Musician'))
|
||||
->condition('name', array('John', 'Paul'), 'IN')
|
||||
->execute();
|
||||
$this->assertIdentical($num_updated, 2, 'Updated 2 records.');
|
||||
|
||||
$num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
|
||||
$this->assertIdentical($num_matches, '2', 'Updated fields successfully.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests WHERE NOT IN clauses.
|
||||
*/
|
||||
function testNotInConditionUpdate() {
|
||||
// The o is lowercase in the 'NoT IN' operator, to make sure the operators
|
||||
// work in mixed case.
|
||||
$num_updated = db_update('test')
|
||||
->fields(array('job' => 'Musician'))
|
||||
->condition('name', array('John', 'Paul', 'George'), 'NoT IN')
|
||||
->execute();
|
||||
$this->assertIdentical($num_updated, 1, 'Updated 1 record.');
|
||||
|
||||
$num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
|
||||
$this->assertIdentical($num_matches, '1', 'Updated fields successfully.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests BETWEEN conditional clauses.
|
||||
*/
|
||||
function testBetweenConditionUpdate() {
|
||||
$num_updated = db_update('test')
|
||||
->fields(array('job' => 'Musician'))
|
||||
->condition('age', array(25, 26), 'BETWEEN')
|
||||
->execute();
|
||||
$this->assertIdentical($num_updated, 2, 'Updated 2 records.');
|
||||
|
||||
$num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
|
||||
$this->assertIdentical($num_matches, '2', 'Updated fields successfully.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests LIKE conditionals.
|
||||
*/
|
||||
function testLikeConditionUpdate() {
|
||||
$num_updated = db_update('test')
|
||||
->fields(array('job' => 'Musician'))
|
||||
->condition('name', '%ge%', 'LIKE')
|
||||
->execute();
|
||||
$this->assertIdentical($num_updated, 1, 'Updated 1 record.');
|
||||
|
||||
$num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
|
||||
$this->assertIdentical($num_matches, '1', 'Updated fields successfully.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests UPDATE with expression values.
|
||||
*/
|
||||
function testUpdateExpression() {
|
||||
$before_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchField();
|
||||
$GLOBALS['larry_test'] = 1;
|
||||
$num_updated = db_update('test')
|
||||
->condition('name', 'Ringo')
|
||||
->fields(array('job' => 'Musician'))
|
||||
->expression('age', 'age + :age', array(':age' => 4))
|
||||
->execute();
|
||||
$this->assertIdentical($num_updated, 1, 'Updated 1 record.');
|
||||
|
||||
$num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
|
||||
$this->assertIdentical($num_matches, '1', 'Updated fields successfully.');
|
||||
|
||||
$person = db_query('SELECT * FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetch();
|
||||
$this->assertEqual($person->name, 'Ringo', 'Name set correctly.');
|
||||
$this->assertEqual($person->age, $before_age + 4, 'Age set correctly.');
|
||||
$this->assertEqual($person->job, 'Musician', 'Job set correctly.');
|
||||
$GLOBALS['larry_test'] = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests UPDATE with only expression values.
|
||||
*/
|
||||
function testUpdateOnlyExpression() {
|
||||
$before_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchField();
|
||||
$num_updated = db_update('test')
|
||||
->condition('name', 'Ringo')
|
||||
->expression('age', 'age + :age', array(':age' => 4))
|
||||
->execute();
|
||||
$this->assertIdentical($num_updated, 1, 'Updated 1 record.');
|
||||
|
||||
$after_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchField();
|
||||
$this->assertEqual($before_age + 4, $after_age, 'Age updated correctly');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test UPDATE with a subselect value.
|
||||
*/
|
||||
function testSubSelectUpdate() {
|
||||
$subselect = db_select('test_task', 't');
|
||||
$subselect->addExpression('MAX(priority) + :increment', 'max_priority', array(':increment' => 30));
|
||||
// Clone this to make sure we are running a different query when
|
||||
// asserting.
|
||||
$select = clone $subselect;
|
||||
$query = db_update('test')
|
||||
->expression('age', $subselect)
|
||||
->condition('name', 'Ringo');
|
||||
// Save the number of rows that updated for assertion later.
|
||||
$num_updated = $query->execute();
|
||||
$after_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchField();
|
||||
$expected_age = $select->execute()->fetchField();
|
||||
$this->assertEqual($after_age, $expected_age);
|
||||
$this->assertEqual(1, $num_updated, t('Expected 1 row to be updated in subselect update query.'));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Database;
|
||||
|
||||
/**
|
||||
* Tests the Update query builder with LOB fields.
|
||||
*
|
||||
* @group Database
|
||||
*/
|
||||
class UpdateLobTest extends DatabaseTestBase {
|
||||
|
||||
/**
|
||||
* Confirms that we can update a blob column.
|
||||
*/
|
||||
function testUpdateOneBlob() {
|
||||
$data = "This is\000a test.";
|
||||
$this->assertTrue(strlen($data) === 15, 'Test data contains a NULL.');
|
||||
$id = db_insert('test_one_blob')
|
||||
->fields(array('blob1' => $data))
|
||||
->execute();
|
||||
|
||||
$data .= $data;
|
||||
db_update('test_one_blob')
|
||||
->condition('id', $id)
|
||||
->fields(array('blob1' => $data))
|
||||
->execute();
|
||||
|
||||
$r = db_query('SELECT * FROM {test_one_blob} WHERE id = :id', array(':id' => $id))->fetchAssoc();
|
||||
$this->assertTrue($r['blob1'] === $data, format_string('Can update a blob: id @id, @data.', array('@id' => $id, '@data' => serialize($r))));
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms that we can update two blob columns in the same table.
|
||||
*/
|
||||
function testUpdateMultipleBlob() {
|
||||
$id = db_insert('test_two_blobs')
|
||||
->fields(array(
|
||||
'blob1' => 'This is',
|
||||
'blob2' => 'a test',
|
||||
))
|
||||
->execute();
|
||||
|
||||
db_update('test_two_blobs')
|
||||
->condition('id', $id)
|
||||
->fields(array('blob1' => 'and so', 'blob2' => 'is this'))
|
||||
->execute();
|
||||
|
||||
$r = db_query('SELECT * FROM {test_two_blobs} WHERE id = :id', array(':id' => $id))->fetchAssoc();
|
||||
$this->assertTrue($r['blob1'] === 'and so' && $r['blob2'] === 'is this', 'Can update multiple blobs per row.');
|
||||
}
|
||||
}
|
||||
157
core/tests/Drupal/KernelTests/Core/Database/UpdateTest.php
Normal file
157
core/tests/Drupal/KernelTests/Core/Database/UpdateTest.php
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Database;
|
||||
|
||||
/**
|
||||
* Tests the update query builder.
|
||||
*
|
||||
* @group Database
|
||||
*/
|
||||
class UpdateTest extends DatabaseTestBase {
|
||||
|
||||
/**
|
||||
* Confirms that we can update a single record successfully.
|
||||
*/
|
||||
function testSimpleUpdate() {
|
||||
$num_updated = db_update('test')
|
||||
->fields(array('name' => 'Tiffany'))
|
||||
->condition('id', 1)
|
||||
->execute();
|
||||
$this->assertIdentical($num_updated, 1, 'Updated 1 record.');
|
||||
|
||||
$saved_name = db_query('SELECT name FROM {test} WHERE id = :id', array(':id' => 1))->fetchField();
|
||||
$this->assertIdentical($saved_name, 'Tiffany', 'Updated name successfully.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms updating to NULL.
|
||||
*/
|
||||
function testSimpleNullUpdate() {
|
||||
$this->ensureSampleDataNull();
|
||||
$num_updated = db_update('test_null')
|
||||
->fields(array('age' => NULL))
|
||||
->condition('name', 'Kermit')
|
||||
->execute();
|
||||
$this->assertIdentical($num_updated, 1, 'Updated 1 record.');
|
||||
|
||||
$saved_age = db_query('SELECT age FROM {test_null} WHERE name = :name', array(':name' => 'Kermit'))->fetchField();
|
||||
$this->assertNull($saved_age, 'Updated name successfully.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms that we can update multiple records successfully.
|
||||
*/
|
||||
function testMultiUpdate() {
|
||||
$num_updated = db_update('test')
|
||||
->fields(array('job' => 'Musician'))
|
||||
->condition('job', 'Singer')
|
||||
->execute();
|
||||
$this->assertIdentical($num_updated, 2, 'Updated 2 records.');
|
||||
|
||||
$num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
|
||||
$this->assertIdentical($num_matches, '2', 'Updated fields successfully.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms that we can update multiple records with a non-equality condition.
|
||||
*/
|
||||
function testMultiGTUpdate() {
|
||||
$num_updated = db_update('test')
|
||||
->fields(array('job' => 'Musician'))
|
||||
->condition('age', 26, '>')
|
||||
->execute();
|
||||
$this->assertIdentical($num_updated, 2, 'Updated 2 records.');
|
||||
|
||||
$num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
|
||||
$this->assertIdentical($num_matches, '2', 'Updated fields successfully.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms that we can update multiple records with a where call.
|
||||
*/
|
||||
function testWhereUpdate() {
|
||||
$num_updated = db_update('test')
|
||||
->fields(array('job' => 'Musician'))
|
||||
->where('age > :age', array(':age' => 26))
|
||||
->execute();
|
||||
$this->assertIdentical($num_updated, 2, 'Updated 2 records.');
|
||||
|
||||
$num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
|
||||
$this->assertIdentical($num_matches, '2', 'Updated fields successfully.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms that we can stack condition and where calls.
|
||||
*/
|
||||
function testWhereAndConditionUpdate() {
|
||||
$update = db_update('test')
|
||||
->fields(array('job' => 'Musician'))
|
||||
->where('age > :age', array(':age' => 26))
|
||||
->condition('name', 'Ringo');
|
||||
$num_updated = $update->execute();
|
||||
$this->assertIdentical($num_updated, 1, 'Updated 1 record.');
|
||||
|
||||
$num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
|
||||
$this->assertIdentical($num_matches, '1', 'Updated fields successfully.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests updating with expressions.
|
||||
*/
|
||||
function testExpressionUpdate() {
|
||||
// Ensure that expressions are handled properly. This should set every
|
||||
// record's age to a square of itself.
|
||||
$num_rows = db_update('test')
|
||||
->expression('age', 'age * age')
|
||||
->execute();
|
||||
$this->assertIdentical($num_rows, 4, 'Updated 4 records.');
|
||||
|
||||
$saved_name = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => pow(26, 2)))->fetchField();
|
||||
$this->assertIdentical($saved_name, 'Paul', 'Successfully updated values using an algebraic expression.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests return value on update.
|
||||
*/
|
||||
function testUpdateAffectedRows() {
|
||||
// At 5am in the morning, all band members but those with a priority 1 task
|
||||
// are sleeping. So we set their tasks to 'sleep'. 5 records match the
|
||||
// condition and therefore are affected by the query, even though two of
|
||||
// them actually don't have to be changed because their value was already
|
||||
// 'sleep'. Still, execute() should return 5 affected rows, not only 3,
|
||||
// because that's cross-db expected behavior.
|
||||
$num_rows = db_update('test_task')
|
||||
->condition('priority', 1, '<>')
|
||||
->fields(array('task' => 'sleep'))
|
||||
->execute();
|
||||
$this->assertIdentical($num_rows, 5, 'Correctly returned 5 affected rows.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm that we can update the primary key of a record successfully.
|
||||
*/
|
||||
function testPrimaryKeyUpdate() {
|
||||
$num_updated = db_update('test')
|
||||
->fields(array('id' => 42, 'name' => 'John'))
|
||||
->condition('id', 1)
|
||||
->execute();
|
||||
$this->assertIdentical($num_updated, 1, 'Updated 1 record.');
|
||||
|
||||
$saved_name= db_query('SELECT name FROM {test} WHERE id = :id', array(':id' => 42))->fetchField();
|
||||
$this->assertIdentical($saved_name, 'John', 'Updated primary key successfully.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm that we can update values in a column with special name.
|
||||
*/
|
||||
function testSpecialColumnUpdate() {
|
||||
$num_updated = db_update('test_special_columns')
|
||||
->fields(array('offset' => 'New offset value'))
|
||||
->condition('id', 1)
|
||||
->execute();
|
||||
$this->assertIdentical($num_updated, 1, 'Updated 1 special column record.');
|
||||
|
||||
$saved_value = db_query('SELECT "offset" FROM {test_special_columns} WHERE id = :id', array(':id' => 1))->fetchField();
|
||||
$this->assertIdentical($saved_value, 'New offset value', 'Updated special column name value successfully.');
|
||||
}
|
||||
}
|
||||
56
core/tests/Drupal/KernelTests/Core/Database/UpsertTest.php
Normal file
56
core/tests/Drupal/KernelTests/Core/Database/UpsertTest.php
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Database;
|
||||
|
||||
use Drupal\Core\Database\Database;
|
||||
|
||||
/**
|
||||
* Tests the Upsert query builder.
|
||||
*
|
||||
* @group Database
|
||||
*/
|
||||
class UpsertTest extends DatabaseTestBase {
|
||||
|
||||
/**
|
||||
* Confirms that we can upsert (update-or-insert) records successfully.
|
||||
*/
|
||||
public function testUpsert() {
|
||||
$connection = Database::getConnection();
|
||||
$num_records_before = $connection->query('SELECT COUNT(*) FROM {test_people}')->fetchField();
|
||||
|
||||
$upsert = $connection->upsert('test_people')
|
||||
->key('job')
|
||||
->fields(['job', 'age', 'name']);
|
||||
|
||||
// Add a new row.
|
||||
$upsert->values([
|
||||
'job' => 'Presenter',
|
||||
'age' => 31,
|
||||
'name' => 'Tiffany',
|
||||
]);
|
||||
|
||||
// Update an existing row.
|
||||
$upsert->values([
|
||||
'job' => 'Speaker',
|
||||
// The initial age was 30.
|
||||
'age' => 32,
|
||||
'name' => 'Meredith',
|
||||
]);
|
||||
|
||||
$upsert->execute();
|
||||
|
||||
$num_records_after = $connection->query('SELECT COUNT(*) FROM {test_people}')->fetchField();
|
||||
$this->assertEqual($num_records_before + 1, $num_records_after, 'Rows were inserted and updated properly.');
|
||||
|
||||
$person = $connection->query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Presenter'))->fetch();
|
||||
$this->assertEqual($person->job, 'Presenter', 'Job set correctly.');
|
||||
$this->assertEqual($person->age, 31, 'Age set correctly.');
|
||||
$this->assertEqual($person->name, 'Tiffany', 'Name set correctly.');
|
||||
|
||||
$person = $connection->query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch();
|
||||
$this->assertEqual($person->job, 'Speaker', 'Job was not changed.');
|
||||
$this->assertEqual($person->age, 32, 'Age updated correctly.');
|
||||
$this->assertEqual($person->name, 'Meredith', 'Name was not changed.');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Entity;
|
||||
|
||||
use Drupal\Core\TypedData\DataDefinition;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests validation constraints for BundleConstraintValidator.
|
||||
*
|
||||
* @group Entity
|
||||
*/
|
||||
class BundleConstraintValidatorTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* The typed data manager to use.
|
||||
*
|
||||
* @var \Drupal\Core\TypedData\TypedDataManager
|
||||
*/
|
||||
protected $typedData;
|
||||
|
||||
public static $modules = array('node', 'field', 'text', 'user');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installEntitySchema('user');
|
||||
$this->typedData = $this->container->get('typed_data_manager');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests bundle constraint validation.
|
||||
*/
|
||||
public function testValidation() {
|
||||
// Test with multiple values.
|
||||
$this->assertValidation(array('foo', 'bar'));
|
||||
// Test with a single string value as well.
|
||||
$this->assertValidation('foo');
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the BundleConstraintValidator test for a given bundle.
|
||||
*
|
||||
* @param string|array $bundle
|
||||
* Bundle/bundles to use as constraint option.
|
||||
*/
|
||||
protected function assertValidation($bundle) {
|
||||
// Create a typed data definition with a Bundle constraint.
|
||||
$definition = DataDefinition::create('entity_reference')
|
||||
->addConstraint('Bundle', $bundle);
|
||||
|
||||
// Test the validation.
|
||||
$node = $this->container->get('entity.manager')->getStorage('node')->create(array('type' => 'foo'));
|
||||
|
||||
$typed_data = $this->typedData->create($definition, $node);
|
||||
$violations = $typed_data->validate();
|
||||
$this->assertEqual($violations->count(), 0, 'Validation passed for correct value.');
|
||||
|
||||
// Test the validation when an invalid value is passed.
|
||||
$page_node = $this->container->get('entity.manager')->getStorage('node')->create(array('type' => 'baz'));
|
||||
|
||||
$typed_data = $this->typedData->create($definition, $page_node);
|
||||
$violations = $typed_data->validate();
|
||||
$this->assertEqual($violations->count(), 1, 'Validation failed for incorrect value.');
|
||||
|
||||
// Make sure the information provided by a violation is correct.
|
||||
$violation = $violations[0];
|
||||
$this->assertEqual($violation->getMessage(), t('The entity must be of bundle %bundle.', array('%bundle' => implode(', ', (array) $bundle))), 'The message for invalid value is correct.');
|
||||
$this->assertEqual($violation->getRoot(), $typed_data, 'Violation root is correct.');
|
||||
$this->assertEqual($violation->getInvalidValue(), $page_node, 'The invalid value is set correctly in the violation.');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,665 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Entity;
|
||||
|
||||
use Drupal\Core\Config\Entity\Query\QueryFactory;
|
||||
use Drupal\config_test\Entity\ConfigQueryTest;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests Config Entity Query functionality.
|
||||
*
|
||||
* @group Entity
|
||||
* @see \Drupal\Core\Config\Entity\Query
|
||||
*/
|
||||
class ConfigEntityQueryTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
static $modules = array('config_test');
|
||||
|
||||
/**
|
||||
* Stores the search results for alter comparison.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $queryResults;
|
||||
|
||||
/**
|
||||
* The query factory used to construct all queries in the test.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\Query\QueryFactory
|
||||
*/
|
||||
protected $factory;
|
||||
|
||||
/**
|
||||
* Stores all config entities created for the test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $entities;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->entities = array();
|
||||
$this->factory = $this->container->get('entity.query');
|
||||
|
||||
// These two are here to make sure that matchArray needs to go over several
|
||||
// non-matches on every levels.
|
||||
$array['level1']['level2a'] = 9;
|
||||
$array['level1a']['level2'] = 9;
|
||||
// The tests match array.level1.level2.
|
||||
$array['level1']['level2'] = 1;
|
||||
$entity = ConfigQueryTest::create(array(
|
||||
'label' => $this->randomMachineName(),
|
||||
'id' => '1',
|
||||
'number' => 31,
|
||||
'array' => $array,
|
||||
));
|
||||
$this->entities[] = $entity;
|
||||
$entity->enforceIsNew();
|
||||
$entity->save();
|
||||
|
||||
$array['level1']['level2'] = 2;
|
||||
$entity = ConfigQueryTest::create(array(
|
||||
'label' => $this->randomMachineName(),
|
||||
'id' => '2',
|
||||
'number' => 41,
|
||||
'array' => $array,
|
||||
));
|
||||
$this->entities[] = $entity;
|
||||
$entity->enforceIsNew();
|
||||
$entity->save();
|
||||
|
||||
$array['level1']['level2'] = 1;
|
||||
$entity = ConfigQueryTest::create(array(
|
||||
'label' => 'test_prefix_' . $this->randomMachineName(),
|
||||
'id' => '3',
|
||||
'number' => 59,
|
||||
'array' => $array,
|
||||
));
|
||||
$this->entities[] = $entity;
|
||||
$entity->enforceIsNew();
|
||||
$entity->save();
|
||||
|
||||
$array['level1']['level2'] = 2;
|
||||
$entity = ConfigQueryTest::create(array(
|
||||
'label' => $this->randomMachineName() . '_test_suffix',
|
||||
'id' => '4',
|
||||
'number' => 26,
|
||||
'array' => $array,
|
||||
));
|
||||
$this->entities[] = $entity;
|
||||
$entity->enforceIsNew();
|
||||
$entity->save();
|
||||
|
||||
$array['level1']['level2'] = 3;
|
||||
$entity = ConfigQueryTest::create(array(
|
||||
'label' => $this->randomMachineName() . '_TEST_contains_' . $this->randomMachineName(),
|
||||
'id' => '5',
|
||||
'number' => 53,
|
||||
'array' => $array,
|
||||
));
|
||||
$this->entities[] = $entity;
|
||||
$entity->enforceIsNew();
|
||||
$entity->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests basic functionality.
|
||||
*/
|
||||
public function testConfigEntityQuery() {
|
||||
// Run a test without any condition.
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->execute();
|
||||
$this->assertResults(array('1', '2', '3', '4', '5'));
|
||||
// No conditions, OR.
|
||||
$this->queryResults = $this->factory->get('config_query_test', 'OR')
|
||||
->execute();
|
||||
$this->assertResults(array('1', '2', '3', '4', '5'));
|
||||
|
||||
// Filter by ID with equality.
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->condition('id', '3')
|
||||
->execute();
|
||||
$this->assertResults(array('3'));
|
||||
|
||||
// Filter by label with a known prefix.
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->condition('label', 'test_prefix', 'STARTS_WITH')
|
||||
->execute();
|
||||
$this->assertResults(array('3'));
|
||||
|
||||
// Filter by label with a known suffix.
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->condition('label', 'test_suffix', 'ENDS_WITH')
|
||||
->execute();
|
||||
$this->assertResults(array('4'));
|
||||
|
||||
// Filter by label with a known containing word.
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->condition('label', 'test_contains', 'CONTAINS')
|
||||
->execute();
|
||||
$this->assertResults(array('5'));
|
||||
|
||||
// Filter by ID with the IN operator.
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->condition('id', array('2', '3'), 'IN')
|
||||
->execute();
|
||||
$this->assertResults(array('2', '3'));
|
||||
|
||||
// Filter by ID with the implicit IN operator.
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->condition('id', array('2', '3'))
|
||||
->execute();
|
||||
$this->assertResults(array('2', '3'));
|
||||
|
||||
// Filter by ID with the > operator.
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->condition('id', '3', '>')
|
||||
->execute();
|
||||
$this->assertResults(array('4', '5'));
|
||||
|
||||
// Filter by ID with the >= operator.
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->condition('id', '3', '>=')
|
||||
->execute();
|
||||
$this->assertResults(array('3', '4', '5'));
|
||||
|
||||
// Filter by ID with the <> operator.
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->condition('id', '3', '<>')
|
||||
->execute();
|
||||
$this->assertResults(array('1', '2', '4', '5'));
|
||||
|
||||
// Filter by ID with the < operator.
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->condition('id', '3', '<')
|
||||
->execute();
|
||||
$this->assertResults(array('1', '2'));
|
||||
|
||||
// Filter by ID with the <= operator.
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->condition('id', '3', '<=')
|
||||
->execute();
|
||||
$this->assertResults(array('1', '2', '3'));
|
||||
|
||||
// Filter by two conditions on the same field.
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->condition('label', 'test_pref', 'STARTS_WITH')
|
||||
->condition('label', 'test_prefix', 'STARTS_WITH')
|
||||
->execute();
|
||||
$this->assertResults(array('3'));
|
||||
|
||||
// Filter by two conditions on different fields. The first query matches for
|
||||
// a different ID, so the result is empty.
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->condition('label', 'test_prefix', 'STARTS_WITH')
|
||||
->condition('id', '5')
|
||||
->execute();
|
||||
$this->assertResults(array());
|
||||
|
||||
// Filter by two different conditions on different fields. This time the
|
||||
// first condition matches on one item, but the second one does as well.
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->condition('label', 'test_prefix', 'STARTS_WITH')
|
||||
->condition('id', '3')
|
||||
->execute();
|
||||
$this->assertResults(array('3'));
|
||||
|
||||
// Filter by two different conditions, of which the first one matches for
|
||||
// every entry, the second one as well, but just the third one filters so
|
||||
// that just two are left.
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->condition('id', '1', '>=')
|
||||
->condition('number', 10, '>=')
|
||||
->condition('number', 50, '>=')
|
||||
->execute();
|
||||
$this->assertResults(array('3', '5'));
|
||||
|
||||
// Filter with an OR condition group.
|
||||
$this->queryResults = $this->factory->get('config_query_test', 'OR')
|
||||
->condition('id', 1)
|
||||
->condition('id', '2')
|
||||
->execute();
|
||||
$this->assertResults(array('1', '2'));
|
||||
|
||||
// Simplify it with IN.
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->condition('id', array('1', '2'))
|
||||
->execute();
|
||||
$this->assertResults(array('1', '2'));
|
||||
// Try explicit IN.
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->condition('id', array('1', '2'), 'IN')
|
||||
->execute();
|
||||
$this->assertResults(array('1', '2'));
|
||||
// Try not IN.
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->condition('id', array('1', '2'), 'NOT IN')
|
||||
->execute();
|
||||
$this->assertResults(array('3', '4', '5'));
|
||||
|
||||
// Filter with an OR condition group on different fields.
|
||||
$this->queryResults = $this->factory->get('config_query_test', 'OR')
|
||||
->condition('id', 1)
|
||||
->condition('number', 41)
|
||||
->execute();
|
||||
$this->assertResults(array('1', '2'));
|
||||
|
||||
// Filter with an OR condition group on different fields but matching on the
|
||||
// same entity.
|
||||
$this->queryResults = $this->factory->get('config_query_test', 'OR')
|
||||
->condition('id', 1)
|
||||
->condition('number', 31)
|
||||
->execute();
|
||||
$this->assertResults(array('1'));
|
||||
|
||||
// NO simple conditions, YES complex conditions, 'AND'.
|
||||
$query = $this->factory->get('config_query_test', 'AND');
|
||||
$and_condition_1 = $query->orConditionGroup()
|
||||
->condition('id', '2')
|
||||
->condition('label', $this->entities[0]->label);
|
||||
$and_condition_2 = $query->orConditionGroup()
|
||||
->condition('id', 1)
|
||||
->condition('label', $this->entities[3]->label);
|
||||
$this->queryResults = $query
|
||||
->condition($and_condition_1)
|
||||
->condition($and_condition_2)
|
||||
->execute();
|
||||
$this->assertResults(array('1'));
|
||||
|
||||
// NO simple conditions, YES complex conditions, 'OR'.
|
||||
$query = $this->factory->get('config_query_test', 'OR');
|
||||
$and_condition_1 = $query->andConditionGroup()
|
||||
->condition('id', 1)
|
||||
->condition('label', $this->entities[0]->label);
|
||||
$and_condition_2 = $query->andConditionGroup()
|
||||
->condition('id', '2')
|
||||
->condition('label', $this->entities[1]->label);
|
||||
$this->queryResults = $query
|
||||
->condition($and_condition_1)
|
||||
->condition($and_condition_2)
|
||||
->execute();
|
||||
$this->assertResults(array('1', '2'));
|
||||
|
||||
// YES simple conditions, YES complex conditions, 'AND'.
|
||||
$query = $this->factory->get('config_query_test', 'AND');
|
||||
$and_condition_1 = $query->orConditionGroup()
|
||||
->condition('id', '2')
|
||||
->condition('label', $this->entities[0]->label);
|
||||
$and_condition_2 = $query->orConditionGroup()
|
||||
->condition('id', 1)
|
||||
->condition('label', $this->entities[3]->label);
|
||||
$this->queryResults = $query
|
||||
->condition('number', 31)
|
||||
->condition($and_condition_1)
|
||||
->condition($and_condition_2)
|
||||
->execute();
|
||||
$this->assertResults(array('1'));
|
||||
|
||||
// YES simple conditions, YES complex conditions, 'OR'.
|
||||
$query = $this->factory->get('config_query_test', 'OR');
|
||||
$and_condition_1 = $query->orConditionGroup()
|
||||
->condition('id', '2')
|
||||
->condition('label', $this->entities[0]->label);
|
||||
$and_condition_2 = $query->orConditionGroup()
|
||||
->condition('id', 1)
|
||||
->condition('label', $this->entities[3]->label);
|
||||
$this->queryResults = $query
|
||||
->condition('number', 53)
|
||||
->condition($and_condition_1)
|
||||
->condition($and_condition_2)
|
||||
->execute();
|
||||
$this->assertResults(array('1', '2', '4', '5'));
|
||||
|
||||
// Test the exists and notExists conditions.
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->exists('id')
|
||||
->execute();
|
||||
$this->assertResults(array('1', '2', '3', '4', '5'));
|
||||
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->exists('non-existent')
|
||||
->execute();
|
||||
$this->assertResults(array());
|
||||
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->notExists('id')
|
||||
->execute();
|
||||
$this->assertResults(array());
|
||||
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->notExists('non-existent')
|
||||
->execute();
|
||||
$this->assertResults(array('1', '2', '3', '4', '5'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests ID conditions.
|
||||
*/
|
||||
public function testStringIdConditions() {
|
||||
// We need an entity with a non-numeric ID.
|
||||
$entity = ConfigQueryTest::create(array(
|
||||
'label' => $this->randomMachineName(),
|
||||
'id' => 'foo.bar',
|
||||
));
|
||||
$this->entities[] = $entity;
|
||||
$entity->enforceIsNew();
|
||||
$entity->save();
|
||||
|
||||
// Test 'STARTS_WITH' condition.
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->condition('id', 'foo.bar', 'STARTS_WITH')
|
||||
->execute();
|
||||
$this->assertResults(array('foo.bar'));
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->condition('id', 'f', 'STARTS_WITH')
|
||||
->execute();
|
||||
$this->assertResults(array('foo.bar'));
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->condition('id', 'miss', 'STARTS_WITH')
|
||||
->execute();
|
||||
$this->assertResults(array());
|
||||
|
||||
// Test 'CONTAINS' condition.
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->condition('id', 'foo.bar', 'CONTAINS')
|
||||
->execute();
|
||||
$this->assertResults(array('foo.bar'));
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->condition('id', 'oo.ba', 'CONTAINS')
|
||||
->execute();
|
||||
$this->assertResults(array('foo.bar'));
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->condition('id', 'miss', 'CONTAINS')
|
||||
->execute();
|
||||
$this->assertResults(array());
|
||||
|
||||
// Test 'ENDS_WITH' condition.
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->condition('id', 'foo.bar', 'ENDS_WITH')
|
||||
->execute();
|
||||
$this->assertResults(array('foo.bar'));
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->condition('id', 'r', 'ENDS_WITH')
|
||||
->execute();
|
||||
$this->assertResults(array('foo.bar'));
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->condition('id', 'miss', 'ENDS_WITH')
|
||||
->execute();
|
||||
$this->assertResults(array());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests count query.
|
||||
*/
|
||||
public function testCount() {
|
||||
// Test count on no conditions.
|
||||
$count = $this->factory->get('config_query_test')
|
||||
->count()
|
||||
->execute();
|
||||
$this->assertIdentical($count, count($this->entities));
|
||||
|
||||
// Test count on a complex query.
|
||||
$query = $this->factory->get('config_query_test', 'OR');
|
||||
$and_condition_1 = $query->andConditionGroup()
|
||||
->condition('id', 1)
|
||||
->condition('label', $this->entities[0]->label);
|
||||
$and_condition_2 = $query->andConditionGroup()
|
||||
->condition('id', '2')
|
||||
->condition('label', $this->entities[1]->label);
|
||||
$count = $query
|
||||
->condition($and_condition_1)
|
||||
->condition($and_condition_2)
|
||||
->count()
|
||||
->execute();
|
||||
$this->assertIdentical($count, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests sorting and range on config entity queries.
|
||||
*/
|
||||
public function testSortRange() {
|
||||
// Sort by simple ascending/descending.
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->sort('number', 'DESC')
|
||||
->execute();
|
||||
$this->assertIdentical(array_values($this->queryResults), array('3', '5', '2', '1', '4'));
|
||||
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->sort('number', 'ASC')
|
||||
->execute();
|
||||
$this->assertIdentical(array_values($this->queryResults), array('4', '1', '2', '5', '3'));
|
||||
|
||||
// Apply some filters and sort.
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->condition('id', '3', '>')
|
||||
->sort('number', 'DESC')
|
||||
->execute();
|
||||
$this->assertIdentical(array_values($this->queryResults), array('5', '4'));
|
||||
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->condition('id', '3', '>')
|
||||
->sort('number', 'ASC')
|
||||
->execute();
|
||||
$this->assertIdentical(array_values($this->queryResults), array('4', '5'));
|
||||
|
||||
// Apply a pager and sort.
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->sort('number', 'DESC')
|
||||
->range('2', '2')
|
||||
->execute();
|
||||
$this->assertIdentical(array_values($this->queryResults), array('2', '1'));
|
||||
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->sort('number', 'ASC')
|
||||
->range('2', '2')
|
||||
->execute();
|
||||
$this->assertIdentical(array_values($this->queryResults), array('2', '5'));
|
||||
|
||||
// Add a range to a query without a start parameter.
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->range(0, '3')
|
||||
->sort('id', 'ASC')
|
||||
->execute();
|
||||
$this->assertIdentical(array_values($this->queryResults), array('1', '2', '3'));
|
||||
|
||||
// Apply a pager with limit 4.
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->pager('4', 0)
|
||||
->sort('id', 'ASC')
|
||||
->execute();
|
||||
$this->assertIdentical(array_values($this->queryResults), array('1', '2', '3', '4'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests sorting with tableSort on config entity queries.
|
||||
*/
|
||||
public function testTableSort() {
|
||||
$header = array(
|
||||
array('data' => t('ID'), 'specifier' => 'id'),
|
||||
array('data' => t('Number'), 'specifier' => 'number'),
|
||||
);
|
||||
|
||||
// Sort key: id
|
||||
// Sorting with 'DESC' upper case
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->tableSort($header)
|
||||
->sort('id', 'DESC')
|
||||
->execute();
|
||||
$this->assertIdentical(array_values($this->queryResults), array('5', '4', '3', '2', '1'));
|
||||
|
||||
// Sorting with 'ASC' upper case
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->tableSort($header)
|
||||
->sort('id', 'ASC')
|
||||
->execute();
|
||||
$this->assertIdentical(array_values($this->queryResults), array('1', '2', '3', '4', '5'));
|
||||
|
||||
// Sorting with 'desc' lower case
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->tableSort($header)
|
||||
->sort('id', 'desc')
|
||||
->execute();
|
||||
$this->assertIdentical(array_values($this->queryResults), array('5', '4', '3', '2', '1'));
|
||||
|
||||
// Sorting with 'asc' lower case
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->tableSort($header)
|
||||
->sort('id', 'asc')
|
||||
->execute();
|
||||
$this->assertIdentical(array_values($this->queryResults), array('1', '2', '3', '4', '5'));
|
||||
|
||||
// Sort key: number
|
||||
// Sorting with 'DeSc' mixed upper and lower case
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->tableSort($header)
|
||||
->sort('number', 'DeSc')
|
||||
->execute();
|
||||
$this->assertIdentical(array_values($this->queryResults), array('3', '5', '2', '1', '4'));
|
||||
|
||||
// Sorting with 'AsC' mixed upper and lower case
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->tableSort($header)
|
||||
->sort('number', 'AsC')
|
||||
->execute();
|
||||
$this->assertIdentical(array_values($this->queryResults), array('4', '1', '2', '5', '3'));
|
||||
|
||||
// Sorting with 'dEsC' mixed upper and lower case
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->tableSort($header)
|
||||
->sort('number', 'dEsC')
|
||||
->execute();
|
||||
$this->assertIdentical(array_values($this->queryResults), array('3', '5', '2', '1', '4'));
|
||||
|
||||
// Sorting with 'aSc' mixed upper and lower case
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->tableSort($header)
|
||||
->sort('number', 'aSc')
|
||||
->execute();
|
||||
$this->assertIdentical(array_values($this->queryResults), array('4', '1', '2', '5', '3'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests dotted path matching.
|
||||
*/
|
||||
public function testDotted() {
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->condition('array.level1.*', 1)
|
||||
->execute();
|
||||
$this->assertResults(array('1', '3'));
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->condition('*.level1.level2', 2)
|
||||
->execute();
|
||||
$this->assertResults(array('2', '4'));
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->condition('array.level1.*', 3)
|
||||
->execute();
|
||||
$this->assertResults(array('5'));
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->condition('array.level1.level2', 3)
|
||||
->execute();
|
||||
$this->assertResults(array('5'));
|
||||
// Make sure that values on the wildcard level do not match if there are
|
||||
// sub-keys defined. This must not find anything even if entity 2 has a
|
||||
// top-level key number with value 41.
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->condition('*.level1.level2', 41)
|
||||
->execute();
|
||||
$this->assertResults(array());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests case sensitivity.
|
||||
*/
|
||||
public function testCaseSensitivity() {
|
||||
// Filter by label with a known containing case-sensitive word.
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->condition('label', 'TEST', 'CONTAINS')
|
||||
->execute();
|
||||
$this->assertResults(array('3', '4', '5'));
|
||||
|
||||
$this->queryResults = $this->factory->get('config_query_test')
|
||||
->condition('label', 'test', 'CONTAINS')
|
||||
->execute();
|
||||
$this->assertResults(array('3', '4', '5'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests lookup keys are added to the key value store.
|
||||
*/
|
||||
public function testLookupKeys() {
|
||||
\Drupal::service('state')->set('config_test.lookup_keys', TRUE);
|
||||
\Drupal::entityManager()->clearCachedDefinitions();
|
||||
$key_value = $this->container->get('keyvalue')->get(QueryFactory::CONFIG_LOOKUP_PREFIX . 'config_test');
|
||||
|
||||
$test_entities = [];
|
||||
$entity = entity_create('config_test', array(
|
||||
'label' => $this->randomMachineName(),
|
||||
'id' => '1',
|
||||
'style' => 'test',
|
||||
));
|
||||
$test_entities[$entity->getConfigDependencyName()] = $entity;
|
||||
$entity->enforceIsNew();
|
||||
$entity->save();
|
||||
|
||||
|
||||
$expected[] = $entity->getConfigDependencyName();
|
||||
$this->assertEqual($expected, $key_value->get('style:test'));
|
||||
|
||||
$entity = entity_create('config_test', array(
|
||||
'label' => $this->randomMachineName(),
|
||||
'id' => '2',
|
||||
'style' => 'test',
|
||||
));
|
||||
$test_entities[$entity->getConfigDependencyName()] = $entity;
|
||||
$entity->enforceIsNew();
|
||||
$entity->save();
|
||||
$expected[] = $entity->getConfigDependencyName();
|
||||
$this->assertEqual($expected, $key_value->get('style:test'));
|
||||
|
||||
$entity = entity_create('config_test', array(
|
||||
'label' => $this->randomMachineName(),
|
||||
'id' => '3',
|
||||
'style' => 'blah',
|
||||
));
|
||||
$entity->enforceIsNew();
|
||||
$entity->save();
|
||||
// Do not add this entity to the list of expected result as it has a
|
||||
// different value.
|
||||
$this->assertEqual($expected, $key_value->get('style:test'));
|
||||
$this->assertEqual([$entity->getConfigDependencyName()], $key_value->get('style:blah'));
|
||||
|
||||
// Ensure that a delete clears a key.
|
||||
$entity->delete();
|
||||
$this->assertEqual(NULL, $key_value->get('style:blah'));
|
||||
|
||||
// Ensure that delete only clears one key.
|
||||
$entity_id = array_pop($expected);
|
||||
$test_entities[$entity_id]->delete();
|
||||
$this->assertEqual($expected, $key_value->get('style:test'));
|
||||
$entity_id = array_pop($expected);
|
||||
$test_entities[$entity_id]->delete();
|
||||
$this->assertEqual(NULL, $key_value->get('style:test'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts the results as expected regardless of order.
|
||||
*
|
||||
* @param array $expected
|
||||
* Array of expected entity IDs.
|
||||
*/
|
||||
protected function assertResults($expected) {
|
||||
$this->assertIdentical(count($this->queryResults), count($expected));
|
||||
foreach ($expected as $value) {
|
||||
// This also tests whether $this->queryResults[$value] is even set at all.
|
||||
$this->assertIdentical($this->queryResults[$value], $value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,540 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Entity;
|
||||
|
||||
use Drupal\entity_test\Entity\EntityTestMulChanged;
|
||||
use Drupal\entity_test\Entity\EntityTestMulRevChanged;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
|
||||
/**
|
||||
* Tests basic EntityChangedInterface functionality.
|
||||
*
|
||||
* @group Entity
|
||||
*/
|
||||
class ContentEntityChangedTest extends EntityKernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['language', 'user', 'system', 'field', 'text', 'filter', 'entity_test'];
|
||||
|
||||
/**
|
||||
* The EntityTestMulChanged entity type storage.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityStorageInterface
|
||||
*/
|
||||
protected $mulChangedStorage;
|
||||
|
||||
/**
|
||||
* The EntityTestMulRevChanged entity type storage.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityStorageInterface
|
||||
*/
|
||||
protected $mulRevChangedStorage;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Enable an additional language.
|
||||
ConfigurableLanguage::createFromLangcode('de')->save();
|
||||
ConfigurableLanguage::createFromLangcode('fr')->save();
|
||||
|
||||
$this->installEntitySchema('entity_test_mul_changed');
|
||||
$this->installEntitySchema('entity_test_mulrev_changed');
|
||||
|
||||
$this->mulChangedStorage = $this->entityManager->getStorage('entity_test_mul_changed');
|
||||
$this->mulRevChangedStorage = $this->entityManager->getStorage('entity_test_mulrev_changed');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests basic EntityChangedInterface functionality.
|
||||
*/
|
||||
public function testChanged() {
|
||||
$user1 = $this->createUser();
|
||||
$user2 = $this->createUser();
|
||||
|
||||
// Create a test entity.
|
||||
$entity = EntityTestMulChanged::create(array(
|
||||
'name' => $this->randomString(),
|
||||
'user_id' => $user1->id(),
|
||||
'language' => 'en',
|
||||
));
|
||||
$entity->save();
|
||||
|
||||
$this->assertTrue(
|
||||
$entity->getChangedTime() >= REQUEST_TIME,
|
||||
'Changed time of original language is valid.'
|
||||
);
|
||||
|
||||
// We can't assert equality here because the created time is set to the
|
||||
// request time, while instances of ChangedTestItem use the current
|
||||
// timestamp every time. Therefor we check if the changed timestamp is
|
||||
// between the created time and now.
|
||||
$this->assertTrue(
|
||||
($entity->getChangedTime() >= $entity->get('created')->value) &&
|
||||
(($entity->getChangedTime() - $entity->get('created')->value) <= time() - REQUEST_TIME),
|
||||
'Changed and created time of original language can be assumed to be identical.'
|
||||
);
|
||||
|
||||
$this->assertEqual(
|
||||
$entity->getChangedTime(), $entity->getChangedTimeAcrossTranslations(),
|
||||
'Changed time of original language is the same as changed time across all translations.'
|
||||
);
|
||||
|
||||
$changed_en = $entity->getChangedTime();
|
||||
|
||||
/** @var \Drupal\entity_test\Entity\EntityTestMulRevChanged $german */
|
||||
$german = $entity->addTranslation('de');
|
||||
|
||||
$entity->save();
|
||||
|
||||
$this->assertEqual(
|
||||
$entity->getChangedTime(), $changed_en,
|
||||
'Changed time of original language did not change.'
|
||||
);
|
||||
|
||||
$this->assertTrue(
|
||||
$german->getChangedTime() > $entity->getChangedTime(),
|
||||
'Changed time of the German translation is newer then the original language.'
|
||||
);
|
||||
|
||||
$this->assertEqual(
|
||||
$german->getChangedTime(), $entity->getChangedTimeAcrossTranslations(),
|
||||
'Changed time of the German translation is the newest time across all translations.'
|
||||
);
|
||||
|
||||
$changed_de = $german->getChangedTime();
|
||||
|
||||
$entity->save();
|
||||
|
||||
$this->assertEqual(
|
||||
$entity->getChangedTime(), $changed_en,
|
||||
'Changed time of original language did not change.'
|
||||
);
|
||||
|
||||
$this->assertEqual(
|
||||
$german->getChangedTime(), $changed_de,
|
||||
'Changed time of the German translation did not change.'
|
||||
);
|
||||
|
||||
$entity->setOwner($user2);
|
||||
|
||||
$entity->save();
|
||||
|
||||
$this->assertTrue(
|
||||
$entity->getChangedTime() > $changed_en,
|
||||
'Changed time of original language did change.'
|
||||
);
|
||||
|
||||
$this->assertEqual(
|
||||
$german->getChangedTime(), $changed_de,
|
||||
'Changed time of the German translation did not change.'
|
||||
);
|
||||
|
||||
$this->assertTrue(
|
||||
$entity->getChangedTime() > $german->getChangedTime(),
|
||||
'Changed time of original language is newer then the German translation.'
|
||||
);
|
||||
|
||||
$this->assertEqual(
|
||||
$entity->getChangedTime(), $entity->getChangedTimeAcrossTranslations(),
|
||||
'Changed time of the original language is the newest time across all translations.'
|
||||
);
|
||||
|
||||
$changed_en = $entity->getChangedTime();
|
||||
|
||||
// Save entity without any changes.
|
||||
$entity->save();
|
||||
|
||||
$this->assertEqual(
|
||||
$entity->getChangedTime(), $changed_en,
|
||||
'Changed time of original language did not change.'
|
||||
);
|
||||
|
||||
$this->assertEqual(
|
||||
$german->getChangedTime(), $changed_de,
|
||||
'Changed time of the German translation did not change.'
|
||||
);
|
||||
|
||||
// At this point the changed time of the original language (en) is newer
|
||||
// than the changed time of the German translation. Now test that entity
|
||||
// queries work as expected.
|
||||
$query = $this->mulChangedStorage->getQuery();
|
||||
$ids = $query->condition('changed', $changed_en)->execute();
|
||||
|
||||
$this->assertEqual(
|
||||
reset($ids), $entity->id(),
|
||||
'Entity query can access changed time of original language.'
|
||||
);
|
||||
|
||||
$query = $this->mulChangedStorage->getQuery();
|
||||
$ids = $query->condition('changed', $changed_en, '=', 'en')->execute();
|
||||
|
||||
$this->assertEqual(
|
||||
reset($ids), $entity->id(),
|
||||
'Entity query can access changed time of original language by setting the original language as condition.'
|
||||
);
|
||||
|
||||
$query = $this->mulChangedStorage->getQuery();
|
||||
$ids = $query->condition('changed', $changed_de, '=', 'en')->execute();
|
||||
|
||||
$this->assertFalse(
|
||||
$ids,
|
||||
'There\'s no original entity stored having the changed time of the German translation.'
|
||||
);
|
||||
|
||||
$query = $this->mulChangedStorage->getQuery();
|
||||
$ids = $query->condition('changed', $changed_en)->condition('default_langcode', '1')->execute();
|
||||
|
||||
$this->assertEqual(
|
||||
reset($ids), $entity->id(),
|
||||
'Entity query can access changed time of default language.'
|
||||
);
|
||||
|
||||
$query = $this->mulChangedStorage->getQuery();
|
||||
$ids = $query->condition('changed', $changed_de)->condition('default_langcode', '1')->execute();
|
||||
|
||||
$this->assertFalse(
|
||||
$ids,
|
||||
'There\'s no entity stored using the default language having the changed time of the German translation.'
|
||||
);
|
||||
|
||||
$query = $this->mulChangedStorage->getQuery();
|
||||
$ids = $query->condition('changed', $changed_de)->execute();
|
||||
|
||||
$this->assertEqual(
|
||||
reset($ids), $entity->id(),
|
||||
'Entity query can access changed time of the German translation.'
|
||||
);
|
||||
|
||||
$query = $this->mulChangedStorage->getQuery();
|
||||
$ids = $query->condition('changed', $changed_de, '=', 'de')->execute();
|
||||
|
||||
$this->assertEqual(
|
||||
reset($ids), $entity->id(),
|
||||
'Entity query can access changed time of the German translation.'
|
||||
);
|
||||
|
||||
$query = $this->mulChangedStorage->getQuery();
|
||||
$ids = $query->condition('changed', $changed_en, '=', 'de')->execute();
|
||||
|
||||
$this->assertFalse(
|
||||
$ids,
|
||||
'There\'s no German translation stored having the changed time of the original language.'
|
||||
);
|
||||
|
||||
$query = $this->mulChangedStorage->getQuery();
|
||||
$ids = $query->condition('changed', $changed_de, '>')->execute();
|
||||
|
||||
$this->assertEqual(
|
||||
reset($ids), $entity->id(),
|
||||
'Entity query can access changed time regardless of translation.'
|
||||
);
|
||||
|
||||
$query = $this->mulChangedStorage->getQuery();
|
||||
$ids = $query->condition('changed', $changed_en, '<')->execute();
|
||||
|
||||
$this->assertEqual(
|
||||
reset($ids), $entity->id(),
|
||||
'Entity query can access changed time regardless of translation.'
|
||||
);
|
||||
|
||||
$query = $this->mulChangedStorage->getQuery();
|
||||
$ids = $query->condition('changed', 0, '>')->execute();
|
||||
|
||||
$this->assertEqual(
|
||||
reset($ids), $entity->id(),
|
||||
'Entity query can access changed time regardless of translation.'
|
||||
);
|
||||
|
||||
$query = $this->mulChangedStorage->getQuery();
|
||||
$ids = $query->condition('changed', $changed_en, '>')->execute();
|
||||
|
||||
$this->assertFalse(
|
||||
$ids,
|
||||
'Entity query can access changed time regardless of translation.'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests revisionable EntityChangedInterface functionality.
|
||||
*/
|
||||
public function testRevisionChanged() {
|
||||
$user1 = $this->createUser();
|
||||
$user2 = $this->createUser();
|
||||
|
||||
// Create a test entity.
|
||||
$entity = EntityTestMulRevChanged::create(array(
|
||||
'name' => $this->randomString(),
|
||||
'user_id' => $user1->id(),
|
||||
'language' => 'en',
|
||||
));
|
||||
$entity->save();
|
||||
|
||||
$this->assertTrue(
|
||||
$entity->getChangedTime() >= REQUEST_TIME,
|
||||
'Changed time of original language is valid.'
|
||||
);
|
||||
|
||||
// We can't assert equality here because the created time is set to the
|
||||
// request time while instances of ChangedTestItem use the current
|
||||
// timestamp every time.
|
||||
$this->assertTrue(
|
||||
($entity->getChangedTime() >= $entity->get('created')->value) &&
|
||||
(($entity->getChangedTime() - $entity->get('created')->value) <= time() - REQUEST_TIME),
|
||||
'Changed and created time of original language can be assumed to be identical.'
|
||||
);
|
||||
|
||||
$this->assertEqual(
|
||||
$entity->getChangedTime(), $entity->getChangedTimeAcrossTranslations(),
|
||||
'Changed time of original language is the same as changed time across all translations.'
|
||||
);
|
||||
|
||||
$this->assertTrue(
|
||||
$this->getRevisionTranslationAffectedFlag($entity),
|
||||
'Changed flag of original language is set for a new entity.'
|
||||
);
|
||||
|
||||
$changed_en = $entity->getChangedTime();
|
||||
|
||||
$entity->setNewRevision();
|
||||
// Save entity without any changes but create new revision.
|
||||
$entity->save();
|
||||
// A new revision without any changes should not set a new changed time.
|
||||
$this->assertEqual(
|
||||
$entity->getChangedTime(), $changed_en,
|
||||
'Changed time of original language did not change.'
|
||||
);
|
||||
|
||||
$this->assertFalse(
|
||||
$this->getRevisionTranslationAffectedFlag($entity),
|
||||
'Changed flag of original language is not set for new revision without changes.'
|
||||
);
|
||||
|
||||
$entity->setNewRevision();
|
||||
$entity->setOwner($user2);
|
||||
$entity->save();
|
||||
|
||||
$this->assertTrue(
|
||||
$entity->getChangedTime() > $changed_en,
|
||||
'Changed time of original language has been updated by new revision.'
|
||||
);
|
||||
|
||||
$this->assertTrue(
|
||||
$this->getRevisionTranslationAffectedFlag($entity),
|
||||
'Changed flag of original language is set for new revision with changes.'
|
||||
);
|
||||
|
||||
$changed_en = $entity->getChangedTime();
|
||||
|
||||
/** @var \Drupal\entity_test\Entity\EntityTestMulRevChanged $german */
|
||||
$german = $entity->addTranslation('de');
|
||||
|
||||
$entity->save();
|
||||
|
||||
$this->assertEqual(
|
||||
$entity->getChangedTime(), $changed_en,
|
||||
'Changed time of original language did not change.'
|
||||
);
|
||||
|
||||
$this->assertTrue(
|
||||
$german->getChangedTime() > $entity->getChangedTime(),
|
||||
'Changed time of the German translation is newer then the original language.'
|
||||
);
|
||||
|
||||
$this->assertEqual(
|
||||
$german->getChangedTime(), $entity->getChangedTimeAcrossTranslations(),
|
||||
'Changed time of the German translation is the newest time across all translations.'
|
||||
);
|
||||
|
||||
$this->assertTrue(
|
||||
$this->getRevisionTranslationAffectedFlag($entity),
|
||||
'Changed flag of original language is not reset by adding a new translation.'
|
||||
);
|
||||
|
||||
$this->assertTrue(
|
||||
$this->getRevisionTranslationAffectedFlag($german),
|
||||
'Changed flag of German translation is set when adding the translation.'
|
||||
);
|
||||
|
||||
$changed_de = $german->getChangedTime();
|
||||
|
||||
$entity->setNewRevision();
|
||||
// Save entity without any changes but create new revision.
|
||||
$entity->save();
|
||||
|
||||
$this->assertEqual(
|
||||
$entity->getChangedTime(), $changed_en,
|
||||
'Changed time of original language did not change.'
|
||||
);
|
||||
|
||||
$this->assertEqual(
|
||||
$german->getChangedTime(), $changed_de,
|
||||
'Changed time of the German translation did not change.'
|
||||
);
|
||||
|
||||
$this->assertFalse(
|
||||
$this->getRevisionTranslationAffectedFlag($entity),
|
||||
'Changed flag of original language is not set for new revision without changes.'
|
||||
);
|
||||
|
||||
$this->assertFalse(
|
||||
$this->getRevisionTranslationAffectedFlag($german),
|
||||
'Changed flag of the German translation is not set for new revision without changes.'
|
||||
);
|
||||
|
||||
$entity->setNewRevision();
|
||||
$german->setOwner($user2);
|
||||
$entity->save();
|
||||
|
||||
$this->assertEqual(
|
||||
$entity->getChangedTime(), $changed_en,
|
||||
'Changed time of original language did not change.'
|
||||
);
|
||||
|
||||
$this->assertTrue(
|
||||
$german->getChangedTime() > $changed_de,
|
||||
'Changed time of the German translation did change.'
|
||||
);
|
||||
|
||||
$this->assertEqual(
|
||||
$german->getChangedTime(), $entity->getChangedTimeAcrossTranslations(),
|
||||
'Changed time of the German translation is the newest time across all translations.'
|
||||
);
|
||||
|
||||
$this->assertFalse(
|
||||
$this->getRevisionTranslationAffectedFlag($entity),
|
||||
'Changed flag of original language is not set when changing the German Translation.'
|
||||
);
|
||||
|
||||
$this->assertTrue(
|
||||
$this->getRevisionTranslationAffectedFlag($german),
|
||||
'Changed flag of German translation is set when changing the German translation.'
|
||||
);
|
||||
|
||||
$french = $entity->addTranslation('fr');
|
||||
|
||||
$entity->setNewRevision();
|
||||
$entity->save();
|
||||
|
||||
$this->assertEqual(
|
||||
$entity->getChangedTime(), $changed_en,
|
||||
'Changed time of original language did not change.'
|
||||
);
|
||||
|
||||
$this->assertTrue(
|
||||
$french->getChangedTime() > $entity->getChangedTime(),
|
||||
'Changed time of the French translation is newer then the original language.'
|
||||
);
|
||||
|
||||
$this->assertTrue(
|
||||
$french->getChangedTime() > $entity->getChangedTime(),
|
||||
'Changed time of the French translation is newer then the German translation.'
|
||||
);
|
||||
|
||||
$this->assertEqual(
|
||||
$french->getChangedTime(), $entity->getChangedTimeAcrossTranslations(),
|
||||
'Changed time of the French translation is the newest time across all translations.'
|
||||
);
|
||||
|
||||
$this->assertFalse(
|
||||
$this->getRevisionTranslationAffectedFlag($entity),
|
||||
'Changed flag of original language is reset by adding a new translation and a new revision.'
|
||||
);
|
||||
|
||||
$this->assertFalse(
|
||||
$this->getRevisionTranslationAffectedFlag($german),
|
||||
'Changed flag of German translation is reset by adding a new translation and a new revision.'
|
||||
);
|
||||
|
||||
$this->assertTrue(
|
||||
$this->getRevisionTranslationAffectedFlag($french),
|
||||
'Changed flag of French translation is set when adding the translation and a new revision.'
|
||||
);
|
||||
|
||||
$entity->removeTranslation('fr');
|
||||
|
||||
$entity->setNewRevision();
|
||||
$entity->save();
|
||||
|
||||
// This block simulates exactly the flow of a node form submission of a new
|
||||
// translation and a new revision.
|
||||
$form_entity_builder_entity = EntityTestMulRevChanged::load($entity->id());
|
||||
// ContentTranslationController::prepareTranslation().
|
||||
$form_entity_builder_entity = $form_entity_builder_entity->addTranslation('fr', $form_entity_builder_entity->toArray());
|
||||
// EntityForm::buildEntity() during form submit.
|
||||
$form_entity_builder_clone = clone $form_entity_builder_entity;
|
||||
// NodeForm::submitForm().
|
||||
$form_entity_builder_clone->setNewRevision();
|
||||
// EntityForm::save().
|
||||
$form_entity_builder_clone->save();
|
||||
|
||||
// The assertion fails unless https://www.drupal.org/node/2513094 is
|
||||
// committed.
|
||||
$this->assertFalse(
|
||||
$this->getRevisionTranslationAffectedFlag($entity),
|
||||
'Changed flag of original language is reset by adding a new translation and a new revision.'
|
||||
);
|
||||
|
||||
$this->assertFalse(
|
||||
$this->getRevisionTranslationAffectedFlag($german),
|
||||
'Changed flag of German translation is reset by adding a new translation and a new revision.'
|
||||
);
|
||||
|
||||
$this->assertTrue(
|
||||
$this->getRevisionTranslationAffectedFlag($french),
|
||||
'Changed flag of French translation is set when adding the translation and a new revision.'
|
||||
);
|
||||
|
||||
$german->setOwner($user1);
|
||||
$german->setRevisionTranslationAffected(FALSE);
|
||||
$entity->save();
|
||||
|
||||
$this->assertFalse(
|
||||
$this->getRevisionTranslationAffectedFlag($german),
|
||||
'German translation changed but the changed flag is reset manually.'
|
||||
);
|
||||
|
||||
$entity->setNewRevision();
|
||||
$german->setRevisionTranslationAffected(TRUE);
|
||||
$entity->save();
|
||||
|
||||
$this->assertTrue(
|
||||
$this->getRevisionTranslationAffectedFlag($german),
|
||||
'German translation is not changed and a new revision is created but the changed flag is set manually.'
|
||||
);
|
||||
|
||||
$german->setOwner($user2);
|
||||
$entity->setNewRevision();
|
||||
$german->setRevisionTranslationAffected(FALSE);
|
||||
$entity->save();
|
||||
|
||||
$this->assertFalse(
|
||||
$this->getRevisionTranslationAffectedFlag($german),
|
||||
'German translation changed and a new revision is created but the changed flag is reset manually.'
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the revision translation affected flag value.
|
||||
*
|
||||
* @param \Drupal\entity_test\Entity\EntityTestMulRevChanged $entity
|
||||
* The entity object to be checked.
|
||||
*
|
||||
* @return bool
|
||||
* The flag value.
|
||||
*/
|
||||
protected function getRevisionTranslationAffectedFlag(EntityTestMulRevChanged $entity) {
|
||||
$query = $this->mulRevChangedStorage->getQuery();
|
||||
$ids = $query->condition('revision_translation_affected', 1, '=', $entity->language()->getId())->execute();
|
||||
$id = reset($ids);
|
||||
return (bool) ($id == $entity->id());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Entity;
|
||||
|
||||
use Drupal\entity_test\Entity\EntityTestMul;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
|
||||
/**
|
||||
* Tests proper cloning of content entities.
|
||||
*
|
||||
* @group Entity
|
||||
*/
|
||||
class ContentEntityCloneTest extends EntityKernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['language', 'entity_test'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Enable an additional language.
|
||||
ConfigurableLanguage::createFromLangcode('de')->save();
|
||||
|
||||
$this->installEntitySchema('entity_test_mul');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if entity references on fields are still correct after cloning.
|
||||
*/
|
||||
public function testFieldEntityReferenceAfterClone() {
|
||||
$user = $this->createUser();
|
||||
|
||||
// Create a test entity.
|
||||
$entity = EntityTestMul::create([
|
||||
'name' => $this->randomString(),
|
||||
'user_id' => $user->id(),
|
||||
'language' => 'en',
|
||||
]);
|
||||
|
||||
$clone = clone $entity->addTranslation('de');
|
||||
|
||||
$this->assertEqual($entity->getTranslationLanguages(), $clone->getTranslationLanguages(), 'The entity and its clone have the same translation languages.');
|
||||
|
||||
$default_langcode = $entity->getUntranslated()->language()->getId();
|
||||
foreach (array_keys($clone->getTranslationLanguages()) as $langcode) {
|
||||
$translation = $clone->getTranslation($langcode);
|
||||
foreach ($translation->getFields() as $field_name => $field) {
|
||||
if ($field->getFieldDefinition()->isTranslatable()) {
|
||||
$args = ['%field_name' => $field_name, '%langcode' => $langcode];
|
||||
$this->assertEqual($langcode, $field->getEntity()->language()->getId(), format_string('Translatable field %field_name on translation %langcode has correct entity reference in translation %langcode after cloning.', $args));
|
||||
}
|
||||
else {
|
||||
$args = ['%field_name' => $field_name, '%langcode' => $langcode, '%default_langcode' => $default_langcode];
|
||||
$this->assertEqual($default_langcode, $field->getEntity()->language()->getId(), format_string('Non translatable field %field_name on translation %langcode has correct entity reference in the default translation %default_langcode after cloning.', $args));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Entity;
|
||||
|
||||
use Drupal\contact\Entity\ContactForm;
|
||||
use Drupal\Core\Config\ConfigImporter;
|
||||
use Drupal\Core\Config\StorageComparer;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests ContentEntityNullStorage entity query support.
|
||||
*
|
||||
* @see \Drupal\Core\Entity\ContentEntityNullStorage
|
||||
* @see \Drupal\Core\Entity\Query\Null\Query
|
||||
*
|
||||
* @group Entity
|
||||
*/
|
||||
class ContentEntityNullStorageTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('system', 'contact', 'user');
|
||||
|
||||
/**
|
||||
* Tests using entity query with ContentEntityNullStorage.
|
||||
*
|
||||
* @see \Drupal\Core\Entity\Query\Null\Query
|
||||
*/
|
||||
public function testEntityQuery() {
|
||||
$this->assertIdentical(0, \Drupal::entityQuery('contact_message')->count()->execute(), 'Counting a null storage returns 0.');
|
||||
$this->assertIdentical([], \Drupal::entityQuery('contact_message')->execute(), 'Querying a null storage returns an empty array.');
|
||||
$this->assertIdentical([], \Drupal::entityQuery('contact_message')->condition('contact_form', 'test')->execute(), 'Querying a null storage returns an empty array and conditions are ignored.');
|
||||
$this->assertIdentical([], \Drupal::entityQueryAggregate('contact_message')->aggregate('name', 'AVG')->execute(), 'Aggregate querying a null storage returns an empty array');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests deleting a contact form entity via a configuration import.
|
||||
*
|
||||
* @see \Drupal\Core\Entity\Event\BundleConfigImportValidate
|
||||
*/
|
||||
public function testDeleteThroughImport() {
|
||||
$contact_form = ContactForm::create(['id' => 'test']);
|
||||
$contact_form->save();
|
||||
|
||||
$this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.sync'));
|
||||
|
||||
// Set up the ConfigImporter object for testing.
|
||||
$storage_comparer = new StorageComparer(
|
||||
$this->container->get('config.storage.sync'),
|
||||
$this->container->get('config.storage'),
|
||||
$this->container->get('config.manager')
|
||||
);
|
||||
$config_importer = new ConfigImporter(
|
||||
$storage_comparer->createChangelist(),
|
||||
$this->container->get('event_dispatcher'),
|
||||
$this->container->get('config.manager'),
|
||||
$this->container->get('lock'),
|
||||
$this->container->get('config.typed'),
|
||||
$this->container->get('module_handler'),
|
||||
$this->container->get('module_installer'),
|
||||
$this->container->get('theme_handler'),
|
||||
$this->container->get('string_translation')
|
||||
);
|
||||
|
||||
// Delete the contact message in sync.
|
||||
$sync = $this->container->get('config.storage.sync');
|
||||
$sync->delete($contact_form->getConfigDependencyName());
|
||||
|
||||
// Import.
|
||||
$config_importer->reset()->import();
|
||||
$this->assertNull(ContactForm::load($contact_form->id()), 'The contact form has been deleted.');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Entity;
|
||||
|
||||
use Drupal\Core\Field\BaseFieldDefinition;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
|
||||
/**
|
||||
* Tests the default table mapping class for content entities stored in SQL.
|
||||
*
|
||||
* @see \Drupal\Core\Entity\Sql\DefaultTableMapping
|
||||
* @see \Drupal\Core\Entity\Sql\TableMappingInterface
|
||||
*
|
||||
* @coversDefaultClass \Drupal\Core\Entity\Sql\DefaultTableMapping
|
||||
* @group Entity
|
||||
*/
|
||||
class DefaultTableMappingIntegrationTest extends EntityKernelTestBase {
|
||||
|
||||
/**
|
||||
* The table mapping for the tested entity type.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\Sql\TableMappingInterface
|
||||
*/
|
||||
protected $tableMapping;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['entity_test_extra'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Setup some fields for entity_test_extra to create.
|
||||
$definitions['multivalued_base_field'] = BaseFieldDefinition::create('string')
|
||||
->setName('multivalued_base_field')
|
||||
->setTargetEntityTypeId('entity_test_mulrev')
|
||||
->setTargetBundle('entity_test_mulrev')
|
||||
->setCardinality(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
|
||||
$this->state->set('entity_test_mulrev.additional_base_field_definitions', $definitions);
|
||||
|
||||
$this->entityManager->clearCachedDefinitions();
|
||||
$this->tableMapping = $this->entityManager->getStorage('entity_test_mulrev')->getTableMapping();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests DefaultTableMapping::getFieldTableName().
|
||||
*
|
||||
* @covers ::getFieldTableName
|
||||
*/
|
||||
public function testGetFieldTableName() {
|
||||
// Test the field table name for a single-valued base field, which is stored
|
||||
// in the entity's base table.
|
||||
$expected = 'entity_test_mulrev';
|
||||
$this->assertEquals($this->tableMapping->getFieldTableName('uuid'), $expected);
|
||||
|
||||
// Test the field table name for a translatable and revisionable base field,
|
||||
// which is stored in the entity's data table.
|
||||
$expected = 'entity_test_mulrev_property_data';
|
||||
$this->assertEquals($this->tableMapping->getFieldTableName('name'), $expected);
|
||||
|
||||
// Test the field table name for a multi-valued base field, which is stored
|
||||
// in a dedicated table.
|
||||
$expected = 'entity_test_mulrev__multivalued_base_field';
|
||||
$this->assertEquals($this->tableMapping->getFieldTableName('multivalued_base_field'), $expected);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,327 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Entity\Element;
|
||||
|
||||
use Drupal\Core\Entity\Element\EntityAutocomplete;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Form\FormInterface;
|
||||
use Drupal\Core\Form\FormState;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\entity_test\Entity\EntityTest;
|
||||
use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
|
||||
use Drupal\user\Entity\User;
|
||||
|
||||
/**
|
||||
* Tests the EntityAutocomplete Form API element.
|
||||
*
|
||||
* @group Form
|
||||
*/
|
||||
class EntityAutocompleteElementFormTest extends EntityKernelTestBase implements FormInterface {
|
||||
|
||||
/**
|
||||
* User for testing.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $testUser;
|
||||
|
||||
/**
|
||||
* User for autocreate testing.
|
||||
*
|
||||
* @var \Drupal\user\UserInterface
|
||||
*/
|
||||
protected $testAutocreateUser;
|
||||
|
||||
/**
|
||||
* An array of entities to be referenced in this test.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityInterface[]
|
||||
*/
|
||||
protected $referencedEntities;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installSchema('system', ['key_value_expire']);
|
||||
\Drupal::service('router.builder')->rebuild();
|
||||
|
||||
$this->testUser = User::create(array(
|
||||
'name' => 'foobar1',
|
||||
'mail' => 'foobar1@example.com',
|
||||
));
|
||||
$this->testUser->save();
|
||||
\Drupal::service('current_user')->setAccount($this->testUser);
|
||||
|
||||
$this->testAutocreateUser = User::create(array(
|
||||
'name' => 'foobar2',
|
||||
'mail' => 'foobar2@example.com',
|
||||
));
|
||||
$this->testAutocreateUser->save();
|
||||
|
||||
for ($i = 1; $i < 3; $i++) {
|
||||
$entity = EntityTest::create(array(
|
||||
'name' => $this->randomMachineName()
|
||||
));
|
||||
$entity->save();
|
||||
$this->referencedEntities[] = $entity;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId() {
|
||||
return 'test_entity_autocomplete';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$form['single'] = array(
|
||||
'#type' => 'entity_autocomplete',
|
||||
'#target_type' => 'entity_test',
|
||||
);
|
||||
$form['single_autocreate'] = array(
|
||||
'#type' => 'entity_autocomplete',
|
||||
'#target_type' => 'entity_test',
|
||||
'#autocreate' => array(
|
||||
'bundle' => 'entity_test',
|
||||
),
|
||||
);
|
||||
$form['single_autocreate_specific_uid'] = array(
|
||||
'#type' => 'entity_autocomplete',
|
||||
'#target_type' => 'entity_test',
|
||||
'#autocreate' => array(
|
||||
'bundle' => 'entity_test',
|
||||
'uid' => $this->testAutocreateUser->id(),
|
||||
),
|
||||
);
|
||||
|
||||
$form['tags'] = array(
|
||||
'#type' => 'entity_autocomplete',
|
||||
'#target_type' => 'entity_test',
|
||||
'#tags' => TRUE,
|
||||
);
|
||||
$form['tags_autocreate'] = array(
|
||||
'#type' => 'entity_autocomplete',
|
||||
'#target_type' => 'entity_test',
|
||||
'#tags' => TRUE,
|
||||
'#autocreate' => array(
|
||||
'bundle' => 'entity_test',
|
||||
),
|
||||
);
|
||||
$form['tags_autocreate_specific_uid'] = array(
|
||||
'#type' => 'entity_autocomplete',
|
||||
'#target_type' => 'entity_test',
|
||||
'#tags' => TRUE,
|
||||
'#autocreate' => array(
|
||||
'bundle' => 'entity_test',
|
||||
'uid' => $this->testAutocreateUser->id(),
|
||||
),
|
||||
);
|
||||
|
||||
$form['single_no_validate'] = array(
|
||||
'#type' => 'entity_autocomplete',
|
||||
'#target_type' => 'entity_test',
|
||||
'#validate_reference' => FALSE,
|
||||
);
|
||||
$form['single_autocreate_no_validate'] = array(
|
||||
'#type' => 'entity_autocomplete',
|
||||
'#target_type' => 'entity_test',
|
||||
'#validate_reference' => FALSE,
|
||||
'#autocreate' => array(
|
||||
'bundle' => 'entity_test',
|
||||
),
|
||||
);
|
||||
|
||||
$form['single_access'] = array(
|
||||
'#type' => 'entity_autocomplete',
|
||||
'#target_type' => 'entity_test',
|
||||
'#default_value' => $this->referencedEntities[0],
|
||||
);
|
||||
$form['tags_access'] = array(
|
||||
'#type' => 'entity_autocomplete',
|
||||
'#target_type' => 'entity_test',
|
||||
'#tags' => TRUE,
|
||||
'#default_value' => array($this->referencedEntities[0], $this->referencedEntities[1]),
|
||||
);
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) { }
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateForm(array &$form, FormStateInterface $form_state) { }
|
||||
|
||||
/**
|
||||
* Tests valid entries in the EntityAutocomplete Form API element.
|
||||
*/
|
||||
public function testValidEntityAutocompleteElement() {
|
||||
$form_state = (new FormState())
|
||||
->setValues([
|
||||
'single' => $this->getAutocompleteInput($this->referencedEntities[0]),
|
||||
'single_autocreate' => 'single - autocreated entity label',
|
||||
'single_autocreate_specific_uid' => 'single - autocreated entity label with specific uid',
|
||||
'tags' => $this->getAutocompleteInput($this->referencedEntities[0]) . ', ' . $this->getAutocompleteInput($this->referencedEntities[1]),
|
||||
'tags_autocreate' =>
|
||||
$this->getAutocompleteInput($this->referencedEntities[0])
|
||||
. ', tags - autocreated entity label, '
|
||||
. $this->getAutocompleteInput($this->referencedEntities[1]),
|
||||
'tags_autocreate_specific_uid' =>
|
||||
$this->getAutocompleteInput($this->referencedEntities[0])
|
||||
. ', tags - autocreated entity label with specific uid, '
|
||||
. $this->getAutocompleteInput($this->referencedEntities[1]),
|
||||
]);
|
||||
$form_builder = $this->container->get('form_builder');
|
||||
$form_builder->submitForm($this, $form_state);
|
||||
|
||||
// Valid form state.
|
||||
$this->assertEqual(count($form_state->getErrors()), 0);
|
||||
|
||||
// Test the 'single' element.
|
||||
$this->assertEqual($form_state->getValue('single'), $this->referencedEntities[0]->id());
|
||||
|
||||
// Test the 'single_autocreate' element.
|
||||
$value = $form_state->getValue('single_autocreate');
|
||||
$this->assertEqual($value['entity']->label(), 'single - autocreated entity label');
|
||||
$this->assertEqual($value['entity']->bundle(), 'entity_test');
|
||||
$this->assertEqual($value['entity']->getOwnerId(), $this->testUser->id());
|
||||
|
||||
// Test the 'single_autocreate_specific_uid' element.
|
||||
$value = $form_state->getValue('single_autocreate_specific_uid');
|
||||
$this->assertEqual($value['entity']->label(), 'single - autocreated entity label with specific uid');
|
||||
$this->assertEqual($value['entity']->bundle(), 'entity_test');
|
||||
$this->assertEqual($value['entity']->getOwnerId(), $this->testAutocreateUser->id());
|
||||
|
||||
// Test the 'tags' element.
|
||||
$expected = array(
|
||||
array('target_id' => $this->referencedEntities[0]->id()),
|
||||
array('target_id' => $this->referencedEntities[1]->id()),
|
||||
);
|
||||
$this->assertEqual($form_state->getValue('tags'), $expected);
|
||||
|
||||
// Test the 'single_autocreate' element.
|
||||
$value = $form_state->getValue('tags_autocreate');
|
||||
// First value is an existing entity.
|
||||
$this->assertEqual($value[0]['target_id'], $this->referencedEntities[0]->id());
|
||||
// Second value is an autocreated entity.
|
||||
$this->assertTrue(!isset($value[1]['target_id']));
|
||||
$this->assertEqual($value[1]['entity']->label(), 'tags - autocreated entity label');
|
||||
$this->assertEqual($value[1]['entity']->getOwnerId(), $this->testUser->id());
|
||||
// Third value is an existing entity.
|
||||
$this->assertEqual($value[2]['target_id'], $this->referencedEntities[1]->id());
|
||||
|
||||
// Test the 'tags_autocreate_specific_uid' element.
|
||||
$value = $form_state->getValue('tags_autocreate_specific_uid');
|
||||
// First value is an existing entity.
|
||||
$this->assertEqual($value[0]['target_id'], $this->referencedEntities[0]->id());
|
||||
// Second value is an autocreated entity.
|
||||
$this->assertTrue(!isset($value[1]['target_id']));
|
||||
$this->assertEqual($value[1]['entity']->label(), 'tags - autocreated entity label with specific uid');
|
||||
$this->assertEqual($value[1]['entity']->getOwnerId(), $this->testAutocreateUser->id());
|
||||
// Third value is an existing entity.
|
||||
$this->assertEqual($value[2]['target_id'], $this->referencedEntities[1]->id());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests invalid entries in the EntityAutocomplete Form API element.
|
||||
*/
|
||||
public function testInvalidEntityAutocompleteElement() {
|
||||
$form_builder = $this->container->get('form_builder');
|
||||
|
||||
// Test 'single' with a entity label that doesn't exist
|
||||
$form_state = (new FormState())
|
||||
->setValues([
|
||||
'single' => 'single - non-existent label',
|
||||
]);
|
||||
$form_builder->submitForm($this, $form_state);
|
||||
$this->assertEqual(count($form_state->getErrors()), 1);
|
||||
$this->assertEqual($form_state->getErrors()['single'], t('There are no entities matching "%value".', array('%value' => 'single - non-existent label')));
|
||||
|
||||
// Test 'single' with a entity ID that doesn't exist.
|
||||
$form_state = (new FormState())
|
||||
->setValues([
|
||||
'single' => 'single - non-existent label (42)',
|
||||
]);
|
||||
$form_builder->submitForm($this, $form_state);
|
||||
$this->assertEqual(count($form_state->getErrors()), 1);
|
||||
$this->assertEqual($form_state->getErrors()['single'], t('The referenced entity (%type: %id) does not exist.', array('%type' => 'entity_test', '%id' => 42)));
|
||||
|
||||
// Do the same tests as above but on an element with '#validate_reference'
|
||||
// set to FALSE.
|
||||
$form_state = (new FormState())
|
||||
->setValues([
|
||||
'single_no_validate' => 'single - non-existent label',
|
||||
'single_autocreate_no_validate' => 'single - autocreate non-existent label'
|
||||
]);
|
||||
$form_builder->submitForm($this, $form_state);
|
||||
|
||||
// The element without 'autocreate' support still has to emit a warning when
|
||||
// the input doesn't end with an entity ID enclosed in parentheses.
|
||||
$this->assertEqual(count($form_state->getErrors()), 1);
|
||||
$this->assertEqual($form_state->getErrors()['single_no_validate'], t('There are no entities matching "%value".', array('%value' => 'single - non-existent label')));
|
||||
|
||||
$form_state = (new FormState())
|
||||
->setValues([
|
||||
'single_no_validate' => 'single - non-existent label (42)',
|
||||
'single_autocreate_no_validate' => 'single - autocreate non-existent label (43)'
|
||||
]);
|
||||
$form_builder->submitForm($this, $form_state);
|
||||
|
||||
// The input is complete (i.e. contains an entity ID at the end), no errors
|
||||
// are triggered.
|
||||
$this->assertEqual(count($form_state->getErrors()), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that access is properly checked by the EntityAutocomplete element.
|
||||
*/
|
||||
public function testEntityAutocompleteAccess() {
|
||||
$form_builder = $this->container->get('form_builder');
|
||||
$form = $form_builder->getForm($this);
|
||||
|
||||
// Check that the current user has proper access to view entity labels.
|
||||
$expected = $this->referencedEntities[0]->label() . ' (' . $this->referencedEntities[0]->id() . ')';
|
||||
$this->assertEqual($form['single_access']['#value'], $expected);
|
||||
|
||||
$expected .= ', ' . $this->referencedEntities[1]->label() . ' (' . $this->referencedEntities[1]->id() . ')';
|
||||
$this->assertEqual($form['tags_access']['#value'], $expected);
|
||||
|
||||
// Set up a non-admin user that is *not* allowed to view test entities.
|
||||
\Drupal::currentUser()->setAccount($this->createUser(array(), array()));
|
||||
|
||||
// Rebuild the form.
|
||||
$form = $form_builder->getForm($this);
|
||||
|
||||
$expected = t('- Restricted access -') . ' (' . $this->referencedEntities[0]->id() . ')';
|
||||
$this->assertEqual($form['single_access']['#value'], $expected);
|
||||
|
||||
$expected .= ', ' . t('- Restricted access -') . ' (' . $this->referencedEntities[1]->id() . ')';
|
||||
$this->assertEqual($form['tags_access']['#value'], $expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an entity label in the format needed by the EntityAutocomplete
|
||||
* element.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* A Drupal entity.
|
||||
*
|
||||
* @return string
|
||||
* A string that can be used as a value for EntityAutocomplete elements.
|
||||
*/
|
||||
protected function getAutocompleteInput(EntityInterface $entity) {
|
||||
return EntityAutocomplete::getEntityLabels(array($entity));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,225 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Entity;
|
||||
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\Core\Access\AccessibleInterface;
|
||||
use Drupal\Core\Entity\EntityAccessControlHandler;
|
||||
use Drupal\Core\Session\AnonymousUserSession;
|
||||
use Drupal\entity_test\Entity\EntityTest;
|
||||
use Drupal\entity_test\Entity\EntityTestDefaultAccess;
|
||||
use Drupal\entity_test\Entity\EntityTestLabel;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\user\Entity\User;
|
||||
|
||||
/**
|
||||
* Tests the entity access control handler.
|
||||
*
|
||||
* @group Entity
|
||||
*/
|
||||
class EntityAccessControlHandlerTest extends EntityLanguageTestBase {
|
||||
|
||||
/**
|
||||
* Asserts entity access correctly grants or denies access.
|
||||
*/
|
||||
function assertEntityAccess($ops, AccessibleInterface $object, AccountInterface $account = NULL) {
|
||||
foreach ($ops as $op => $result) {
|
||||
$message = format_string("Entity access returns @result with operation '@op'.", array(
|
||||
'@result' => !isset($result) ? 'null' : ($result ? 'true' : 'false'),
|
||||
'@op' => $op,
|
||||
));
|
||||
|
||||
$this->assertEqual($result, $object->access($op, $account), $message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures user labels are accessible for everyone.
|
||||
*/
|
||||
public function testUserLabelAccess() {
|
||||
// Set up a non-admin user.
|
||||
\Drupal::currentUser()->setAccount($this->createUser(['uid' => 2]));
|
||||
|
||||
$anonymous_user = User::getAnonymousUser();
|
||||
$user = $this->createUser();
|
||||
|
||||
// The current user is allowed to view the anonymous user label.
|
||||
$this->assertEntityAccess(array(
|
||||
'create' => FALSE,
|
||||
'update' => FALSE,
|
||||
'delete' => FALSE,
|
||||
'view' => FALSE,
|
||||
'view label' => TRUE,
|
||||
), $anonymous_user);
|
||||
|
||||
// The current user is allowed to view user labels.
|
||||
$this->assertEntityAccess(array(
|
||||
'create' => FALSE,
|
||||
'update' => FALSE,
|
||||
'delete' => FALSE,
|
||||
'view' => FALSE,
|
||||
'view label' => TRUE,
|
||||
), $user);
|
||||
|
||||
// Switch to a anonymous user account.
|
||||
$account_switcher = \Drupal::service('account_switcher');
|
||||
$account_switcher->switchTo(new AnonymousUserSession());
|
||||
|
||||
// The anonymous user is allowed to view the anonymous user label.
|
||||
$this->assertEntityAccess(array(
|
||||
'create' => FALSE,
|
||||
'update' => FALSE,
|
||||
'delete' => FALSE,
|
||||
'view' => FALSE,
|
||||
'view label' => TRUE,
|
||||
), $anonymous_user);
|
||||
|
||||
// The anonymous user is allowed to view user labels.
|
||||
$this->assertEntityAccess(array(
|
||||
'create' => FALSE,
|
||||
'update' => FALSE,
|
||||
'delete' => FALSE,
|
||||
'view' => FALSE,
|
||||
'view label' => TRUE,
|
||||
), $user);
|
||||
|
||||
// Restore user account.
|
||||
$account_switcher->switchBack();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures entity access is properly working.
|
||||
*/
|
||||
function testEntityAccess() {
|
||||
// Set up a non-admin user that is allowed to view test entities.
|
||||
\Drupal::currentUser()->setAccount($this->createUser(array('uid' => 2), array('view test entity')));
|
||||
|
||||
// Use the 'entity_test_label' entity type in order to test the 'view label'
|
||||
// access operation.
|
||||
$entity = EntityTestLabel::create(array(
|
||||
'name' => 'test',
|
||||
));
|
||||
|
||||
// The current user is allowed to view entities.
|
||||
$this->assertEntityAccess(array(
|
||||
'create' => FALSE,
|
||||
'update' => FALSE,
|
||||
'delete' => FALSE,
|
||||
'view' => TRUE,
|
||||
'view label' => TRUE,
|
||||
), $entity);
|
||||
|
||||
// The custom user is not allowed to perform any operation on test entities,
|
||||
// except for viewing their label.
|
||||
$custom_user = $this->createUser();
|
||||
$this->assertEntityAccess(array(
|
||||
'create' => FALSE,
|
||||
'update' => FALSE,
|
||||
'delete' => FALSE,
|
||||
'view' => FALSE,
|
||||
'view label' => TRUE,
|
||||
), $entity, $custom_user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures default entity access is checked when necessary.
|
||||
*
|
||||
* This ensures that the default checkAccess() implementation of the
|
||||
* entity access control handler is considered if hook_entity_access() has not
|
||||
* explicitly forbidden access. Therefore the default checkAccess()
|
||||
* implementation can forbid access, even after access was already explicitly
|
||||
* allowed by hook_entity_access().
|
||||
*
|
||||
* @see \Drupal\entity_test\EntityTestAccessControlHandler::checkAccess()
|
||||
* @see entity_test_entity_access()
|
||||
*/
|
||||
function testDefaultEntityAccess() {
|
||||
// Set up a non-admin user that is allowed to view test entities.
|
||||
\Drupal::currentUser()->setAccount($this->createUser(array('uid' => 2), array('view test entity')));
|
||||
$entity = EntityTest::create(array(
|
||||
'name' => 'forbid_access',
|
||||
));
|
||||
|
||||
// The user is denied access to the entity.
|
||||
$this->assertEntityAccess(array(
|
||||
'create' => FALSE,
|
||||
'update' => FALSE,
|
||||
'delete' => FALSE,
|
||||
'view' => FALSE,
|
||||
), $entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the default handler is used as a fallback.
|
||||
*/
|
||||
function testEntityAccessDefaultController() {
|
||||
// The implementation requires that the global user id can be loaded.
|
||||
\Drupal::currentUser()->setAccount($this->createUser(array('uid' => 2)));
|
||||
|
||||
// Check that the default access control handler is used for entities that don't
|
||||
// have a specific access control handler defined.
|
||||
$handler = $this->container->get('entity.manager')->getAccessControlHandler('entity_test_default_access');
|
||||
$this->assertTrue($handler instanceof EntityAccessControlHandler, 'The default entity handler is used for the entity_test_default_access entity type.');
|
||||
|
||||
$entity = EntityTestDefaultAccess::create();
|
||||
$this->assertEntityAccess(array(
|
||||
'create' => FALSE,
|
||||
'update' => FALSE,
|
||||
'delete' => FALSE,
|
||||
'view' => FALSE,
|
||||
), $entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures entity access for entity translations is properly working.
|
||||
*/
|
||||
function testEntityTranslationAccess() {
|
||||
|
||||
// Set up a non-admin user that is allowed to view test entity translations.
|
||||
\Drupal::currentUser()->setAccount($this->createUser(array('uid' => 2), array('view test entity translations')));
|
||||
|
||||
// Create two test languages.
|
||||
foreach (array('foo', 'bar') as $langcode) {
|
||||
ConfigurableLanguage::create(array(
|
||||
'id' => $langcode,
|
||||
'label' => $this->randomString(),
|
||||
))->save();
|
||||
}
|
||||
|
||||
$entity = EntityTest::create(array(
|
||||
'name' => 'test',
|
||||
'langcode' => 'foo',
|
||||
));
|
||||
$entity->save();
|
||||
|
||||
$translation = $entity->addTranslation('bar');
|
||||
$this->assertEntityAccess(array(
|
||||
'view' => TRUE,
|
||||
), $translation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests hook invocations.
|
||||
*/
|
||||
public function testHooks() {
|
||||
$state = $this->container->get('state');
|
||||
$entity = EntityTest::create(array(
|
||||
'name' => 'test',
|
||||
));
|
||||
|
||||
// Test hook_entity_create_access() and hook_ENTITY_TYPE_create_access().
|
||||
$entity->access('create');
|
||||
$this->assertEqual($state->get('entity_test_entity_create_access'), TRUE);
|
||||
$this->assertIdentical($state->get('entity_test_entity_create_access_context'), [
|
||||
'entity_type_id' => 'entity_test',
|
||||
'langcode' => LanguageInterface::LANGCODE_DEFAULT,
|
||||
]);
|
||||
$this->assertEqual($state->get('entity_test_entity_test_create_access'), TRUE);
|
||||
|
||||
// Test hook_entity_access() and hook_ENTITY_TYPE_access().
|
||||
$entity->access('view');
|
||||
$this->assertEqual($state->get('entity_test_entity_access'), TRUE);
|
||||
$this->assertEqual($state->get('entity_test_entity_test_access'), TRUE);
|
||||
}
|
||||
}
|
||||
173
core/tests/Drupal/KernelTests/Core/Entity/EntityApiTest.php
Normal file
173
core/tests/Drupal/KernelTests/Core/Entity/EntityApiTest.php
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Entity;
|
||||
|
||||
use Drupal\Core\Entity\EntityStorageException;
|
||||
use Drupal\entity_test\Entity\EntityTest;
|
||||
use Drupal\user\UserInterface;
|
||||
|
||||
/**
|
||||
* Tests basic CRUD functionality.
|
||||
*
|
||||
* @group Entity
|
||||
*/
|
||||
class EntityApiTest extends EntityKernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
foreach (entity_test_entity_types() as $entity_type_id) {
|
||||
// The entity_test schema is installed by the parent.
|
||||
if ($entity_type_id != 'entity_test') {
|
||||
$this->installEntitySchema($entity_type_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests basic CRUD functionality of the Entity API.
|
||||
*/
|
||||
public function testCRUD() {
|
||||
// All entity variations have to have the same results.
|
||||
foreach (entity_test_entity_types() as $entity_type) {
|
||||
$this->assertCRUD($entity_type, $this->createUser());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a test set for a defined entity type and user.
|
||||
*
|
||||
* @param string $entity_type
|
||||
* The entity type to run the tests with.
|
||||
* @param \Drupal\user\UserInterface $user1
|
||||
* The user to run the tests with.
|
||||
*/
|
||||
protected function assertCRUD($entity_type, UserInterface $user1) {
|
||||
// Create some test entities.
|
||||
$entity = $this->container->get('entity_type.manager')
|
||||
->getStorage($entity_type)
|
||||
->create(array('name' => 'test', 'user_id' => $user1->id()));
|
||||
$entity->save();
|
||||
$entity = $this->container->get('entity_type.manager')
|
||||
->getStorage($entity_type)
|
||||
->create(array('name' => 'test2', 'user_id' => $user1->id()));
|
||||
$entity->save();
|
||||
$entity = $this->container->get('entity_type.manager')
|
||||
->getStorage($entity_type)
|
||||
->create(array('name' => 'test', 'user_id' => NULL));
|
||||
$entity->save();
|
||||
|
||||
$entities = array_values(entity_load_multiple_by_properties($entity_type, array('name' => 'test')));
|
||||
$this->assertEqual($entities[0]->name->value, 'test', format_string('%entity_type: Created and loaded entity', array('%entity_type' => $entity_type)));
|
||||
$this->assertEqual($entities[1]->name->value, 'test', format_string('%entity_type: Created and loaded entity', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Test loading a single entity.
|
||||
$loaded_entity = entity_load($entity_type, $entity->id());
|
||||
$this->assertEqual($loaded_entity->id(), $entity->id(), format_string('%entity_type: Loaded a single entity by id.', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Test deleting an entity.
|
||||
$entities = array_values(entity_load_multiple_by_properties($entity_type, array('name' => 'test2')));
|
||||
$entities[0]->delete();
|
||||
$entities = array_values(entity_load_multiple_by_properties($entity_type, array('name' => 'test2')));
|
||||
$this->assertEqual($entities, array(), format_string('%entity_type: Entity deleted.', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Test updating an entity.
|
||||
$entities = array_values(entity_load_multiple_by_properties($entity_type, array('name' => 'test')));
|
||||
$entities[0]->name->value = 'test3';
|
||||
$entities[0]->save();
|
||||
$entity = entity_load($entity_type, $entities[0]->id());
|
||||
$this->assertEqual($entity->name->value, 'test3', format_string('%entity_type: Entity updated.', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Try deleting multiple test entities by deleting all.
|
||||
$ids = array_keys(entity_load_multiple($entity_type));
|
||||
entity_delete_multiple($entity_type, $ids);
|
||||
|
||||
$all = entity_load_multiple($entity_type);
|
||||
$this->assertTrue(empty($all), format_string('%entity_type: Deleted all entities.', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Verify that all data got deleted.
|
||||
$definition = \Drupal::entityManager()->getDefinition($entity_type);
|
||||
$this->assertEqual(0, db_query('SELECT COUNT(*) FROM {' . $definition->getBaseTable() . '}')->fetchField(), 'Base table was emptied');
|
||||
if ($data_table = $definition->getDataTable()) {
|
||||
$this->assertEqual(0, db_query('SELECT COUNT(*) FROM {' . $data_table . '}')->fetchField(), 'Data table was emptied');
|
||||
}
|
||||
if ($revision_table = $definition->getRevisionTable()) {
|
||||
$this->assertEqual(0, db_query('SELECT COUNT(*) FROM {' . $revision_table . '}')->fetchField(), 'Data table was emptied');
|
||||
}
|
||||
|
||||
// Test deleting a list of entities not indexed by entity id.
|
||||
$entities = array();
|
||||
$entity = entity_create($entity_type, array('name' => 'test', 'user_id' => $user1->id()));
|
||||
$entity->save();
|
||||
$entities['test'] = $entity;
|
||||
$entity = entity_create($entity_type, array('name' => 'test2', 'user_id' => $user1->id()));
|
||||
$entity->save();
|
||||
$entities['test2'] = $entity;
|
||||
$controller = \Drupal::entityManager()->getStorage($entity_type);
|
||||
$controller->delete($entities);
|
||||
|
||||
// Verify that entities got deleted.
|
||||
$all = entity_load_multiple($entity_type);
|
||||
$this->assertTrue(empty($all), format_string('%entity_type: Deleted all entities.', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Verify that all data got deleted from the tables.
|
||||
$definition = \Drupal::entityManager()->getDefinition($entity_type);
|
||||
$this->assertEqual(0, db_query('SELECT COUNT(*) FROM {' . $definition->getBaseTable() . '}')->fetchField(), 'Base table was emptied');
|
||||
if ($data_table = $definition->getDataTable()) {
|
||||
$this->assertEqual(0, db_query('SELECT COUNT(*) FROM {' . $data_table . '}')->fetchField(), 'Data table was emptied');
|
||||
}
|
||||
if ($revision_table = $definition->getRevisionTable()) {
|
||||
$this->assertEqual(0, db_query('SELECT COUNT(*) FROM {' . $revision_table . '}')->fetchField(), 'Data table was emptied');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that exceptions are thrown when saving or deleting an entity.
|
||||
*/
|
||||
public function testEntityStorageExceptionHandling() {
|
||||
$entity = EntityTest::create(array('name' => 'test'));
|
||||
try {
|
||||
$GLOBALS['entity_test_throw_exception'] = TRUE;
|
||||
$entity->save();
|
||||
$this->fail('Entity presave EntityStorageException thrown but not caught.');
|
||||
}
|
||||
catch (EntityStorageException $e) {
|
||||
$this->assertEqual($e->getcode(), 1, 'Entity presave EntityStorageException caught.');
|
||||
}
|
||||
|
||||
$entity = EntityTest::create(array('name' => 'test2'));
|
||||
try {
|
||||
unset($GLOBALS['entity_test_throw_exception']);
|
||||
$entity->save();
|
||||
$this->pass('Exception presave not thrown and not caught.');
|
||||
}
|
||||
catch (EntityStorageException $e) {
|
||||
$this->assertNotEqual($e->getCode(), 1, 'Entity presave EntityStorageException caught.');
|
||||
}
|
||||
|
||||
$entity = EntityTest::create(array('name' => 'test3'));
|
||||
$entity->save();
|
||||
try {
|
||||
$GLOBALS['entity_test_throw_exception'] = TRUE;
|
||||
$entity->delete();
|
||||
$this->fail('Entity predelete EntityStorageException not thrown.');
|
||||
}
|
||||
catch (EntityStorageException $e) {
|
||||
$this->assertEqual($e->getCode(), 2, 'Entity predelete EntityStorageException caught.');
|
||||
}
|
||||
|
||||
unset($GLOBALS['entity_test_throw_exception']);
|
||||
$entity = EntityTest::create(array('name' => 'test4'));
|
||||
$entity->save();
|
||||
try {
|
||||
$entity->delete();
|
||||
$this->pass('Entity predelete EntityStorageException not thrown and not caught.');
|
||||
}
|
||||
catch (EntityStorageException $e) {
|
||||
$this->assertNotEqual($e->getCode(), 2, 'Entity predelete EntityStorageException thrown.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,165 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Entity;
|
||||
|
||||
use Drupal\Component\Serialization\Json;
|
||||
use Drupal\Component\Utility\Crypt;
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\Component\Utility\Tags;
|
||||
use Drupal\Core\Site\Settings;
|
||||
use Drupal\system\Controller\EntityAutocompleteController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||
|
||||
/**
|
||||
* Tests the autocomplete functionality.
|
||||
*
|
||||
* @group Entity
|
||||
*/
|
||||
class EntityAutocompleteTest extends EntityKernelTestBase {
|
||||
|
||||
/**
|
||||
* The entity type used in this test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $entityType = 'entity_test';
|
||||
|
||||
/**
|
||||
* The bundle used in this test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $bundle = 'entity_test';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installSchema('system', ['key_value']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests autocompletion edge cases with slashes in the names.
|
||||
*/
|
||||
function testEntityReferenceAutocompletion() {
|
||||
// Add an entity with a slash in its name.
|
||||
$entity_1 = $this->container->get('entity_type.manager')
|
||||
->getStorage($this->entityType)
|
||||
->create(array('name' => '10/16/2011'));
|
||||
$entity_1->save();
|
||||
|
||||
// Add another entity that differs after the slash character.
|
||||
$entity_2 = $this->container->get('entity_type.manager')
|
||||
->getStorage($this->entityType)
|
||||
->create(array('name' => '10/17/2011'));
|
||||
$entity_2->save();
|
||||
|
||||
// Add another entity that has both a comma, a slash and markup.
|
||||
$entity_3 = $this->container->get('entity_type.manager')
|
||||
->getStorage($this->entityType)
|
||||
->create(array('name' => 'label with, and / test'));
|
||||
$entity_3->save();
|
||||
|
||||
// Try to autocomplete a entity label that matches both entities.
|
||||
// We should get both entities in a JSON encoded string.
|
||||
$input = '10/';
|
||||
$data = $this->getAutocompleteResult($input);
|
||||
$this->assertIdentical($data[0]['label'], Html::escape($entity_1->name->value), 'Autocomplete returned the first matching entity');
|
||||
$this->assertIdentical($data[1]['label'], Html::escape($entity_2->name->value), 'Autocomplete returned the second matching entity');
|
||||
|
||||
// Try to autocomplete a entity label that matches the first entity.
|
||||
// We should only get the first entity in a JSON encoded string.
|
||||
$input = '10/16';
|
||||
$data = $this->getAutocompleteResult($input);
|
||||
$target = array(
|
||||
'value' => $entity_1->name->value . ' (1)',
|
||||
'label' => Html::escape($entity_1->name->value),
|
||||
);
|
||||
$this->assertIdentical(reset($data), $target, 'Autocomplete returns only the expected matching entity.');
|
||||
|
||||
// Try to autocomplete a entity label that matches the second entity, and
|
||||
// the first entity is already typed in the autocomplete (tags) widget.
|
||||
$input = $entity_1->name->value . ' (1), 10/17';
|
||||
$data = $this->getAutocompleteResult($input);
|
||||
$this->assertIdentical($data[0]['label'], Html::escape($entity_2->name->value), 'Autocomplete returned the second matching entity');
|
||||
|
||||
// Try to autocomplete a entity label with both a comma, a slash and markup.
|
||||
$input = '"label with, and /"';
|
||||
$data = $this->getAutocompleteResult($input);
|
||||
$n = $entity_3->name->value . ' (3)';
|
||||
// Entity labels containing commas or quotes must be wrapped in quotes.
|
||||
$n = Tags::encode($n);
|
||||
$target = array(
|
||||
'value' => $n,
|
||||
'label' => Html::escape($entity_3->name->value),
|
||||
);
|
||||
$this->assertIdentical(reset($data), $target, 'Autocomplete returns an entity label containing a comma and a slash.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that missing or invalid selection setting key are handled correctly.
|
||||
*/
|
||||
public function testSelectionSettingsHandling() {
|
||||
$entity_reference_controller = EntityAutocompleteController::create($this->container);
|
||||
$request = Request::create('entity_reference_autocomplete/' . $this->entityType . '/default');
|
||||
$request->query->set('q', $this->randomString());
|
||||
|
||||
try {
|
||||
// Pass an invalid selection settings key (i.e. one that does not exist
|
||||
// in the key/value store).
|
||||
$selection_settings_key = $this->randomString();
|
||||
$entity_reference_controller->handleAutocomplete($request, $this->entityType, 'default', $selection_settings_key);
|
||||
|
||||
$this->fail('Non-existent selection settings key throws an exception.');
|
||||
}
|
||||
catch (AccessDeniedHttpException $e) {
|
||||
$this->pass('Non-existent selection settings key throws an exception.');
|
||||
}
|
||||
|
||||
try {
|
||||
// Generate a valid hash key but store a modified settings array.
|
||||
$selection_settings = [];
|
||||
$selection_settings_key = Crypt::hmacBase64(serialize($selection_settings) . $this->entityType . 'default', Settings::getHashSalt());
|
||||
|
||||
$selection_settings[$this->randomMachineName()] = $this->randomString();
|
||||
\Drupal::keyValue('entity_autocomplete')->set($selection_settings_key, $selection_settings);
|
||||
|
||||
$entity_reference_controller->handleAutocomplete($request, $this->entityType, 'default', $selection_settings_key);
|
||||
}
|
||||
catch (AccessDeniedHttpException $e) {
|
||||
if ($e->getMessage() == 'Invalid selection settings key.') {
|
||||
$this->pass('Invalid selection settings key throws an exception.');
|
||||
}
|
||||
else {
|
||||
$this->fail('Invalid selection settings key throws an exception.');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the result of an Entity reference autocomplete request.
|
||||
*
|
||||
* @param string $input
|
||||
* The label of the entity to query by.
|
||||
*
|
||||
* @return mixed
|
||||
* The JSON value encoded in its appropriate PHP type.
|
||||
*/
|
||||
protected function getAutocompleteResult($input) {
|
||||
$request = Request::create('entity_reference_autocomplete/' . $this->entityType . '/default');
|
||||
$request->query->set('q', $input);
|
||||
|
||||
$selection_settings = [];
|
||||
$selection_settings_key = Crypt::hmacBase64(serialize($selection_settings) . $this->entityType . 'default', Settings::getHashSalt());
|
||||
\Drupal::keyValue('entity_autocomplete')->set($selection_settings_key, $selection_settings);
|
||||
|
||||
$entity_reference_controller = EntityAutocompleteController::create($this->container);
|
||||
$result = $entity_reference_controller->handleAutocomplete($request, $this->entityType, 'default', $selection_settings_key)->getContent();
|
||||
|
||||
return Json::decode($result);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Entity;
|
||||
|
||||
/**
|
||||
* Tests adding a custom bundle field.
|
||||
*
|
||||
* @group Entity
|
||||
*/
|
||||
class EntityBundleFieldTest extends EntityKernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('entity_schema_test');
|
||||
|
||||
/**
|
||||
* The module handler.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ModuleHandlerInterface
|
||||
*/
|
||||
protected $moduleHandler;
|
||||
|
||||
/**
|
||||
* The database connection used.
|
||||
*
|
||||
* @var \Drupal\Core\Database\Connection
|
||||
*/
|
||||
protected $database;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installSchema('user', array('users_data'));
|
||||
$this->moduleHandler = $this->container->get('module_handler');
|
||||
$this->database = $this->container->get('database');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests making use of a custom bundle field.
|
||||
*/
|
||||
public function testCustomBundleFieldUsage() {
|
||||
entity_test_create_bundle('custom');
|
||||
|
||||
// Check that an entity with bundle entity_test does not have the custom
|
||||
// field.
|
||||
$storage = $this->entityManager->getStorage('entity_test');
|
||||
$entity = $storage->create([
|
||||
'type' => 'entity_test',
|
||||
]);
|
||||
$this->assertFalse($entity->hasField('custom_bundle_field'));
|
||||
|
||||
// Check that the custom bundle has the defined custom field and check
|
||||
// saving and deleting of custom field data.
|
||||
$entity = $storage->create([
|
||||
'type' => 'custom',
|
||||
]);
|
||||
$this->assertTrue($entity->hasField('custom_bundle_field'));
|
||||
|
||||
// Ensure that the field exists in the field map.
|
||||
$field_map = \Drupal::entityManager()->getFieldMap();
|
||||
$this->assertEqual($field_map['entity_test']['custom_bundle_field'], ['type' => 'string', 'bundles' => ['custom' => 'custom']]);
|
||||
|
||||
$entity->custom_bundle_field->value = 'swanky';
|
||||
$entity->save();
|
||||
$storage->resetCache();
|
||||
$entity = $storage->load($entity->id());
|
||||
$this->assertEqual($entity->custom_bundle_field->value, 'swanky', 'Entity was saved correctly');
|
||||
|
||||
$entity->custom_bundle_field->value = 'cozy';
|
||||
$entity->save();
|
||||
$storage->resetCache();
|
||||
$entity = $storage->load($entity->id());
|
||||
$this->assertEqual($entity->custom_bundle_field->value, 'cozy', 'Entity was updated correctly.');
|
||||
|
||||
$entity->delete();
|
||||
/** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */
|
||||
$table_mapping = $storage->getTableMapping();
|
||||
$table = $table_mapping->getDedicatedDataTableName($entity->getFieldDefinition('custom_bundle_field'));
|
||||
$result = $this->database->select($table, 'f')
|
||||
->fields('f')
|
||||
->condition('f.entity_id', $entity->id())
|
||||
->execute();
|
||||
$this->assertFalse($result->fetchAssoc(), 'Field data has been deleted');
|
||||
|
||||
// Create another entity to test that values are marked as deleted when a
|
||||
// bundle is deleted.
|
||||
$entity = $storage->create(['type' => 'custom', 'custom_bundle_field' => 'new']);
|
||||
$entity->save();
|
||||
entity_test_delete_bundle('custom');
|
||||
|
||||
$table = $table_mapping->getDedicatedDataTableName($entity->getFieldDefinition('custom_bundle_field'));
|
||||
$result = $this->database->select($table, 'f')
|
||||
->condition('f.entity_id', $entity->id())
|
||||
->condition('deleted', 1)
|
||||
->countQuery()
|
||||
->execute();
|
||||
$this->assertEqual(1, $result->fetchField(), 'Field data has been deleted');
|
||||
|
||||
// Ensure that the field no longer exists in the field map.
|
||||
$field_map = \Drupal::entityManager()->getFieldMap();
|
||||
$this->assertFalse(isset($field_map['entity_test']['custom_bundle_field']));
|
||||
|
||||
// @todo Test field purge and table deletion once supported. See
|
||||
// https://www.drupal.org/node/2282119.
|
||||
// $this->assertFalse($this->database->schema()->tableExists($table), 'Custom field table was deleted');
|
||||
}
|
||||
|
||||
}
|
||||
559
core/tests/Drupal/KernelTests/Core/Entity/EntityCrudHookTest.php
Normal file
559
core/tests/Drupal/KernelTests/Core/Entity/EntityCrudHookTest.php
Normal file
|
|
@ -0,0 +1,559 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Entity;
|
||||
|
||||
use Drupal\comment\Entity\Comment;
|
||||
use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface;
|
||||
use Drupal\comment\Tests\CommentTestTrait;
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\block\Entity\Block;
|
||||
use Drupal\entity_test\Entity\EntityTest;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
use Drupal\taxonomy\Entity\Term;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\taxonomy\Entity\Vocabulary;
|
||||
use Drupal\user\Entity\User;
|
||||
use Drupal\file\Entity\File;
|
||||
|
||||
/**
|
||||
* Tests the invocation of hooks when creating, inserting, loading, updating or
|
||||
* deleting an entity.
|
||||
*
|
||||
* Tested hooks are:
|
||||
* - hook_entity_insert() and hook_ENTITY_TYPE_insert()
|
||||
* - hook_entity_load() and hook_ENTITY_TYPE_load()
|
||||
* - hook_entity_update() and hook_ENTITY_TYPE_update()
|
||||
* - hook_entity_predelete() and hook_ENTITY_TYPE_predelete()
|
||||
* - hook_entity_delete() and hook_ENTITY_TYPE_delete()
|
||||
*
|
||||
* These hooks are each tested for several entity types.
|
||||
*
|
||||
* @group Entity
|
||||
*/
|
||||
class EntityCrudHookTest extends EntityKernelTestBase {
|
||||
|
||||
use CommentTestTrait;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('block', 'block_test', 'entity_crud_hook_test', 'file', 'taxonomy', 'node', 'comment');
|
||||
|
||||
protected $ids = array();
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installSchema('user', array('users_data'));
|
||||
$this->installSchema('file', array('file_usage'));
|
||||
$this->installSchema('node', array('node_access'));
|
||||
$this->installSchema('comment', array('comment_entity_statistics'));
|
||||
$this->installConfig(['node', 'comment']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the order of CRUD hook execution messages.
|
||||
*
|
||||
* entity_crud_hook_test.module implements all core entity CRUD hooks and
|
||||
* stores a message for each in $GLOBALS['entity_crud_hook_test'].
|
||||
*
|
||||
* @param $messages
|
||||
* An array of plain-text messages in the order they should appear.
|
||||
*/
|
||||
protected function assertHookMessageOrder($messages) {
|
||||
$positions = array();
|
||||
foreach ($messages as $message) {
|
||||
// Verify that each message is found and record its position.
|
||||
$position = array_search($message, $GLOBALS['entity_crud_hook_test']);
|
||||
if ($this->assertTrue($position !== FALSE, $message)) {
|
||||
$positions[] = $position;
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the positions and ensure they remain in the same order.
|
||||
$sorted = $positions;
|
||||
sort($sorted);
|
||||
$this->assertTrue($sorted == $positions, 'The hook messages appear in the correct order.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests hook invocations for CRUD operations on blocks.
|
||||
*/
|
||||
public function testBlockHooks() {
|
||||
$entity = Block::create(array(
|
||||
'id' => 'stark_test_html',
|
||||
'plugin' => 'test_html',
|
||||
'theme' => 'stark',
|
||||
));
|
||||
|
||||
$this->assertHookMessageOrder(array(
|
||||
'entity_crud_hook_test_block_create called',
|
||||
'entity_crud_hook_test_entity_create called for type block',
|
||||
));
|
||||
|
||||
$GLOBALS['entity_crud_hook_test'] = array();
|
||||
$entity->save();
|
||||
|
||||
$this->assertHookMessageOrder(array(
|
||||
'entity_crud_hook_test_block_presave called',
|
||||
'entity_crud_hook_test_entity_presave called for type block',
|
||||
'entity_crud_hook_test_block_insert called',
|
||||
'entity_crud_hook_test_entity_insert called for type block',
|
||||
));
|
||||
|
||||
$GLOBALS['entity_crud_hook_test'] = array();
|
||||
$entity = Block::load($entity->id());
|
||||
|
||||
$this->assertHookMessageOrder(array(
|
||||
'entity_crud_hook_test_entity_load called for type block',
|
||||
'entity_crud_hook_test_block_load called',
|
||||
));
|
||||
|
||||
$GLOBALS['entity_crud_hook_test'] = array();
|
||||
$entity->label = 'New label';
|
||||
$entity->save();
|
||||
|
||||
$this->assertHookMessageOrder(array(
|
||||
'entity_crud_hook_test_block_presave called',
|
||||
'entity_crud_hook_test_entity_presave called for type block',
|
||||
'entity_crud_hook_test_block_update called',
|
||||
'entity_crud_hook_test_entity_update called for type block',
|
||||
));
|
||||
|
||||
$GLOBALS['entity_crud_hook_test'] = array();
|
||||
$entity->delete();
|
||||
|
||||
$this->assertHookMessageOrder(array(
|
||||
'entity_crud_hook_test_block_predelete called',
|
||||
'entity_crud_hook_test_entity_predelete called for type block',
|
||||
'entity_crud_hook_test_block_delete called',
|
||||
'entity_crud_hook_test_entity_delete called for type block',
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests hook invocations for CRUD operations on comments.
|
||||
*/
|
||||
public function testCommentHooks() {
|
||||
$account = $this->createUser();
|
||||
NodeType::create([
|
||||
'type' => 'article',
|
||||
'name' => 'Article',
|
||||
])->save();
|
||||
$this->addDefaultCommentField('node', 'article', 'comment', CommentItemInterface::OPEN);
|
||||
|
||||
$node = Node::create([
|
||||
'uid' => $account->id(),
|
||||
'type' => 'article',
|
||||
'title' => 'Test node',
|
||||
'status' => 1,
|
||||
'promote' => 0,
|
||||
'sticky' => 0,
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
'created' => REQUEST_TIME,
|
||||
'changed' => REQUEST_TIME,
|
||||
]);
|
||||
$node->save();
|
||||
$nid = $node->id();
|
||||
$GLOBALS['entity_crud_hook_test'] = array();
|
||||
|
||||
$comment = Comment::create(array(
|
||||
'cid' => NULL,
|
||||
'pid' => 0,
|
||||
'entity_id' => $nid,
|
||||
'entity_type' => 'node',
|
||||
'field_name' => 'comment',
|
||||
'uid' => $account->id(),
|
||||
'subject' => 'Test comment',
|
||||
'created' => REQUEST_TIME,
|
||||
'changed' => REQUEST_TIME,
|
||||
'status' => 1,
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
));
|
||||
|
||||
$this->assertHookMessageOrder(array(
|
||||
'entity_crud_hook_test_comment_create called',
|
||||
'entity_crud_hook_test_entity_create called for type comment',
|
||||
));
|
||||
|
||||
$GLOBALS['entity_crud_hook_test'] = array();
|
||||
$comment->save();
|
||||
|
||||
$this->assertHookMessageOrder(array(
|
||||
'entity_crud_hook_test_comment_presave called',
|
||||
'entity_crud_hook_test_entity_presave called for type comment',
|
||||
'entity_crud_hook_test_comment_insert called',
|
||||
'entity_crud_hook_test_entity_insert called for type comment',
|
||||
));
|
||||
|
||||
$GLOBALS['entity_crud_hook_test'] = array();
|
||||
$comment = Comment::load($comment->id());
|
||||
|
||||
$this->assertHookMessageOrder(array(
|
||||
'entity_crud_hook_test_entity_load called for type comment',
|
||||
'entity_crud_hook_test_comment_load called',
|
||||
));
|
||||
|
||||
$GLOBALS['entity_crud_hook_test'] = array();
|
||||
$comment->setSubject('New subject');
|
||||
$comment->save();
|
||||
|
||||
$this->assertHookMessageOrder(array(
|
||||
'entity_crud_hook_test_comment_presave called',
|
||||
'entity_crud_hook_test_entity_presave called for type comment',
|
||||
'entity_crud_hook_test_comment_update called',
|
||||
'entity_crud_hook_test_entity_update called for type comment',
|
||||
));
|
||||
|
||||
$GLOBALS['entity_crud_hook_test'] = array();
|
||||
$comment->delete();
|
||||
|
||||
$this->assertHookMessageOrder(array(
|
||||
'entity_crud_hook_test_comment_predelete called',
|
||||
'entity_crud_hook_test_entity_predelete called for type comment',
|
||||
'entity_crud_hook_test_comment_delete called',
|
||||
'entity_crud_hook_test_entity_delete called for type comment',
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests hook invocations for CRUD operations on files.
|
||||
*/
|
||||
public function testFileHooks() {
|
||||
$this->installEntitySchema('file');
|
||||
|
||||
$url = 'public://entity_crud_hook_test.file';
|
||||
file_put_contents($url, 'Test test test');
|
||||
$file = File::create([
|
||||
'fid' => NULL,
|
||||
'uid' => 1,
|
||||
'filename' => 'entity_crud_hook_test.file',
|
||||
'uri' => $url,
|
||||
'filemime' => 'text/plain',
|
||||
'filesize' => filesize($url),
|
||||
'status' => 1,
|
||||
'created' => REQUEST_TIME,
|
||||
'changed' => REQUEST_TIME,
|
||||
]);
|
||||
|
||||
$this->assertHookMessageOrder(array(
|
||||
'entity_crud_hook_test_file_create called',
|
||||
'entity_crud_hook_test_entity_create called for type file',
|
||||
));
|
||||
|
||||
$GLOBALS['entity_crud_hook_test'] = array();
|
||||
$file->save();
|
||||
|
||||
$this->assertHookMessageOrder(array(
|
||||
'entity_crud_hook_test_file_presave called',
|
||||
'entity_crud_hook_test_entity_presave called for type file',
|
||||
'entity_crud_hook_test_file_insert called',
|
||||
'entity_crud_hook_test_entity_insert called for type file',
|
||||
));
|
||||
|
||||
$GLOBALS['entity_crud_hook_test'] = array();
|
||||
$file = File::load($file->id());
|
||||
|
||||
$this->assertHookMessageOrder(array(
|
||||
'entity_crud_hook_test_entity_load called for type file',
|
||||
'entity_crud_hook_test_file_load called',
|
||||
));
|
||||
|
||||
$GLOBALS['entity_crud_hook_test'] = array();
|
||||
$file->setFilename('new.entity_crud_hook_test.file');
|
||||
$file->save();
|
||||
|
||||
$this->assertHookMessageOrder(array(
|
||||
'entity_crud_hook_test_file_presave called',
|
||||
'entity_crud_hook_test_entity_presave called for type file',
|
||||
'entity_crud_hook_test_file_update called',
|
||||
'entity_crud_hook_test_entity_update called for type file',
|
||||
));
|
||||
|
||||
$GLOBALS['entity_crud_hook_test'] = array();
|
||||
$file->delete();
|
||||
|
||||
$this->assertHookMessageOrder(array(
|
||||
'entity_crud_hook_test_file_predelete called',
|
||||
'entity_crud_hook_test_entity_predelete called for type file',
|
||||
'entity_crud_hook_test_file_delete called',
|
||||
'entity_crud_hook_test_entity_delete called for type file',
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests hook invocations for CRUD operations on nodes.
|
||||
*/
|
||||
public function testNodeHooks() {
|
||||
$account = $this->createUser();
|
||||
|
||||
$node = Node::create([
|
||||
'uid' => $account->id(),
|
||||
'type' => 'article',
|
||||
'title' => 'Test node',
|
||||
'status' => 1,
|
||||
'promote' => 0,
|
||||
'sticky' => 0,
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
'created' => REQUEST_TIME,
|
||||
'changed' => REQUEST_TIME,
|
||||
]);
|
||||
|
||||
$this->assertHookMessageOrder(array(
|
||||
'entity_crud_hook_test_node_create called',
|
||||
'entity_crud_hook_test_entity_create called for type node',
|
||||
));
|
||||
|
||||
$GLOBALS['entity_crud_hook_test'] = array();
|
||||
$node->save();
|
||||
|
||||
$this->assertHookMessageOrder(array(
|
||||
'entity_crud_hook_test_node_presave called',
|
||||
'entity_crud_hook_test_entity_presave called for type node',
|
||||
'entity_crud_hook_test_node_insert called',
|
||||
'entity_crud_hook_test_entity_insert called for type node',
|
||||
));
|
||||
|
||||
$GLOBALS['entity_crud_hook_test'] = array();
|
||||
$node = Node::load($node->id());
|
||||
|
||||
$this->assertHookMessageOrder(array(
|
||||
'entity_crud_hook_test_entity_load called for type node',
|
||||
'entity_crud_hook_test_node_load called',
|
||||
));
|
||||
|
||||
$GLOBALS['entity_crud_hook_test'] = array();
|
||||
$node->title = 'New title';
|
||||
$node->save();
|
||||
|
||||
$this->assertHookMessageOrder(array(
|
||||
'entity_crud_hook_test_node_presave called',
|
||||
'entity_crud_hook_test_entity_presave called for type node',
|
||||
'entity_crud_hook_test_node_update called',
|
||||
'entity_crud_hook_test_entity_update called for type node',
|
||||
));
|
||||
|
||||
$GLOBALS['entity_crud_hook_test'] = array();
|
||||
$node->delete();
|
||||
|
||||
$this->assertHookMessageOrder(array(
|
||||
'entity_crud_hook_test_node_predelete called',
|
||||
'entity_crud_hook_test_entity_predelete called for type node',
|
||||
'entity_crud_hook_test_node_delete called',
|
||||
'entity_crud_hook_test_entity_delete called for type node',
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests hook invocations for CRUD operations on taxonomy terms.
|
||||
*/
|
||||
public function testTaxonomyTermHooks() {
|
||||
$this->installEntitySchema('taxonomy_term');
|
||||
|
||||
$vocabulary = Vocabulary::create([
|
||||
'name' => 'Test vocabulary',
|
||||
'vid' => 'test',
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
'description' => NULL,
|
||||
'module' => 'entity_crud_hook_test',
|
||||
]);
|
||||
$vocabulary->save();
|
||||
$GLOBALS['entity_crud_hook_test'] = array();
|
||||
|
||||
$term = Term::create([
|
||||
'vid' => $vocabulary->id(),
|
||||
'name' => 'Test term',
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
'description' => NULL,
|
||||
'format' => 1,
|
||||
]);
|
||||
|
||||
$this->assertHookMessageOrder(array(
|
||||
'entity_crud_hook_test_taxonomy_term_create called',
|
||||
'entity_crud_hook_test_entity_create called for type taxonomy_term',
|
||||
));
|
||||
|
||||
$GLOBALS['entity_crud_hook_test'] = array();
|
||||
$term->save();
|
||||
|
||||
$this->assertHookMessageOrder(array(
|
||||
'entity_crud_hook_test_taxonomy_term_presave called',
|
||||
'entity_crud_hook_test_entity_presave called for type taxonomy_term',
|
||||
'entity_crud_hook_test_taxonomy_term_insert called',
|
||||
'entity_crud_hook_test_entity_insert called for type taxonomy_term',
|
||||
));
|
||||
|
||||
$GLOBALS['entity_crud_hook_test'] = array();
|
||||
$term = Term::load($term->id());
|
||||
|
||||
$this->assertHookMessageOrder(array(
|
||||
'entity_crud_hook_test_entity_load called for type taxonomy_term',
|
||||
'entity_crud_hook_test_taxonomy_term_load called',
|
||||
));
|
||||
|
||||
$GLOBALS['entity_crud_hook_test'] = array();
|
||||
$term->setName('New name');
|
||||
$term->save();
|
||||
|
||||
$this->assertHookMessageOrder(array(
|
||||
'entity_crud_hook_test_taxonomy_term_presave called',
|
||||
'entity_crud_hook_test_entity_presave called for type taxonomy_term',
|
||||
'entity_crud_hook_test_taxonomy_term_update called',
|
||||
'entity_crud_hook_test_entity_update called for type taxonomy_term',
|
||||
));
|
||||
|
||||
$GLOBALS['entity_crud_hook_test'] = array();
|
||||
$term->delete();
|
||||
|
||||
$this->assertHookMessageOrder(array(
|
||||
'entity_crud_hook_test_taxonomy_term_predelete called',
|
||||
'entity_crud_hook_test_entity_predelete called for type taxonomy_term',
|
||||
'entity_crud_hook_test_taxonomy_term_delete called',
|
||||
'entity_crud_hook_test_entity_delete called for type taxonomy_term',
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests hook invocations for CRUD operations on taxonomy vocabularies.
|
||||
*/
|
||||
public function testTaxonomyVocabularyHooks() {
|
||||
$this->installEntitySchema('taxonomy_term');
|
||||
|
||||
$vocabulary = Vocabulary::create([
|
||||
'name' => 'Test vocabulary',
|
||||
'vid' => 'test',
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
'description' => NULL,
|
||||
'module' => 'entity_crud_hook_test',
|
||||
]);
|
||||
|
||||
$this->assertHookMessageOrder(array(
|
||||
'entity_crud_hook_test_taxonomy_vocabulary_create called',
|
||||
'entity_crud_hook_test_entity_create called for type taxonomy_vocabulary',
|
||||
));
|
||||
|
||||
$GLOBALS['entity_crud_hook_test'] = array();
|
||||
$vocabulary->save();
|
||||
|
||||
$this->assertHookMessageOrder(array(
|
||||
'entity_crud_hook_test_taxonomy_vocabulary_presave called',
|
||||
'entity_crud_hook_test_entity_presave called for type taxonomy_vocabulary',
|
||||
'entity_crud_hook_test_taxonomy_vocabulary_insert called',
|
||||
'entity_crud_hook_test_entity_insert called for type taxonomy_vocabulary',
|
||||
));
|
||||
|
||||
$GLOBALS['entity_crud_hook_test'] = array();
|
||||
$vocabulary = Vocabulary::load($vocabulary->id());
|
||||
|
||||
$this->assertHookMessageOrder(array(
|
||||
'entity_crud_hook_test_entity_load called for type taxonomy_vocabulary',
|
||||
'entity_crud_hook_test_taxonomy_vocabulary_load called',
|
||||
));
|
||||
|
||||
$GLOBALS['entity_crud_hook_test'] = array();
|
||||
$vocabulary->set('name', 'New name');
|
||||
$vocabulary->save();
|
||||
|
||||
$this->assertHookMessageOrder(array(
|
||||
'entity_crud_hook_test_taxonomy_vocabulary_presave called',
|
||||
'entity_crud_hook_test_entity_presave called for type taxonomy_vocabulary',
|
||||
'entity_crud_hook_test_taxonomy_vocabulary_update called',
|
||||
'entity_crud_hook_test_entity_update called for type taxonomy_vocabulary',
|
||||
));
|
||||
|
||||
$GLOBALS['entity_crud_hook_test'] = array();
|
||||
$vocabulary->delete();
|
||||
|
||||
$this->assertHookMessageOrder(array(
|
||||
'entity_crud_hook_test_taxonomy_vocabulary_predelete called',
|
||||
'entity_crud_hook_test_entity_predelete called for type taxonomy_vocabulary',
|
||||
'entity_crud_hook_test_taxonomy_vocabulary_delete called',
|
||||
'entity_crud_hook_test_entity_delete called for type taxonomy_vocabulary',
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests hook invocations for CRUD operations on users.
|
||||
*/
|
||||
public function testUserHooks() {
|
||||
$account = User::create([
|
||||
'name' => 'Test user',
|
||||
'mail' => 'test@example.com',
|
||||
'created' => REQUEST_TIME,
|
||||
'status' => 1,
|
||||
'language' => 'en',
|
||||
]);
|
||||
|
||||
$this->assertHookMessageOrder(array(
|
||||
'entity_crud_hook_test_user_create called',
|
||||
'entity_crud_hook_test_entity_create called for type user',
|
||||
));
|
||||
|
||||
$GLOBALS['entity_crud_hook_test'] = array();
|
||||
$account->save();
|
||||
|
||||
$this->assertHookMessageOrder(array(
|
||||
'entity_crud_hook_test_user_presave called',
|
||||
'entity_crud_hook_test_entity_presave called for type user',
|
||||
'entity_crud_hook_test_user_insert called',
|
||||
'entity_crud_hook_test_entity_insert called for type user',
|
||||
));
|
||||
|
||||
$GLOBALS['entity_crud_hook_test'] = array();
|
||||
User::load($account->id());
|
||||
|
||||
$this->assertHookMessageOrder(array(
|
||||
'entity_crud_hook_test_entity_load called for type user',
|
||||
'entity_crud_hook_test_user_load called',
|
||||
));
|
||||
|
||||
$GLOBALS['entity_crud_hook_test'] = array();
|
||||
$account->name = 'New name';
|
||||
$account->save();
|
||||
|
||||
$this->assertHookMessageOrder(array(
|
||||
'entity_crud_hook_test_user_presave called',
|
||||
'entity_crud_hook_test_entity_presave called for type user',
|
||||
'entity_crud_hook_test_user_update called',
|
||||
'entity_crud_hook_test_entity_update called for type user',
|
||||
));
|
||||
|
||||
$GLOBALS['entity_crud_hook_test'] = array();
|
||||
user_delete($account->id());
|
||||
|
||||
$this->assertHookMessageOrder(array(
|
||||
'entity_crud_hook_test_user_predelete called',
|
||||
'entity_crud_hook_test_entity_predelete called for type user',
|
||||
'entity_crud_hook_test_user_delete called',
|
||||
'entity_crud_hook_test_entity_delete called for type user',
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests rollback from failed entity save.
|
||||
*/
|
||||
function testEntityRollback() {
|
||||
// Create a block.
|
||||
try {
|
||||
EntityTest::create(array('name' => 'fail_insert'))->save();
|
||||
$this->fail('Expected exception has not been thrown.');
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$this->pass('Expected exception has been thrown.');
|
||||
}
|
||||
|
||||
if (Database::getConnection()->supportsTransactions()) {
|
||||
// Check that the block does not exist in the database.
|
||||
$ids = \Drupal::entityQuery('entity_test')->condition('name', 'fail_insert')->execute();
|
||||
$this->assertTrue(empty($ids), 'Transactions supported, and entity not found in database.');
|
||||
}
|
||||
else {
|
||||
// Check that the block exists in the database.
|
||||
$ids = \Drupal::entityQuery('entity_test')->condition('name', 'fail_insert')->execute();
|
||||
$this->assertFalse(empty($ids), 'Transactions not supported, and entity found in database.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,809 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Entity;
|
||||
|
||||
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\Database\DatabaseExceptionWrapper;
|
||||
use Drupal\Core\Database\IntegrityConstraintViolationException;
|
||||
use Drupal\Core\Entity\ContentEntityType;
|
||||
use Drupal\Core\Entity\EntityStorageException;
|
||||
use Drupal\Core\Entity\EntityTypeEvents;
|
||||
use Drupal\Core\Entity\Exception\FieldStorageDefinitionUpdateForbiddenException;
|
||||
use Drupal\Core\Field\BaseFieldDefinition;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionEvents;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\entity_test\Entity\EntityTestUpdate;
|
||||
use Drupal\system\Tests\Entity\EntityDefinitionTestTrait;
|
||||
|
||||
/**
|
||||
* Tests EntityDefinitionUpdateManager functionality.
|
||||
*
|
||||
* @group Entity
|
||||
*/
|
||||
class EntityDefinitionUpdateTest extends EntityKernelTestBase {
|
||||
|
||||
use EntityDefinitionTestTrait;
|
||||
|
||||
/**
|
||||
* The entity definition update manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface
|
||||
*/
|
||||
protected $entityDefinitionUpdateManager;
|
||||
|
||||
/**
|
||||
* The database connection.
|
||||
*
|
||||
* @var \Drupal\Core\Database\Connection
|
||||
*/
|
||||
protected $database;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->entityDefinitionUpdateManager = $this->container->get('entity.definition_update_manager');
|
||||
$this->database = $this->container->get('database');
|
||||
|
||||
// Install every entity type's schema that wasn't installed in the parent
|
||||
// method.
|
||||
foreach (array_diff_key($this->entityManager->getDefinitions(), array_flip(array('user', 'entity_test'))) as $entity_type_id => $entity_type) {
|
||||
$this->installEntitySchema($entity_type_id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that new entity type definitions are correctly handled.
|
||||
*/
|
||||
public function testNewEntityType() {
|
||||
$entity_type_id = 'entity_test_new';
|
||||
$schema = $this->database->schema();
|
||||
|
||||
// Check that the "entity_test_new" is not defined.
|
||||
$entity_types = $this->entityManager->getDefinitions();
|
||||
$this->assertFalse(isset($entity_types[$entity_type_id]), 'The "entity_test_new" entity type does not exist.');
|
||||
$this->assertFalse($schema->tableExists($entity_type_id), 'Schema for the "entity_test_new" entity type does not exist.');
|
||||
|
||||
// Check that the "entity_test_new" is now defined and the related schema
|
||||
// has been created.
|
||||
$this->enableNewEntityType();
|
||||
$entity_types = $this->entityManager->getDefinitions();
|
||||
$this->assertTrue(isset($entity_types[$entity_type_id]), 'The "entity_test_new" entity type exists.');
|
||||
$this->assertTrue($schema->tableExists($entity_type_id), 'Schema for the "entity_test_new" entity type has been created.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests when no definition update is needed.
|
||||
*/
|
||||
public function testNoUpdates() {
|
||||
// Ensure that the definition update manager reports no updates.
|
||||
$this->assertFalse($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that no updates are needed.');
|
||||
$this->assertIdentical($this->entityDefinitionUpdateManager->getChangeSummary(), array(), 'EntityDefinitionUpdateManager reports an empty change summary.');
|
||||
|
||||
// Ensure that applyUpdates() runs without error (it's not expected to do
|
||||
// anything when there aren't updates).
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests updating entity schema when there are no existing entities.
|
||||
*/
|
||||
public function testEntityTypeUpdateWithoutData() {
|
||||
// The 'entity_test_update' entity type starts out non-revisionable, so
|
||||
// ensure the revision table hasn't been created during setUp().
|
||||
$this->assertFalse($this->database->schema()->tableExists('entity_test_update_revision'), 'Revision table not created for entity_test_update.');
|
||||
|
||||
// Update it to be revisionable and ensure the definition update manager
|
||||
// reports that an update is needed.
|
||||
$this->updateEntityTypeToRevisionable();
|
||||
$this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.');
|
||||
$expected = array(
|
||||
'entity_test_update' => array(
|
||||
t('The %entity_type entity type needs to be updated.', ['%entity_type' => $this->entityManager->getDefinition('entity_test_update')->getLabel()]),
|
||||
),
|
||||
);
|
||||
$this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected); //, 'EntityDefinitionUpdateManager reports the expected change summary.');
|
||||
|
||||
// Run the update and ensure the revision table is created.
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
$this->assertTrue($this->database->schema()->tableExists('entity_test_update_revision'), 'Revision table created for entity_test_update.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests updating entity schema when there are existing entities.
|
||||
*/
|
||||
public function testEntityTypeUpdateWithData() {
|
||||
// Save an entity.
|
||||
$this->entityManager->getStorage('entity_test_update')->create()->save();
|
||||
|
||||
// Update the entity type to be revisionable and try to apply the update.
|
||||
// It's expected to throw an exception.
|
||||
$this->updateEntityTypeToRevisionable();
|
||||
try {
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
$this->fail('EntityStorageException thrown when trying to apply an update that requires data migration.');
|
||||
}
|
||||
catch (EntityStorageException $e) {
|
||||
$this->pass('EntityStorageException thrown when trying to apply an update that requires data migration.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests creating, updating, and deleting a base field if no entities exist.
|
||||
*/
|
||||
public function testBaseFieldCreateUpdateDeleteWithoutData() {
|
||||
// Add a base field, ensure the update manager reports it, and the update
|
||||
// creates its schema.
|
||||
$this->addBaseField();
|
||||
$this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.');
|
||||
$expected = array(
|
||||
'entity_test_update' => array(
|
||||
t('The %field_name field needs to be installed.', ['%field_name' => t('A new base field')]),
|
||||
),
|
||||
);
|
||||
$this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.');
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
$this->assertTrue($this->database->schema()->fieldExists('entity_test_update', 'new_base_field'), 'Column created in shared table for new_base_field.');
|
||||
|
||||
// Add an index on the base field, ensure the update manager reports it,
|
||||
// and the update creates it.
|
||||
$this->addBaseFieldIndex();
|
||||
$this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.');
|
||||
$expected = array(
|
||||
'entity_test_update' => array(
|
||||
t('The %field_name field needs to be updated.', ['%field_name' => t('A new base field')]),
|
||||
),
|
||||
);
|
||||
$this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.');
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
$this->assertTrue($this->database->schema()->indexExists('entity_test_update', 'entity_test_update_field__new_base_field'), 'Index created.');
|
||||
|
||||
// Remove the above index, ensure the update manager reports it, and the
|
||||
// update deletes it.
|
||||
$this->removeBaseFieldIndex();
|
||||
$this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.');
|
||||
$expected = array(
|
||||
'entity_test_update' => array(
|
||||
t('The %field_name field needs to be updated.', ['%field_name' => t('A new base field')]),
|
||||
),
|
||||
);
|
||||
$this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.');
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
$this->assertFalse($this->database->schema()->indexExists('entity_test_update', 'entity_test_update_field__new_base_field'), 'Index deleted.');
|
||||
|
||||
// Update the type of the base field from 'string' to 'text', ensure the
|
||||
// update manager reports it, and the update adjusts the schema
|
||||
// accordingly.
|
||||
$this->modifyBaseField();
|
||||
$this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.');
|
||||
$expected = array(
|
||||
'entity_test_update' => array(
|
||||
t('The %field_name field needs to be updated.', ['%field_name' => t('A new base field')]),
|
||||
),
|
||||
);
|
||||
$this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.');
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
$this->assertFalse($this->database->schema()->fieldExists('entity_test_update', 'new_base_field'), 'Original column deleted in shared table for new_base_field.');
|
||||
$this->assertTrue($this->database->schema()->fieldExists('entity_test_update', 'new_base_field__value'), 'Value column created in shared table for new_base_field.');
|
||||
$this->assertTrue($this->database->schema()->fieldExists('entity_test_update', 'new_base_field__format'), 'Format column created in shared table for new_base_field.');
|
||||
|
||||
// Remove the base field, ensure the update manager reports it, and the
|
||||
// update deletes the schema.
|
||||
$this->removeBaseField();
|
||||
$this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.');
|
||||
$expected = array(
|
||||
'entity_test_update' => array(
|
||||
t('The %field_name field needs to be uninstalled.', ['%field_name' => t('A new base field')]),
|
||||
),
|
||||
);
|
||||
$this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.');
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
$this->assertFalse($this->database->schema()->fieldExists('entity_test_update', 'new_base_field_value'), 'Value column deleted from shared table for new_base_field.');
|
||||
$this->assertFalse($this->database->schema()->fieldExists('entity_test_update', 'new_base_field_format'), 'Format column deleted from shared table for new_base_field.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests creating, updating, and deleting a bundle field if no entities exist.
|
||||
*/
|
||||
public function testBundleFieldCreateUpdateDeleteWithoutData() {
|
||||
// Add a bundle field, ensure the update manager reports it, and the update
|
||||
// creates its schema.
|
||||
$this->addBundleField();
|
||||
$this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.');
|
||||
$expected = array(
|
||||
'entity_test_update' => array(
|
||||
t('The %field_name field needs to be installed.', ['%field_name' => t('A new bundle field')]),
|
||||
),
|
||||
);
|
||||
$this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.');
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
$this->assertTrue($this->database->schema()->tableExists('entity_test_update__new_bundle_field'), 'Dedicated table created for new_bundle_field.');
|
||||
|
||||
// Update the type of the base field from 'string' to 'text', ensure the
|
||||
// update manager reports it, and the update adjusts the schema
|
||||
// accordingly.
|
||||
$this->modifyBundleField();
|
||||
$this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.');
|
||||
$expected = array(
|
||||
'entity_test_update' => [
|
||||
t('The %field_name field needs to be updated.', ['%field_name' => t('A new bundle field')]),
|
||||
],
|
||||
);
|
||||
$this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.');
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
$this->assertTrue($this->database->schema()->fieldExists('entity_test_update__new_bundle_field', 'new_bundle_field_format'), 'Format column created in dedicated table for new_base_field.');
|
||||
|
||||
// Remove the bundle field, ensure the update manager reports it, and the
|
||||
// update deletes the schema.
|
||||
$this->removeBundleField();
|
||||
$this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.');
|
||||
$expected = array(
|
||||
'entity_test_update' => array(
|
||||
t('The %field_name field needs to be uninstalled.', ['%field_name' => t('A new bundle field')]),
|
||||
),
|
||||
);
|
||||
$this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.');
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
$this->assertFalse($this->database->schema()->tableExists('entity_test_update__new_bundle_field'), 'Dedicated table deleted for new_bundle_field.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests creating and deleting a base field if entities exist.
|
||||
*
|
||||
* This tests deletion when there are existing entities, but not existing data
|
||||
* for the field being deleted.
|
||||
*
|
||||
* @see testBaseFieldDeleteWithExistingData()
|
||||
*/
|
||||
public function testBaseFieldCreateDeleteWithExistingEntities() {
|
||||
// Save an entity.
|
||||
$name = $this->randomString();
|
||||
$storage = $this->entityManager->getStorage('entity_test_update');
|
||||
$entity = $storage->create(array('name' => $name));
|
||||
$entity->save();
|
||||
|
||||
// Add a base field and run the update. Ensure the base field's column is
|
||||
// created and the prior saved entity data is still there.
|
||||
$this->addBaseField();
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
$schema_handler = $this->database->schema();
|
||||
$this->assertTrue($schema_handler->fieldExists('entity_test_update', 'new_base_field'), 'Column created in shared table for new_base_field.');
|
||||
$entity = $this->entityManager->getStorage('entity_test_update')->load($entity->id());
|
||||
$this->assertIdentical($entity->name->value, $name, 'Entity data preserved during field creation.');
|
||||
|
||||
// Remove the base field and run the update. Ensure the base field's column
|
||||
// is deleted and the prior saved entity data is still there.
|
||||
$this->removeBaseField();
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
$this->assertFalse($schema_handler->fieldExists('entity_test_update', 'new_base_field'), 'Column deleted from shared table for new_base_field.');
|
||||
$entity = $this->entityManager->getStorage('entity_test_update')->load($entity->id());
|
||||
$this->assertIdentical($entity->name->value, $name, 'Entity data preserved during field deletion.');
|
||||
|
||||
// Add a base field with a required property and run the update. Ensure
|
||||
// 'not null' is not applied and thus no exception is thrown.
|
||||
$this->addBaseField('shape_required');
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
$assert = $schema_handler->fieldExists('entity_test_update', 'new_base_field__shape') && $schema_handler->fieldExists('entity_test_update', 'new_base_field__color');
|
||||
$this->assertTrue($assert, 'Columns created in shared table for new_base_field.');
|
||||
|
||||
// Recreate the field after emptying the base table and check that its
|
||||
// columns are not 'not null'.
|
||||
// @todo Revisit this test when allowing for required storage field
|
||||
// definitions. See https://www.drupal.org/node/2390495.
|
||||
$entity->delete();
|
||||
$this->removeBaseField();
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
$assert = !$schema_handler->fieldExists('entity_test_update', 'new_base_field__shape') && !$schema_handler->fieldExists('entity_test_update', 'new_base_field__color');
|
||||
$this->assert($assert, 'Columns removed from the shared table for new_base_field.');
|
||||
$this->addBaseField('shape_required');
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
$assert = $schema_handler->fieldExists('entity_test_update', 'new_base_field__shape') && $schema_handler->fieldExists('entity_test_update', 'new_base_field__color');
|
||||
$this->assertTrue($assert, 'Columns created again in shared table for new_base_field.');
|
||||
$entity = $storage->create(array('name' => $name));
|
||||
$entity->save();
|
||||
$this->pass('The new_base_field columns are still nullable');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests creating and deleting a bundle field if entities exist.
|
||||
*
|
||||
* This tests deletion when there are existing entities, but not existing data
|
||||
* for the field being deleted.
|
||||
*
|
||||
* @see testBundleFieldDeleteWithExistingData()
|
||||
*/
|
||||
public function testBundleFieldCreateDeleteWithExistingEntities() {
|
||||
// Save an entity.
|
||||
$name = $this->randomString();
|
||||
$storage = $this->entityManager->getStorage('entity_test_update');
|
||||
$entity = $storage->create(array('name' => $name));
|
||||
$entity->save();
|
||||
|
||||
// Add a bundle field and run the update. Ensure the bundle field's table
|
||||
// is created and the prior saved entity data is still there.
|
||||
$this->addBundleField();
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
$schema_handler = $this->database->schema();
|
||||
$this->assertTrue($schema_handler->tableExists('entity_test_update__new_bundle_field'), 'Dedicated table created for new_bundle_field.');
|
||||
$entity = $this->entityManager->getStorage('entity_test_update')->load($entity->id());
|
||||
$this->assertIdentical($entity->name->value, $name, 'Entity data preserved during field creation.');
|
||||
|
||||
// Remove the base field and run the update. Ensure the bundle field's
|
||||
// table is deleted and the prior saved entity data is still there.
|
||||
$this->removeBundleField();
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
$this->assertFalse($schema_handler->tableExists('entity_test_update__new_bundle_field'), 'Dedicated table deleted for new_bundle_field.');
|
||||
$entity = $this->entityManager->getStorage('entity_test_update')->load($entity->id());
|
||||
$this->assertIdentical($entity->name->value, $name, 'Entity data preserved during field deletion.');
|
||||
|
||||
// Test that required columns are created as 'not null'.
|
||||
$this->addBundleField('shape_required');
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
$message = 'The new_bundle_field_shape column is not nullable.';
|
||||
$values = array(
|
||||
'bundle' => $entity->bundle(),
|
||||
'deleted'=> 0,
|
||||
'entity_id' => $entity->id(),
|
||||
'revision_id' => $entity->id(),
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
'delta' => 0,
|
||||
'new_bundle_field_color' => $this->randomString(),
|
||||
);
|
||||
try {
|
||||
// Try to insert a record without providing a value for the 'not null'
|
||||
// column. This should fail.
|
||||
$this->database->insert('entity_test_update__new_bundle_field')
|
||||
->fields($values)
|
||||
->execute();
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (\RuntimeException $e) {
|
||||
if ($e instanceof DatabaseExceptionWrapper || $e instanceof IntegrityConstraintViolationException) {
|
||||
// Now provide a value for the 'not null' column. This is expected to
|
||||
// succeed.
|
||||
$values['new_bundle_field_shape'] = $this->randomString();
|
||||
$this->database->insert('entity_test_update__new_bundle_field')
|
||||
->fields($values)
|
||||
->execute();
|
||||
$this->pass($message);
|
||||
} else {
|
||||
// Keep throwing it.
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests deleting a base field when it has existing data.
|
||||
*/
|
||||
public function testBaseFieldDeleteWithExistingData() {
|
||||
// Add the base field and run the update.
|
||||
$this->addBaseField();
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
|
||||
// Save an entity with the base field populated.
|
||||
$this->entityManager->getStorage('entity_test_update')->create(array('new_base_field' => 'foo'))->save();
|
||||
|
||||
// Remove the base field and apply updates. It's expected to throw an
|
||||
// exception.
|
||||
// @todo Revisit that expectation once purging is implemented for
|
||||
// all fields: https://www.drupal.org/node/2282119.
|
||||
$this->removeBaseField();
|
||||
try {
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
$this->fail('FieldStorageDefinitionUpdateForbiddenException thrown when trying to apply an update that deletes a non-purgeable field with data.');
|
||||
}
|
||||
catch (FieldStorageDefinitionUpdateForbiddenException $e) {
|
||||
$this->pass('FieldStorageDefinitionUpdateForbiddenException thrown when trying to apply an update that deletes a non-purgeable field with data.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests deleting a bundle field when it has existing data.
|
||||
*/
|
||||
public function testBundleFieldDeleteWithExistingData() {
|
||||
// Add the bundle field and run the update.
|
||||
$this->addBundleField();
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
|
||||
// Save an entity with the bundle field populated.
|
||||
entity_test_create_bundle('custom');
|
||||
$this->entityManager->getStorage('entity_test_update')->create(array('type' => 'test_bundle', 'new_bundle_field' => 'foo'))->save();
|
||||
|
||||
// Remove the bundle field and apply updates. It's expected to throw an
|
||||
// exception.
|
||||
// @todo Revisit that expectation once purging is implemented for
|
||||
// all fields: https://www.drupal.org/node/2282119.
|
||||
$this->removeBundleField();
|
||||
try {
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
$this->fail('FieldStorageDefinitionUpdateForbiddenException thrown when trying to apply an update that deletes a non-purgeable field with data.');
|
||||
}
|
||||
catch (FieldStorageDefinitionUpdateForbiddenException $e) {
|
||||
$this->pass('FieldStorageDefinitionUpdateForbiddenException thrown when trying to apply an update that deletes a non-purgeable field with data.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests updating a base field when it has existing data.
|
||||
*/
|
||||
public function testBaseFieldUpdateWithExistingData() {
|
||||
// Add the base field and run the update.
|
||||
$this->addBaseField();
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
|
||||
// Save an entity with the base field populated.
|
||||
$this->entityManager->getStorage('entity_test_update')->create(array('new_base_field' => 'foo'))->save();
|
||||
|
||||
// Change the field's field type and apply updates. It's expected to
|
||||
// throw an exception.
|
||||
$this->modifyBaseField();
|
||||
try {
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
$this->fail('FieldStorageDefinitionUpdateForbiddenException thrown when trying to update a field schema that has data.');
|
||||
}
|
||||
catch (FieldStorageDefinitionUpdateForbiddenException $e) {
|
||||
$this->pass('FieldStorageDefinitionUpdateForbiddenException thrown when trying to update a field schema that has data.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests updating a bundle field when it has existing data.
|
||||
*/
|
||||
public function testBundleFieldUpdateWithExistingData() {
|
||||
// Add the bundle field and run the update.
|
||||
$this->addBundleField();
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
|
||||
// Save an entity with the bundle field populated.
|
||||
entity_test_create_bundle('custom');
|
||||
$this->entityManager->getStorage('entity_test_update')->create(array('type' => 'test_bundle', 'new_bundle_field' => 'foo'))->save();
|
||||
|
||||
// Change the field's field type and apply updates. It's expected to
|
||||
// throw an exception.
|
||||
$this->modifyBundleField();
|
||||
try {
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
$this->fail('FieldStorageDefinitionUpdateForbiddenException thrown when trying to update a field schema that has data.');
|
||||
}
|
||||
catch (FieldStorageDefinitionUpdateForbiddenException $e) {
|
||||
$this->pass('FieldStorageDefinitionUpdateForbiddenException thrown when trying to update a field schema that has data.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests creating and deleting a multi-field index when there are no existing entities.
|
||||
*/
|
||||
public function testEntityIndexCreateDeleteWithoutData() {
|
||||
// Add an entity index and ensure the update manager reports that as an
|
||||
// update to the entity type.
|
||||
$this->addEntityIndex();
|
||||
$this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.');
|
||||
$expected = array(
|
||||
'entity_test_update' => array(
|
||||
t('The %entity_type entity type needs to be updated.', ['%entity_type' => $this->entityManager->getDefinition('entity_test_update')->getLabel()]),
|
||||
),
|
||||
);
|
||||
$this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.');
|
||||
|
||||
// Run the update and ensure the new index is created.
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
$this->assertTrue($this->database->schema()->indexExists('entity_test_update', 'entity_test_update__new_index'), 'Index created.');
|
||||
|
||||
// Remove the index and ensure the update manager reports that as an
|
||||
// update to the entity type.
|
||||
$this->removeEntityIndex();
|
||||
$this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.');
|
||||
$expected = array(
|
||||
'entity_test_update' => array(
|
||||
t('The %entity_type entity type needs to be updated.', ['%entity_type' => $this->entityManager->getDefinition('entity_test_update')->getLabel()]),
|
||||
),
|
||||
);
|
||||
$this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.');
|
||||
|
||||
// Run the update and ensure the index is deleted.
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
$this->assertFalse($this->database->schema()->indexExists('entity_test_update', 'entity_test_update__new_index'), 'Index deleted.');
|
||||
|
||||
// Test that composite indexes are handled correctly when dropping and
|
||||
// re-creating one of their columns.
|
||||
$this->addEntityIndex();
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
$storage_definition = $this->entityDefinitionUpdateManager->getFieldStorageDefinition('name', 'entity_test_update');
|
||||
$this->entityDefinitionUpdateManager->updateFieldStorageDefinition($storage_definition);
|
||||
$this->assertTrue($this->database->schema()->indexExists('entity_test_update', 'entity_test_update__new_index'), 'Index created.');
|
||||
$this->entityDefinitionUpdateManager->uninstallFieldStorageDefinition($storage_definition);
|
||||
$this->assertFalse($this->database->schema()->indexExists('entity_test_update', 'entity_test_update__new_index'), 'Index deleted.');
|
||||
$this->entityDefinitionUpdateManager->installFieldStorageDefinition('name', 'entity_test_update', 'entity_test', $storage_definition);
|
||||
$this->assertTrue($this->database->schema()->indexExists('entity_test_update', 'entity_test_update__new_index'), 'Index created again.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests creating a multi-field index when there are existing entities.
|
||||
*/
|
||||
public function testEntityIndexCreateWithData() {
|
||||
// Save an entity.
|
||||
$name = $this->randomString();
|
||||
$entity = $this->entityManager->getStorage('entity_test_update')->create(array('name' => $name));
|
||||
$entity->save();
|
||||
|
||||
// Add an entity index, run the update. Ensure that the index is created
|
||||
// despite having data.
|
||||
$this->addEntityIndex();
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
$this->assertTrue($this->database->schema()->indexExists('entity_test_update', 'entity_test_update__new_index'), 'Index added.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests entity type and field storage definition events.
|
||||
*/
|
||||
public function testDefinitionEvents() {
|
||||
/** @var \Drupal\entity_test\EntityTestDefinitionSubscriber $event_subscriber */
|
||||
$event_subscriber = $this->container->get('entity_test.definition.subscriber');
|
||||
$event_subscriber->enableEventTracking();
|
||||
|
||||
// Test field storage definition events.
|
||||
$storage_definition = current($this->entityManager->getFieldStorageDefinitions('entity_test_rev'));
|
||||
$this->assertFalse($event_subscriber->hasEventFired(FieldStorageDefinitionEvents::DELETE), 'Entity type delete was not dispatched yet.');
|
||||
$this->entityManager->onFieldStorageDefinitionDelete($storage_definition);
|
||||
$this->assertTrue($event_subscriber->hasEventFired(FieldStorageDefinitionEvents::DELETE), 'Entity type delete event successfully dispatched.');
|
||||
$this->assertFalse($event_subscriber->hasEventFired(FieldStorageDefinitionEvents::CREATE), 'Entity type create was not dispatched yet.');
|
||||
$this->entityManager->onFieldStorageDefinitionCreate($storage_definition);
|
||||
$this->assertTrue($event_subscriber->hasEventFired(FieldStorageDefinitionEvents::CREATE), 'Entity type create event successfully dispatched.');
|
||||
$this->assertFalse($event_subscriber->hasEventFired(FieldStorageDefinitionEvents::UPDATE), 'Entity type update was not dispatched yet.');
|
||||
$this->entityManager->onFieldStorageDefinitionUpdate($storage_definition, $storage_definition);
|
||||
$this->assertTrue($event_subscriber->hasEventFired(FieldStorageDefinitionEvents::UPDATE), 'Entity type update event successfully dispatched.');
|
||||
|
||||
// Test entity type events.
|
||||
$entity_type = $this->entityManager->getDefinition('entity_test_rev');
|
||||
$this->assertFalse($event_subscriber->hasEventFired(EntityTypeEvents::CREATE), 'Entity type create was not dispatched yet.');
|
||||
$this->entityManager->onEntityTypeCreate($entity_type);
|
||||
$this->assertTrue($event_subscriber->hasEventFired(EntityTypeEvents::CREATE), 'Entity type create event successfully dispatched.');
|
||||
$this->assertFalse($event_subscriber->hasEventFired(EntityTypeEvents::UPDATE), 'Entity type update was not dispatched yet.');
|
||||
$this->entityManager->onEntityTypeUpdate($entity_type, $entity_type);
|
||||
$this->assertTrue($event_subscriber->hasEventFired(EntityTypeEvents::UPDATE), 'Entity type update event successfully dispatched.');
|
||||
$this->assertFalse($event_subscriber->hasEventFired(EntityTypeEvents::DELETE), 'Entity type delete was not dispatched yet.');
|
||||
$this->entityManager->onEntityTypeDelete($entity_type);
|
||||
$this->assertTrue($event_subscriber->hasEventFired(EntityTypeEvents::DELETE), 'Entity type delete event successfully dispatched.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests updating entity schema and creating a base field.
|
||||
*
|
||||
* This tests updating entity schema and creating a base field at the same
|
||||
* time when there are no existing entities.
|
||||
*/
|
||||
public function testEntityTypeSchemaUpdateAndBaseFieldCreateWithoutData() {
|
||||
$this->updateEntityTypeToRevisionable();
|
||||
$this->addBaseField();
|
||||
$message = 'Successfully updated entity schema and created base field at the same time.';
|
||||
// Entity type updates create base fields as well, thus make sure doing both
|
||||
// at the same time does not lead to errors due to the base field being
|
||||
// created twice.
|
||||
try {
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
$this->pass($message);
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$this->fail($message);
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests updating entity schema and creating a revisionable base field.
|
||||
*
|
||||
* This tests updating entity schema and creating a revisionable base field
|
||||
* at the same time when there are no existing entities.
|
||||
*/
|
||||
public function testEntityTypeSchemaUpdateAndRevisionableBaseFieldCreateWithoutData() {
|
||||
$this->updateEntityTypeToRevisionable();
|
||||
$this->addRevisionableBaseField();
|
||||
$message = 'Successfully updated entity schema and created revisionable base field at the same time.';
|
||||
// Entity type updates create base fields as well, thus make sure doing both
|
||||
// at the same time does not lead to errors due to the base field being
|
||||
// created twice.
|
||||
try {
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
$this->pass($message);
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$this->fail($message);
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests applying single updates.
|
||||
*/
|
||||
public function testSingleActionCalls() {
|
||||
$db_schema = $this->database->schema();
|
||||
|
||||
// Ensure that a non-existing entity type cannot be installed.
|
||||
$message = 'A non-existing entity type cannot be installed';
|
||||
try {
|
||||
$this->entityDefinitionUpdateManager->installEntityType(new ContentEntityType(['id' => 'foo']));
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (PluginNotFoundException $e) {
|
||||
$this->pass($message);
|
||||
}
|
||||
|
||||
// Ensure that a field cannot be installed on non-existing entity type.
|
||||
$message = 'A field cannot be installed on a non-existing entity type';
|
||||
try {
|
||||
$storage_definition = BaseFieldDefinition::create('string')
|
||||
->setLabel(t('A new revisionable base field'))
|
||||
->setRevisionable(TRUE);
|
||||
$this->entityDefinitionUpdateManager->installFieldStorageDefinition('bar', 'foo', 'entity_test', $storage_definition);
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (PluginNotFoundException $e) {
|
||||
$this->pass($message);
|
||||
}
|
||||
|
||||
// Ensure that a non-existing field cannot be installed.
|
||||
$storage_definition = BaseFieldDefinition::create('string')
|
||||
->setLabel(t('A new revisionable base field'))
|
||||
->setRevisionable(TRUE);
|
||||
$this->entityDefinitionUpdateManager->installFieldStorageDefinition('bar', 'entity_test_update', 'entity_test', $storage_definition);
|
||||
$this->assertFalse($db_schema->fieldExists('entity_test_update', 'bar'), "A non-existing field cannot be installed.");
|
||||
|
||||
// Ensure that installing an existing entity type is a no-op.
|
||||
$entity_type = $this->entityDefinitionUpdateManager->getEntityType('entity_test_update');
|
||||
$this->entityDefinitionUpdateManager->installEntityType($entity_type);
|
||||
$this->assertTrue($db_schema->tableExists('entity_test_update'), 'Installing an existing entity type is a no-op');
|
||||
|
||||
// Create a new base field.
|
||||
$this->addRevisionableBaseField();
|
||||
$storage_definition = BaseFieldDefinition::create('string')
|
||||
->setLabel(t('A new revisionable base field'))
|
||||
->setRevisionable(TRUE);
|
||||
$this->assertFalse($db_schema->fieldExists('entity_test_update', 'new_base_field'), "New field 'new_base_field' does not exist before applying the update.");
|
||||
$this->entityDefinitionUpdateManager->installFieldStorageDefinition('new_base_field', 'entity_test_update', 'entity_test', $storage_definition);
|
||||
$this->assertTrue($db_schema->fieldExists('entity_test_update', 'new_base_field'), "New field 'new_base_field' has been created on the 'entity_test_update' table.");
|
||||
|
||||
// Ensure that installing an existing field is a no-op.
|
||||
$this->entityDefinitionUpdateManager->installFieldStorageDefinition('new_base_field', 'entity_test_update', 'entity_test', $storage_definition);
|
||||
$this->assertTrue($db_schema->fieldExists('entity_test_update', 'new_base_field'), 'Installing an existing field is a no-op');
|
||||
|
||||
// Update an existing field schema.
|
||||
$this->modifyBaseField();
|
||||
$storage_definition = BaseFieldDefinition::create('text')
|
||||
->setName('new_base_field')
|
||||
->setTargetEntityTypeId('entity_test_update')
|
||||
->setLabel(t('A new revisionable base field'))
|
||||
->setRevisionable(TRUE);
|
||||
$this->entityDefinitionUpdateManager->updateFieldStorageDefinition($storage_definition);
|
||||
$this->assertFalse($db_schema->fieldExists('entity_test_update', 'new_base_field'), "Previous schema for 'new_base_field' no longer exists.");
|
||||
$this->assertTrue(
|
||||
$db_schema->fieldExists('entity_test_update', 'new_base_field__value') && $db_schema->fieldExists('entity_test_update', 'new_base_field__format'),
|
||||
"New schema for 'new_base_field' has been created."
|
||||
);
|
||||
|
||||
// Drop an existing field schema.
|
||||
$this->entityDefinitionUpdateManager->uninstallFieldStorageDefinition($storage_definition);
|
||||
$this->assertFalse(
|
||||
$db_schema->fieldExists('entity_test_update', 'new_base_field__value') || $db_schema->fieldExists('entity_test_update', 'new_base_field__format'),
|
||||
"The schema for 'new_base_field' has been dropped."
|
||||
);
|
||||
|
||||
// Make the entity type revisionable.
|
||||
$this->updateEntityTypeToRevisionable();
|
||||
$this->assertFalse($db_schema->tableExists('entity_test_update_revision'), "The 'entity_test_update_revision' does not exist before applying the update.");
|
||||
$entity_type = $this->entityDefinitionUpdateManager->getEntityType('entity_test_update');
|
||||
$keys = $entity_type->getKeys();
|
||||
$keys['revision'] = 'revision_id';
|
||||
$entity_type->set('entity_keys', $keys);
|
||||
$this->entityDefinitionUpdateManager->updateEntityType($entity_type);
|
||||
$this->assertTrue($db_schema->tableExists('entity_test_update_revision'), "The 'entity_test_update_revision' table has been created.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that a new field and index on a shared table are created.
|
||||
*
|
||||
* @see Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema::createSharedTableSchema
|
||||
*/
|
||||
public function testCreateFieldAndIndexOnSharedTable() {
|
||||
$this->addBaseField();
|
||||
$this->addBaseFieldIndex();
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
$this->assertTrue($this->database->schema()->fieldExists('entity_test_update', 'new_base_field'), "New field 'new_base_field' has been created on the 'entity_test_update' table.");
|
||||
$this->assertTrue($this->database->schema()->indexExists('entity_test_update', 'entity_test_update_field__new_base_field'), "New index 'entity_test_update_field__new_base_field' has been created on the 'entity_test_update' table.");
|
||||
// Check index size in for MySQL.
|
||||
if (Database::getConnection()->driver() == 'mysql') {
|
||||
$result = Database::getConnection()->query('SHOW INDEX FROM {entity_test_update} WHERE key_name = \'entity_test_update_field__new_base_field\' and column_name = \'new_base_field\'')->fetchObject();
|
||||
$this->assertEqual(191, $result->Sub_part, 'The index length has been restricted to 191 characters for UTF8MB4 compatibility.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that a new entity level index is created when data exists.
|
||||
*
|
||||
* @see Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema::onEntityTypeUpdate
|
||||
*/
|
||||
public function testCreateIndexUsingEntityStorageSchemaWithData() {
|
||||
// Save an entity.
|
||||
$name = $this->randomString();
|
||||
$storage = $this->entityManager->getStorage('entity_test_update');
|
||||
$entity = $storage->create(array('name' => $name));
|
||||
$entity->save();
|
||||
|
||||
// Create an index.
|
||||
$indexes = array(
|
||||
'entity_test_update__type_index' => array('type'),
|
||||
);
|
||||
$this->state->set('entity_test_update.additional_entity_indexes', $indexes);
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
$this->assertTrue($this->database->schema()->indexExists('entity_test_update', 'entity_test_update__type_index'), "New index 'entity_test_update__type_index' has been created on the 'entity_test_update' table.");
|
||||
// Check index size in for MySQL.
|
||||
if (Database::getConnection()->driver() == 'mysql') {
|
||||
$result = Database::getConnection()->query('SHOW INDEX FROM {entity_test_update} WHERE key_name = \'entity_test_update__type_index\' and column_name = \'type\'')->fetchObject();
|
||||
$this->assertEqual(191, $result->Sub_part, 'The index length has been restricted to 191 characters for UTF8MB4 compatibility.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests updating a base field when it has existing data.
|
||||
*/
|
||||
public function testBaseFieldEntityKeyUpdateWithExistingData() {
|
||||
// Add the base field and run the update.
|
||||
$this->addBaseField();
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
|
||||
// Save an entity with the base field populated.
|
||||
$this->entityManager->getStorage('entity_test_update')->create(['new_base_field' => $this->randomString()])->save();
|
||||
|
||||
// Save an entity with the base field not populated.
|
||||
/** @var \Drupal\entity_test\Entity\EntityTestUpdate $entity */
|
||||
$entity = $this->entityManager->getStorage('entity_test_update')->create();
|
||||
$entity->save();
|
||||
|
||||
// Promote the base field to an entity key. This will trigger the addition
|
||||
// of a NOT NULL constraint.
|
||||
$this->makeBaseFieldEntityKey();
|
||||
|
||||
// Try to apply the update and verify they fail since we have a NULL value.
|
||||
$message = 'An error occurs when trying to enabling NOT NULL constraints with NULL data.';
|
||||
try {
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (EntityStorageException $e) {
|
||||
$this->pass($message);
|
||||
}
|
||||
|
||||
// Check that the update is correctly applied when no NULL data is left.
|
||||
$entity->set('new_base_field', $this->randomString());
|
||||
$entity->save();
|
||||
$this->entityDefinitionUpdateManager->applyUpdates();
|
||||
$this->pass('The update is correctly performed when no NULL data exists.');
|
||||
|
||||
// Check that the update actually applied a NOT NULL constraint.
|
||||
$entity->set('new_base_field', NULL);
|
||||
$message = 'The NOT NULL constraint was correctly applied.';
|
||||
try {
|
||||
$entity->save();
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (EntityStorageException $e) {
|
||||
$this->pass($message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that field schema is correctly handled with long-named fields.
|
||||
*/
|
||||
function testLongNameFieldIndexes() {
|
||||
$this->addLongNameBaseField();
|
||||
$entity_type_id = 'entity_test_update';
|
||||
$entity_type = $this->entityManager->getDefinition($entity_type_id);
|
||||
$definitions = EntityTestUpdate::baseFieldDefinitions($entity_type);
|
||||
$name = 'new_long_named_entity_reference_base_field';
|
||||
$this->entityDefinitionUpdateManager->installFieldStorageDefinition($name, $entity_type_id, 'entity_test', $definitions[$name]);
|
||||
$this->assertFalse($this->entityDefinitionUpdateManager->needsUpdates(), 'Entity and field schema data are correctly detected.');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Entity;
|
||||
|
||||
use Drupal\Component\Uuid\Uuid;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
|
||||
/**
|
||||
* Tests default values for entity fields.
|
||||
*
|
||||
* @group Entity
|
||||
*/
|
||||
class EntityFieldDefaultValueTest extends EntityKernelTestBase {
|
||||
|
||||
/**
|
||||
* The UUID object to be used for generating UUIDs.
|
||||
*
|
||||
* @var \Drupal\Component\Uuid\UuidInterface
|
||||
*/
|
||||
protected $uuid;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
// Initiate the generator object.
|
||||
$this->uuid = $this->container->get('uuid');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests default values on entities and fields.
|
||||
*/
|
||||
public function testDefaultValues() {
|
||||
// All entity variations have to have the same results.
|
||||
foreach (entity_test_entity_types() as $entity_type) {
|
||||
$this->assertDefaultValues($entity_type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a test set for a defined entity type.
|
||||
*
|
||||
* @param string $entity_type_id
|
||||
* The entity type to run the tests with.
|
||||
*/
|
||||
protected function assertDefaultValues($entity_type_id) {
|
||||
$entity = $this->container->get('entity_type.manager')
|
||||
->getStorage($entity_type_id)
|
||||
->create();
|
||||
$definition = $this->entityManager->getDefinition($entity_type_id);
|
||||
$langcode_key = $definition->getKey('langcode');
|
||||
$this->assertEqual($entity->{$langcode_key}->value, 'en', SafeMarkup::format('%entity_type: Default language', array('%entity_type' => $entity_type_id)));
|
||||
$this->assertTrue(Uuid::isValid($entity->uuid->value), SafeMarkup::format('%entity_type: Default UUID', array('%entity_type' => $entity_type_id)));
|
||||
$this->assertEqual($entity->name->getValue(), array(), 'Field has one empty value by default.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests custom default value callbacks.
|
||||
*/
|
||||
public function testDefaultValueCallback() {
|
||||
$entity = $this->entityManager->getStorage('entity_test_default_value')->create();
|
||||
// The description field has a default value callback for testing, see
|
||||
// entity_test_field_default_value().
|
||||
$string = 'description_' . $entity->language()->getId();
|
||||
$expected = array(
|
||||
array(
|
||||
'shape' => "shape:0:$string",
|
||||
'color' => "color:0:$string",
|
||||
),
|
||||
array(
|
||||
'shape' => "shape:1:$string",
|
||||
'color' => "color:1:$string",
|
||||
),
|
||||
);
|
||||
$this->assertEqual($entity->description->getValue(), $expected);
|
||||
}
|
||||
|
||||
}
|
||||
747
core/tests/Drupal/KernelTests/Core/Entity/EntityFieldTest.php
Normal file
747
core/tests/Drupal/KernelTests/Core/Entity/EntityFieldTest.php
Normal file
|
|
@ -0,0 +1,747 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Entity;
|
||||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Field\BaseFieldDefinition;
|
||||
use Drupal\Core\Field\FieldDefinitionInterface;
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Drupal\Core\Field\FieldItemInterface;
|
||||
use Drupal\Core\TypedData\ComplexDataDefinitionInterface;
|
||||
use Drupal\Core\TypedData\DataDefinitionInterface;
|
||||
use Drupal\Core\TypedData\Type\StringInterface;
|
||||
use Drupal\Core\TypedData\TypedDataInterface;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
|
||||
/**
|
||||
* Tests the Entity Field API.
|
||||
*
|
||||
* @group Entity
|
||||
*/
|
||||
class EntityFieldTest extends EntityKernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('filter', 'text', 'node', 'user', 'field_test');
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $entityName;
|
||||
|
||||
/**
|
||||
* @var \Drupal\user\Entity\User
|
||||
*/
|
||||
protected $entityUser;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $entityFieldText;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
foreach (entity_test_entity_types() as $entity_type_id) {
|
||||
// The entity_test schema is installed by the parent.
|
||||
if ($entity_type_id != 'entity_test') {
|
||||
$this->installEntitySchema($entity_type_id);
|
||||
}
|
||||
}
|
||||
|
||||
// Create the test field.
|
||||
module_load_install('entity_test');
|
||||
entity_test_install();
|
||||
|
||||
// Install required default configuration for filter module.
|
||||
$this->installConfig(array('system', 'filter'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a test entity.
|
||||
*
|
||||
* @return \Drupal\Core\Entity\EntityInterface
|
||||
*/
|
||||
protected function createTestEntity($entity_type) {
|
||||
$this->entityName = $this->randomMachineName();
|
||||
$this->entityUser = $this->createUser();
|
||||
$this->entityFieldText = $this->randomMachineName();
|
||||
|
||||
// Pass in the value of the name field when creating. With the user
|
||||
// field we test setting a field after creation.
|
||||
$entity = $this->container->get('entity_type.manager')
|
||||
->getStorage($entity_type)
|
||||
->create();
|
||||
$entity->user_id->target_id = $this->entityUser->id();
|
||||
$entity->name->value = $this->entityName;
|
||||
|
||||
// Set a value for the test field.
|
||||
$entity->field_test_text->value = $this->entityFieldText;
|
||||
|
||||
return $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests reading and writing properties and field items.
|
||||
*/
|
||||
public function testReadWrite() {
|
||||
// All entity variations have to have the same results.
|
||||
foreach (entity_test_entity_types() as $entity_type) {
|
||||
$this->doTestReadWrite($entity_type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the read write test set for a defined entity type.
|
||||
*
|
||||
* @param string $entity_type
|
||||
* The entity type to run the tests with.
|
||||
*/
|
||||
protected function doTestReadWrite($entity_type) {
|
||||
$entity = $this->createTestEntity($entity_type);
|
||||
|
||||
$langcode = 'en';
|
||||
|
||||
// Access the name field.
|
||||
$this->assertTrue($entity->name instanceof FieldItemListInterface, format_string('%entity_type: Field implements interface', array('%entity_type' => $entity_type)));
|
||||
$this->assertTrue($entity->name[0] instanceof FieldItemInterface, format_string('%entity_type: Field item implements interface', array('%entity_type' => $entity_type)));
|
||||
|
||||
$this->assertEqual($this->entityName, $entity->name->value, format_string('%entity_type: Name value can be read.', array('%entity_type' => $entity_type)));
|
||||
$this->assertEqual($this->entityName, $entity->name[0]->value, format_string('%entity_type: Name value can be read through list access.', array('%entity_type' => $entity_type)));
|
||||
$this->assertEqual($entity->name->getValue(), array(0 => array('value' => $this->entityName)), format_string('%entity_type: Plain field value returned.', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Change the name.
|
||||
$new_name = $this->randomMachineName();
|
||||
$entity->name->value = $new_name;
|
||||
$this->assertEqual($new_name, $entity->name->value, format_string('%entity_type: Name can be updated and read.', array('%entity_type' => $entity_type)));
|
||||
$this->assertEqual($entity->name->getValue(), array(0 => array('value' => $new_name)), format_string('%entity_type: Plain field value reflects the update.', array('%entity_type' => $entity_type)));
|
||||
|
||||
$new_name = $this->randomMachineName();
|
||||
$entity->name[0]->value = $new_name;
|
||||
$this->assertEqual($new_name, $entity->name->value, format_string('%entity_type: Name can be updated and read through list access.', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Access the user field.
|
||||
$this->assertTrue($entity->user_id instanceof FieldItemListInterface, format_string('%entity_type: Field implements interface', array('%entity_type' => $entity_type)));
|
||||
$this->assertTrue($entity->user_id[0] instanceof FieldItemInterface, format_string('%entity_type: Field item implements interface', array('%entity_type' => $entity_type)));
|
||||
|
||||
$this->assertEqual($this->entityUser->id(), $entity->user_id->target_id, format_string('%entity_type: User id can be read.', array('%entity_type' => $entity_type)));
|
||||
$this->assertEqual($this->entityUser->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: User name can be read.', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Change the assigned user by entity.
|
||||
$new_user1 = $this->createUser();
|
||||
$entity->user_id->entity = $new_user1;
|
||||
$this->assertEqual($new_user1->id(), $entity->user_id->target_id, format_string('%entity_type: Updated user id can be read.', array('%entity_type' => $entity_type)));
|
||||
$this->assertEqual($new_user1->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: Updated username value can be read.', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Change the assigned user by id.
|
||||
$new_user2 = $this->createUser();
|
||||
$entity->user_id->target_id = $new_user2->id();
|
||||
$this->assertEqual($new_user2->id(), $entity->user_id->target_id, format_string('%entity_type: Updated user id can be read.', array('%entity_type' => $entity_type)));
|
||||
$this->assertEqual($new_user2->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: Updated username value can be read.', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Try unsetting a field property.
|
||||
$entity->name->value = NULL;
|
||||
$entity->user_id->target_id = NULL;
|
||||
$this->assertNull($entity->name->value, format_string('%entity_type: Name field is not set.', array('%entity_type' => $entity_type)));
|
||||
$this->assertNull($entity->user_id->target_id, format_string('%entity_type: User ID field is not set.', array('%entity_type' => $entity_type)));
|
||||
$this->assertNull($entity->user_id->entity, format_string('%entity_type: User entity field is not set.', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Test setting the values via the typed data API works as well.
|
||||
// Change the assigned user by entity.
|
||||
$entity->user_id->first()->get('entity')->setValue($new_user2);
|
||||
$this->assertEqual($new_user2->id(), $entity->user_id->target_id, format_string('%entity_type: Updated user id can be read.', array('%entity_type' => $entity_type)));
|
||||
$this->assertEqual($new_user2->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: Updated user name value can be read.', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Change the assigned user by id.
|
||||
$entity->user_id->first()->get('target_id')->setValue($new_user2->id());
|
||||
$this->assertEqual($new_user2->id(), $entity->user_id->target_id, format_string('%entity_type: Updated user id can be read.', array('%entity_type' => $entity_type)));
|
||||
$this->assertEqual($new_user2->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: Updated user name value can be read.', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Try unsetting a field.
|
||||
$entity->name->first()->get('value')->setValue(NULL);
|
||||
$entity->user_id->first()->get('target_id')->setValue(NULL);
|
||||
$this->assertNull($entity->name->value, format_string('%entity_type: Name field is not set.', array('%entity_type' => $entity_type)));
|
||||
$this->assertNull($entity->user_id->target_id, format_string('%entity_type: User ID field is not set.', array('%entity_type' => $entity_type)));
|
||||
$this->assertNull($entity->user_id->entity, format_string('%entity_type: User entity field is not set.', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Create a fresh entity so target_id does not get its property object
|
||||
// instantiated, then verify setting a new value via typed data API works.
|
||||
$entity2 = $this->container->get('entity_type.manager')
|
||||
->getStorage($entity_type)
|
||||
->create(array(
|
||||
'user_id' => array('target_id' => $new_user1->id()),
|
||||
));
|
||||
// Access the property object, and set a value.
|
||||
$entity2->user_id->first()->get('target_id')->setValue($new_user2->id());
|
||||
$this->assertEqual($new_user2->id(), $entity2->user_id->target_id, format_string('%entity_type: Updated user id can be read.', array('%entity_type' => $entity_type)));
|
||||
$this->assertEqual($new_user2->name->value, $entity2->user_id->entity->name->value, format_string('%entity_type: Updated user name value can be read.', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Test using isset(), empty() and unset().
|
||||
$entity->name->value = 'test unset';
|
||||
unset($entity->name->value);
|
||||
$this->assertFalse(isset($entity->name->value), format_string('%entity_type: Name is not set.', array('%entity_type' => $entity_type)));
|
||||
$this->assertFalse(isset($entity->name[0]->value), format_string('%entity_type: Name is not set.', array('%entity_type' => $entity_type)));
|
||||
$this->assertTrue(empty($entity->name->value), format_string('%entity_type: Name is empty.', array('%entity_type' => $entity_type)));
|
||||
$this->assertTrue(empty($entity->name[0]->value), format_string('%entity_type: Name is empty.', array('%entity_type' => $entity_type)));
|
||||
|
||||
$entity->name->value = 'a value';
|
||||
$this->assertTrue(isset($entity->name->value), format_string('%entity_type: Name is set.', array('%entity_type' => $entity_type)));
|
||||
$this->assertTrue(isset($entity->name[0]->value), format_string('%entity_type: Name is set.', array('%entity_type' => $entity_type)));
|
||||
$this->assertFalse(empty($entity->name->value), format_string('%entity_type: Name is not empty.', array('%entity_type' => $entity_type)));
|
||||
$this->assertFalse(empty($entity->name[0]->value), format_string('%entity_type: Name is not empty.', array('%entity_type' => $entity_type)));
|
||||
$this->assertTrue(isset($entity->name[0]), format_string('%entity_type: Name string item is set.', array('%entity_type' => $entity_type)));
|
||||
$this->assertFalse(isset($entity->name[1]), format_string('%entity_type: Second name string item is not set as it does not exist', array('%entity_type' => $entity_type)));
|
||||
$this->assertTrue(isset($entity->name), format_string('%entity_type: Name field is set.', array('%entity_type' => $entity_type)));
|
||||
$this->assertFalse(isset($entity->nameInvalid), format_string('%entity_type: Not existing field is not set.', array('%entity_type' => $entity_type)));
|
||||
|
||||
unset($entity->name[0]);
|
||||
$this->assertFalse(isset($entity->name[0]), format_string('%entity_type: Name field item is not set.', array('%entity_type' => $entity_type)));
|
||||
$this->assertFalse(isset($entity->name[0]->value), format_string('%entity_type: Name is not set.', array('%entity_type' => $entity_type)));
|
||||
$this->assertFalse(isset($entity->name->value), format_string('%entity_type: Name is not set.', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Test emptying a field by assigning an empty value. NULL and array()
|
||||
// behave the same.
|
||||
foreach ([NULL, array(), 'unset'] as $empty) {
|
||||
// Make sure a value is present
|
||||
$entity->name->value = 'a value';
|
||||
$this->assertTrue(isset($entity->name->value), format_string('%entity_type: Name is set.', array('%entity_type' => $entity_type)));
|
||||
// Now, empty the field.
|
||||
if ($empty === 'unset') {
|
||||
unset($entity->name);
|
||||
}
|
||||
else {
|
||||
$entity->name = $empty;
|
||||
}
|
||||
$this->assertTrue(isset($entity->name), format_string('%entity_type: Name field is set.', array('%entity_type' => $entity_type)));
|
||||
$this->assertTrue($entity->name->isEmpty(), format_string('%entity_type: Name field is set.', array('%entity_type' => $entity_type)));
|
||||
$this->assertIdentical(count($entity->name), 0, format_string('%entity_type: Name field contains no items.', array('%entity_type' => $entity_type)));
|
||||
$this->assertIdentical($entity->name->getValue(), array(), format_string('%entity_type: Name field value is an empty array.', array('%entity_type' => $entity_type)));
|
||||
$this->assertFalse(isset($entity->name[0]), format_string('%entity_type: Name field item is not set.', array('%entity_type' => $entity_type)));
|
||||
$this->assertFalse(isset($entity->name[0]->value), format_string('%entity_type: First name item value is not set.', array('%entity_type' => $entity_type)));
|
||||
$this->assertFalse(isset($entity->name->value), format_string('%entity_type: Name value is not set.', array('%entity_type' => $entity_type)));
|
||||
}
|
||||
|
||||
// Access the language field.
|
||||
$langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('langcode');
|
||||
$this->assertEqual($langcode, $entity->{$langcode_key}->value, format_string('%entity_type: Language code can be read.', array('%entity_type' => $entity_type)));
|
||||
$this->assertEqual(\Drupal::languageManager()->getLanguage($langcode), $entity->{$langcode_key}->language, format_string('%entity_type: Language object can be read.', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Change the language by code.
|
||||
$entity->{$langcode_key}->value = \Drupal::languageManager()->getDefaultLanguage()->getId();
|
||||
$this->assertEqual(\Drupal::languageManager()->getDefaultLanguage()->getId(), $entity->{$langcode_key}->value, format_string('%entity_type: Language code can be read.', array('%entity_type' => $entity_type)));
|
||||
$this->assertEqual(\Drupal::languageManager()->getDefaultLanguage(), $entity->{$langcode_key}->language, format_string('%entity_type: Language object can be read.', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Revert language by code then try setting it by language object.
|
||||
$entity->{$langcode_key}->value = $langcode;
|
||||
$entity->{$langcode_key}->language = \Drupal::languageManager()->getDefaultLanguage();
|
||||
$this->assertEqual(\Drupal::languageManager()->getDefaultLanguage()->getId(), $entity->{$langcode_key}->value, format_string('%entity_type: Language code can be read.', array('%entity_type' => $entity_type)));
|
||||
$this->assertEqual(\Drupal::languageManager()->getDefaultLanguage(), $entity->{$langcode_key}->language, format_string('%entity_type: Language object can be read.', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Access the text field and test updating.
|
||||
$this->assertEqual($entity->field_test_text->value, $this->entityFieldText, format_string('%entity_type: Text field can be read.', array('%entity_type' => $entity_type)));
|
||||
$new_text = $this->randomMachineName();
|
||||
$entity->field_test_text->value = $new_text;
|
||||
$this->assertEqual($entity->field_test_text->value, $new_text, format_string('%entity_type: Updated text field can be read.', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Test creating the entity by passing in plain values.
|
||||
$this->entityName = $this->randomMachineName();
|
||||
$name_item[0]['value'] = $this->entityName;
|
||||
$this->entityUser = $this->createUser();
|
||||
$user_item[0]['target_id'] = $this->entityUser->id();
|
||||
$this->entityFieldText = $this->randomMachineName();
|
||||
$text_item[0]['value'] = $this->entityFieldText;
|
||||
|
||||
$entity = $this->container->get('entity_type.manager')
|
||||
->getStorage($entity_type)
|
||||
->create(array(
|
||||
'name' => $name_item,
|
||||
'user_id' => $user_item,
|
||||
'field_test_text' => $text_item,
|
||||
));
|
||||
$this->assertEqual($this->entityName, $entity->name->value, format_string('%entity_type: Name value can be read.', array('%entity_type' => $entity_type)));
|
||||
$this->assertEqual($this->entityUser->id(), $entity->user_id->target_id, format_string('%entity_type: User id can be read.', array('%entity_type' => $entity_type)));
|
||||
$this->assertEqual($this->entityUser->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: User name can be read.', array('%entity_type' => $entity_type)));
|
||||
$this->assertEqual($this->entityFieldText, $entity->field_test_text->value, format_string('%entity_type: Text field can be read.', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Tests copying field values by assigning the TypedData objects.
|
||||
$entity2 = $this->createTestEntity($entity_type);
|
||||
$entity2->name = $entity->name;
|
||||
$entity2->user_id = $entity->user_id;
|
||||
$entity2->field_test_text = $entity->field_test_text;
|
||||
$this->assertFalse($entity->name === $entity2->name, format_string('%entity_type: Copying properties results in a different field object.', array('%entity_type' => $entity_type)));
|
||||
$this->assertEqual($entity->name->value, $entity2->name->value, format_string('%entity_type: Name field copied.', array('%entity_type' => $entity_type)));
|
||||
$this->assertEqual($entity->user_id->target_id, $entity2->user_id->target_id, format_string('%entity_type: User id field copied.', array('%entity_type' => $entity_type)));
|
||||
$this->assertEqual($entity->field_test_text->value, $entity2->field_test_text->value, format_string('%entity_type: Text field copied.', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Tests that assigning TypedData objects to non-field properties keeps the
|
||||
// assigned value as is.
|
||||
$entity2 = $this->createTestEntity($entity_type);
|
||||
$entity2->_not_a_field = $entity->name;
|
||||
$this->assertTrue($entity2->_not_a_field === $entity->name, format_string('%entity_type: Typed data objects can be copied to non-field properties as is.', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Tests adding a value to a field item list.
|
||||
$entity->name[] = 'Another name';
|
||||
$this->assertEqual($entity->name[1]->value, 'Another name', format_string('%entity_type: List item added via [] and the first property.', array('%entity_type' => $entity_type)));
|
||||
$entity->name[] = array('value' => 'Third name');
|
||||
$this->assertEqual($entity->name[2]->value, 'Third name', format_string('%entity_type: List item added via [] and an array of properties.', array('%entity_type' => $entity_type)));
|
||||
$entity->name[3] = array('value' => 'Fourth name');
|
||||
$this->assertEqual($entity->name[3]->value, 'Fourth name', format_string('%entity_type: List item added via offset and an array of properties.', array('%entity_type' => $entity_type)));
|
||||
unset($entity->name[3]);
|
||||
|
||||
// Test removing and empty-ing list items.
|
||||
$this->assertEqual(count($entity->name), 3, format_string('%entity_type: List has 3 items.', array('%entity_type' => $entity_type)));
|
||||
unset($entity->name[1]);
|
||||
$this->assertEqual(count($entity->name), 2, format_string('%entity_type: Second list item has been removed.', array('%entity_type' => $entity_type)));
|
||||
$this->assertEqual($entity->name[1]->value, 'Third name', format_string('%entity_type: The subsequent items have been shifted up.', array('%entity_type' => $entity_type)));
|
||||
$this->assertEqual($entity->name[1]->getName(), 1, format_string('%entity_type: The items names have been updated to their new delta.', array('%entity_type' => $entity_type)));
|
||||
$entity->name[1] = NULL;
|
||||
$this->assertEqual(count($entity->name), 2, format_string('%entity_type: Assigning NULL does not reduce array count.', array('%entity_type' => $entity_type)));
|
||||
$this->assertTrue($entity->name[1]->isEmpty(), format_string('%entity_type: Assigning NULL empties the item.', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Test using isEmpty().
|
||||
unset($entity->name[1]);
|
||||
$this->assertFalse($entity->name[0]->isEmpty(), format_string('%entity_type: Name item is not empty.', array('%entity_type' => $entity_type)));
|
||||
$entity->name->value = NULL;
|
||||
$this->assertTrue($entity->name[0]->isEmpty(), format_string('%entity_type: Name item is empty.', array('%entity_type' => $entity_type)));
|
||||
$this->assertTrue($entity->name->isEmpty(), format_string('%entity_type: Name field is empty.', array('%entity_type' => $entity_type)));
|
||||
$this->assertEqual(count($entity->name), 1, format_string('%entity_type: Empty item is considered when counting.', array('%entity_type' => $entity_type)));
|
||||
$this->assertEqual(count(iterator_to_array($entity->name->getIterator())), count($entity->name), format_string('%entity_type: Count matches iterator count.', array('%entity_type' => $entity_type)));
|
||||
$this->assertTrue($entity->name->getValue() === array(0 => array('value' => NULL)), format_string('%entity_type: Name field value contains a NULL value.', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Test using filterEmptyItems().
|
||||
$entity->name = array(NULL, 'foo');
|
||||
$this->assertEqual(count($entity->name), 2, format_string('%entity_type: List has 2 items.', array('%entity_type' => $entity_type)));
|
||||
$entity->name->filterEmptyItems();
|
||||
$this->assertEqual(count($entity->name), 1, format_string('%entity_type: The empty item was removed.', array('%entity_type' => $entity_type)));
|
||||
$this->assertEqual($entity->name[0]->value, 'foo', format_string('%entity_type: The items were renumbered.', array('%entity_type' => $entity_type)));
|
||||
$this->assertEqual($entity->name[0]->getName(), 0, format_string('%entity_type: The deltas were updated in the items.', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Test get and set field values.
|
||||
$entity->name = 'foo';
|
||||
$this->assertEqual($entity->name[0]->toArray(), array('value' => 'foo'), format_string('%entity_type: Field value has been retrieved via toArray()', array('%entity_type' => $entity_type)));
|
||||
|
||||
$values = $entity->toArray();
|
||||
$this->assertEqual($values['name'], array(0 => array('value' => 'foo')), format_string('%entity_type: Field value has been retrieved via toArray() from an entity.', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Make sure the user id can be set to zero.
|
||||
$user_item[0]['target_id'] = 0;
|
||||
$entity = $this->container->get('entity_type.manager')
|
||||
->getStorage($entity_type)
|
||||
->create(array(
|
||||
'name' => $name_item,
|
||||
'user_id' => $user_item,
|
||||
'field_test_text' => $text_item,
|
||||
));
|
||||
$this->assertNotNull($entity->user_id->target_id, format_string('%entity_type: User id is not NULL', array('%entity_type' => $entity_type)));
|
||||
$this->assertIdentical($entity->user_id->target_id, 0, format_string('%entity_type: User id has been set to 0', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Test setting the ID with the value only.
|
||||
$entity = $this->container->get('entity_type.manager')
|
||||
->getStorage($entity_type)
|
||||
->create(array(
|
||||
'name' => $name_item,
|
||||
'user_id' => 0,
|
||||
'field_test_text' => $text_item,
|
||||
));
|
||||
$this->assertNotNull($entity->user_id->target_id, format_string('%entity_type: User id is not NULL', array('%entity_type' => $entity_type)));
|
||||
$this->assertIdentical($entity->user_id->target_id, 0, format_string('%entity_type: User id has been set to 0', array('%entity_type' => $entity_type)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to save and load an entity again.
|
||||
*/
|
||||
public function testSave() {
|
||||
// All entity variations have to have the same results.
|
||||
foreach (entity_test_entity_types() as $entity_type) {
|
||||
$this->doTestSave($entity_type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the save tests for the given entity type.
|
||||
*
|
||||
* @param string $entity_type
|
||||
* The entity type to run the tests with.
|
||||
*/
|
||||
protected function doTestSave($entity_type) {
|
||||
$langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('langcode');
|
||||
$entity = $this->createTestEntity($entity_type);
|
||||
$entity->save();
|
||||
$this->assertTrue((bool) $entity->id(), format_string('%entity_type: Entity has received an id.', array('%entity_type' => $entity_type)));
|
||||
|
||||
$entity = entity_load($entity_type, $entity->id());
|
||||
$this->assertTrue((bool) $entity->id(), format_string('%entity_type: Entity loaded.', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Access the name field.
|
||||
$this->assertEqual(1, $entity->id->value, format_string('%entity_type: ID value can be read.', array('%entity_type' => $entity_type)));
|
||||
$this->assertTrue(is_string($entity->uuid->value), format_string('%entity_type: UUID value can be read.', array('%entity_type' => $entity_type)));
|
||||
$this->assertEqual('en', $entity->{$langcode_key}->value, format_string('%entity_type: Language code can be read.', array('%entity_type' => $entity_type)));
|
||||
$this->assertEqual(\Drupal::languageManager()->getLanguage('en'), $entity->{$langcode_key}->language, format_string('%entity_type: Language object can be read.', array('%entity_type' => $entity_type)));
|
||||
$this->assertEqual($this->entityUser->id(), $entity->user_id->target_id, format_string('%entity_type: User id can be read.', array('%entity_type' => $entity_type)));
|
||||
$this->assertEqual($this->entityUser->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: User name can be read.', array('%entity_type' => $entity_type)));
|
||||
$this->assertEqual($this->entityFieldText, $entity->field_test_text->value, format_string('%entity_type: Text field can be read.', array('%entity_type' => $entity_type)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests introspection and getting metadata upfront.
|
||||
*/
|
||||
public function testIntrospection() {
|
||||
// All entity variations have to have the same results.
|
||||
foreach (entity_test_entity_types() as $entity_type) {
|
||||
$this->doTestIntrospection($entity_type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the introspection tests for the given entity type.
|
||||
*
|
||||
* @param string $entity_type
|
||||
* The entity type to run the tests with.
|
||||
*/
|
||||
protected function doTestIntrospection($entity_type) {
|
||||
// Test getting metadata upfront. The entity types used for this test have
|
||||
// a default bundle that is the same as the entity type.
|
||||
$definitions = \Drupal::entityManager()->getFieldDefinitions($entity_type, $entity_type);
|
||||
$this->assertEqual($definitions['name']->getType(), 'string', $entity_type .': Name field found.');
|
||||
$this->assertEqual($definitions['user_id']->getType(), 'entity_reference', $entity_type .': User field found.');
|
||||
$this->assertEqual($definitions['field_test_text']->getType(), 'text', $entity_type .': Test-text-field field found.');
|
||||
|
||||
// Test deriving further metadata.
|
||||
$this->assertTrue($definitions['name'] instanceof FieldDefinitionInterface);
|
||||
$field_item_definition = $definitions['name']->getItemDefinition();
|
||||
$this->assertTrue($field_item_definition instanceof ComplexDataDefinitionInterface);
|
||||
$this->assertEqual($field_item_definition->getDataType(), 'field_item:string');
|
||||
$value_definition = $field_item_definition->getPropertyDefinition('value');
|
||||
$this->assertTrue($value_definition instanceof DataDefinitionInterface);
|
||||
$this->assertEqual($value_definition->getDataType(), 'string');
|
||||
|
||||
// Test deriving metadata from references.
|
||||
$entity_definition = \Drupal\Core\Entity\TypedData\EntityDataDefinition::create($entity_type);
|
||||
$langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('langcode');
|
||||
$reference_definition = $entity_definition->getPropertyDefinition($langcode_key)
|
||||
->getPropertyDefinition('language')
|
||||
->getTargetDefinition();
|
||||
$this->assertEqual($reference_definition->getDataType(), 'language');
|
||||
|
||||
$reference_definition = $entity_definition->getPropertyDefinition('user_id')
|
||||
->getPropertyDefinition('entity')
|
||||
->getTargetDefinition();
|
||||
|
||||
$this->assertTrue($reference_definition instanceof \Drupal\Core\Entity\TypedData\EntityDataDefinitionInterface, 'Definition of the referenced user retrieved.');
|
||||
$this->assertEqual($reference_definition->getEntityTypeId(), 'user', 'Referenced entity is of type "user".');
|
||||
|
||||
// Test propagating down.
|
||||
$name_definition = $reference_definition->getPropertyDefinition('name');
|
||||
$this->assertTrue($name_definition instanceof FieldDefinitionInterface);
|
||||
$this->assertEqual($name_definition->getPropertyDefinition('value')->getDataType(), 'string');
|
||||
|
||||
// Test introspecting an entity object.
|
||||
// @todo: Add bundles and test bundles as well.
|
||||
$entity = $this->container->get('entity_type.manager')
|
||||
->getStorage($entity_type)
|
||||
->create();
|
||||
|
||||
$definitions = $entity->getFieldDefinitions();
|
||||
$this->assertEqual($definitions['name']->getType(), 'string', $entity_type .': Name field found.');
|
||||
$this->assertEqual($definitions['user_id']->getType(), 'entity_reference', $entity_type .': User field found.');
|
||||
$this->assertEqual($definitions['field_test_text']->getType(), 'text', $entity_type .': Test-text-field field found.');
|
||||
|
||||
$name_properties = $entity->name->getFieldDefinition()->getPropertyDefinitions();
|
||||
$this->assertEqual($name_properties['value']->getDataType(), 'string', $entity_type .': String value property of the name found.');
|
||||
|
||||
$userref_properties = $entity->user_id->getFieldDefinition()->getPropertyDefinitions();
|
||||
$this->assertEqual($userref_properties['target_id']->getDataType(), 'integer', $entity_type .': Entity id property of the user found.');
|
||||
$this->assertEqual($userref_properties['entity']->getDataType(), 'entity_reference', $entity_type .': Entity reference property of the user found.');
|
||||
|
||||
$textfield_properties = $entity->field_test_text->getFieldDefinition()->getFieldStorageDefinition()->getPropertyDefinitions();
|
||||
$this->assertEqual($textfield_properties['value']->getDataType(), 'string', $entity_type .': String value property of the test-text field found.');
|
||||
$this->assertEqual($textfield_properties['format']->getDataType(), 'filter_format', $entity_type .': String format field of the test-text field found.');
|
||||
$this->assertEqual($textfield_properties['processed']->getDataType(), 'string', $entity_type .': String processed property of the test-text field found.');
|
||||
|
||||
// Make sure provided contextual information is right.
|
||||
$entity_adapter = $entity->getTypedData();
|
||||
$this->assertIdentical($entity_adapter->getRoot(), $entity_adapter, 'Entity is root object.');
|
||||
$this->assertEqual($entity_adapter->getPropertyPath(), '');
|
||||
$this->assertEqual($entity_adapter->getName(), '');
|
||||
$this->assertEqual($entity_adapter->getParent(), NULL);
|
||||
|
||||
$field = $entity->user_id;
|
||||
$this->assertIdentical($field->getRoot()->getValue(), $entity, 'Entity is root object.');
|
||||
$this->assertIdentical($field->getEntity(), $entity, 'getEntity() returns the entity.');
|
||||
$this->assertEqual($field->getPropertyPath(), 'user_id');
|
||||
$this->assertEqual($field->getName(), 'user_id');
|
||||
$this->assertIdentical($field->getParent()->getValue(), $entity, 'Parent object matches.');
|
||||
|
||||
$field_item = $field[0];
|
||||
$this->assertIdentical($field_item->getRoot()->getValue(), $entity, 'Entity is root object.');
|
||||
$this->assertIdentical($field_item->getEntity(), $entity, 'getEntity() returns the entity.');
|
||||
$this->assertEqual($field_item->getPropertyPath(), 'user_id.0');
|
||||
$this->assertEqual($field_item->getName(), '0');
|
||||
$this->assertIdentical($field_item->getParent(), $field, 'Parent object matches.');
|
||||
|
||||
$item_value = $field_item->get('entity');
|
||||
$this->assertIdentical($item_value->getRoot()->getValue(), $entity, 'Entity is root object.');
|
||||
$this->assertEqual($item_value->getPropertyPath(), 'user_id.0.entity');
|
||||
$this->assertEqual($item_value->getName(), 'entity');
|
||||
$this->assertIdentical($item_value->getParent(), $field_item, 'Parent object matches.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests iterating over properties.
|
||||
*/
|
||||
public function testIterator() {
|
||||
// All entity variations have to have the same results.
|
||||
foreach (entity_test_entity_types() as $entity_type) {
|
||||
$this->doTestIterator($entity_type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the iterator tests for the given entity type.
|
||||
*
|
||||
* @param string $entity_type
|
||||
* The entity type to run the tests with.
|
||||
*/
|
||||
protected function doTestIterator($entity_type) {
|
||||
$entity = $this->createTestEntity($entity_type);
|
||||
|
||||
foreach ($entity as $name => $field) {
|
||||
$this->assertTrue($field instanceof FieldItemListInterface, $entity_type . ": Field $name implements interface.");
|
||||
|
||||
foreach ($field as $delta => $item) {
|
||||
$this->assertTrue($field[0] instanceof FieldItemInterface, $entity_type . ": Item $delta of field $name implements interface.");
|
||||
|
||||
foreach ($item as $value_name => $value_property) {
|
||||
$this->assertTrue($value_property instanceof TypedDataInterface, $entity_type . ": Value $value_name of item $delta of field $name implements interface.");
|
||||
|
||||
$value = $value_property->getValue();
|
||||
$this->assertTrue(!isset($value) || is_scalar($value) || $value instanceof EntityInterface, $entity_type . ": Value $value_name of item $delta of field $name is a primitive or an entity.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$fields = $entity->getFields();
|
||||
$this->assertEqual(array_keys($fields), array_keys($entity->getTypedData()->getDataDefinition()->getPropertyDefinitions()), format_string('%entity_type: All fields returned.', array('%entity_type' => $entity_type)));
|
||||
$this->assertEqual($fields, iterator_to_array($entity->getIterator()), format_string('%entity_type: Entity iterator iterates over all fields.', array('%entity_type' => $entity_type)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests working with the entity based upon the TypedData API.
|
||||
*/
|
||||
public function testDataStructureInterfaces() {
|
||||
// All entity variations have to have the same results.
|
||||
foreach (entity_test_entity_types() as $entity_type) {
|
||||
$this->doTestDataStructureInterfaces($entity_type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the data structure interfaces tests for the given entity type.
|
||||
*
|
||||
* @param string $entity_type
|
||||
* The entity type to run the tests with.
|
||||
*/
|
||||
protected function doTestDataStructureInterfaces($entity_type) {
|
||||
$entity = $this->createTestEntity($entity_type);
|
||||
|
||||
// Test using the whole tree of typed data by navigating through the tree of
|
||||
// contained properties and getting all contained strings, limited by a
|
||||
// certain depth.
|
||||
$strings = array();
|
||||
$this->getContainedStrings($entity->getTypedData(), 0, $strings);
|
||||
|
||||
// @todo: Once the user entity has defined properties this should contain
|
||||
// the user name and other user entity strings as well.
|
||||
$target_strings = array(
|
||||
$entity->uuid->value,
|
||||
'en',
|
||||
$this->entityName,
|
||||
// Bundle name.
|
||||
$entity->bundle(),
|
||||
$this->entityFieldText,
|
||||
// Field format.
|
||||
NULL,
|
||||
);
|
||||
asort($strings);
|
||||
asort($target_strings);
|
||||
$this->assertEqual(array_values($strings), array_values($target_strings), format_string('%entity_type: All contained strings found.', array('%entity_type' => $entity_type)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursive helper for getting all contained strings,
|
||||
* i.e. properties of type string.
|
||||
*/
|
||||
public function getContainedStrings(TypedDataInterface $wrapper, $depth, array &$strings) {
|
||||
|
||||
if ($wrapper instanceof StringInterface) {
|
||||
$strings[] = $wrapper->getValue();
|
||||
}
|
||||
|
||||
// Recurse until a certain depth is reached if possible.
|
||||
if ($depth < 7) {
|
||||
if ($wrapper instanceof \Drupal\Core\TypedData\ListInterface) {
|
||||
foreach ($wrapper as $item) {
|
||||
$this->getContainedStrings($item, $depth + 1, $strings);
|
||||
}
|
||||
}
|
||||
elseif ($wrapper instanceof \Drupal\Core\TypedData\ComplexDataInterface) {
|
||||
foreach ($wrapper as $property) {
|
||||
$this->getContainedStrings($property, $depth + 1, $strings);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure data types are correctly derived for all entity types.
|
||||
*/
|
||||
public function testDataTypes() {
|
||||
$types = \Drupal::typedDataManager()->getDefinitions();
|
||||
foreach (entity_test_entity_types() as $entity_type) {
|
||||
$this->assertTrue($types['entity:' . $entity_type]['class'], 'Entity data type registered.');
|
||||
}
|
||||
// Check bundle types are provided as well.
|
||||
entity_test_create_bundle('bundle');
|
||||
$types = \Drupal::typedDataManager()->getDefinitions();
|
||||
$this->assertTrue($types['entity:entity_test:bundle']['class'], 'Entity bundle data type registered.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a base field override on a non-existing base field.
|
||||
*
|
||||
* @see entity_test_entity_base_field_info_alter()
|
||||
*/
|
||||
public function testBaseFieldNonExistingBaseField() {
|
||||
$this->entityManager->getStorage('node_type')->create(array(
|
||||
'type' => 'page',
|
||||
'name' => 'page',
|
||||
))->save();
|
||||
$this->entityManager->clearCachedFieldDefinitions();
|
||||
$fields = $this->entityManager->getFieldDefinitions('node', 'page');
|
||||
$override = $fields['status']->getConfig('page');
|
||||
$override->setLabel($this->randomString())->save();
|
||||
\Drupal::state()->set('entity_test.node_remove_status_field', TRUE);
|
||||
$this->entityManager->clearCachedFieldDefinitions();
|
||||
$fields = $this->entityManager->getFieldDefinitions('node', 'page');
|
||||
// A base field override on a non-existing base field should not cause a
|
||||
// field definition to come into existence.
|
||||
$this->assertFalse(isset($fields['status']), 'Node\'s status base field does not exist.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests creating a field override config for a bundle field.
|
||||
*
|
||||
* @see entity_test_entity_base_field_info_alter()
|
||||
*/
|
||||
public function testFieldOverrideBundleField() {
|
||||
// First make sure the bundle field override in code, which is provided by
|
||||
// the test entity works.
|
||||
entity_test_create_bundle('some_test_bundle', 'Some test bundle', 'entity_test_field_override');
|
||||
$field_definitions = $this->entityManager->getFieldDefinitions('entity_test_field_override', 'entity_test_field_override');
|
||||
$this->assertEqual($field_definitions['name']->getDescription(), 'The default description.');
|
||||
$this->assertNull($field_definitions['name']->getTargetBundle());
|
||||
|
||||
$field_definitions = $this->entityManager->getFieldDefinitions('entity_test_field_override', 'some_test_bundle');
|
||||
$this->assertEqual($field_definitions['name']->getDescription(), 'Custom description.');
|
||||
$this->assertEqual($field_definitions['name']->getTargetBundle(), 'some_test_bundle');
|
||||
|
||||
// Now create a config override of the bundle field.
|
||||
$field_config = $field_definitions['name']->getConfig('some_test_bundle');
|
||||
$field_config->setTranslatable(FALSE);
|
||||
$field_config->save();
|
||||
|
||||
// Make sure both overrides are present.
|
||||
$this->entityManager->clearCachedFieldDefinitions();
|
||||
$field_definitions = $this->entityManager->getFieldDefinitions('entity_test_field_override', 'some_test_bundle');
|
||||
$this->assertEqual($field_definitions['name']->getDescription(), 'Custom description.');
|
||||
$this->assertEqual($field_definitions['name']->getTargetBundle(), 'some_test_bundle');
|
||||
$this->assertFalse($field_definitions['name']->isTranslatable());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests validation constraints provided by the Entity API.
|
||||
*/
|
||||
public function testEntityConstraintValidation() {
|
||||
$entity = $this->createTestEntity('entity_test');
|
||||
$entity->save();
|
||||
// Create a reference field item and let it reference the entity.
|
||||
$definition = BaseFieldDefinition::create('entity_reference')
|
||||
->setLabel('Test entity')
|
||||
->setSetting('target_type', 'entity_test');
|
||||
$reference_field = \Drupal::typedDataManager()->create($definition);
|
||||
$reference = $reference_field->appendItem(array('entity' => $entity))->get('entity');
|
||||
|
||||
// Test validation the typed data object.
|
||||
$violations = $reference->validate();
|
||||
$this->assertEqual($violations->count(), 0);
|
||||
|
||||
// Test validating an entity of the wrong type.
|
||||
$user = $this->createUser();
|
||||
$user->save();
|
||||
$node = $node = Node::create([
|
||||
'type' => 'page',
|
||||
'uid' => $user->id(),
|
||||
'title' => $this->randomString(),
|
||||
]);
|
||||
$reference->setValue($node);
|
||||
$violations = $reference->validate();
|
||||
$this->assertEqual($violations->count(), 1);
|
||||
|
||||
// Test bundle validation.
|
||||
NodeType::create(array('type' => 'article'))
|
||||
->save();
|
||||
$definition = BaseFieldDefinition::create('entity_reference')
|
||||
->setLabel('Test entity')
|
||||
->setSetting('target_type', 'node')
|
||||
->setSetting('handler_settings', ['target_bundles' => ['article' => 'article']]);
|
||||
$reference_field = \Drupal::TypedDataManager()->create($definition);
|
||||
$reference_field->appendItem(array('entity' => $node));
|
||||
$violations = $reference_field->validate();
|
||||
$this->assertEqual($violations->count(), 1);
|
||||
|
||||
$node = Node::create([
|
||||
'type' => 'article',
|
||||
'uid' => $user->id(),
|
||||
'title' => $this->randomString(),
|
||||
]);
|
||||
$node->save();
|
||||
$reference_field->entity = $node;
|
||||
$violations = $reference_field->validate();
|
||||
$this->assertEqual($violations->count(), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests getting processed property values via a computed property.
|
||||
*/
|
||||
public function testComputedProperties() {
|
||||
// All entity variations have to have the same results.
|
||||
foreach (entity_test_entity_types() as $entity_type) {
|
||||
$this->doTestComputedProperties($entity_type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the computed properties tests for the given entity type.
|
||||
*
|
||||
* @param string $entity_type
|
||||
* The entity type to run the tests with.
|
||||
*/
|
||||
protected function doTestComputedProperties($entity_type) {
|
||||
$entity = $this->createTestEntity($entity_type);
|
||||
$entity->field_test_text->value = "The <strong>text</strong> text to filter.";
|
||||
$entity->field_test_text->format = filter_default_format();
|
||||
|
||||
$target = "<p>The <strong>text</strong> text to filter.</p>\n";
|
||||
$this->assertEqual($entity->field_test_text->processed, $target, format_string('%entity_type: Text is processed with the default filter.', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Save and load entity and make sure it still works.
|
||||
$entity->save();
|
||||
$entity = entity_load($entity_type, $entity->id());
|
||||
$this->assertEqual($entity->field_test_text->processed, $target, format_string('%entity_type: Text is processed with the default filter.', array('%entity_type' => $entity_type)));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,10 +1,5 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\KernelTests\Core\Entity\EntityKernelTestBase.
|
||||
*/
|
||||
|
||||
namespace Drupal\KernelTests\Core\Entity;
|
||||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,136 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Entity;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
|
||||
/**
|
||||
* Base class for language-aware entity tests.
|
||||
*/
|
||||
abstract class EntityLanguageTestBase extends EntityKernelTestBase {
|
||||
|
||||
/**
|
||||
* The language manager service.
|
||||
*
|
||||
* @var \Drupal\Core\Language\LanguageManagerInterface
|
||||
*/
|
||||
protected $languageManager;
|
||||
|
||||
/**
|
||||
* The available language codes.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $langcodes;
|
||||
|
||||
/**
|
||||
* The test field name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $fieldName;
|
||||
|
||||
/**
|
||||
* The untranslatable test field name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $untranslatableFieldName;
|
||||
|
||||
public static $modules = array('language', 'entity_test');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->languageManager = $this->container->get('language_manager');
|
||||
|
||||
foreach (entity_test_entity_types() as $entity_type_id) {
|
||||
// The entity_test schema is installed by the parent.
|
||||
if ($entity_type_id != 'entity_test') {
|
||||
$this->installEntitySchema($entity_type_id);
|
||||
}
|
||||
}
|
||||
|
||||
$this->installConfig(array('language'));
|
||||
|
||||
// Create the test field.
|
||||
module_load_install('entity_test');
|
||||
entity_test_install();
|
||||
|
||||
// Enable translations for the test entity type.
|
||||
$this->state->set('entity_test.translation', TRUE);
|
||||
|
||||
// Create a translatable test field.
|
||||
$this->fieldName = Unicode::strtolower($this->randomMachineName() . '_field_name');
|
||||
|
||||
// Create an untranslatable test field.
|
||||
$this->untranslatableFieldName = Unicode::strtolower($this->randomMachineName() . '_field_name');
|
||||
|
||||
// Create field fields in all entity variations.
|
||||
foreach (entity_test_entity_types() as $entity_type) {
|
||||
FieldStorageConfig::create(array(
|
||||
'field_name' => $this->fieldName,
|
||||
'entity_type' => $entity_type,
|
||||
'type' => 'text',
|
||||
'cardinality' => 4,
|
||||
))->save();
|
||||
FieldConfig::create([
|
||||
'field_name' => $this->fieldName,
|
||||
'entity_type' => $entity_type,
|
||||
'bundle' => $entity_type,
|
||||
'translatable' => TRUE,
|
||||
])->save();
|
||||
|
||||
FieldStorageConfig::create(array(
|
||||
'field_name' => $this->untranslatableFieldName,
|
||||
'entity_type' => $entity_type,
|
||||
'type' => 'text',
|
||||
'cardinality' => 4,
|
||||
))->save();
|
||||
FieldConfig::create([
|
||||
'field_name' => $this->untranslatableFieldName,
|
||||
'entity_type' => $entity_type,
|
||||
'bundle' => $entity_type,
|
||||
'translatable' => FALSE,
|
||||
])->save();
|
||||
}
|
||||
|
||||
// Create the default languages.
|
||||
$this->installConfig(array('language'));
|
||||
|
||||
// Create test languages.
|
||||
$this->langcodes = array();
|
||||
for ($i = 0; $i < 3; ++$i) {
|
||||
$language = ConfigurableLanguage::create(array(
|
||||
'id' => 'l' . $i,
|
||||
'label' => $this->randomString(),
|
||||
'weight' => $i,
|
||||
));
|
||||
$this->langcodes[$i] = $language->getId();
|
||||
$language->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles field storage translatability.
|
||||
*
|
||||
* @param string $entity_type
|
||||
* The type of the entity fields are attached to.
|
||||
*/
|
||||
protected function toggleFieldTranslatability($entity_type, $bundle) {
|
||||
$fields = array($this->fieldName, $this->untranslatableFieldName);
|
||||
foreach ($fields as $field_name) {
|
||||
$field = FieldConfig::loadByName($entity_type, $bundle, $field_name);
|
||||
$translatable = !$field->isTranslatable();
|
||||
$field->set('translatable', $translatable);
|
||||
$field->save();
|
||||
$field = FieldConfig::loadByName($entity_type, $bundle, $field_name);
|
||||
$this->assertEqual($field->isTranslatable(), $translatable, 'Field translatability changed.');
|
||||
}
|
||||
\Drupal::cache('entity')->deleteAll();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,584 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Entity;
|
||||
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
|
||||
/**
|
||||
* Tests the Entity Query Aggregation API.
|
||||
*
|
||||
* @group Entity
|
||||
* @see \Drupal\entity_test\Entity\EntityTest
|
||||
*/
|
||||
class EntityQueryAggregateTest extends EntityKernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array();
|
||||
|
||||
/**
|
||||
* The entity_test storage to create the test entities.
|
||||
*
|
||||
* @var \Drupal\entity_test\EntityTestStorage
|
||||
*/
|
||||
protected $entityStorage;
|
||||
|
||||
/**
|
||||
* The actual query result, to compare later.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $queryResult;
|
||||
|
||||
/**
|
||||
* The query factory to create entity queries.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\Query\QueryFactory
|
||||
*/
|
||||
public $factory;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->entityStorage = $this->entityManager->getStorage('entity_test');
|
||||
$this->factory = $this->container->get('entity.query');
|
||||
|
||||
// Add some fieldapi fields to be used in the test.
|
||||
for ($i = 1; $i <= 2; $i++) {
|
||||
$field_name = 'field_test_' . $i;
|
||||
FieldStorageConfig::create(array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'integer',
|
||||
'cardinality' => 2,
|
||||
))->save();
|
||||
FieldConfig::create([
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'bundle' => 'entity_test',
|
||||
])->save();
|
||||
}
|
||||
|
||||
$entity = $this->entityStorage->create(array(
|
||||
'id' => 1,
|
||||
'user_id' => 1,
|
||||
'field_test_1' => 1,
|
||||
'field_test_2' => 2,
|
||||
));
|
||||
$entity->enforceIsNew();
|
||||
$entity->save();
|
||||
|
||||
$entity = $this->entityStorage->create(array(
|
||||
'id' => 2,
|
||||
'user_id' => 2,
|
||||
'field_test_1' => 1,
|
||||
'field_test_2' => 7,
|
||||
));
|
||||
$entity->enforceIsNew();
|
||||
$entity->save();
|
||||
$entity = $this->entityStorage->create(array(
|
||||
'id' => 3,
|
||||
'user_id' => 2,
|
||||
'field_test_1' => 2,
|
||||
'field_test_2' => 1,
|
||||
));
|
||||
$entity->enforceIsNew();
|
||||
$entity->save();
|
||||
$entity = $this->entityStorage->create(array(
|
||||
'id' => 4,
|
||||
'user_id' => 2,
|
||||
'field_test_1' => 2,
|
||||
'field_test_2' => 8,
|
||||
));
|
||||
$entity->enforceIsNew();
|
||||
$entity->save();
|
||||
$entity = $this->entityStorage->create(array(
|
||||
'id' => 5,
|
||||
'user_id' => 3,
|
||||
'field_test_1' => 2,
|
||||
'field_test_2' => 2,
|
||||
));
|
||||
$entity->enforceIsNew();
|
||||
$entity->save();
|
||||
$entity = $this->entityStorage->create(array(
|
||||
'id' => 6,
|
||||
'user_id' => 3,
|
||||
'field_test_1' => 3,
|
||||
'field_test_2' => 8,
|
||||
));
|
||||
$entity->enforceIsNew();
|
||||
$entity->save();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test aggregation support.
|
||||
*/
|
||||
public function testAggregation() {
|
||||
// Apply a simple groupby.
|
||||
$this->queryResult = $this->factory->getAggregate('entity_test')
|
||||
->groupBy('user_id')
|
||||
->execute();
|
||||
|
||||
$this->assertResults(array(
|
||||
array('user_id' => 1),
|
||||
array('user_id' => 2),
|
||||
array('user_id' => 3),
|
||||
));
|
||||
|
||||
$function_expected = array();
|
||||
$function_expected['count'] = array(array('id_count' => 6));
|
||||
$function_expected['min'] = array(array('id_min' => 1));
|
||||
$function_expected['max'] = array(array('id_max' => 6));
|
||||
$function_expected['sum'] = array(array('id_sum' => 21));
|
||||
$function_expected['avg'] = array(array('id_avg' => (21.0/6.0)));
|
||||
|
||||
// Apply a simple aggregation for different aggregation functions.
|
||||
foreach ($function_expected as $aggregation_function => $expected) {
|
||||
$this->queryResult = $this->factory->getAggregate('entity_test')
|
||||
->aggregate('id', $aggregation_function)
|
||||
->execute();
|
||||
$this->assertEqual($this->queryResult, $expected);
|
||||
}
|
||||
|
||||
// Apply aggregation and groupby on the same query.
|
||||
$this->queryResult = $this->factory->getAggregate('entity_test')
|
||||
->aggregate('id', 'COUNT')
|
||||
->groupBy('user_id')
|
||||
->execute();
|
||||
$this->assertResults(array(
|
||||
array('user_id' => 1, 'id_count' => 1),
|
||||
array('user_id' => 2, 'id_count' => 3),
|
||||
array('user_id' => 3, 'id_count' => 2),
|
||||
));
|
||||
|
||||
// Apply aggregation and a condition which matches.
|
||||
$this->queryResult = $this->factory->getAggregate('entity_test')
|
||||
->aggregate('id', 'COUNT')
|
||||
->groupBy('id')
|
||||
->conditionAggregate('id', 'COUNT', 8)
|
||||
->execute();
|
||||
$this->assertResults(array());
|
||||
|
||||
// Don't call aggregate to test the implicit aggregate call.
|
||||
$this->queryResult = $this->factory->getAggregate('entity_test')
|
||||
->groupBy('id')
|
||||
->conditionAggregate('id', 'COUNT', 8)
|
||||
->execute();
|
||||
$this->assertResults(array());
|
||||
|
||||
// Apply aggregation and a condition which matches.
|
||||
$this->queryResult = $this->factory->getAggregate('entity_test')
|
||||
->aggregate('id', 'count')
|
||||
->groupBy('id')
|
||||
->conditionAggregate('id', 'COUNT', 6)
|
||||
->execute();
|
||||
$this->assertResults(array(array('id_count' => 6)));
|
||||
|
||||
// Apply aggregation, a groupby and a condition which matches partially via
|
||||
// the operator '='.
|
||||
$this->queryResult = $this->factory->getAggregate('entity_test')
|
||||
->aggregate('id', 'count')
|
||||
->conditionAggregate('id', 'count', 2)
|
||||
->groupBy('user_id')
|
||||
->execute();
|
||||
$this->assertResults(array(array('id_count' => 2, 'user_id' => 3)));
|
||||
|
||||
// Apply aggregation, a groupby and a condition which matches partially via
|
||||
// the operator '>'.
|
||||
$this->queryResult = $this->factory->getAggregate('entity_test')
|
||||
->aggregate('id', 'count')
|
||||
->conditionAggregate('id', 'COUNT', 1, '>')
|
||||
->groupBy('user_id')
|
||||
->execute();
|
||||
$this->assertResults(array(
|
||||
array('id_count' => 2, 'user_id' => 3),
|
||||
array('id_count' => 3, 'user_id' => 2),
|
||||
));
|
||||
|
||||
// Apply aggregation and a sort. This might not be useful, but have a proper
|
||||
// test coverage.
|
||||
$this->queryResult = $this->factory->getAggregate('entity_test')
|
||||
->aggregate('id', 'COUNT')
|
||||
->sortAggregate('id', 'COUNT')
|
||||
->execute();
|
||||
$this->assertSortedResults(array(array('id_count' => 6)));
|
||||
|
||||
// Don't call aggregate to test the implicit aggregate call.
|
||||
$this->queryResult = $this->factory->getAggregate('entity_test')
|
||||
->sortAggregate('id', 'COUNT')
|
||||
->execute();
|
||||
$this->assertSortedResults(array(array('id_count' => 6)));
|
||||
|
||||
// Apply aggregation, groupby and a sort descending.
|
||||
$this->queryResult = $this->factory->getAggregate('entity_test')
|
||||
->aggregate('id', 'COUNT')
|
||||
->groupBy('user_id')
|
||||
->sortAggregate('id', 'COUNT', 'DESC')
|
||||
->execute();
|
||||
$this->assertSortedResults(array(
|
||||
array('user_id' => 2, 'id_count' => 3),
|
||||
array('user_id' => 3, 'id_count' => 2),
|
||||
array('user_id' => 1, 'id_count' => 1),
|
||||
));
|
||||
|
||||
// Apply aggregation, groupby and a sort ascending.
|
||||
$this->queryResult = $this->factory->getAggregate('entity_test')
|
||||
->aggregate('id', 'COUNT')
|
||||
->groupBy('user_id')
|
||||
->sortAggregate('id', 'COUNT', 'ASC')
|
||||
->execute();
|
||||
$this->assertSortedResults(array(
|
||||
array('user_id' => 1, 'id_count' => 1),
|
||||
array('user_id' => 3, 'id_count' => 2),
|
||||
array('user_id' => 2, 'id_count' => 3),
|
||||
));
|
||||
|
||||
// Apply aggregation, groupby, an aggregation condition and a sort with the
|
||||
// operator '='.
|
||||
$this->queryResult = $this->factory->getAggregate('entity_test')
|
||||
->aggregate('id', 'COUNT')
|
||||
->groupBy('user_id')
|
||||
->sortAggregate('id', 'COUNT')
|
||||
->conditionAggregate('id', 'COUNT', 2)
|
||||
->execute();
|
||||
$this->assertSortedResults(array(array('id_count' => 2, 'user_id' => 3)));
|
||||
|
||||
// Apply aggregation, groupby, an aggregation condition and a sort with the
|
||||
// operator '<' and order ASC.
|
||||
$this->queryResult = $this->factory->getAggregate('entity_test')
|
||||
->aggregate('id', 'COUNT')
|
||||
->groupBy('user_id')
|
||||
->sortAggregate('id', 'COUNT', 'ASC')
|
||||
->conditionAggregate('id', 'COUNT', 3, '<')
|
||||
->execute();
|
||||
$this->assertSortedResults(array(
|
||||
array('id_count' => 1, 'user_id' => 1),
|
||||
array('id_count' => 2, 'user_id' => 3),
|
||||
));
|
||||
|
||||
// Apply aggregation, groupby, an aggregation condition and a sort with the
|
||||
// operator '<' and order DESC.
|
||||
$this->queryResult = $this->factory->getAggregate('entity_test')
|
||||
->aggregate('id', 'COUNT')
|
||||
->groupBy('user_id')
|
||||
->sortAggregate('id', 'COUNT', 'DESC')
|
||||
->conditionAggregate('id', 'COUNT', 3, '<')
|
||||
->execute();
|
||||
$this->assertSortedResults(array(
|
||||
array('id_count' => 2, 'user_id' => 3),
|
||||
array('id_count' => 1, 'user_id' => 1),
|
||||
));
|
||||
|
||||
// Test aggregation/groupby support for fieldapi fields.
|
||||
|
||||
// Just group by a fieldapi field.
|
||||
$this->queryResult = $this->factory->getAggregate('entity_test')
|
||||
->groupBy('field_test_1')
|
||||
->execute();
|
||||
$this->assertResults(array(
|
||||
array('field_test_1' => 1),
|
||||
array('field_test_1' => 2),
|
||||
array('field_test_1' => 3),
|
||||
));
|
||||
|
||||
// Group by a fieldapi field and aggregate a normal property.
|
||||
$this->queryResult = $this->factory->getAggregate('entity_test')
|
||||
->aggregate('user_id', 'COUNT')
|
||||
->groupBy('field_test_1')
|
||||
->execute();
|
||||
|
||||
$this->assertResults(array(
|
||||
array('field_test_1' => 1, 'user_id_count' => 2),
|
||||
array('field_test_1' => 2, 'user_id_count' => 3),
|
||||
array('field_test_1' => 3, 'user_id_count' => 1),
|
||||
));
|
||||
|
||||
// Group by a normal property and aggregate a fieldapi field.
|
||||
$this->queryResult = $this->factory->getAggregate('entity_test')
|
||||
->aggregate('field_test_1', 'COUNT')
|
||||
->groupBy('user_id')
|
||||
->execute();
|
||||
|
||||
$this->assertResults(array(
|
||||
array('user_id' => 1, 'field_test_1_count' => 1),
|
||||
array('user_id' => 2, 'field_test_1_count' => 3),
|
||||
array('user_id' => 3, 'field_test_1_count' => 2),
|
||||
));
|
||||
|
||||
$this->queryResult = $this->factory->getAggregate('entity_test')
|
||||
->aggregate('field_test_1', 'SUM')
|
||||
->groupBy('user_id')
|
||||
->execute();
|
||||
$this->assertResults(array(
|
||||
array('user_id' => 1, 'field_test_1_sum' => 1),
|
||||
array('user_id' => 2, 'field_test_1_sum' => 5),
|
||||
array('user_id' => 3, 'field_test_1_sum' => 5),
|
||||
));
|
||||
|
||||
// Aggregate by two different fieldapi fields.
|
||||
$this->queryResult = $this->factory->getAggregate('entity_test')
|
||||
->aggregate('field_test_1', 'SUM')
|
||||
->aggregate('field_test_2', 'SUM')
|
||||
->groupBy('user_id')
|
||||
->execute();
|
||||
$this->assertResults(array(
|
||||
array('user_id' => 1, 'field_test_1_sum' => 1, 'field_test_2_sum' => 2),
|
||||
array('user_id' => 2, 'field_test_1_sum' => 5, 'field_test_2_sum' => 16),
|
||||
array('user_id' => 3, 'field_test_1_sum' => 5, 'field_test_2_sum' => 10),
|
||||
));
|
||||
|
||||
// This time aggregate the same field twice.
|
||||
$this->queryResult = $this->factory->getAggregate('entity_test')
|
||||
->aggregate('field_test_1', 'SUM')
|
||||
->aggregate('field_test_1', 'COUNT')
|
||||
->groupBy('user_id')
|
||||
->execute();
|
||||
$this->assertResults(array(
|
||||
array('user_id' => 1, 'field_test_1_sum' => 1, 'field_test_1_count' => 1),
|
||||
array('user_id' => 2, 'field_test_1_sum' => 5, 'field_test_1_count' => 3),
|
||||
array('user_id' => 3, 'field_test_1_sum' => 5, 'field_test_1_count' => 2),
|
||||
));
|
||||
|
||||
// Group by and aggregate by a fieldapi field.
|
||||
$this->queryResult = $this->factory->getAggregate('entity_test')
|
||||
->groupBy('field_test_1')
|
||||
->aggregate('field_test_2', 'COUNT')
|
||||
->execute();
|
||||
$this->assertResults(array(
|
||||
array('field_test_1' => 1, 'field_test_2_count' => 2),
|
||||
array('field_test_1' => 2, 'field_test_2_count' => 3),
|
||||
array('field_test_1' => 3, 'field_test_2_count' => 1),
|
||||
));
|
||||
|
||||
// Group by and aggregate by a fieldapi field and use multiple aggregate
|
||||
// functions.
|
||||
$this->queryResult = $this->factory->getAggregate('entity_test')
|
||||
->groupBy('field_test_1')
|
||||
->aggregate('field_test_2', 'COUNT')
|
||||
->aggregate('field_test_2', 'SUM')
|
||||
->execute();
|
||||
$this->assertResults(array(
|
||||
array('field_test_1' => 1, 'field_test_2_count' => 2, 'field_test_2_sum' => 9),
|
||||
array('field_test_1' => 2, 'field_test_2_count' => 3, 'field_test_2_sum' => 11),
|
||||
array('field_test_1' => 3, 'field_test_2_count' => 1, 'field_test_2_sum' => 8),
|
||||
));
|
||||
|
||||
// Apply an aggregate condition for a fieldapi field and group by a simple
|
||||
// property.
|
||||
$this->queryResult = $this->factory->getAggregate('entity_test')
|
||||
->conditionAggregate('field_test_1', 'COUNT', 3)
|
||||
->groupBy('user_id')
|
||||
->execute();
|
||||
$this->assertResults(array(
|
||||
array('user_id' => 2, 'field_test_1_count' => 3),
|
||||
array('user_id' => 3, 'field_test_1_count' => 2),
|
||||
));
|
||||
|
||||
$this->queryResult = $this->factory->getAggregate('entity_test')
|
||||
->aggregate('field_test_1', 'SUM')
|
||||
->conditionAggregate('field_test_1', 'COUNT', 2, '>')
|
||||
->groupBy('user_id')
|
||||
->execute();
|
||||
$this->assertResults(array(
|
||||
array('user_id' => 2, 'field_test_1_sum' => 5, 'field_test_1_count' => 3),
|
||||
array('user_id' => 3, 'field_test_1_sum' => 5, 'field_test_1_count' => 2),
|
||||
));
|
||||
|
||||
// Apply an aggregate condition for a simple property and a group by a
|
||||
// fieldapi field.
|
||||
$this->queryResult = $this->factory->getAggregate('entity_test')
|
||||
->conditionAggregate('user_id', 'COUNT', 2)
|
||||
->groupBy('field_test_1')
|
||||
->execute();
|
||||
$this->assertResults(array(
|
||||
array('field_test_1' => 1, 'user_id_count' => 2),
|
||||
));
|
||||
|
||||
$this->queryResult = $this->factory->getAggregate('entity_test')
|
||||
->conditionAggregate('user_id', 'COUNT', 2, '>')
|
||||
->groupBy('field_test_1')
|
||||
->execute();
|
||||
$this->assertResults(array(
|
||||
array('field_test_1' => 1, 'user_id_count' => 2),
|
||||
array('field_test_1' => 2, 'user_id_count' => 3),
|
||||
));
|
||||
|
||||
// Apply an aggregate condition and a group by fieldapi fields.
|
||||
$this->queryResult = $this->factory->getAggregate('entity_test')
|
||||
->groupBy('field_test_1')
|
||||
->conditionAggregate('field_test_2', 'COUNT', 2)
|
||||
->execute();
|
||||
$this->assertResults(array(
|
||||
array('field_test_1' => 1, 'field_test_2_count' => 2),
|
||||
));
|
||||
$this->queryResult = $this->factory->getAggregate('entity_test')
|
||||
->groupBy('field_test_1')
|
||||
->conditionAggregate('field_test_2', 'COUNT', 2, '>')
|
||||
->execute();
|
||||
$this->assertResults(array(
|
||||
array('field_test_1' => 1, 'field_test_2_count' => 2),
|
||||
array('field_test_1' => 2, 'field_test_2_count' => 3),
|
||||
));
|
||||
|
||||
// Apply an aggregate condition and a group by fieldapi fields with multiple
|
||||
// conditions via AND.
|
||||
$this->queryResult = $this->factory->getAggregate('entity_test')
|
||||
->groupBy('field_test_1')
|
||||
->conditionAggregate('field_test_2', 'COUNT', 2)
|
||||
->conditionAggregate('field_test_2', 'SUM', 8)
|
||||
->execute();
|
||||
$this->assertResults(array());
|
||||
|
||||
// Apply an aggregate condition and a group by fieldapi fields with multiple
|
||||
// conditions via OR.
|
||||
$this->queryResult = $this->factory->getAggregate('entity_test', 'OR')
|
||||
->groupBy('field_test_1')
|
||||
->conditionAggregate('field_test_2', 'COUNT', 2)
|
||||
->conditionAggregate('field_test_2', 'SUM', 8)
|
||||
->execute();
|
||||
$this->assertResults(array(
|
||||
array('field_test_1' => 1, 'field_test_2_count' => 2, 'field_test_2_sum' => 9),
|
||||
array('field_test_1' => 3, 'field_test_2_count' => 1, 'field_test_2_sum' => 8),
|
||||
));
|
||||
|
||||
// Group by a normal property and aggregate a fieldapi field and sort by the
|
||||
// groupby field.
|
||||
$this->queryResult = $this->factory->getAggregate('entity_test')
|
||||
->aggregate('field_test_1', 'COUNT')
|
||||
->groupBy('user_id')
|
||||
->sort('user_id', 'DESC')
|
||||
->execute();
|
||||
$this->assertSortedResults(array(
|
||||
array('user_id' => 3, 'field_test_1_count' => 2),
|
||||
array('user_id' => 2, 'field_test_1_count' => 3),
|
||||
array('user_id' => 1, 'field_test_1_count' => 1),
|
||||
));
|
||||
|
||||
$this->queryResult = $this->factory->getAggregate('entity_test')
|
||||
->aggregate('field_test_1', 'COUNT')
|
||||
->groupBy('user_id')
|
||||
->sort('user_id', 'ASC')
|
||||
->execute();
|
||||
$this->assertSortedResults(array(
|
||||
array('user_id' => 1, 'field_test_1_count' => 1),
|
||||
array('user_id' => 2, 'field_test_1_count' => 3),
|
||||
array('user_id' => 3, 'field_test_1_count' => 2),
|
||||
));
|
||||
|
||||
$this->queryResult = $this->factory->getAggregate('entity_test')
|
||||
->conditionAggregate('field_test_1', 'COUNT', 2, '>')
|
||||
->groupBy('user_id')
|
||||
->sort('user_id', 'ASC')
|
||||
->execute();
|
||||
$this->assertSortedResults(array(
|
||||
array('user_id' => 2, 'field_test_1_count' => 3),
|
||||
array('user_id' => 3, 'field_test_1_count' => 2),
|
||||
));
|
||||
|
||||
// Group by a normal property, aggregate a fieldapi field, and sort by the
|
||||
// aggregated field.
|
||||
$this->queryResult = $this->factory->getAggregate('entity_test')
|
||||
->sortAggregate('field_test_1', 'COUNT', 'DESC')
|
||||
->groupBy('user_id')
|
||||
->execute();
|
||||
$this->assertSortedResults(array(
|
||||
array('user_id' => 2, 'field_test_1_count' => 3),
|
||||
array('user_id' => 3, 'field_test_1_count' => 2),
|
||||
array('user_id' => 1, 'field_test_1_count' => 1),
|
||||
));
|
||||
|
||||
$this->queryResult = $this->factory->getAggregate('entity_test')
|
||||
->sortAggregate('field_test_1', 'COUNT', 'ASC')
|
||||
->groupBy('user_id')
|
||||
->execute();
|
||||
$this->assertSortedResults(array(
|
||||
array('user_id' => 1, 'field_test_1_count' => 1),
|
||||
array('user_id' => 3, 'field_test_1_count' => 2),
|
||||
array('user_id' => 2, 'field_test_1_count' => 3),
|
||||
));
|
||||
|
||||
// Group by and aggregate by fieldapi field, and sort by the groupby field.
|
||||
$this->queryResult = $this->factory->getAggregate('entity_test')
|
||||
->groupBy('field_test_1')
|
||||
->aggregate('field_test_2', 'COUNT')
|
||||
->sort('field_test_1', 'ASC')
|
||||
->execute();
|
||||
$this->assertSortedResults(array(
|
||||
array('field_test_1' => 1, 'field_test_2_count' => 2),
|
||||
array('field_test_1' => 2, 'field_test_2_count' => 3),
|
||||
array('field_test_1' => 3, 'field_test_2_count' => 1),
|
||||
));
|
||||
|
||||
$this->queryResult = $this->factory->getAggregate('entity_test')
|
||||
->groupBy('field_test_1')
|
||||
->aggregate('field_test_2', 'COUNT')
|
||||
->sort('field_test_1', 'DESC')
|
||||
->execute();
|
||||
$this->assertSortedResults(array(
|
||||
array('field_test_1' => 3, 'field_test_2_count' => 1),
|
||||
array('field_test_1' => 2, 'field_test_2_count' => 3),
|
||||
array('field_test_1' => 1, 'field_test_2_count' => 2),
|
||||
));
|
||||
|
||||
// Groupby and aggregate by fieldapi field, and sort by the aggregated
|
||||
// field.
|
||||
$this->queryResult = $this->factory->getAggregate('entity_test')
|
||||
->groupBy('field_test_1')
|
||||
->sortAggregate('field_test_2', 'COUNT', 'DESC')
|
||||
->execute();
|
||||
$this->assertSortedResults(array(
|
||||
array('field_test_1' => 2, 'field_test_2_count' => 3),
|
||||
array('field_test_1' => 1, 'field_test_2_count' => 2),
|
||||
array('field_test_1' => 3, 'field_test_2_count' => 1),
|
||||
));
|
||||
|
||||
$this->queryResult = $this->factory->getAggregate('entity_test')
|
||||
->groupBy('field_test_1')
|
||||
->sortAggregate('field_test_2', 'COUNT', 'ASC')
|
||||
->execute();
|
||||
$this->assertSortedResults(array(
|
||||
array('field_test_1' => 3, 'field_test_2_count' => 1),
|
||||
array('field_test_1' => 1, 'field_test_2_count' => 2),
|
||||
array('field_test_1' => 2, 'field_test_2_count' => 3),
|
||||
));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts the results as expected regardless of order between and in rows.
|
||||
*
|
||||
* @param array $expected
|
||||
* An array of the expected results.
|
||||
*/
|
||||
protected function assertResults($expected, $sorted = FALSE) {
|
||||
$found = TRUE;
|
||||
$expected_keys = array_keys($expected);
|
||||
foreach ($this->queryResult as $key => $row) {
|
||||
$keys = $sorted ? array($key) : $expected_keys;
|
||||
foreach ($keys as $key) {
|
||||
$expected_row = $expected[$key];
|
||||
if (!array_diff_assoc($row, $expected_row) && !array_diff_assoc($expected_row, $row)) {
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
$found = FALSE;
|
||||
break;
|
||||
}
|
||||
return $this->assertTrue($found, strtr('!expected expected, !found found', array('!expected' => print_r($expected, TRUE), '!found' => print_r($this->queryResult, TRUE))));
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts the results as expected regardless of order in rows.
|
||||
*
|
||||
* @param array $expected
|
||||
* An array of the expected results.
|
||||
*/
|
||||
protected function assertSortedResults($expected) {
|
||||
return $this->assertResults($expected, TRUE);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,172 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Entity;
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\entity_test\Entity\EntityTest;
|
||||
use Drupal\field\Tests\EntityReference\EntityReferenceTestTrait;
|
||||
use Drupal\taxonomy\Entity\Vocabulary;
|
||||
use Drupal\taxonomy\Entity\Term;
|
||||
|
||||
/**
|
||||
* Tests the Entity Query relationship API.
|
||||
*
|
||||
* @group Entity
|
||||
*/
|
||||
class EntityQueryRelationshipTest extends EntityKernelTestBase {
|
||||
|
||||
use EntityReferenceTestTrait;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('taxonomy');
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Entity\Query\QueryFactory
|
||||
*/
|
||||
protected $factory;
|
||||
|
||||
/**
|
||||
* Term entities.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $terms;
|
||||
|
||||
/**
|
||||
* User entities.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $accounts;
|
||||
|
||||
/**
|
||||
* entity_test entities.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $entities;
|
||||
|
||||
/**
|
||||
* The name of the taxonomy field used for test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $fieldName;
|
||||
|
||||
/**
|
||||
* The results returned by EntityQuery.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $queryResults;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('taxonomy_term');
|
||||
|
||||
// We want an entity reference field. It needs a vocabulary, terms, a field
|
||||
// storage and a field. First, create the vocabulary.
|
||||
$vocabulary = Vocabulary::create([
|
||||
'vid' => Unicode::strtolower($this->randomMachineName()),
|
||||
]);
|
||||
$vocabulary->save();
|
||||
|
||||
// Second, create the field.
|
||||
entity_test_create_bundle('test_bundle');
|
||||
$this->fieldName = strtolower($this->randomMachineName());
|
||||
$handler_settings = array(
|
||||
'target_bundles' => array(
|
||||
$vocabulary->id() => $vocabulary->id(),
|
||||
),
|
||||
'auto_create' => TRUE,
|
||||
);
|
||||
$this->createEntityReferenceField('entity_test', 'test_bundle', $this->fieldName, NULL, 'taxonomy_term', 'default', $handler_settings);
|
||||
|
||||
// Create two terms and also two accounts.
|
||||
for ($i = 0; $i <= 1; $i++) {
|
||||
$term = Term::create([
|
||||
'name' => $this->randomMachineName(),
|
||||
'vid' => $vocabulary->id(),
|
||||
]);
|
||||
$term->save();
|
||||
$this->terms[] = $term;
|
||||
$this->accounts[] = $this->createUser();
|
||||
}
|
||||
// Create three entity_test entities, the 0th entity will point to the
|
||||
// 0th account and 0th term, the 1st and 2nd entity will point to the
|
||||
// 1st account and 1st term.
|
||||
for ($i = 0; $i <= 2; $i++) {
|
||||
$entity = EntityTest::create(array('type' => 'test_bundle'));
|
||||
$entity->name->value = $this->randomMachineName();
|
||||
$index = $i ? 1 : 0;
|
||||
$entity->user_id->target_id = $this->accounts[$index]->id();
|
||||
$entity->{$this->fieldName}->target_id = $this->terms[$index]->id();
|
||||
$entity->save();
|
||||
$this->entities[] = $entity;
|
||||
}
|
||||
$this->factory = \Drupal::service('entity.query');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests querying.
|
||||
*/
|
||||
public function testQuery() {
|
||||
// This returns the 0th entity as that's only one pointing to the 0th
|
||||
// account.
|
||||
$this->queryResults = $this->factory->get('entity_test')
|
||||
->condition("user_id.entity.name", $this->accounts[0]->getUsername())
|
||||
->execute();
|
||||
$this->assertResults(array(0));
|
||||
// This returns the 1st and 2nd entity as those point to the 1st account.
|
||||
$this->queryResults = $this->factory->get('entity_test')
|
||||
->condition("user_id.entity.name", $this->accounts[0]->getUsername(), '<>')
|
||||
->execute();
|
||||
$this->assertResults(array(1, 2));
|
||||
// This returns all three entities because all of them point to an
|
||||
// account.
|
||||
$this->queryResults = $this->factory->get('entity_test')
|
||||
->exists("user_id.entity.name")
|
||||
->execute();
|
||||
$this->assertResults(array(0, 1, 2));
|
||||
// This returns no entities because all of them point to an account.
|
||||
$this->queryResults = $this->factory->get('entity_test')
|
||||
->notExists("user_id.entity.name")
|
||||
->execute();
|
||||
$this->assertEqual(count($this->queryResults), 0);
|
||||
// This returns the 0th entity as that's only one pointing to the 0th
|
||||
// term (test without specifying the field column).
|
||||
$this->queryResults = $this->factory->get('entity_test')
|
||||
->condition("$this->fieldName.entity.name", $this->terms[0]->name->value)
|
||||
->execute();
|
||||
$this->assertResults(array(0));
|
||||
// This returns the 0th entity as that's only one pointing to the 0th
|
||||
// term (test with specifying the column name).
|
||||
$this->queryResults = $this->factory->get('entity_test')
|
||||
->condition("$this->fieldName.target_id.entity.name", $this->terms[0]->name->value)
|
||||
->execute();
|
||||
$this->assertResults(array(0));
|
||||
// This returns the 1st and 2nd entity as those point to the 1st term.
|
||||
$this->queryResults = $this->factory->get('entity_test')
|
||||
->condition("$this->fieldName.entity.name", $this->terms[0]->name->value, '<>')
|
||||
->execute();
|
||||
$this->assertResults(array(1, 2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert the results.
|
||||
*
|
||||
* @param array $expected
|
||||
* A list of indexes in the $this->entities array.
|
||||
*/
|
||||
protected function assertResults($expected) {
|
||||
$this->assertEqual(count($this->queryResults), count($expected));
|
||||
foreach ($expected as $key) {
|
||||
$id = $this->entities[$key]->id();
|
||||
$this->assertEqual($this->queryResults[$id], $id);
|
||||
}
|
||||
}
|
||||
}
|
||||
865
core/tests/Drupal/KernelTests/Core/Entity/EntityQueryTest.php
Normal file
865
core/tests/Drupal/KernelTests/Core/Entity/EntityQueryTest.php
Normal file
|
|
@ -0,0 +1,865 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Entity;
|
||||
|
||||
use Drupal\Component\Utility\Unicode;
|
||||
use Drupal\entity_test\Entity\EntityTest;
|
||||
use Drupal\entity_test\Entity\EntityTestMulRev;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\taxonomy\Entity\Term;
|
||||
use Drupal\taxonomy\Entity\Vocabulary;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Tests Entity Query functionality.
|
||||
*
|
||||
* @group Entity
|
||||
*/
|
||||
class EntityQueryTest extends EntityKernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('field_test', 'language');
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $queryResults;
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Entity\Query\QueryFactory
|
||||
*/
|
||||
protected $factory;
|
||||
|
||||
/**
|
||||
* A list of bundle machine names created for this test.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $bundles;
|
||||
|
||||
/**
|
||||
* Field name for the greetings field.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $greetings;
|
||||
|
||||
/**
|
||||
* Field name for the figures field.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $figures;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('entity_test_mulrev');
|
||||
|
||||
$this->installConfig(array('language'));
|
||||
|
||||
$figures = Unicode::strtolower($this->randomMachineName());
|
||||
$greetings = Unicode::strtolower($this->randomMachineName());
|
||||
foreach (array($figures => 'shape', $greetings => 'text') as $field_name => $field_type) {
|
||||
$field_storage = FieldStorageConfig::create(array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'entity_test_mulrev',
|
||||
'type' => $field_type,
|
||||
'cardinality' => 2,
|
||||
));
|
||||
$field_storage->save();
|
||||
$field_storages[] = $field_storage;
|
||||
}
|
||||
$bundles = array();
|
||||
for ($i = 0; $i < 2; $i++) {
|
||||
// For the sake of tablesort, make sure the second bundle is higher than
|
||||
// the first one. Beware: MySQL is not case sensitive.
|
||||
do {
|
||||
$bundle = $this->randomMachineName();
|
||||
} while ($bundles && strtolower($bundles[0]) >= strtolower($bundle));
|
||||
entity_test_create_bundle($bundle);
|
||||
foreach ($field_storages as $field_storage) {
|
||||
FieldConfig::create([
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => $bundle,
|
||||
])->save();
|
||||
}
|
||||
$bundles[] = $bundle;
|
||||
}
|
||||
// Each unit is a list of field name, langcode and a column-value array.
|
||||
$units[] = array($figures, 'en', array(
|
||||
'color' => 'red',
|
||||
'shape' => 'triangle',
|
||||
));
|
||||
$units[] = array($figures, 'en', array(
|
||||
'color' => 'blue',
|
||||
'shape' => 'circle',
|
||||
));
|
||||
// To make it easier to test sorting, the greetings get formats according
|
||||
// to their langcode.
|
||||
$units[] = array($greetings, 'tr', array(
|
||||
'value' => 'merhaba',
|
||||
'format' => 'format-tr'
|
||||
));
|
||||
$units[] = array($greetings, 'pl', array(
|
||||
'value' => 'siema',
|
||||
'format' => 'format-pl'
|
||||
));
|
||||
// Make these languages available to the greetings field.
|
||||
ConfigurableLanguage::createFromLangcode('tr')->save();
|
||||
ConfigurableLanguage::createFromLangcode('pl')->save();
|
||||
// Calculate the cartesian product of the unit array by looking at the
|
||||
// bits of $i and add the unit at the bits that are 1. For example,
|
||||
// decimal 13 is binary 1101 so unit 3,2 and 0 will be added to the
|
||||
// entity.
|
||||
for ($i = 1; $i <= 15; $i++) {
|
||||
$entity = EntityTestMulRev::create(array(
|
||||
'type' => $bundles[$i & 1],
|
||||
'name' => $this->randomMachineName(),
|
||||
'langcode' => 'en',
|
||||
));
|
||||
// Make sure the name is set for every language that we might create.
|
||||
foreach (array('tr', 'pl') as $langcode) {
|
||||
$entity->addTranslation($langcode)->name = $this->randomMachineName();
|
||||
}
|
||||
foreach (array_reverse(str_split(decbin($i))) as $key => $bit) {
|
||||
if ($bit) {
|
||||
list($field_name, $langcode, $values) = $units[$key];
|
||||
$entity->getTranslation($langcode)->{$field_name}[] = $values;
|
||||
}
|
||||
}
|
||||
$entity->save();
|
||||
}
|
||||
$this->bundles = $bundles;
|
||||
$this->figures = $figures;
|
||||
$this->greetings = $greetings;
|
||||
$this->factory = \Drupal::service('entity.query');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test basic functionality.
|
||||
*/
|
||||
function testEntityQuery() {
|
||||
$greetings = $this->greetings;
|
||||
$figures = $this->figures;
|
||||
$this->queryResults = $this->factory->get('entity_test_mulrev')
|
||||
->exists($greetings, 'tr')
|
||||
->condition("$figures.color", 'red')
|
||||
->sort('id')
|
||||
->execute();
|
||||
// As unit 0 was the red triangle and unit 2 was the turkish greeting,
|
||||
// bit 0 and bit 2 needs to be set.
|
||||
$this->assertResult(5, 7, 13, 15);
|
||||
|
||||
$query = $this->factory->get('entity_test_mulrev', 'OR')
|
||||
->exists($greetings, 'tr')
|
||||
->condition("$figures.color", 'red')
|
||||
->sort('id');
|
||||
$count_query = clone $query;
|
||||
$this->assertEqual(12, $count_query->count()->execute());
|
||||
$this->queryResults = $query->execute();
|
||||
// Now bit 0 (1, 3, 5, 7, 9, 11, 13, 15) or bit 2 (4, 5, 6, 7, 12, 13, 14,
|
||||
// 15) needs to be set.
|
||||
$this->assertResult(1, 3, 4, 5, 6, 7, 9, 11, 12, 13, 14, 15);
|
||||
|
||||
// Test cloning of query conditions.
|
||||
$query = $this->factory->get('entity_test_mulrev')
|
||||
->condition("$figures.color", 'red')
|
||||
->sort('id');
|
||||
$cloned_query = clone $query;
|
||||
$cloned_query
|
||||
->condition("$figures.shape", 'circle');
|
||||
// Bit 0 (1, 3, 5, 7, 9, 11, 13, 15) needs to be set.
|
||||
$this->queryResults = $query->execute();
|
||||
$this->assertResult(1, 3, 5, 7, 9, 11, 13, 15);
|
||||
// No red color has a circle shape.
|
||||
$this->queryResults = $cloned_query->execute();
|
||||
$this->assertResult();
|
||||
|
||||
$query = $this->factory->get('entity_test_mulrev');
|
||||
$group = $query->orConditionGroup()
|
||||
->exists($greetings, 'tr')
|
||||
->condition("$figures.color", 'red');
|
||||
$this->queryResults = $query
|
||||
->condition($group)
|
||||
->condition("$greetings.value", 'sie', 'STARTS_WITH')
|
||||
->sort('revision_id')
|
||||
->execute();
|
||||
// Bit 3 and (bit 0 or 2) -- the above 8 part of the above.
|
||||
$this->assertResult(9, 11, 12, 13, 14, 15);
|
||||
|
||||
// No figure has both the colors blue and red at the same time.
|
||||
$this->queryResults = $this->factory->get('entity_test_mulrev')
|
||||
->condition("$figures.color", 'blue')
|
||||
->condition("$figures.color", 'red')
|
||||
->sort('id')
|
||||
->execute();
|
||||
$this->assertResult();
|
||||
|
||||
// But an entity might have a red and a blue figure both.
|
||||
$query = $this->factory->get('entity_test_mulrev');
|
||||
$group_blue = $query->andConditionGroup()->condition("$figures.color", 'blue');
|
||||
$group_red = $query->andConditionGroup()->condition("$figures.color", 'red');
|
||||
$this->queryResults = $query
|
||||
->condition($group_blue)
|
||||
->condition($group_red)
|
||||
->sort('revision_id')
|
||||
->execute();
|
||||
// Unit 0 and unit 1, so bits 0 1.
|
||||
$this->assertResult(3, 7, 11, 15);
|
||||
|
||||
// Do the same test but with IN operator.
|
||||
$query = $this->factory->get('entity_test_mulrev');
|
||||
$group_blue = $query->andConditionGroup()->condition("$figures.color", array('blue'), 'IN');
|
||||
$group_red = $query->andConditionGroup()->condition("$figures.color", array('red'), 'IN');
|
||||
$this->queryResults = $query
|
||||
->condition($group_blue)
|
||||
->condition($group_red)
|
||||
->sort('id')
|
||||
->execute();
|
||||
// Unit 0 and unit 1, so bits 0 1.
|
||||
$this->assertResult(3, 7, 11, 15);
|
||||
|
||||
// An entity might have either red or blue figure.
|
||||
$this->queryResults = $this->factory->get('entity_test_mulrev')
|
||||
->condition("$figures.color", array('blue', 'red'), 'IN')
|
||||
->sort('id')
|
||||
->execute();
|
||||
// Bit 0 or 1 is on.
|
||||
$this->assertResult(1, 2, 3, 5, 6, 7, 9, 10, 11, 13, 14, 15);
|
||||
|
||||
$this->queryResults = $this->factory->get('entity_test_mulrev')
|
||||
->exists("$figures.color")
|
||||
->notExists("$greetings.value")
|
||||
->sort('id')
|
||||
->execute();
|
||||
// Bit 0 or 1 is on but 2 and 3 are not.
|
||||
$this->assertResult(1, 2, 3);
|
||||
// Now update the 'merhaba' string to xsiemax which is not a meaningful
|
||||
// word but allows us to test revisions and string operations.
|
||||
$ids = $this->factory->get('entity_test_mulrev')
|
||||
->condition("$greetings.value", 'merhaba')
|
||||
->sort('id')
|
||||
->execute();
|
||||
$entities = entity_load_multiple('entity_test_mulrev', $ids);
|
||||
$first_entity = reset($entities);
|
||||
$old_name = $first_entity->name->value;
|
||||
foreach ($entities as $entity) {
|
||||
$entity->setNewRevision();
|
||||
$entity->getTranslation('tr')->$greetings->value = 'xsiemax';
|
||||
$entity->name->value .= 'x';
|
||||
$entity->save();
|
||||
}
|
||||
// We changed the entity names, so the current revision should not match.
|
||||
$this->queryResults = $this->factory->get('entity_test_mulrev')
|
||||
->condition('name.value', $old_name)
|
||||
->execute();
|
||||
$this->assertResult();
|
||||
// Only if all revisions are queried, we find the old revision.
|
||||
$this->queryResults = $this->factory->get('entity_test_mulrev')
|
||||
->condition('name.value', $old_name)
|
||||
->allRevisions()
|
||||
->sort('revision_id')
|
||||
->execute();
|
||||
$this->assertRevisionResult(array($first_entity->id()), array($first_entity->id()));
|
||||
// When querying current revisions, this string is no longer found.
|
||||
$this->queryResults = $this->factory->get('entity_test_mulrev')
|
||||
->condition("$greetings.value", 'merhaba')
|
||||
->execute();
|
||||
$this->assertResult();
|
||||
$this->queryResults = $this->factory->get('entity_test_mulrev')
|
||||
->condition("$greetings.value", 'merhaba')
|
||||
->allRevisions()
|
||||
->sort('revision_id')
|
||||
->execute();
|
||||
// The query only matches the original revisions.
|
||||
$this->assertRevisionResult(array(4, 5, 6, 7, 12, 13, 14, 15), array(4, 5, 6, 7, 12, 13, 14, 15));
|
||||
$results = $this->factory->get('entity_test_mulrev')
|
||||
->condition("$greetings.value", 'siema', 'CONTAINS')
|
||||
->sort('id')
|
||||
->execute();
|
||||
// This matches both the original and new current revisions, multiple
|
||||
// revisions are returned for some entities.
|
||||
$assert = array(16 => '4', 17 => '5', 18 => '6', 19 => '7', 8 => '8', 9 => '9', 10 => '10', 11 => '11', 20 => '12', 21 => '13', 22 => '14', 23 => '15');
|
||||
$this->assertIdentical($results, $assert);
|
||||
$results = $this->factory->get('entity_test_mulrev')
|
||||
->condition("$greetings.value", 'siema', 'STARTS_WITH')
|
||||
->sort('revision_id')
|
||||
->execute();
|
||||
// Now we only get the ones that originally were siema, entity id 8 and
|
||||
// above.
|
||||
$this->assertIdentical($results, array_slice($assert, 4, 8, TRUE));
|
||||
$results = $this->factory->get('entity_test_mulrev')
|
||||
->condition("$greetings.value", 'a', 'ENDS_WITH')
|
||||
->sort('revision_id')
|
||||
->execute();
|
||||
// It is very important that we do not get the ones which only have
|
||||
// xsiemax despite originally they were merhaba, ie. ended with a.
|
||||
$this->assertIdentical($results, array_slice($assert, 4, 8, TRUE));
|
||||
$results = $this->factory->get('entity_test_mulrev')
|
||||
->condition("$greetings.value", 'a', 'ENDS_WITH')
|
||||
->allRevisions()
|
||||
->sort('id')
|
||||
->sort('revision_id')
|
||||
->execute();
|
||||
// Now we get everything.
|
||||
$assert = array(4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', 10 => '10', 11 => '11', 12 => '12', 20 => '12', 13 => '13', 21 => '13', 14 => '14', 22 => '14', 15 => '15', 23 => '15');
|
||||
$this->assertIdentical($results, $assert);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test sort().
|
||||
*
|
||||
* Warning: this is complicated.
|
||||
*/
|
||||
function testSort() {
|
||||
$greetings = $this->greetings;
|
||||
$figures = $this->figures;
|
||||
// Order up and down on a number.
|
||||
$this->queryResults = $this->factory->get('entity_test_mulrev')
|
||||
->sort('id')
|
||||
->execute();
|
||||
$this->assertResult(range(1, 15));
|
||||
$this->queryResults = $this->factory->get('entity_test_mulrev')
|
||||
->sort('id', 'DESC')
|
||||
->execute();
|
||||
$this->assertResult(range(15, 1));
|
||||
$query = $this->factory->get('entity_test_mulrev')
|
||||
->sort("$figures.color")
|
||||
->sort("$greetings.format")
|
||||
->sort('id');
|
||||
// As we do not have any conditions, here are the possible colors and
|
||||
// language codes, already in order, with the first occurrence of the
|
||||
// entity id marked with *:
|
||||
// 8 NULL pl *
|
||||
// 12 NULL pl *
|
||||
|
||||
// 4 NULL tr *
|
||||
// 12 NULL tr
|
||||
|
||||
// 2 blue NULL *
|
||||
// 3 blue NULL *
|
||||
|
||||
// 10 blue pl *
|
||||
// 11 blue pl *
|
||||
// 14 blue pl *
|
||||
// 15 blue pl *
|
||||
|
||||
// 6 blue tr *
|
||||
// 7 blue tr *
|
||||
// 14 blue tr
|
||||
// 15 blue tr
|
||||
|
||||
// 1 red NULL
|
||||
// 3 red NULL
|
||||
|
||||
// 9 red pl *
|
||||
// 11 red pl
|
||||
// 13 red pl *
|
||||
// 15 red pl
|
||||
|
||||
// 5 red tr *
|
||||
// 7 red tr
|
||||
// 13 red tr
|
||||
// 15 red tr
|
||||
$count_query = clone $query;
|
||||
$this->assertEqual(15, $count_query->count()->execute());
|
||||
$this->queryResults = $query->execute();
|
||||
$this->assertResult(8, 12, 4, 2, 3, 10, 11, 14, 15, 6, 7, 1, 9, 13, 5);
|
||||
|
||||
// Test the pager by setting element #1 to page 2 with a page size of 4.
|
||||
// Results will be #8-12 from above.
|
||||
$request = Request::createFromGlobals();
|
||||
$request->query->replace(array(
|
||||
'page' => '0,2',
|
||||
));
|
||||
\Drupal::getContainer()->get('request_stack')->push($request);
|
||||
$this->queryResults = $this->factory->get('entity_test_mulrev')
|
||||
->sort("$figures.color")
|
||||
->sort("$greetings.format")
|
||||
->sort('id')
|
||||
->pager(4, 1)
|
||||
->execute();
|
||||
$this->assertResult(15, 6, 7, 1);
|
||||
|
||||
// Now test the reversed order.
|
||||
$query = $this->factory->get('entity_test_mulrev')
|
||||
->sort("$figures.color", 'DESC')
|
||||
->sort("$greetings.format", 'DESC')
|
||||
->sort('id', 'DESC');
|
||||
$count_query = clone $query;
|
||||
$this->assertEqual(15, $count_query->count()->execute());
|
||||
$this->queryResults = $query->execute();
|
||||
$this->assertResult(15, 13, 7, 5, 11, 9, 3, 1, 14, 6, 10, 2, 12, 4, 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test tablesort().
|
||||
*/
|
||||
public function testTableSort() {
|
||||
// While ordering on bundles do not give us a definite order, we can still
|
||||
// assert that all entities from one bundle are after the other as the
|
||||
// order dictates.
|
||||
$request = Request::createFromGlobals();
|
||||
$request->query->replace(array(
|
||||
'sort' => 'asc',
|
||||
'order' => 'Type',
|
||||
));
|
||||
\Drupal::getContainer()->get('request_stack')->push($request);
|
||||
|
||||
$header = array(
|
||||
'id' => array('data' => 'Id', 'specifier' => 'id'),
|
||||
'type' => array('data' => 'Type', 'specifier' => 'type'),
|
||||
);
|
||||
|
||||
$this->queryResults = array_values($this->factory->get('entity_test_mulrev')
|
||||
->tableSort($header)
|
||||
->execute());
|
||||
$this->assertBundleOrder('asc');
|
||||
|
||||
$request->query->add(array(
|
||||
'sort' => 'desc',
|
||||
));
|
||||
\Drupal::getContainer()->get('request_stack')->push($request);
|
||||
|
||||
$header = array(
|
||||
'id' => array('data' => 'Id', 'specifier' => 'id'),
|
||||
'type' => array('data' => 'Type', 'specifier' => 'type'),
|
||||
);
|
||||
$this->queryResults = array_values($this->factory->get('entity_test_mulrev')
|
||||
->tableSort($header)
|
||||
->execute());
|
||||
$this->assertBundleOrder('desc');
|
||||
|
||||
// Ordering on ID is definite, however.
|
||||
$request->query->add(array(
|
||||
'order' => 'Id',
|
||||
));
|
||||
\Drupal::getContainer()->get('request_stack')->push($request);
|
||||
$this->queryResults = $this->factory->get('entity_test_mulrev')
|
||||
->tableSort($header)
|
||||
->execute();
|
||||
$this->assertResult(range(15, 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that count queries are separated across entity types.
|
||||
*/
|
||||
public function testCount() {
|
||||
// Create a field with the same name in a different entity type.
|
||||
$field_name = $this->figures;
|
||||
$field_storage = FieldStorageConfig::create(array(
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => 'entity_test',
|
||||
'type' => 'shape',
|
||||
'cardinality' => 2,
|
||||
'translatable' => TRUE,
|
||||
));
|
||||
$field_storage->save();
|
||||
$bundle = $this->randomMachineName();
|
||||
FieldConfig::create([
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => $bundle,
|
||||
])->save();
|
||||
|
||||
$entity = EntityTest::create(array(
|
||||
'id' => 1,
|
||||
'type' => $bundle,
|
||||
));
|
||||
$entity->enforceIsNew();
|
||||
$entity->save();
|
||||
|
||||
// As the single entity of this type we just saved does not have a value
|
||||
// in the color field, the result should be 0.
|
||||
$count = $this->factory->get('entity_test')
|
||||
->exists("$field_name.color")
|
||||
->count()
|
||||
->execute();
|
||||
$this->assertFalse($count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that nested condition groups work as expected.
|
||||
*/
|
||||
public function testNestedConditionGroups() {
|
||||
// Query for all entities of the first bundle that have either a red
|
||||
// triangle as a figure or the Turkish greeting as a greeting.
|
||||
$query = $this->factory->get('entity_test_mulrev');
|
||||
|
||||
$first_and = $query->andConditionGroup()
|
||||
->condition($this->figures . '.color', 'red')
|
||||
->condition($this->figures . '.shape', 'triangle');
|
||||
$second_and = $query->andConditionGroup()
|
||||
->condition($this->greetings . '.value', 'merhaba')
|
||||
->condition($this->greetings . '.format', 'format-tr');
|
||||
|
||||
$or = $query->orConditionGroup()
|
||||
->condition($first_and)
|
||||
->condition($second_and);
|
||||
|
||||
$this->queryResults = $query
|
||||
->condition($or)
|
||||
->condition('type', reset($this->bundles))
|
||||
->sort('id')
|
||||
->execute();
|
||||
|
||||
$this->assertResult(6, 14);
|
||||
}
|
||||
|
||||
protected function assertResult() {
|
||||
$assert = array();
|
||||
$expected = func_get_args();
|
||||
if ($expected && is_array($expected[0])) {
|
||||
$expected = $expected[0];
|
||||
}
|
||||
foreach ($expected as $binary) {
|
||||
$assert[$binary] = strval($binary);
|
||||
}
|
||||
$this->assertIdentical($this->queryResults, $assert);
|
||||
}
|
||||
|
||||
protected function assertRevisionResult($keys, $expected) {
|
||||
$assert = array();
|
||||
foreach ($expected as $key => $binary) {
|
||||
$assert[$keys[$key]] = strval($binary);
|
||||
}
|
||||
$this->assertIdentical($this->queryResults, $assert);
|
||||
return $assert;
|
||||
}
|
||||
|
||||
protected function assertBundleOrder($order) {
|
||||
// This loop is for bundle1 entities.
|
||||
for ($i = 1; $i <= 15; $i +=2) {
|
||||
$ok = TRUE;
|
||||
$index1 = array_search($i, $this->queryResults);
|
||||
$this->assertNotIdentical($index1, FALSE, "$i found at $index1.");
|
||||
// This loop is for bundle2 entities.
|
||||
for ($j = 2; $j <= 15; $j += 2) {
|
||||
if ($ok) {
|
||||
if ($order == 'asc') {
|
||||
$ok = $index1 > array_search($j, $this->queryResults);
|
||||
}
|
||||
else {
|
||||
$ok = $index1 < array_search($j, $this->queryResults);
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->assertTrue($ok, "$i is after all entities in bundle2");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test adding a tag and metadata to the Entity query object.
|
||||
*
|
||||
* The tags and metadata should propagate to the SQL query object.
|
||||
*/
|
||||
public function testMetaData() {
|
||||
$query = \Drupal::entityQuery('entity_test_mulrev');
|
||||
$query
|
||||
->addTag('efq_metadata_test')
|
||||
->addMetaData('foo', 'bar')
|
||||
->execute();
|
||||
|
||||
global $efq_test_metadata;
|
||||
$this->assertEqual($efq_test_metadata, 'bar', 'Tag and metadata propagated to the SQL query object.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test case sensitive and in-sensitive query conditions.
|
||||
*/
|
||||
public function testCaseSensitivity() {
|
||||
$bundle = $this->randomMachineName();
|
||||
|
||||
$field_storage = FieldStorageConfig::create(array(
|
||||
'field_name' => 'field_ci',
|
||||
'entity_type' => 'entity_test_mulrev',
|
||||
'type' => 'string',
|
||||
'cardinality' => 1,
|
||||
'translatable' => FALSE,
|
||||
'settings' => array(
|
||||
'case_sensitive' => FALSE,
|
||||
)
|
||||
));
|
||||
$field_storage->save();
|
||||
|
||||
FieldConfig::create(array(
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => $bundle,
|
||||
))->save();
|
||||
|
||||
$field_storage = FieldStorageConfig::create(array(
|
||||
'field_name' => 'field_cs',
|
||||
'entity_type' => 'entity_test_mulrev',
|
||||
'type' => 'string',
|
||||
'cardinality' => 1,
|
||||
'translatable' => FALSE,
|
||||
'settings' => array(
|
||||
'case_sensitive' => TRUE,
|
||||
),
|
||||
));
|
||||
$field_storage->save();
|
||||
|
||||
FieldConfig::create(array(
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => $bundle,
|
||||
))->save();
|
||||
|
||||
$fixtures = array();
|
||||
|
||||
for ($i = 0; $i < 2; $i++) {
|
||||
// If the last 4 of the string are all numbers, then there is no
|
||||
// difference between upper and lowercase and the case sensitive CONTAINS
|
||||
// test will fail. Ensure that can not happen by appending a non-numeric
|
||||
// character. See https://www.drupal.org/node/2397297.
|
||||
$string = $this->randomMachineName(7) . 'a';
|
||||
$fixtures[] = array(
|
||||
'original' => $string,
|
||||
'uppercase' => Unicode::strtoupper($string),
|
||||
'lowercase' => Unicode::strtolower($string),
|
||||
);
|
||||
}
|
||||
|
||||
EntityTestMulRev::create(array(
|
||||
'type' => $bundle,
|
||||
'name' => $this->randomMachineName(),
|
||||
'langcode' => 'en',
|
||||
'field_ci' => $fixtures[0]['uppercase'] . $fixtures[1]['lowercase'],
|
||||
'field_cs' => $fixtures[0]['uppercase'] . $fixtures[1]['lowercase']
|
||||
))->save();
|
||||
|
||||
// Check the case insensitive field, = operator.
|
||||
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
|
||||
'field_ci', $fixtures[0]['lowercase'] . $fixtures[1]['lowercase']
|
||||
)->execute();
|
||||
$this->assertIdentical(count($result), 1, 'Case insensitive, lowercase');
|
||||
|
||||
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
|
||||
'field_ci', $fixtures[0]['uppercase'] . $fixtures[1]['uppercase']
|
||||
)->execute();
|
||||
$this->assertIdentical(count($result), 1, 'Case insensitive, uppercase');
|
||||
|
||||
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
|
||||
'field_ci', $fixtures[0]['uppercase'] . $fixtures[1]['lowercase']
|
||||
)->execute();
|
||||
$this->assertIdentical(count($result), 1, 'Case insensitive, mixed.');
|
||||
|
||||
// Check the case sensitive field, = operator.
|
||||
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
|
||||
'field_cs', $fixtures[0]['lowercase'] . $fixtures[1]['lowercase']
|
||||
)->execute();
|
||||
$this->assertIdentical(count($result), 0, 'Case sensitive, lowercase.');
|
||||
|
||||
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
|
||||
'field_cs', $fixtures[0]['uppercase'] . $fixtures[1]['uppercase']
|
||||
)->execute();
|
||||
$this->assertIdentical(count($result), 0, 'Case sensitive, uppercase.');
|
||||
|
||||
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
|
||||
'field_cs', $fixtures[0]['uppercase'] . $fixtures[1]['lowercase']
|
||||
)->execute();
|
||||
$this->assertIdentical(count($result), 1, 'Case sensitive, exact match.');
|
||||
|
||||
// Check the case insensitive field, IN operator.
|
||||
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
|
||||
'field_ci', array($fixtures[0]['lowercase'] . $fixtures[1]['lowercase']), 'IN'
|
||||
)->execute();
|
||||
$this->assertIdentical(count($result), 1, 'Case insensitive, lowercase');
|
||||
|
||||
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
|
||||
'field_ci', array($fixtures[0]['uppercase'] . $fixtures[1]['uppercase']), 'IN'
|
||||
)->execute();
|
||||
$this->assertIdentical(count($result), 1, 'Case insensitive, uppercase');
|
||||
|
||||
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
|
||||
'field_ci', array($fixtures[0]['uppercase'] . $fixtures[1]['lowercase']), 'IN'
|
||||
)->execute();
|
||||
$this->assertIdentical(count($result), 1, 'Case insensitive, mixed');
|
||||
|
||||
// Check the case sensitive field, IN operator.
|
||||
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
|
||||
'field_cs', array($fixtures[0]['lowercase'] . $fixtures[1]['lowercase']), 'IN'
|
||||
)->execute();
|
||||
$this->assertIdentical(count($result), 0, 'Case sensitive, lowercase');
|
||||
|
||||
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
|
||||
'field_cs', array($fixtures[0]['uppercase'] . $fixtures[1]['uppercase']), 'IN'
|
||||
)->execute();
|
||||
$this->assertIdentical(count($result), 0, 'Case sensitive, uppercase');
|
||||
|
||||
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
|
||||
'field_cs', array($fixtures[0]['uppercase'] . $fixtures[1]['lowercase']), 'IN'
|
||||
)->execute();
|
||||
$this->assertIdentical(count($result), 1, 'Case sensitive, mixed');
|
||||
|
||||
// Check the case insensitive field, STARTS_WITH operator.
|
||||
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
|
||||
'field_ci', $fixtures[0]['lowercase'], 'STARTS_WITH'
|
||||
)->execute();
|
||||
$this->assertIdentical(count($result), 1, 'Case sensitive, lowercase.');
|
||||
|
||||
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
|
||||
'field_ci', $fixtures[0]['uppercase'], 'STARTS_WITH'
|
||||
)->execute();
|
||||
$this->assertIdentical(count($result), 1, 'Case sensitive, exact match.');
|
||||
|
||||
// Check the case sensitive field, STARTS_WITH operator.
|
||||
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
|
||||
'field_cs', $fixtures[0]['lowercase'], 'STARTS_WITH'
|
||||
)->execute();
|
||||
$this->assertIdentical(count($result), 0, 'Case sensitive, lowercase.');
|
||||
|
||||
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
|
||||
'field_cs', $fixtures[0]['uppercase'], 'STARTS_WITH'
|
||||
)->execute();
|
||||
$this->assertIdentical(count($result), 1, 'Case sensitive, exact match.');
|
||||
|
||||
|
||||
// Check the case insensitive field, ENDS_WITH operator.
|
||||
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
|
||||
'field_ci', $fixtures[1]['lowercase'], 'ENDS_WITH'
|
||||
)->execute();
|
||||
$this->assertIdentical(count($result), 1, 'Case sensitive, lowercase.');
|
||||
|
||||
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
|
||||
'field_ci', $fixtures[1]['uppercase'], 'ENDS_WITH'
|
||||
)->execute();
|
||||
$this->assertIdentical(count($result), 1, 'Case sensitive, exact match.');
|
||||
|
||||
// Check the case sensitive field, ENDS_WITH operator.
|
||||
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
|
||||
'field_cs', $fixtures[1]['lowercase'], 'ENDS_WITH'
|
||||
)->execute();
|
||||
$this->assertIdentical(count($result), 1, 'Case sensitive, lowercase.');
|
||||
|
||||
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
|
||||
'field_cs', $fixtures[1]['uppercase'], 'ENDS_WITH'
|
||||
)->execute();
|
||||
$this->assertIdentical(count($result), 0, 'Case sensitive, exact match.');
|
||||
|
||||
|
||||
// Check the case insensitive field, CONTAINS operator, use the inner 8
|
||||
// characters of the uppercase and lowercase strings.
|
||||
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
|
||||
'field_ci', Unicode::substr($fixtures[0]['uppercase'] . $fixtures[1]['lowercase'], 4, 8), 'CONTAINS'
|
||||
)->execute();
|
||||
$this->assertIdentical(count($result), 1, 'Case sensitive, lowercase.');
|
||||
|
||||
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
|
||||
'field_ci', Unicode::strtolower(Unicode::substr($fixtures[0]['uppercase'] . $fixtures[1]['lowercase'], 4, 8)), 'CONTAINS'
|
||||
)->execute();
|
||||
$this->assertIdentical(count($result), 1, 'Case sensitive, exact match.');
|
||||
|
||||
// Check the case sensitive field, CONTAINS operator.
|
||||
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
|
||||
'field_cs', Unicode::substr($fixtures[0]['uppercase'] . $fixtures[1]['lowercase'], 4, 8), 'CONTAINS'
|
||||
)->execute();
|
||||
$this->assertIdentical(count($result), 1, 'Case sensitive, lowercase.');
|
||||
|
||||
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
|
||||
'field_cs', Unicode::strtolower(Unicode::substr($fixtures[0]['uppercase'] . $fixtures[1]['lowercase'], 4, 8)), 'CONTAINS'
|
||||
)->execute();
|
||||
$this->assertIdentical(count($result), 0, 'Case sensitive, exact match.');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test base fields with multiple columns.
|
||||
*/
|
||||
public function testBaseFieldMultipleColumns() {
|
||||
$this->enableModules(['taxonomy']);
|
||||
$this->installEntitySchema('taxonomy_term');
|
||||
|
||||
Vocabulary::create(['vid' => 'tags']);
|
||||
|
||||
$term1 = Term::create([
|
||||
'name' => $this->randomMachineName(),
|
||||
'vid' => 'tags',
|
||||
'description' => array(
|
||||
'value' => $this->randomString(),
|
||||
'format' => 'format1',
|
||||
)]);
|
||||
$term1->save();
|
||||
|
||||
$term2 = Term::create([
|
||||
'name' => $this->randomMachineName(),
|
||||
'vid' => 'tags',
|
||||
'description' => array(
|
||||
'value' => $this->randomString(),
|
||||
'format' => 'format2',
|
||||
)]);
|
||||
$term2->save();
|
||||
|
||||
$ids = \Drupal::entityQuery('taxonomy_term')
|
||||
->condition('description.format', 'format1')
|
||||
->execute();
|
||||
|
||||
$this->assertEqual(count($ids), 1);
|
||||
$this->assertEqual($term1->id(), reset($ids));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test forward-revisions.
|
||||
*/
|
||||
public function testForwardRevisions() {
|
||||
// Ensure entity 14 is returned.
|
||||
$result = \Drupal::entityQuery('entity_test_mulrev')
|
||||
->condition('id', [14], 'IN')
|
||||
->execute();
|
||||
$this->assertEqual(count($result), 1);
|
||||
|
||||
// Set a revision on entity 14 that isn't the current default.
|
||||
$entity = EntityTestMulRev::load(14);
|
||||
$current_values = $entity->{$this->figures}->getValue();
|
||||
|
||||
$entity->setNewRevision(TRUE);
|
||||
$entity->isDefaultRevision(FALSE);
|
||||
$entity->{$this->figures}->setValue([
|
||||
'color' => 'red',
|
||||
'shape' => 'square'
|
||||
]);
|
||||
$entity->save();
|
||||
|
||||
// Entity query should still return entity 14.
|
||||
$result = \Drupal::entityQuery('entity_test_mulrev')
|
||||
->condition('id', [14], 'IN')
|
||||
->execute();
|
||||
$this->assertEqual(count($result), 1);
|
||||
|
||||
// Verify that field conditions on the default and forward revision are
|
||||
// work as expected.
|
||||
$result = \Drupal::entityQuery('entity_test_mulrev')
|
||||
->condition('id', [14], 'IN')
|
||||
->condition("$this->figures.color", $current_values[0]['color'])
|
||||
->execute();
|
||||
$this->assertEqual($result, [14 => '14']);
|
||||
$result = $this->factory->get('entity_test_mulrev')
|
||||
->condition('id', [14], 'IN')
|
||||
->condition("$this->figures.color", 'red')
|
||||
->allRevisions()
|
||||
->execute();
|
||||
$this->assertEqual($result, [16 => '14']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test against SQL inject of condition field. This covers a
|
||||
* database driver's EntityQuery\Condition class.
|
||||
*/
|
||||
public function testInjectionInCondition() {
|
||||
try {
|
||||
$this->queryResults = $this->factory->get('entity_test_mulrev')
|
||||
->condition('1 ; -- ', array(0, 1), 'IN')
|
||||
->sort('id')
|
||||
->execute();
|
||||
$this->fail('SQL Injection attempt in Entity Query condition in operator should result in an exception.');
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$this->pass('SQL Injection attempt in Entity Query condition in operator should result in an exception.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,454 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Entity;
|
||||
|
||||
use Drupal\config\Tests\SchemaCheckTestTrait;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityStorageException;
|
||||
use Drupal\Core\Field\BaseFieldDefinition;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\field\Tests\EntityReference\EntityReferenceTestTrait;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\user\Entity\Role;
|
||||
use Drupal\user\Entity\User;
|
||||
use Drupal\user\RoleInterface;
|
||||
use Drupal\user\UserInterface;
|
||||
use Drupal\entity_test\Entity\EntityTestStringId;
|
||||
|
||||
/**
|
||||
* Tests for the entity reference field.
|
||||
*
|
||||
* @group Entity
|
||||
*/
|
||||
class EntityReferenceFieldTest extends EntityKernelTestBase {
|
||||
|
||||
use SchemaCheckTestTrait;
|
||||
use EntityReferenceTestTrait;
|
||||
|
||||
/**
|
||||
* The entity type used in this test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $entityType = 'entity_test';
|
||||
|
||||
/**
|
||||
* The entity type that is being referenced.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $referencedEntityType = 'entity_test_rev';
|
||||
|
||||
/**
|
||||
* The bundle used in this test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $bundle = 'entity_test';
|
||||
|
||||
/**
|
||||
* The name of the field used in this test.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $fieldName = 'field_test';
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('entity_reference_test');
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('entity_test_rev');
|
||||
|
||||
// Create a field.
|
||||
$this->createEntityReferenceField(
|
||||
$this->entityType,
|
||||
$this->bundle,
|
||||
$this->fieldName,
|
||||
'Field test',
|
||||
$this->referencedEntityType,
|
||||
'default',
|
||||
array('target_bundles' => array($this->bundle)),
|
||||
FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests reference field validation.
|
||||
*/
|
||||
public function testEntityReferenceFieldValidation() {
|
||||
// Test a valid reference.
|
||||
$referenced_entity = $this->container->get('entity_type.manager')
|
||||
->getStorage($this->referencedEntityType)
|
||||
->create(array('type' => $this->bundle));
|
||||
$referenced_entity->save();
|
||||
|
||||
$entity = $this->container->get('entity_type.manager')
|
||||
->getStorage($this->entityType)
|
||||
->create(array('type' => $this->bundle));
|
||||
$entity->{$this->fieldName}->target_id = $referenced_entity->id();
|
||||
$violations = $entity->{$this->fieldName}->validate();
|
||||
$this->assertEqual($violations->count(), 0, 'Validation passes.');
|
||||
|
||||
// Test an invalid reference.
|
||||
$entity->{$this->fieldName}->target_id = 9999;
|
||||
$violations = $entity->{$this->fieldName}->validate();
|
||||
$this->assertEqual($violations->count(), 1, 'Validation throws a violation.');
|
||||
$this->assertEqual($violations[0]->getMessage(), t('The referenced entity (%type: %id) does not exist.', array('%type' => $this->referencedEntityType, '%id' => 9999)));
|
||||
|
||||
// Test a non-referenceable bundle.
|
||||
entity_test_create_bundle('non_referenceable', NULL, $this->referencedEntityType);
|
||||
$referenced_entity = entity_create($this->referencedEntityType, array('type' => 'non_referenceable'));
|
||||
$referenced_entity->save();
|
||||
$entity->{$this->fieldName}->target_id = $referenced_entity->id();
|
||||
$violations = $entity->{$this->fieldName}->validate();
|
||||
$this->assertEqual($violations->count(), 1, 'Validation throws a violation.');
|
||||
$this->assertEqual($violations[0]->getMessage(), t('This entity (%type: %id) cannot be referenced.', array('%type' => $this->referencedEntityType, '%id' => $referenced_entity->id())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the multiple target entities loader.
|
||||
*/
|
||||
public function testReferencedEntitiesMultipleLoad() {
|
||||
// Create the parent entity.
|
||||
$entity = $this->container->get('entity_type.manager')
|
||||
->getStorage($this->entityType)
|
||||
->create(array('type' => $this->bundle));
|
||||
|
||||
// Create three target entities and attach them to parent field.
|
||||
$target_entities = array();
|
||||
$reference_field = array();
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$target_entity = $this->container->get('entity_type.manager')
|
||||
->getStorage($this->referencedEntityType)
|
||||
->create(array('type' => $this->bundle));
|
||||
$target_entity->save();
|
||||
$target_entities[] = $target_entity;
|
||||
$reference_field[]['target_id'] = $target_entity->id();
|
||||
}
|
||||
|
||||
// Also attach a non-existent entity and a NULL target id.
|
||||
$reference_field[3]['target_id'] = 99999;
|
||||
$target_entities[3] = NULL;
|
||||
$reference_field[4]['target_id'] = NULL;
|
||||
$target_entities[4] = NULL;
|
||||
|
||||
// Attach the first created target entity as the sixth item ($delta == 5) of
|
||||
// the parent entity field. We want to test the case when the same target
|
||||
// entity is referenced twice (or more times) in the same entity reference
|
||||
// field.
|
||||
$reference_field[5] = $reference_field[0];
|
||||
$target_entities[5] = $target_entities[0];
|
||||
|
||||
// Create a new target entity that is not saved, thus testing the
|
||||
// "autocreate" feature.
|
||||
$target_entity_unsaved = $this->container->get('entity_type.manager')
|
||||
->getStorage($this->referencedEntityType)
|
||||
->create(array('type' => $this->bundle, 'name' => $this->randomString()));
|
||||
$reference_field[6]['entity'] = $target_entity_unsaved;
|
||||
$target_entities[6] = $target_entity_unsaved;
|
||||
|
||||
// Set the field value.
|
||||
$entity->{$this->fieldName}->setValue($reference_field);
|
||||
|
||||
// Load the target entities using EntityReferenceField::referencedEntities().
|
||||
$entities = $entity->{$this->fieldName}->referencedEntities();
|
||||
|
||||
// Test returned entities:
|
||||
// - Deltas must be preserved.
|
||||
// - Non-existent entities must not be retrieved in target entities result.
|
||||
foreach ($target_entities as $delta => $target_entity) {
|
||||
if (!empty($target_entity)) {
|
||||
if (!$target_entity->isNew()) {
|
||||
// There must be an entity in the loaded set having the same id for
|
||||
// the same delta.
|
||||
$this->assertEqual($target_entity->id(), $entities[$delta]->id());
|
||||
}
|
||||
else {
|
||||
// For entities that were not yet saved, there must an entity in the
|
||||
// loaded set having the same label for the same delta.
|
||||
$this->assertEqual($target_entity->label(), $entities[$delta]->label());
|
||||
}
|
||||
}
|
||||
else {
|
||||
// A non-existent or NULL entity target id must not return any item in
|
||||
// the target entities set.
|
||||
$this->assertFalse(isset($entities[$delta]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests referencing entities with string IDs.
|
||||
*/
|
||||
public function testReferencedEntitiesStringId() {
|
||||
$field_name = 'entity_reference_string_id';
|
||||
$this->installEntitySchema('entity_test_string_id');
|
||||
$this->createEntityReferenceField(
|
||||
$this->entityType,
|
||||
$this->bundle,
|
||||
$field_name,
|
||||
'Field test',
|
||||
'entity_test_string_id',
|
||||
'default',
|
||||
array('target_bundles' => array($this->bundle)),
|
||||
FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED
|
||||
);
|
||||
// Create the parent entity.
|
||||
$entity = $this->container->get('entity_type.manager')
|
||||
->getStorage($this->entityType)
|
||||
->create(array('type' => $this->bundle));
|
||||
|
||||
// Create the default target entity.
|
||||
$target_entity = EntityTestStringId::create([
|
||||
'id' => $this->randomString(),
|
||||
'type' => $this->bundle
|
||||
]);
|
||||
$target_entity->save();
|
||||
|
||||
// Set the field value.
|
||||
$entity->{$field_name}->setValue(array(array('target_id' => $target_entity->id())));
|
||||
|
||||
// Load the target entities using EntityReferenceField::referencedEntities().
|
||||
$entities = $entity->{$field_name}->referencedEntities();
|
||||
$this->assertEqual($entities[0]->id(), $target_entity->id());
|
||||
|
||||
// Test that a string ID works as a default value and the field's config
|
||||
// schema is correct.
|
||||
$field = FieldConfig::loadByName($this->entityType, $this->bundle, $field_name);
|
||||
$field->setDefaultValue($target_entity->id());
|
||||
$field->save();
|
||||
$this->assertConfigSchema(\Drupal::service('config.typed'), 'field.field.' . $field->id(), $field->toArray());
|
||||
|
||||
// Test that the default value works.
|
||||
$entity = $this->container->get('entity_type.manager')
|
||||
->getStorage($this->entityType)
|
||||
->create(array('type' => $this->bundle));
|
||||
$entities = $entity->{$field_name}->referencedEntities();
|
||||
$this->assertEqual($entities[0]->id(), $target_entity->id());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests all the possible ways to autocreate an entity via the API.
|
||||
*/
|
||||
function testAutocreateApi() {
|
||||
$entity = $this->entityManager
|
||||
->getStorage($this->entityType)
|
||||
->create(array('name' => $this->randomString()));
|
||||
|
||||
// Test content entity autocreation.
|
||||
$this->assertUserAutocreate($entity, function(EntityInterface $entity, UserInterface $user) {
|
||||
$entity->set('user_id', $user);
|
||||
});
|
||||
$this->assertUserAutocreate($entity, function(EntityInterface $entity, UserInterface $user) {
|
||||
$entity->set('user_id', $user, FALSE);
|
||||
});
|
||||
$this->assertUserAutocreate($entity, function(EntityInterface $entity, UserInterface $user) {
|
||||
$entity->user_id->setValue($user);
|
||||
});
|
||||
$this->assertUserAutocreate($entity, function(EntityInterface $entity, UserInterface $user) {
|
||||
$entity->user_id[0]->get('entity')->setValue($user);
|
||||
});
|
||||
$this->assertUserAutocreate($entity, function(EntityInterface $entity, UserInterface $user) {
|
||||
$entity->user_id->setValue(array('entity' => $user, 'target_id' => NULL));
|
||||
});
|
||||
try {
|
||||
$message = 'Setting both the entity and an invalid target_id property fails.';
|
||||
$this->assertUserAutocreate($entity, function(EntityInterface $entity, UserInterface $user) {
|
||||
$user->save();
|
||||
$entity->user_id->setValue(array('entity' => $user, 'target_id' => $this->generateRandomEntityId()));
|
||||
});
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (\InvalidArgumentException $e) {
|
||||
$this->pass($message);
|
||||
}
|
||||
$this->assertUserAutocreate($entity, function(EntityInterface $entity, UserInterface $user) {
|
||||
$entity->user_id = $user;
|
||||
});
|
||||
$this->assertUserAutocreate($entity, function(EntityInterface $entity, UserInterface $user) {
|
||||
$entity->user_id->entity = $user;
|
||||
});
|
||||
|
||||
// Test config entity autocreation.
|
||||
$this->assertUserRoleAutocreate($entity, function(EntityInterface $entity, RoleInterface $role) {
|
||||
$entity->set('user_role', $role);
|
||||
});
|
||||
$this->assertUserRoleAutocreate($entity, function(EntityInterface $entity, RoleInterface $role) {
|
||||
$entity->set('user_role', $role, FALSE);
|
||||
});
|
||||
$this->assertUserRoleAutocreate($entity, function(EntityInterface $entity, RoleInterface $role) {
|
||||
$entity->user_role->setValue($role);
|
||||
});
|
||||
$this->assertUserRoleAutocreate($entity, function(EntityInterface $entity, RoleInterface $role) {
|
||||
$entity->user_role[0]->get('entity')->setValue($role);
|
||||
});
|
||||
$this->assertUserRoleAutocreate($entity, function(EntityInterface $entity, RoleInterface $role) {
|
||||
$entity->user_role->setValue(array('entity' => $role, 'target_id' => NULL));
|
||||
});
|
||||
try {
|
||||
$message = 'Setting both the entity and an invalid target_id property fails.';
|
||||
$this->assertUserRoleAutocreate($entity, function(EntityInterface $entity, RoleInterface $role) {
|
||||
$role->save();
|
||||
$entity->user_role->setValue(array('entity' => $role, 'target_id' => $this->generateRandomEntityId(TRUE)));
|
||||
});
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (\InvalidArgumentException $e) {
|
||||
$this->pass($message);
|
||||
}
|
||||
$this->assertUserRoleAutocreate($entity, function(EntityInterface $entity, RoleInterface $role) {
|
||||
$entity->user_role = $role;
|
||||
});
|
||||
$this->assertUserRoleAutocreate($entity, function(EntityInterface $entity, RoleInterface $role) {
|
||||
$entity->user_role->entity = $role;
|
||||
});
|
||||
|
||||
// Test target entity saving after setting it as new.
|
||||
$storage = $this->entityManager->getStorage('user');
|
||||
$user_id = $this->generateRandomEntityId();
|
||||
$user = $storage->create(array('uid' => $user_id, 'name' => $this->randomString()));
|
||||
$entity->user_id = $user;
|
||||
$user->save();
|
||||
$entity->save();
|
||||
$this->assertEqual($entity->user_id->target_id, $user->id());
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the setter callback performs autocreation for users.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* The referencing entity.
|
||||
* @param $setter_callback
|
||||
* A callback setting the target entity on the referencing entity.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the user was autocreated, FALSE otherwise.
|
||||
*/
|
||||
protected function assertUserAutocreate(EntityInterface $entity, $setter_callback) {
|
||||
$storage = $this->entityManager->getStorage('user');
|
||||
$user_id = $this->generateRandomEntityId();
|
||||
$user = $storage->create(array('uid' => $user_id, 'name' => $this->randomString()));
|
||||
$setter_callback($entity, $user);
|
||||
$entity->save();
|
||||
$storage->resetCache();
|
||||
$user = User::load($user_id);
|
||||
return $this->assertEqual($entity->user_id->target_id, $user->id());
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the setter callback performs autocreation for user roles.
|
||||
*
|
||||
* @param \Drupal\Core\Entity\EntityInterface $entity
|
||||
* The referencing entity.
|
||||
* @param $setter_callback
|
||||
* A callback setting the target entity on the referencing entity.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the user was autocreated, FALSE otherwise.
|
||||
*/
|
||||
protected function assertUserRoleAutocreate(EntityInterface $entity, $setter_callback) {
|
||||
$storage = $this->entityManager->getStorage('user_role');
|
||||
$role_id = $this->generateRandomEntityId(TRUE);
|
||||
$role = $storage->create(array('id' => $role_id, 'label' => $this->randomString()));
|
||||
$setter_callback($entity, $role);
|
||||
$entity->save();
|
||||
$storage->resetCache();
|
||||
$role = Role::load($role_id);
|
||||
return $this->assertEqual($entity->user_role->target_id, $role->id());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the target entity is not unnecessarily loaded.
|
||||
*/
|
||||
public function testTargetEntityNoLoad() {
|
||||
// Setup a test entity type with an entity reference field to itself. We use
|
||||
// a special storage class throwing exceptions when a load operation is
|
||||
// triggered to be able to detect them.
|
||||
$entity_type = clone $this->entityManager->getDefinition('entity_test_update');
|
||||
$entity_type->setHandlerClass('storage', '\Drupal\entity_test\EntityTestNoLoadStorage');
|
||||
$this->state->set('entity_test_update.entity_type', $entity_type);
|
||||
$definitions = array(
|
||||
'target_reference' => BaseFieldDefinition::create('entity_reference')
|
||||
->setSetting('target_type', $entity_type->id())
|
||||
->setSetting('handler', 'default')
|
||||
);
|
||||
$this->state->set('entity_test_update.additional_base_field_definitions', $definitions);
|
||||
$this->entityManager->clearCachedDefinitions();
|
||||
$this->installEntitySchema($entity_type->id());
|
||||
|
||||
// Create the target entity.
|
||||
$storage = $this->entityManager->getStorage($entity_type->id());
|
||||
$target_id = $this->generateRandomEntityId();
|
||||
$target = $storage->create(array('id' => $target_id, 'name' => $this->randomString()));
|
||||
$target->save();
|
||||
$this->assertEqual($target_id, $target->id(), 'The target entity has a random identifier.');
|
||||
|
||||
// Check that populating the reference with an existing target id does not
|
||||
// trigger a load operation.
|
||||
$message = 'The target entity was not loaded.';
|
||||
try {
|
||||
$entity = $this->entityManager
|
||||
->getStorage($entity_type->id())
|
||||
->create(array('name' => $this->randomString()));
|
||||
$entity->target_reference = $target_id;
|
||||
$this->pass($message);
|
||||
}
|
||||
catch (EntityStorageException $e) {
|
||||
$this->fail($message);
|
||||
}
|
||||
|
||||
// Check that the storage actually triggers the expected exception when
|
||||
// trying to load the target entity.
|
||||
$message = 'An exception is thrown when trying to load the target entity';
|
||||
try {
|
||||
$storage->load($target_id);
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (EntityStorageException $e) {
|
||||
$this->pass($message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the dependencies entity reference fields are created with.
|
||||
*/
|
||||
public function testEntityReferenceFieldDependencies() {
|
||||
$field_name = 'user_reference_field';
|
||||
$entity_type = 'entity_test';
|
||||
|
||||
$field_storage = FieldStorageConfig::create([
|
||||
'field_name' => $field_name,
|
||||
'type' => 'entity_reference',
|
||||
'entity_type' => $entity_type,
|
||||
'settings' => [
|
||||
'target_type' => 'user',
|
||||
],
|
||||
]);
|
||||
$field_storage->save();
|
||||
$this->assertEqual(['module' => ['entity_test', 'user']], $field_storage->getDependencies());
|
||||
|
||||
$field = FieldConfig::create([
|
||||
'field_name' => $field_name,
|
||||
'entity_type' => $entity_type,
|
||||
'bundle' => 'entity_test',
|
||||
'label' => $field_name,
|
||||
'settings' => [
|
||||
'handler' => 'default',
|
||||
],
|
||||
]);
|
||||
$field->save();
|
||||
$this->assertEqual(['config' => ['field.storage.entity_test.user_reference_field'], 'module' => ['entity_test']], $field->getDependencies());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,133 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Entity\EntityReferenceSelection;
|
||||
|
||||
use Drupal\Component\Utility\Html;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
use Drupal\system\Tests\Entity\EntityUnitTestBase;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
|
||||
/**
|
||||
* Tests sorting referenced items.
|
||||
*
|
||||
* @group entity_reference
|
||||
*/
|
||||
class EntityReferenceSelectionSortTest extends EntityUnitTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('node');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create an Article node type.
|
||||
$article = NodeType::create(array(
|
||||
'type' => 'article',
|
||||
));
|
||||
$article->save();
|
||||
|
||||
// Test as a non-admin.
|
||||
$normal_user = $this->createUser(array(), array('access content'));
|
||||
\Drupal::currentUser()->setAccount($normal_user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert sorting by field and property.
|
||||
*/
|
||||
public function testSort() {
|
||||
// Add text field to entity, to sort by.
|
||||
FieldStorageConfig::create(array(
|
||||
'field_name' => 'field_text',
|
||||
'entity_type' => 'node',
|
||||
'type' => 'text',
|
||||
'entity_types' => array('node'),
|
||||
))->save();
|
||||
|
||||
FieldConfig::create([
|
||||
'label' => 'Text Field',
|
||||
'field_name' => 'field_text',
|
||||
'entity_type' => 'node',
|
||||
'bundle' => 'article',
|
||||
'settings' => array(),
|
||||
'required' => FALSE,
|
||||
])->save();
|
||||
|
||||
// Build a set of test data.
|
||||
$node_values = array(
|
||||
'published1' => array(
|
||||
'type' => 'article',
|
||||
'status' => 1,
|
||||
'title' => 'Node published1 (<&>)',
|
||||
'uid' => 1,
|
||||
'field_text' => array(
|
||||
array(
|
||||
'value' => 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
'published2' => array(
|
||||
'type' => 'article',
|
||||
'status' => 1,
|
||||
'title' => 'Node published2 (<&>)',
|
||||
'uid' => 1,
|
||||
'field_text' => array(
|
||||
array(
|
||||
'value' => 2,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$nodes = array();
|
||||
$node_labels = array();
|
||||
foreach ($node_values as $key => $values) {
|
||||
$node = Node::create($values);
|
||||
$node->save();
|
||||
$nodes[$key] = $node;
|
||||
$node_labels[$key] = Html::escape($node->label());
|
||||
}
|
||||
|
||||
$selection_options = array(
|
||||
'target_type' => 'node',
|
||||
'handler' => 'default',
|
||||
'handler_settings' => array(
|
||||
'target_bundles' => NULL,
|
||||
// Add sorting.
|
||||
'sort' => array(
|
||||
'field' => 'field_text.value',
|
||||
'direction' => 'DESC',
|
||||
),
|
||||
),
|
||||
);
|
||||
$handler = $this->container->get('plugin.manager.entity_reference_selection')->getInstance($selection_options);
|
||||
|
||||
// Not only assert the result, but make sure the keys are sorted as
|
||||
// expected.
|
||||
$result = $handler->getReferenceableEntities();
|
||||
$expected_result = array(
|
||||
$nodes['published2']->id() => $node_labels['published2'],
|
||||
$nodes['published1']->id() => $node_labels['published1'],
|
||||
);
|
||||
$this->assertIdentical($result['article'], $expected_result, 'Query sorted by field returned expected values.');
|
||||
|
||||
// Assert sort by base field.
|
||||
$selection_options['handler_settings']['sort'] = array(
|
||||
'field' => 'nid',
|
||||
'direction' => 'ASC',
|
||||
);
|
||||
$handler = $this->container->get('plugin.manager.entity_reference_selection')->getInstance($selection_options);
|
||||
$result = $handler->getReferenceableEntities();
|
||||
$expected_result = array(
|
||||
$nodes['published1']->id() => $node_labels['published1'],
|
||||
$nodes['published2']->id() => $node_labels['published2'],
|
||||
);
|
||||
$this->assertIdentical($result['article'], $expected_result, 'Query sorted by property returned expected values.');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Entity;
|
||||
|
||||
use Drupal\entity_test\Entity\EntityTestMulRev;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
|
||||
/**
|
||||
* Tests proper revision propagation of entities.
|
||||
*
|
||||
* @group Entity
|
||||
*/
|
||||
class EntityRevisionTranslationTest extends EntityKernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['language'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Enable an additional language.
|
||||
ConfigurableLanguage::createFromLangcode('de')->save();
|
||||
|
||||
$this->installEntitySchema('entity_test_mulrev');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if the translation object has the right revision id after new revision.
|
||||
*/
|
||||
public function testNewRevisionAfterTranslation() {
|
||||
$user = $this->createUser();
|
||||
|
||||
// Create a test entity.
|
||||
$entity = EntityTestMulRev::create([
|
||||
'name' => $this->randomString(),
|
||||
'user_id' => $user->id(),
|
||||
'language' => 'en',
|
||||
]);
|
||||
$entity->save();
|
||||
$old_rev_id = $entity->getRevisionId();
|
||||
|
||||
$translation = $entity->addTranslation('de');
|
||||
$translation->setNewRevision();
|
||||
$translation->save();
|
||||
|
||||
$this->assertTrue($translation->getRevisionId() > $old_rev_id, 'The saved translation in new revision has a newer revision id.');
|
||||
$this->assertTrue($this->reloadEntity($entity)->getRevisionId() > $old_rev_id, 'The entity from the storage has a newer revision id.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if the translation object has the right revision id after new revision.
|
||||
*/
|
||||
public function testRevertRevisionAfterTranslation() {
|
||||
$user = $this->createUser();
|
||||
$storage = $this->entityManager->getStorage('entity_test_mulrev');
|
||||
|
||||
// Create a test entity.
|
||||
$entity = EntityTestMulRev::create([
|
||||
'name' => $this->randomString(),
|
||||
'user_id' => $user->id(),
|
||||
'language' => 'en',
|
||||
]);
|
||||
$entity->save();
|
||||
$old_rev_id = $entity->getRevisionId();
|
||||
|
||||
$translation = $entity->addTranslation('de');
|
||||
$translation->setNewRevision();
|
||||
$translation->save();
|
||||
|
||||
$entity = $this->reloadEntity($entity);
|
||||
|
||||
$this->assertTrue($entity->hasTranslation('de'));
|
||||
|
||||
$entity = $storage->loadRevision($old_rev_id);
|
||||
|
||||
$entity->setNewRevision();
|
||||
$entity->isDefaultRevision(TRUE);
|
||||
$entity->save();
|
||||
|
||||
$entity = $this->reloadEntity($entity);
|
||||
|
||||
$this->assertFalse($entity->hasTranslation('de'));
|
||||
}
|
||||
|
||||
}
|
||||
191
core/tests/Drupal/KernelTests/Core/Entity/EntitySchemaTest.php
Normal file
191
core/tests/Drupal/KernelTests/Core/Entity/EntitySchemaTest.php
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Entity;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
|
||||
/**
|
||||
* Tests adding a custom bundle field.
|
||||
*
|
||||
* @group system
|
||||
*/
|
||||
class EntitySchemaTest extends EntityKernelTestBase {
|
||||
|
||||
/**
|
||||
* The database connection used.
|
||||
*
|
||||
* @var \Drupal\Core\Database\Connection
|
||||
*/
|
||||
protected $database;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installSchema('user', array('users_data'));
|
||||
$this->database = $this->container->get('database');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the custom bundle field creation and deletion.
|
||||
*/
|
||||
public function testCustomFieldCreateDelete() {
|
||||
// Install the module which adds the field.
|
||||
$this->installModule('entity_schema_test');
|
||||
$this->entityManager->clearCachedDefinitions();
|
||||
$storage_definitions = $this->entityManager->getFieldStorageDefinitions('entity_test');
|
||||
$this->assertNotNull($storage_definitions['custom_base_field'], 'Base field definition found.');
|
||||
$this->assertNotNull($storage_definitions['custom_bundle_field'], 'Bundle field definition found.');
|
||||
|
||||
// Make sure the field schema can be created.
|
||||
$this->entityManager->onFieldStorageDefinitionCreate($storage_definitions['custom_base_field']);
|
||||
$this->entityManager->onFieldStorageDefinitionCreate($storage_definitions['custom_bundle_field']);
|
||||
/** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */
|
||||
$table_mapping = $this->entityManager->getStorage('entity_test')->getTableMapping();
|
||||
$base_table = current($table_mapping->getTableNames());
|
||||
$base_column = current($table_mapping->getColumnNames('custom_base_field'));
|
||||
$this->assertTrue($this->database->schema()->fieldExists($base_table, $base_column), 'Table column created');
|
||||
$table = $table_mapping->getDedicatedDataTableName($storage_definitions['custom_bundle_field']);
|
||||
$this->assertTrue($this->database->schema()->tableExists($table), 'Table created');
|
||||
|
||||
// Make sure the field schema can be deleted.
|
||||
$this->entityManager->onFieldStorageDefinitionDelete($storage_definitions['custom_base_field']);
|
||||
$this->entityManager->onFieldStorageDefinitionDelete($storage_definitions['custom_bundle_field']);
|
||||
$this->assertFalse($this->database->schema()->fieldExists($base_table, $base_column), 'Table column dropped');
|
||||
$this->assertFalse($this->database->schema()->tableExists($table), 'Table dropped');
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the entity type definition.
|
||||
*
|
||||
* @param bool $alter
|
||||
* Whether the original definition should be altered or not.
|
||||
*/
|
||||
protected function updateEntityType($alter) {
|
||||
$entity_test_id = 'entity_test';
|
||||
$original = $this->entityManager->getDefinition($entity_test_id);
|
||||
$this->entityManager->clearCachedDefinitions();
|
||||
$this->state->set('entity_schema_update', $alter);
|
||||
$entity_type = $this->entityManager->getDefinition($entity_test_id);
|
||||
$this->entityManager->onEntityTypeUpdate($entity_type, $original);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that entity schema responds to changes in the entity type definition.
|
||||
*/
|
||||
public function testEntitySchemaUpdate() {
|
||||
$this->installModule('entity_schema_test');
|
||||
$storage_definitions = $this->entityManager->getFieldStorageDefinitions('entity_test');
|
||||
$this->entityManager->onFieldStorageDefinitionCreate($storage_definitions['custom_base_field']);
|
||||
$this->entityManager->onFieldStorageDefinitionCreate($storage_definitions['custom_bundle_field']);
|
||||
$schema_handler = $this->database->schema();
|
||||
$tables = array('entity_test', 'entity_test_revision', 'entity_test_field_data', 'entity_test_field_revision');
|
||||
$dedicated_tables = array('entity_test__custom_bundle_field', 'entity_test_revision__custom_bundle_field');
|
||||
|
||||
// Initially only the base table and the dedicated field data table should
|
||||
// exist.
|
||||
foreach ($tables as $index => $table) {
|
||||
$this->assertEqual($schema_handler->tableExists($table), !$index, SafeMarkup::format('Entity schema correct for the @table table.', array('@table' => $table)));
|
||||
}
|
||||
$this->assertTrue($schema_handler->tableExists($dedicated_tables[0]), SafeMarkup::format('Field schema correct for the @table table.', array('@table' => $table)));
|
||||
|
||||
// Update the entity type definition and check that the entity schema now
|
||||
// supports translations and revisions.
|
||||
$this->updateEntityType(TRUE);
|
||||
foreach ($tables as $table) {
|
||||
$this->assertTrue($schema_handler->tableExists($table), SafeMarkup::format('Entity schema correct for the @table table.', array('@table' => $table)));
|
||||
}
|
||||
foreach ($dedicated_tables as $table) {
|
||||
$this->assertTrue($schema_handler->tableExists($table), SafeMarkup::format('Field schema correct for the @table table.', array('@table' => $table)));
|
||||
}
|
||||
|
||||
// Revert changes and check that the entity schema now does not support
|
||||
// neither translations nor revisions.
|
||||
$this->updateEntityType(FALSE);
|
||||
foreach ($tables as $index => $table) {
|
||||
$this->assertEqual($schema_handler->tableExists($table), !$index, SafeMarkup::format('Entity schema correct for the @table table.', array('@table' => $table)));
|
||||
}
|
||||
$this->assertTrue($schema_handler->tableExists($dedicated_tables[0]), SafeMarkup::format('Field schema correct for the @table table.', array('@table' => $table)));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function refreshServices() {
|
||||
parent::refreshServices();
|
||||
$this->database = $this->container->get('database');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that modifying the UUID field for a translatable entity works.
|
||||
*/
|
||||
public function testModifyingTranslatableColumnSchema() {
|
||||
$this->installModule('entity_schema_test');
|
||||
$this->updateEntityType(TRUE);
|
||||
$fields = ['revision_log', 'uuid'];
|
||||
foreach ($fields as $field_name) {
|
||||
$original_definition = $this->entityManager->getBaseFieldDefinitions('entity_test')[$field_name];
|
||||
$new_definition = clone $original_definition;
|
||||
$new_definition->setLabel($original_definition->getLabel() . ', the other one');
|
||||
$this->assertTrue($this->entityManager->getStorage('entity_test')
|
||||
->requiresFieldDataMigration($new_definition, $original_definition));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests fields from an uninstalled module are removed from the schema.
|
||||
*/
|
||||
public function testCleanUpStorageDefinition() {
|
||||
// Find all the entity types provided by the entity_test module and install
|
||||
// the schema for them.
|
||||
$entity_type_ids = [];
|
||||
$entities = \Drupal::entityManager()->getDefinitions();
|
||||
foreach ($entities as $entity_type_id => $definition) {
|
||||
if ($definition->getProvider() == 'entity_test') {
|
||||
$this->installEntitySchema($entity_type_id);
|
||||
$entity_type_ids[] = $entity_type_id;
|
||||
};
|
||||
}
|
||||
|
||||
// Get a list of all the entities in the schema.
|
||||
$key_value_store = \Drupal::keyValue('entity.storage_schema.sql');
|
||||
$schema = $key_value_store->getAll();
|
||||
|
||||
// Count the storage definitions provided by the entity_test module, so that
|
||||
// after uninstall we can be sure there were some to be deleted.
|
||||
$entity_type_id_count = 0;
|
||||
|
||||
foreach (array_keys($schema) as $storage_definition_name) {
|
||||
list($entity_type_id, ,) = explode('.', $storage_definition_name);
|
||||
if (in_array($entity_type_id, $entity_type_ids)) {
|
||||
$entity_type_id_count++;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that there are storage definitions from the entity_test module.
|
||||
$this->assertNotEqual($entity_type_id_count, 0, 'There are storage definitions provided by the entity_test module in the schema.');
|
||||
|
||||
// Uninstall the entity_test module.
|
||||
$this->container->get('module_installer')->uninstall(array('entity_test'));
|
||||
|
||||
// Get a list of all the entities in the schema.
|
||||
$key_value_store = \Drupal::keyValue('entity.storage_schema.sql');
|
||||
$schema = $key_value_store->getAll();
|
||||
|
||||
// Count the storage definitions that come from entity types provided by
|
||||
// the entity_test module.
|
||||
$entity_type_id_count = 0;
|
||||
|
||||
foreach (array_keys($schema) as $storage_definition_name) {
|
||||
list($entity_type_id, ,) = explode('.', $storage_definition_name);
|
||||
if (in_array($entity_type_id, $entity_type_ids)) {
|
||||
$entity_type_id_count++;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that all storage definitions have been removed from the schema.
|
||||
$this->assertEqual($entity_type_id_count, 0, 'After uninstalling entity_test module the schema should not contains fields from entities provided by the module.');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,932 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Entity;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\entity_test\Entity\EntityTestMulRev;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
|
||||
/**
|
||||
* Tests entity translation functionality.
|
||||
*
|
||||
* @group Entity
|
||||
*/
|
||||
class EntityTranslationTest extends EntityLanguageTestBase {
|
||||
|
||||
/**
|
||||
* Tests language related methods of the Entity class.
|
||||
*/
|
||||
public function testEntityLanguageMethods() {
|
||||
// All entity variations have to have the same results.
|
||||
foreach (entity_test_entity_types() as $entity_type) {
|
||||
$this->doTestEntityLanguageMethods($entity_type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the entity language method tests for the given entity type.
|
||||
*
|
||||
* @param string $entity_type
|
||||
* The entity type to run the tests with.
|
||||
*/
|
||||
protected function doTestEntityLanguageMethods($entity_type) {
|
||||
$langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('langcode');
|
||||
$entity = $this->container->get('entity_type.manager')
|
||||
->getStorage($entity_type)
|
||||
->create(array(
|
||||
'name' => 'test',
|
||||
'user_id' => $this->container->get('current_user')->id(),
|
||||
));
|
||||
$this->assertEqual($entity->language()->getId(), $this->languageManager->getDefaultLanguage()->getId(), format_string('%entity_type: Entity created with API has default language.', array('%entity_type' => $entity_type)));
|
||||
$entity = $this->container->get('entity_type.manager')
|
||||
->getStorage($entity_type)
|
||||
->create(array(
|
||||
'name' => 'test',
|
||||
'user_id' => \Drupal::currentUser()->id(),
|
||||
$langcode_key => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
));
|
||||
|
||||
$this->assertEqual($entity->language()->getId(), LanguageInterface::LANGCODE_NOT_SPECIFIED, format_string('%entity_type: Entity language not specified.', array('%entity_type' => $entity_type)));
|
||||
$this->assertFalse($entity->getTranslationLanguages(FALSE), format_string('%entity_type: No translations are available', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Set the value in default language.
|
||||
$entity->set($this->fieldName, array(0 => array('value' => 'default value')));
|
||||
// Get the value.
|
||||
$field = $entity->getTranslation(LanguageInterface::LANGCODE_DEFAULT)->get($this->fieldName);
|
||||
$this->assertEqual($field->value, 'default value', format_string('%entity_type: Untranslated value retrieved.', array('%entity_type' => $entity_type)));
|
||||
$this->assertEqual($field->getLangcode(), LanguageInterface::LANGCODE_NOT_SPECIFIED, format_string('%entity_type: Field object has the expected langcode.', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Try to get add a translation to language neutral entity.
|
||||
$message = 'Adding a translation to a language-neutral entity results in an error.';
|
||||
try {
|
||||
$entity->addTranslation($this->langcodes[1]);
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (\InvalidArgumentException $e) {
|
||||
$this->pass($message);
|
||||
}
|
||||
|
||||
// Now, make the entity language-specific by assigning a language and test
|
||||
// translating it.
|
||||
$default_langcode = $this->langcodes[0];
|
||||
$entity->{$langcode_key}->value = $default_langcode;
|
||||
$entity->{$this->fieldName} = array();
|
||||
$this->assertEqual($entity->language(), \Drupal::languageManager()->getLanguage($this->langcodes[0]), format_string('%entity_type: Entity language retrieved.', array('%entity_type' => $entity_type)));
|
||||
$this->assertFalse($entity->getTranslationLanguages(FALSE), format_string('%entity_type: No translations are available', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Set the value in default language.
|
||||
$entity->set($this->fieldName, array(0 => array('value' => 'default value')));
|
||||
// Get the value.
|
||||
$field = $entity->get($this->fieldName);
|
||||
$this->assertEqual($field->value, 'default value', format_string('%entity_type: Untranslated value retrieved.', array('%entity_type' => $entity_type)));
|
||||
$this->assertEqual($field->getLangcode(), $default_langcode, format_string('%entity_type: Field object has the expected langcode.', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Set a translation.
|
||||
$entity->addTranslation($this->langcodes[1])->set($this->fieldName, array(0 => array('value' => 'translation 1')));
|
||||
$field = $entity->getTranslation($this->langcodes[1])->{$this->fieldName};
|
||||
$this->assertEqual($field->value, 'translation 1', format_string('%entity_type: Translated value set.', array('%entity_type' => $entity_type)));
|
||||
$this->assertEqual($field->getLangcode(), $this->langcodes[1], format_string('%entity_type: Field object has the expected langcode.', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Make sure the untranslated value stays.
|
||||
$field = $entity->get($this->fieldName);
|
||||
$this->assertEqual($field->value, 'default value', 'Untranslated value stays.');
|
||||
$this->assertEqual($field->getLangcode(), $default_langcode, 'Untranslated value has the expected langcode.');
|
||||
|
||||
$translations[$this->langcodes[1]] = \Drupal::languageManager()->getLanguage($this->langcodes[1]);
|
||||
$this->assertEqual($entity->getTranslationLanguages(FALSE), $translations, 'Translations retrieved.');
|
||||
|
||||
// Try to get a value using a language code for a non-existing translation.
|
||||
$message = 'Getting a non existing translation results in an error.';
|
||||
try {
|
||||
$entity->getTranslation($this->langcodes[2])->get($this->fieldName)->value;
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (\InvalidArgumentException $e) {
|
||||
$this->pass($message);
|
||||
}
|
||||
|
||||
// Try to get a not available translation.
|
||||
$this->assertNull($entity->addTranslation($this->langcodes[2])->get($this->fieldName)->value, format_string('%entity_type: A translation that is not available is NULL.', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Try to get a value using an invalid language code.
|
||||
$message = 'Getting an invalid translation results in an error.';
|
||||
try {
|
||||
$entity->getTranslation('invalid')->get($this->fieldName)->value;
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (\InvalidArgumentException $e) {
|
||||
$this->pass($message);
|
||||
}
|
||||
|
||||
// Try to set a value using an invalid language code.
|
||||
try {
|
||||
$entity->getTranslation('invalid')->set($this->fieldName, NULL);
|
||||
$this->fail(format_string('%entity_type: Setting a translation for an invalid language throws an exception.', array('%entity_type' => $entity_type)));
|
||||
}
|
||||
catch (\InvalidArgumentException $e) {
|
||||
$this->pass(format_string('%entity_type: Setting a translation for an invalid language throws an exception.', array('%entity_type' => $entity_type)));
|
||||
}
|
||||
|
||||
// Set the value in default language.
|
||||
$field_name = 'field_test_text';
|
||||
$entity->getTranslation($this->langcodes[1])->set($field_name, array(0 => array('value' => 'default value2')));
|
||||
// Get the value.
|
||||
$field = $entity->get($field_name);
|
||||
$this->assertEqual($field->value, 'default value2', format_string('%entity_type: Untranslated value set into a translation in non-strict mode.', array('%entity_type' => $entity_type)));
|
||||
$this->assertEqual($field->getLangcode(), $default_langcode, format_string('%entity_type: Field object has the expected langcode.', array('%entity_type' => $entity_type)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests multilingual properties.
|
||||
*/
|
||||
public function testMultilingualProperties() {
|
||||
// Test all entity variations with data table support.
|
||||
foreach (entity_test_entity_types(ENTITY_TEST_TYPES_MULTILINGUAL) as $entity_type) {
|
||||
$this->doTestMultilingualProperties($entity_type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the multilingual property tests for the given entity type.
|
||||
*
|
||||
* @param string $entity_type
|
||||
* The entity type to run the tests with.
|
||||
*/
|
||||
protected function doTestMultilingualProperties($entity_type) {
|
||||
$langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('langcode');
|
||||
$default_langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('default_langcode');
|
||||
$name = $this->randomMachineName();
|
||||
$uid = mt_rand(0, 127);
|
||||
$langcode = $this->langcodes[0];
|
||||
|
||||
// Create a language neutral entity and check that properties are stored
|
||||
// as language neutral.
|
||||
$entity = $this->container->get('entity_type.manager')
|
||||
->getStorage($entity_type)
|
||||
->create(array('name' => $name, 'user_id' => $uid, $langcode_key => LanguageInterface::LANGCODE_NOT_SPECIFIED));
|
||||
$entity->save();
|
||||
$entity = entity_load($entity_type, $entity->id());
|
||||
$default_langcode = $entity->language()->getId();
|
||||
$this->assertEqual($default_langcode, LanguageInterface::LANGCODE_NOT_SPECIFIED, format_string('%entity_type: Entity created as language neutral.', array('%entity_type' => $entity_type)));
|
||||
$field = $entity->getTranslation(LanguageInterface::LANGCODE_DEFAULT)->get('name');
|
||||
$this->assertEqual($name, $field->value, format_string('%entity_type: The entity name has been correctly stored as language neutral.', array('%entity_type' => $entity_type)));
|
||||
$this->assertEqual($default_langcode, $field->getLangcode(), format_string('%entity_type: The field object has the expect langcode.', array('%entity_type' => $entity_type)));
|
||||
$this->assertEqual($uid, $entity->getTranslation(LanguageInterface::LANGCODE_DEFAULT)->get('user_id')->target_id, format_string('%entity_type: The entity author has been correctly stored as language neutral.', array('%entity_type' => $entity_type)));
|
||||
|
||||
$translation = $entity->getTranslation(LanguageInterface::LANGCODE_DEFAULT);
|
||||
$field = $translation->get('name');
|
||||
$this->assertEqual($name, $field->value, format_string('%entity_type: The entity name defaults to neutral language.', array('%entity_type' => $entity_type)));
|
||||
$this->assertEqual($default_langcode, $field->getLangcode(), format_string('%entity_type: The field object has the expect langcode.', array('%entity_type' => $entity_type)));
|
||||
$this->assertEqual($uid, $translation->get('user_id')->target_id, format_string('%entity_type: The entity author defaults to neutral language.', array('%entity_type' => $entity_type)));
|
||||
$field = $entity->get('name');
|
||||
$this->assertEqual($name, $field->value, format_string('%entity_type: The entity name can be retrieved without specifying a language.', array('%entity_type' => $entity_type)));
|
||||
$this->assertEqual($default_langcode, $field->getLangcode(), format_string('%entity_type: The field object has the expect langcode.', array('%entity_type' => $entity_type)));
|
||||
$this->assertEqual($uid, $entity->get('user_id')->target_id, format_string('%entity_type: The entity author can be retrieved without specifying a language.', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Create a language-aware entity and check that properties are stored
|
||||
// as language-aware.
|
||||
$entity = $this->container->get('entity_type.manager')
|
||||
->getStorage($entity_type)
|
||||
->create(array('name' => $name, 'user_id' => $uid, $langcode_key => $langcode));
|
||||
$entity->save();
|
||||
$entity = entity_load($entity_type, $entity->id());
|
||||
$default_langcode = $entity->language()->getId();
|
||||
$this->assertEqual($default_langcode, $langcode, format_string('%entity_type: Entity created as language specific.', array('%entity_type' => $entity_type)));
|
||||
$field = $entity->getTranslation($langcode)->get('name');
|
||||
$this->assertEqual($name, $field->value, format_string('%entity_type: The entity name has been correctly stored as a language-aware property.', array('%entity_type' => $entity_type)));
|
||||
$this->assertEqual($default_langcode, $field->getLangcode(), format_string('%entity_type: The field object has the expect langcode.', array('%entity_type' => $entity_type)));
|
||||
$this->assertEqual($uid, $entity->getTranslation($langcode)->get('user_id')->target_id, format_string('%entity_type: The entity author has been correctly stored as a language-aware property.', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Create property translations.
|
||||
$properties = array();
|
||||
$default_langcode = $langcode;
|
||||
foreach ($this->langcodes as $langcode) {
|
||||
if ($langcode != $default_langcode) {
|
||||
$properties[$langcode] = array(
|
||||
'name' => array(0 => $this->randomMachineName()),
|
||||
'user_id' => array(0 => mt_rand(128, 256)),
|
||||
);
|
||||
}
|
||||
else {
|
||||
$properties[$langcode] = array(
|
||||
'name' => array(0 => $name),
|
||||
'user_id' => array(0 => $uid),
|
||||
);
|
||||
}
|
||||
$translation = $entity->hasTranslation($langcode) ? $entity->getTranslation($langcode) : $entity->addTranslation($langcode);
|
||||
foreach ($properties[$langcode] as $field_name => $values) {
|
||||
$translation->set($field_name, $values);
|
||||
}
|
||||
}
|
||||
$entity->save();
|
||||
|
||||
// Check that property translation were correctly stored.
|
||||
$entity = entity_load($entity_type, $entity->id());
|
||||
foreach ($this->langcodes as $langcode) {
|
||||
$args = array(
|
||||
'%entity_type' => $entity_type,
|
||||
'%langcode' => $langcode,
|
||||
);
|
||||
$field = $entity->getTranslation($langcode)->get('name');
|
||||
$this->assertEqual($properties[$langcode]['name'][0], $field->value, format_string('%entity_type: The entity name has been correctly stored for language %langcode.', $args));
|
||||
$field_langcode = ($langcode == $entity->language()->getId()) ? $default_langcode : $langcode;
|
||||
$this->assertEqual($field_langcode, $field->getLangcode(), format_string('%entity_type: The field object has the expected langcode %langcode.', $args));
|
||||
$this->assertEqual($properties[$langcode]['user_id'][0], $entity->getTranslation($langcode)->get('user_id')->target_id, format_string('%entity_type: The entity author has been correctly stored for language %langcode.', $args));
|
||||
}
|
||||
|
||||
// Test query conditions (cache is reset at each call).
|
||||
$translated_id = $entity->id();
|
||||
// Create an additional entity with only the uid set. The uid for the
|
||||
// original language is the same of one used for a translation.
|
||||
$langcode = $this->langcodes[1];
|
||||
$this->container->get('entity_type.manager')
|
||||
->getStorage($entity_type)
|
||||
->create(array(
|
||||
'user_id' => $properties[$langcode]['user_id'],
|
||||
'name' => 'some name',
|
||||
$langcode_key => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
))
|
||||
->save();
|
||||
|
||||
$entities = entity_load_multiple($entity_type);
|
||||
$this->assertEqual(count($entities), 3, format_string('%entity_type: Three entities were created.', array('%entity_type' => $entity_type)));
|
||||
$entities = entity_load_multiple($entity_type, array($translated_id));
|
||||
$this->assertEqual(count($entities), 1, format_string('%entity_type: One entity correctly loaded by id.', array('%entity_type' => $entity_type)));
|
||||
$entities = entity_load_multiple_by_properties($entity_type, array('name' => $name));
|
||||
$this->assertEqual(count($entities), 2, format_string('%entity_type: Two entities correctly loaded by name.', array('%entity_type' => $entity_type)));
|
||||
// @todo The default language condition should go away in favor of an
|
||||
// explicit parameter.
|
||||
$entities = entity_load_multiple_by_properties($entity_type, array('name' => $properties[$langcode]['name'][0], $default_langcode_key => 0));
|
||||
$this->assertEqual(count($entities), 1, format_string('%entity_type: One entity correctly loaded by name translation.', array('%entity_type' => $entity_type)));
|
||||
$entities = entity_load_multiple_by_properties($entity_type, array($langcode_key => $default_langcode, 'name' => $name));
|
||||
$this->assertEqual(count($entities), 1, format_string('%entity_type: One entity correctly loaded by name and language.', array('%entity_type' => $entity_type)));
|
||||
|
||||
$entities = entity_load_multiple_by_properties($entity_type, array($langcode_key => $langcode, 'name' => $properties[$langcode]['name'][0]));
|
||||
$this->assertEqual(count($entities), 0, format_string('%entity_type: No entity loaded by name translation specifying the translation language.', array('%entity_type' => $entity_type)));
|
||||
$entities = entity_load_multiple_by_properties($entity_type, array($langcode_key => $langcode, 'name' => $properties[$langcode]['name'][0], $default_langcode_key => 0));
|
||||
$this->assertEqual(count($entities), 1, format_string('%entity_type: One entity loaded by name translation and language specifying to look for translations.', array('%entity_type' => $entity_type)));
|
||||
$entities = entity_load_multiple_by_properties($entity_type, array('user_id' => $properties[$langcode]['user_id'][0], $default_langcode_key => NULL));
|
||||
$this->assertEqual(count($entities), 2, format_string('%entity_type: Two entities loaded by uid without caring about property translatability.', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Test property conditions and orders with multiple languages in the same
|
||||
// query.
|
||||
$query = \Drupal::entityQuery($entity_type);
|
||||
$group = $query->andConditionGroup()
|
||||
->condition('user_id', $properties[$default_langcode]['user_id'][0], '=', $default_langcode)
|
||||
->condition('name', $properties[$default_langcode]['name'][0], '=', $default_langcode);
|
||||
$result = $query
|
||||
->condition($group)
|
||||
->condition('name', $properties[$langcode]['name'][0], '=', $langcode)
|
||||
->execute();
|
||||
$this->assertEqual(count($result), 1, format_string('%entity_type: One entity loaded by name and uid using different language meta conditions.', array('%entity_type' => $entity_type)));
|
||||
|
||||
// Test mixed property and field conditions.
|
||||
$entity = entity_load($entity_type, reset($result), TRUE);
|
||||
$field_value = $this->randomString();
|
||||
$entity->getTranslation($langcode)->set($this->fieldName, array(array('value' => $field_value)));
|
||||
$entity->save();
|
||||
$query = \Drupal::entityQuery($entity_type);
|
||||
$default_langcode_group = $query->andConditionGroup()
|
||||
->condition('user_id', $properties[$default_langcode]['user_id'][0], '=', $default_langcode)
|
||||
->condition('name', $properties[$default_langcode]['name'][0], '=', $default_langcode);
|
||||
$langcode_group = $query->andConditionGroup()
|
||||
->condition('name', $properties[$langcode]['name'][0], '=', $langcode)
|
||||
->condition("$this->fieldName.value", $field_value, '=', $langcode);
|
||||
$result = $query
|
||||
->condition($langcode_key, $default_langcode)
|
||||
->condition($default_langcode_group)
|
||||
->condition($langcode_group)
|
||||
->execute();
|
||||
$this->assertEqual(count($result), 1, format_string('%entity_type: One entity loaded by name, uid and field value using different language meta conditions.', array('%entity_type' => $entity_type)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the Entity Translation API behavior.
|
||||
*/
|
||||
function testEntityTranslationAPI() {
|
||||
// Test all entity variations with data table support.
|
||||
foreach (entity_test_entity_types(ENTITY_TEST_TYPES_MULTILINGUAL) as $entity_type) {
|
||||
$this->doTestEntityTranslationAPI($entity_type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the Entity Translation API tests for the given entity type.
|
||||
*
|
||||
* @param string $entity_type
|
||||
* The entity type to run the tests with.
|
||||
*/
|
||||
protected function doTestEntityTranslationAPI($entity_type) {
|
||||
$default_langcode = $this->langcodes[0];
|
||||
$langcode = $this->langcodes[1];
|
||||
$langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('langcode');
|
||||
$default_langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('default_langcode');
|
||||
|
||||
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
|
||||
$entity = $this->entityManager
|
||||
->getStorage($entity_type)
|
||||
->create(array('name' => $this->randomMachineName(), $langcode_key => LanguageInterface::LANGCODE_NOT_SPECIFIED));
|
||||
|
||||
$entity->save();
|
||||
$hooks = $this->getHooksInfo();
|
||||
$this->assertFalse($hooks, 'No entity translation hooks are fired when creating an entity.');
|
||||
|
||||
// Verify that we obtain the entity object itself when we attempt to
|
||||
// retrieve a translation referring to it.
|
||||
$translation = $entity->getTranslation(LanguageInterface::LANGCODE_NOT_SPECIFIED);
|
||||
$this->assertFalse($translation->isNewTranslation(), 'Existing translations are not marked as new.');
|
||||
$this->assertIdentical($entity, $translation, 'The translation object corresponding to a non-default language is the entity object itself when the entity is language-neutral.');
|
||||
$entity->{$langcode_key}->value = $default_langcode;
|
||||
$translation = $entity->getTranslation($default_langcode);
|
||||
$this->assertIdentical($entity, $translation, 'The translation object corresponding to the default language (explicit) is the entity object itself.');
|
||||
$translation = $entity->getTranslation(LanguageInterface::LANGCODE_DEFAULT);
|
||||
$this->assertIdentical($entity, $translation, 'The translation object corresponding to the default language (implicit) is the entity object itself.');
|
||||
$this->assertTrue($entity->{$default_langcode_key}->value, 'The translation object is the default one.');
|
||||
|
||||
// Verify that trying to retrieve a translation for a locked language when
|
||||
// the entity is language-aware causes an exception to be thrown.
|
||||
$message = 'A language-neutral translation cannot be retrieved.';
|
||||
try {
|
||||
$entity->getTranslation(LanguageInterface::LANGCODE_NOT_SPECIFIED);
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (\LogicException $e) {
|
||||
$this->pass($message);
|
||||
}
|
||||
|
||||
// Create a translation and verify that the translation object and the
|
||||
// original object behave independently.
|
||||
$name = $default_langcode . '_' . $this->randomMachineName();
|
||||
$entity->name->value = $name;
|
||||
$name_translated = $langcode . '_' . $this->randomMachineName();
|
||||
$translation = $entity->addTranslation($langcode);
|
||||
$this->assertTrue($translation->isNewTranslation(), 'Newly added translations are marked as new.');
|
||||
$this->assertNotIdentical($entity, $translation, 'The entity and the translation object differ from one another.');
|
||||
$this->assertTrue($entity->hasTranslation($langcode), 'The new translation exists.');
|
||||
$this->assertEqual($translation->language()->getId(), $langcode, 'The translation language matches the specified one.');
|
||||
$this->assertEqual($translation->{$langcode_key}->value, $langcode, 'The translation field language value matches the specified one.');
|
||||
$this->assertFalse($translation->{$default_langcode_key}->value, 'The translation object is not the default one.');
|
||||
$this->assertEqual($translation->getUntranslated()->language()->getId(), $default_langcode, 'The original language can still be retrieved.');
|
||||
$translation->name->value = $name_translated;
|
||||
$this->assertEqual($entity->name->value, $name, 'The original name is retained after setting a translated value.');
|
||||
$entity->name->value = $name;
|
||||
$this->assertEqual($translation->name->value, $name_translated, 'The translated name is retained after setting the original value.');
|
||||
|
||||
// Save the translation and check that the expected hooks are fired.
|
||||
$translation->save();
|
||||
$hooks = $this->getHooksInfo();
|
||||
|
||||
$this->assertEqual($hooks['entity_translation_create'], $langcode, 'The generic entity translation creation hook has fired.');
|
||||
$this->assertEqual($hooks[$entity_type . '_translation_create'], $langcode, 'The entity-type-specific entity translation creation hook has fired.');
|
||||
|
||||
$this->assertEqual($hooks['entity_translation_insert'], $langcode, 'The generic entity translation insertion hook has fired.');
|
||||
$this->assertEqual($hooks[$entity_type . '_translation_insert'], $langcode, 'The entity-type-specific entity translation insertion hook has fired.');
|
||||
|
||||
// Verify that changing translation language causes an exception to be
|
||||
// thrown.
|
||||
$message = 'The translation language cannot be changed.';
|
||||
try {
|
||||
$translation->{$langcode_key}->value = $this->langcodes[2];
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (\LogicException $e) {
|
||||
$this->pass($message);
|
||||
}
|
||||
|
||||
// Verify that reassigning the same translation language is allowed.
|
||||
$message = 'The translation language can be reassigned the same value.';
|
||||
try {
|
||||
$translation->{$langcode_key}->value = $langcode;
|
||||
$this->pass($message);
|
||||
}
|
||||
catch (\LogicException $e) {
|
||||
$this->fail($message);
|
||||
}
|
||||
|
||||
// Verify that changing the default translation flag causes an exception to
|
||||
// be thrown.
|
||||
foreach ($entity->getTranslationLanguages() as $t_langcode => $language) {
|
||||
$translation = $entity->getTranslation($t_langcode);
|
||||
$default = $translation->isDefaultTranslation();
|
||||
|
||||
$message = 'The default translation flag can be reassigned the same value.';
|
||||
try {
|
||||
$translation->{$default_langcode_key}->value = $default;
|
||||
$this->pass($message);
|
||||
}
|
||||
catch (\LogicException $e) {
|
||||
$this->fail($message);
|
||||
}
|
||||
|
||||
$message = 'The default translation flag cannot be changed.';
|
||||
try {
|
||||
$translation->{$default_langcode_key}->value = !$default;
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (\LogicException $e) {
|
||||
$this->pass($message);
|
||||
}
|
||||
|
||||
$this->assertEqual($translation->{$default_langcode_key}->value, $default);
|
||||
}
|
||||
|
||||
// Check that after loading an entity the language is the default one.
|
||||
$entity = $this->reloadEntity($entity);
|
||||
$this->assertEqual($entity->language()->getId(), $default_langcode, 'The loaded entity is the original one.');
|
||||
|
||||
// Add another translation and check that everything works as expected. A
|
||||
// new translation object can be obtained also by just specifying a valid
|
||||
// language.
|
||||
$langcode2 = $this->langcodes[2];
|
||||
$translation = $entity->addTranslation($langcode2);
|
||||
$value = $entity !== $translation && $translation->language()->getId() == $langcode2 && $entity->hasTranslation($langcode2);
|
||||
$this->assertTrue($value, 'A new translation object can be obtained also by specifying a valid language.');
|
||||
$this->assertEqual($entity->language()->getId(), $default_langcode, 'The original language has been preserved.');
|
||||
$translation->save();
|
||||
$hooks = $this->getHooksInfo();
|
||||
|
||||
$this->assertEqual($hooks['entity_translation_create'], $langcode2, 'The generic entity translation creation hook has fired.');
|
||||
$this->assertEqual($hooks[$entity_type . '_translation_create'], $langcode2, 'The entity-type-specific entity translation creation hook has fired.');
|
||||
|
||||
$this->assertEqual($hooks['entity_translation_insert'], $langcode2, 'The generic entity translation insertion hook has fired.');
|
||||
$this->assertEqual($hooks[$entity_type . '_translation_insert'], $langcode2, 'The entity-type-specific entity translation insertion hook has fired.');
|
||||
|
||||
// Verify that trying to manipulate a translation object referring to a
|
||||
// removed translation results in exceptions being thrown.
|
||||
$entity = $this->reloadEntity($entity);
|
||||
$translation = $entity->getTranslation($langcode2);
|
||||
$entity->removeTranslation($langcode2);
|
||||
foreach (array('get', 'set', '__get', '__set', 'createDuplicate') as $method) {
|
||||
$message = format_string('The @method method raises an exception when trying to manipulate a removed translation.', array('@method' => $method));
|
||||
try {
|
||||
$translation->{$method}('name', $this->randomMachineName());
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$this->pass($message);
|
||||
}
|
||||
}
|
||||
|
||||
// Verify that deletion hooks are fired when saving an entity with a removed
|
||||
// translation.
|
||||
$entity->save();
|
||||
$hooks = $this->getHooksInfo();
|
||||
$this->assertEqual($hooks['entity_translation_delete'], $langcode2, 'The generic entity translation deletion hook has fired.');
|
||||
$this->assertEqual($hooks[$entity_type . '_translation_delete'], $langcode2, 'The entity-type-specific entity translation deletion hook has fired.');
|
||||
$entity = $this->reloadEntity($entity);
|
||||
$this->assertFalse($entity->hasTranslation($langcode2), 'The translation does not appear among available translations after saving the entity.');
|
||||
|
||||
// Check that removing an invalid translation causes an exception to be
|
||||
// thrown.
|
||||
foreach (array($default_langcode, LanguageInterface::LANGCODE_DEFAULT, $this->randomMachineName()) as $invalid_langcode) {
|
||||
$message = format_string('Removing an invalid translation (@langcode) causes an exception to be thrown.', array('@langcode' => $invalid_langcode));
|
||||
try {
|
||||
$entity->removeTranslation($invalid_langcode);
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$this->pass($message);
|
||||
}
|
||||
}
|
||||
|
||||
// Check that hooks are fired only when actually storing data.
|
||||
$entity = $this->reloadEntity($entity);
|
||||
$entity->addTranslation($langcode2);
|
||||
$entity->removeTranslation($langcode2);
|
||||
$entity->save();
|
||||
$hooks = $this->getHooksInfo();
|
||||
|
||||
$this->assertTrue(isset($hooks['entity_translation_create']), 'The generic entity translation creation hook is run when adding and removing a translation without storing it.');
|
||||
unset($hooks['entity_translation_create']);
|
||||
$this->assertTrue(isset($hooks[$entity_type . '_translation_create']), 'The entity-type-specific entity translation creation hook is run when adding and removing a translation without storing it.');
|
||||
unset($hooks[$entity_type . '_translation_create']);
|
||||
|
||||
$this->assertFalse($hooks, 'No other hooks beyond the entity translation creation hooks are run when adding and removing a translation without storing it.');
|
||||
|
||||
// Check that hooks are fired only when actually storing data.
|
||||
$entity = $this->reloadEntity($entity);
|
||||
$entity->addTranslation($langcode2);
|
||||
$entity->save();
|
||||
$entity = $this->reloadEntity($entity);
|
||||
$this->assertTrue($entity->hasTranslation($langcode2), 'Entity has translation after adding one and saving.');
|
||||
$entity->removeTranslation($langcode2);
|
||||
$entity->save();
|
||||
$entity = $this->reloadEntity($entity);
|
||||
$this->assertFalse($entity->hasTranslation($langcode2), 'Entity does not have translation after removing it and saving.');
|
||||
// Reset hook firing information.
|
||||
$this->getHooksInfo();
|
||||
|
||||
// Verify that entity serialization does not cause stale references to be
|
||||
// left around.
|
||||
$entity = $this->reloadEntity($entity);
|
||||
$translation = $entity->getTranslation($langcode);
|
||||
$entity = unserialize(serialize($entity));
|
||||
$entity->name->value = $this->randomMachineName();
|
||||
$name = $default_langcode . '_' . $this->randomMachineName();
|
||||
$entity->getTranslation($default_langcode)->name->value = $name;
|
||||
$this->assertEqual($entity->name->value, $name, 'No stale reference for the translation object corresponding to the original language.');
|
||||
$translation2 = $entity->getTranslation($langcode);
|
||||
$translation2->name->value .= $this->randomMachineName();
|
||||
$this->assertNotEqual($translation->name->value, $translation2->name->value, 'No stale reference for the actual translation object.');
|
||||
$this->assertEqual($entity, $translation2->getUntranslated(), 'No stale reference in the actual translation object.');
|
||||
|
||||
// Verify that deep-cloning is still available when we are not instantiating
|
||||
// a translation object, which instead relies on shallow cloning.
|
||||
$entity = $this->reloadEntity($entity);
|
||||
$entity->getTranslation($langcode);
|
||||
$cloned = clone $entity;
|
||||
$translation = $cloned->getTranslation($langcode);
|
||||
$this->assertNotIdentical($entity, $translation->getUntranslated(), 'A cloned entity object has no reference to the original one.');
|
||||
$entity->removeTranslation($langcode);
|
||||
$this->assertFalse($entity->hasTranslation($langcode));
|
||||
$this->assertTrue($cloned->hasTranslation($langcode));
|
||||
|
||||
// Check that untranslatable field references keep working after serializing
|
||||
// and cloning the entity.
|
||||
$entity = $this->reloadEntity($entity);
|
||||
$type = $this->randomMachineName();
|
||||
$entity->getTranslation($langcode)->type->value = $type;
|
||||
$entity = unserialize(serialize($entity));
|
||||
$cloned = clone $entity;
|
||||
$translation = $cloned->getTranslation($langcode);
|
||||
$translation->type->value = strrev($type);
|
||||
$this->assertEqual($cloned->type->value, $translation->type->value, 'Untranslatable field references keep working after serializing and cloning the entity.');
|
||||
|
||||
// Check that per-language defaults are properly populated. The
|
||||
// 'entity_test_mul_default_value' entity type is translatable and uses
|
||||
// entity_test_field_default_value() as a "default value callback" for its
|
||||
// 'description' field.
|
||||
$entity = $this->entityManager
|
||||
->getStorage('entity_test_mul_default_value')
|
||||
->create(['name' => $this->randomMachineName(), 'langcode' => $langcode]);
|
||||
$translation = $entity->addTranslation($langcode2);
|
||||
$expected = array(
|
||||
array(
|
||||
'shape' => "shape:0:description_$langcode2",
|
||||
'color' => "color:0:description_$langcode2",
|
||||
),
|
||||
array(
|
||||
'shape' => "shape:1:description_$langcode2",
|
||||
'color' => "color:1:description_$langcode2",
|
||||
),
|
||||
);
|
||||
$this->assertEqual($translation->description->getValue(), $expected, 'Language-aware default values correctly populated.');
|
||||
$this->assertEqual($translation->description->getLangcode(), $langcode2, 'Field object has the expected langcode.');
|
||||
|
||||
// Reset hook firing information.
|
||||
$this->getHooksInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests language fallback applied to field and entity translations.
|
||||
*/
|
||||
function testLanguageFallback() {
|
||||
// Test all entity variations with data table support.
|
||||
foreach (entity_test_entity_types(ENTITY_TEST_TYPES_MULTILINGUAL) as $entity_type) {
|
||||
$this->doTestLanguageFallback($entity_type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the language fallback test for the given entity type.
|
||||
*
|
||||
* @param string $entity_type
|
||||
* The entity type to run the tests with.
|
||||
*/
|
||||
protected function doTestLanguageFallback($entity_type) {
|
||||
/** @var \Drupal\Core\Render\RendererInterface $renderer */
|
||||
$renderer = $this->container->get('renderer');
|
||||
|
||||
$current_langcode = $this->languageManager->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)->getId();
|
||||
$this->langcodes[] = $current_langcode;
|
||||
|
||||
$values = array();
|
||||
foreach ($this->langcodes as $langcode) {
|
||||
$values[$langcode]['name'] = $this->randomMachineName();
|
||||
$values[$langcode]['user_id'] = mt_rand(0, 127);
|
||||
}
|
||||
|
||||
$default_langcode = $this->langcodes[0];
|
||||
$langcode = $this->langcodes[1];
|
||||
$langcode2 = $this->langcodes[2];
|
||||
$langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('langcode');
|
||||
$languages = $this->languageManager->getLanguages();
|
||||
$language = ConfigurableLanguage::load($languages[$langcode]->getId());
|
||||
$language->set('weight', 1);
|
||||
$language->save();
|
||||
$this->languageManager->reset();
|
||||
|
||||
$controller = $this->entityManager->getStorage($entity_type);
|
||||
$entity = $controller->create(array($langcode_key => $default_langcode) + $values[$default_langcode]);
|
||||
$entity->save();
|
||||
|
||||
$entity->addTranslation($langcode, $values[$langcode]);
|
||||
$entity->save();
|
||||
|
||||
// Check that retrieving the current translation works as expected.
|
||||
$entity = $this->reloadEntity($entity);
|
||||
$translation = $this->entityManager->getTranslationFromContext($entity, $langcode2);
|
||||
$this->assertEqual($translation->language()->getId(), $default_langcode, 'The current translation language matches the expected one.');
|
||||
|
||||
// Check that language fallback respects language weight by default.
|
||||
$language = ConfigurableLanguage::load($languages[$langcode]->getId());
|
||||
$language->set('weight', -1);
|
||||
$language->save();
|
||||
$translation = $this->entityManager->getTranslationFromContext($entity, $langcode2);
|
||||
$this->assertEqual($translation->language()->getId(), $langcode, 'The current translation language matches the expected one.');
|
||||
|
||||
// Check that the current translation is properly returned.
|
||||
$translation = $this->entityManager->getTranslationFromContext($entity);
|
||||
$this->assertEqual($langcode, $translation->language()->getId(), 'The current translation language matches the topmost language fallback candidate.');
|
||||
$entity->addTranslation($current_langcode, $values[$current_langcode]);
|
||||
$translation = $this->entityManager->getTranslationFromContext($entity);
|
||||
$this->assertEqual($current_langcode, $translation->language()->getId(), 'The current translation language matches the current language.');
|
||||
|
||||
// Check that if the entity has no translation no fallback is applied.
|
||||
$entity2 = $controller->create(array($langcode_key => $default_langcode));
|
||||
// Get an view builder.
|
||||
$controller = $this->entityManager->getViewBuilder($entity_type);
|
||||
$entity2_build = $controller->view($entity2);
|
||||
$entity2_output = (string) $renderer->renderRoot($entity2_build);
|
||||
$translation = $this->entityManager->getTranslationFromContext($entity2, $default_langcode);
|
||||
$translation_build = $controller->view($translation);
|
||||
$translation_output = (string) $renderer->renderRoot($translation_build);
|
||||
$this->assertIdentical($entity2_output, $translation_output, 'When the entity has no translation no fallback is applied.');
|
||||
|
||||
// Checks that entity translations are rendered properly.
|
||||
$controller = $this->entityManager->getViewBuilder($entity_type);
|
||||
$build = $controller->view($entity);
|
||||
$renderer->renderRoot($build);
|
||||
$this->assertEqual($build['label']['#markup'], $values[$current_langcode]['name'], 'By default the entity is rendered in the current language.');
|
||||
|
||||
$langcodes = array_combine($this->langcodes, $this->langcodes);
|
||||
// We have no translation for the $langcode2 language, hence the expected
|
||||
// result is the topmost existing translation, that is $langcode.
|
||||
$langcodes[$langcode2] = $langcode;
|
||||
foreach ($langcodes as $desired => $expected) {
|
||||
$build = $controller->view($entity, 'full', $desired);
|
||||
// Unset the #cache key so that a fresh render is produced with each pass,
|
||||
// making the renderable array keys available to compare.
|
||||
unset($build['#cache']);
|
||||
$renderer->renderRoot($build);
|
||||
$this->assertEqual($build['label']['#markup'], $values[$expected]['name'], 'The entity is rendered in the expected language.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that field translatability is handled properly.
|
||||
*/
|
||||
function testFieldDefinitions() {
|
||||
// Check that field translatability can be altered to be enabled or disabled
|
||||
// in field definitions.
|
||||
$entity_type = 'entity_test_mulrev';
|
||||
$this->state->set('entity_test.field_definitions.translatable', array('name' => FALSE));
|
||||
$this->entityManager->clearCachedFieldDefinitions();
|
||||
$definitions = $this->entityManager->getBaseFieldDefinitions($entity_type);
|
||||
$this->assertFalse($definitions['name']->isTranslatable(), 'Field translatability can be disabled programmatically.');
|
||||
|
||||
$this->state->set('entity_test.field_definitions.translatable', array('name' => TRUE));
|
||||
$this->entityManager->clearCachedFieldDefinitions();
|
||||
$definitions = $this->entityManager->getBaseFieldDefinitions($entity_type);
|
||||
$this->assertTrue($definitions['name']->isTranslatable(), 'Field translatability can be enabled programmatically.');
|
||||
|
||||
// Check that field translatability is disabled by default.
|
||||
$base_field_definitions = EntityTestMulRev::baseFieldDefinitions($this->entityManager->getDefinition($entity_type));
|
||||
$this->assertTrue(!isset($base_field_definitions['id']->translatable), 'Translatability for the <em>id</em> field is not defined.');
|
||||
$this->assertFalse($definitions['id']->isTranslatable(), 'Field translatability is disabled by default.');
|
||||
|
||||
// Check that entity id keys have the expect translatability.
|
||||
$translatable_fields = array(
|
||||
'id' => TRUE,
|
||||
'uuid' => TRUE,
|
||||
'revision_id' => TRUE,
|
||||
'type' => TRUE,
|
||||
'langcode' => FALSE,
|
||||
);
|
||||
foreach ($translatable_fields as $name => $translatable) {
|
||||
$this->state->set('entity_test.field_definitions.translatable', array($name => $translatable));
|
||||
$this->entityManager->clearCachedFieldDefinitions();
|
||||
$message = format_string('Field %field cannot be translatable.', array('%field' => $name));
|
||||
|
||||
try {
|
||||
$this->entityManager->getBaseFieldDefinitions($entity_type);
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (\LogicException $e) {
|
||||
$this->pass($message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that changing entity language does not break field language.
|
||||
*/
|
||||
public function testLanguageChange() {
|
||||
// Test all entity variations with data table support.
|
||||
foreach (entity_test_entity_types(ENTITY_TEST_TYPES_MULTILINGUAL) as $entity_type) {
|
||||
$this->doTestLanguageChange($entity_type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the entity language change test for the given entity type.
|
||||
*
|
||||
* @param string $entity_type
|
||||
* The entity type to run the tests with.
|
||||
*/
|
||||
protected function doTestLanguageChange($entity_type) {
|
||||
$langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('langcode');
|
||||
$controller = $this->entityManager->getStorage($entity_type);
|
||||
$langcode = $this->langcodes[0];
|
||||
|
||||
// check that field languages match entity language regardless of field
|
||||
// translatability.
|
||||
$values = array(
|
||||
$langcode_key => $langcode,
|
||||
$this->fieldName => $this->randomMachineName(),
|
||||
$this->untranslatableFieldName => $this->randomMachineName(),
|
||||
);
|
||||
$entity = $controller->create($values);
|
||||
foreach (array($this->fieldName, $this->untranslatableFieldName) as $field_name) {
|
||||
$this->assertEqual($entity->get($field_name)->getLangcode(), $langcode, 'Field language works as expected.');
|
||||
}
|
||||
|
||||
// Check that field languages keep matching entity language even after
|
||||
// changing it.
|
||||
$langcode = $this->langcodes[1];
|
||||
$entity->{$langcode_key}->value = $langcode;
|
||||
foreach (array($this->fieldName, $this->untranslatableFieldName) as $field_name) {
|
||||
$this->assertEqual($entity->get($field_name)->getLangcode(), $langcode, 'Field language works as expected after changing entity language.');
|
||||
}
|
||||
|
||||
// Check that entity translation does not affect the language of original
|
||||
// field values and untranslatable ones.
|
||||
$langcode = $this->langcodes[0];
|
||||
$entity->addTranslation($this->langcodes[2], array($this->fieldName => $this->randomMachineName()));
|
||||
$entity->{$langcode_key}->value = $langcode;
|
||||
foreach (array($this->fieldName, $this->untranslatableFieldName) as $field_name) {
|
||||
$this->assertEqual($entity->get($field_name)->getLangcode(), $langcode, 'Field language works as expected after translating the entity and changing language.');
|
||||
}
|
||||
|
||||
// Check that setting the default language to an existing translation
|
||||
// language causes an exception to be thrown.
|
||||
$message = 'An exception is thrown when setting the default language to an existing translation language';
|
||||
try {
|
||||
$entity->{$langcode_key}->value = $this->langcodes[2];
|
||||
$this->fail($message);
|
||||
}
|
||||
catch (\InvalidArgumentException $e) {
|
||||
$this->pass($message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests how entity adapters work with translations.
|
||||
*/
|
||||
function testEntityAdapter() {
|
||||
$entity_type = 'entity_test';
|
||||
$default_langcode = 'en';
|
||||
$values[$default_langcode] = array('name' => $this->randomString());
|
||||
$controller = $this->entityManager->getStorage($entity_type);
|
||||
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
|
||||
$entity = $controller->create($values[$default_langcode]);
|
||||
|
||||
foreach ($this->langcodes as $langcode) {
|
||||
$values[$langcode] = array('name' => $this->randomString());
|
||||
$entity->addTranslation($langcode, $values[$langcode]);
|
||||
}
|
||||
|
||||
$langcodes = array_merge(array($default_langcode), $this->langcodes);
|
||||
foreach ($langcodes as $langcode) {
|
||||
$adapter = $entity->getTranslation($langcode)->getTypedData();
|
||||
$name = $adapter->get('name')->value;
|
||||
$this->assertEqual($name, $values[$langcode]['name'], SafeMarkup::format('Name correctly retrieved from "@langcode" adapter', array('@langcode' => $langcode)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if entity references are correct after adding a new translation.
|
||||
*/
|
||||
public function testFieldEntityReference() {
|
||||
$entity_type = 'entity_test_mul';
|
||||
$controller = $this->entityManager->getStorage($entity_type);
|
||||
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
|
||||
$entity = $controller->create();
|
||||
|
||||
foreach ($this->langcodes as $langcode) {
|
||||
$entity->addTranslation($langcode);
|
||||
}
|
||||
|
||||
$default_langcode = $entity->getUntranslated()->language()->getId();
|
||||
foreach (array_keys($entity->getTranslationLanguages()) as $langcode) {
|
||||
$translation = $entity->getTranslation($langcode);
|
||||
foreach ($translation->getFields() as $field_name => $field) {
|
||||
if ($field->getFieldDefinition()->isTranslatable()) {
|
||||
$args = ['%field_name' => $field_name, '%langcode' => $langcode];
|
||||
$this->assertEqual($langcode, $field->getEntity()->language()->getId(), format_string('Translatable field %field_name on translation %langcode has correct entity reference in translation %langcode.', $args));
|
||||
}
|
||||
else {
|
||||
$args = ['%field_name' => $field_name, '%langcode' => $langcode, '%default_langcode' => $default_langcode];
|
||||
$this->assertEqual($default_langcode, $field->getEntity()->language()->getId(), format_string('Non translatable field %field_name on translation %langcode has correct entity reference in the default translation %default_langcode.', $args));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if entity translation statuses are correct after removing two
|
||||
* translation.
|
||||
*/
|
||||
public function testDeleteEntityTranslation() {
|
||||
$entity_type = 'entity_test_mul';
|
||||
$controller = $this->entityManager->getStorage($entity_type);
|
||||
|
||||
// Create a translatable test field.
|
||||
$field_storage = FieldStorageConfig::create([
|
||||
'entity_type' => $entity_type,
|
||||
'field_name' => 'translatable_test_field',
|
||||
'type' => 'field_test',
|
||||
]);
|
||||
$field_storage->save();
|
||||
|
||||
$field = FieldConfig::create([
|
||||
'field_storage' => $field_storage,
|
||||
'label' => $this->randomMachineName(),
|
||||
'bundle' => $entity_type,
|
||||
]);
|
||||
$field->save();
|
||||
|
||||
// Create an untranslatable test field.
|
||||
$field_storage = FieldStorageConfig::create([
|
||||
'entity_type' => $entity_type,
|
||||
'field_name' => 'untranslatable_test_field',
|
||||
'type' => 'field_test',
|
||||
'translatable' => FALSE,
|
||||
]);
|
||||
$field_storage->save();
|
||||
|
||||
$field = FieldConfig::create([
|
||||
'field_storage' => $field_storage,
|
||||
'label' => $this->randomMachineName(),
|
||||
'bundle' => $entity_type,
|
||||
]);
|
||||
$field->save();
|
||||
|
||||
// Create an entity with both translatable and untranslatable test fields.
|
||||
$values = array(
|
||||
'name' => $this->randomString(),
|
||||
'translatable_test_field' => $this->randomString(),
|
||||
'untranslatable_test_field' => $this->randomString(),
|
||||
);
|
||||
|
||||
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
|
||||
$entity = $controller->create($values);
|
||||
|
||||
foreach ($this->langcodes as $langcode) {
|
||||
$entity->addTranslation($langcode, $values);
|
||||
}
|
||||
$entity->save();
|
||||
|
||||
// Assert there are no deleted languages in the lists yet.
|
||||
$this->assertNull(\Drupal::state()->get('entity_test.delete.translatable_test_field'));
|
||||
$this->assertNull(\Drupal::state()->get('entity_test.delete.untranslatable_test_field'));
|
||||
|
||||
// Remove the second and third langcodes from the entity.
|
||||
$entity->removeTranslation('l1');
|
||||
$entity->removeTranslation('l2');
|
||||
$entity->save();
|
||||
|
||||
// Ensure that for the translatable test field the second and third
|
||||
// langcodes are in the deleted languages list.
|
||||
$actual = \Drupal::state()->get('entity_test.delete.translatable_test_field');
|
||||
$expected_translatable = ['l1', 'l2'];
|
||||
sort($actual);
|
||||
sort($expected_translatable);
|
||||
$this->assertEqual($actual, $expected_translatable);
|
||||
// Ensure that the untranslatable test field is untouched.
|
||||
$this->assertNull(\Drupal::state()->get('entity_test.delete.untranslatable_test_field'));
|
||||
|
||||
// Delete the entity, which removes all remaining translations.
|
||||
$entity->delete();
|
||||
|
||||
// All languages have been deleted now.
|
||||
$actual = \Drupal::state()->get('entity_test.delete.translatable_test_field');
|
||||
$expected_translatable[] = 'en';
|
||||
$expected_translatable[] = 'l0';
|
||||
sort($actual);
|
||||
sort($expected_translatable);
|
||||
$this->assertEqual($actual, $expected_translatable);
|
||||
|
||||
// The untranslatable field is shared and only deleted once, for the
|
||||
// default langcode.
|
||||
$actual = \Drupal::state()->get('entity_test.delete.untranslatable_test_field');
|
||||
$expected_untranslatable = ['en'];
|
||||
sort($actual);
|
||||
sort($expected_untranslatable);
|
||||
$this->assertEqual($actual, $expected_untranslatable);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Entity;
|
||||
|
||||
use Drupal\Core\TypedData\DataDefinition;
|
||||
|
||||
/**
|
||||
* Tests validation constraints for EntityTypeConstraintValidator.
|
||||
*
|
||||
* @group Entity
|
||||
*/
|
||||
class EntityTypeConstraintValidatorTest extends EntityKernelTestBase {
|
||||
|
||||
/**
|
||||
* The typed data manager to use.
|
||||
*
|
||||
* @var \Drupal\Core\TypedData\TypedDataManager
|
||||
*/
|
||||
protected $typedData;
|
||||
|
||||
public static $modules = array('node', 'field', 'user');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->typedData = $this->container->get('typed_data_manager');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the EntityTypeConstraintValidator.
|
||||
*/
|
||||
public function testValidation() {
|
||||
// Create a typed data definition with an EntityType constraint.
|
||||
$entity_type = 'node';
|
||||
$definition = DataDefinition::create('entity_reference')
|
||||
->setConstraints(array(
|
||||
'EntityType' => $entity_type,
|
||||
)
|
||||
);
|
||||
|
||||
// Test the validation.
|
||||
$node = $this->container->get('entity.manager')->getStorage('node')->create(array('type' => 'page'));
|
||||
$typed_data = $this->typedData->create($definition, $node);
|
||||
$violations = $typed_data->validate();
|
||||
$this->assertEqual($violations->count(), 0, 'Validation passed for correct value.');
|
||||
|
||||
// Test the validation when an invalid value (in this case a user entity)
|
||||
// is passed.
|
||||
$account = $this->createUser();
|
||||
|
||||
$typed_data = $this->typedData->create($definition, $account);
|
||||
$violations = $typed_data->validate();
|
||||
$this->assertEqual($violations->count(), 1, 'Validation failed for incorrect value.');
|
||||
|
||||
// Make sure the information provided by a violation is correct.
|
||||
$violation = $violations[0];
|
||||
$this->assertEqual($violation->getMessage(), t('The entity must be of type %type.', array('%type' => $entity_type)), 'The message for invalid value is correct.');
|
||||
$this->assertEqual($violation->getRoot(), $typed_data, 'Violation root is correct.');
|
||||
$this->assertEqual($violation->getInvalidValue(), $account, 'The invalid value is set correctly in the violation.');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Entity;
|
||||
|
||||
/**
|
||||
* Tests entity level validation constraints.
|
||||
*
|
||||
* @group Entity
|
||||
*/
|
||||
class EntityTypeConstraintsTest extends EntityKernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installEntitySchema('entity_test_constraints');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests defining entity constraints via entity type annotations and hooks.
|
||||
*/
|
||||
public function testConstraintDefinition() {
|
||||
// Test reading the annotation. There should be two constraints, the defined
|
||||
// constraint and the automatically added EntityChanged constraint.
|
||||
$entity_type = $this->entityManager->getDefinition('entity_test_constraints');
|
||||
$default_constraints = ['NotNull' => [], 'EntityChanged' => NULL];
|
||||
$this->assertEqual($default_constraints, $entity_type->getConstraints());
|
||||
|
||||
// Enable our test module and test extending constraints.
|
||||
$this->enableModules(['entity_test_constraints']);
|
||||
$this->container->get('module_handler')->resetImplementations();
|
||||
|
||||
$extra_constraints = ['Test' => []];
|
||||
$this->state->set('entity_test_constraints.build', $extra_constraints);
|
||||
// Re-fetch the entity manager from the new container built after the new
|
||||
// modules were enabled.
|
||||
$this->entityManager = $this->container->get('entity.manager');
|
||||
$this->entityManager->clearCachedDefinitions();
|
||||
$entity_type = $this->entityManager->getDefinition('entity_test_constraints');
|
||||
$this->assertEqual($default_constraints + $extra_constraints, $entity_type->getConstraints());
|
||||
|
||||
// Test altering constraints.
|
||||
$altered_constraints = ['Test' => [ 'some_setting' => TRUE]];
|
||||
$this->state->set('entity_test_constraints.alter', $altered_constraints);
|
||||
// Clear the cache in state instance in the Drupal container, so it can pick
|
||||
// up the modified value.
|
||||
\Drupal::state()->resetCache();
|
||||
$this->entityManager->clearCachedDefinitions();
|
||||
$entity_type = $this->entityManager->getDefinition('entity_test_constraints');
|
||||
$this->assertEqual($altered_constraints, $entity_type->getConstraints());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests entity constraints are validated.
|
||||
*/
|
||||
public function testConstraintValidation() {
|
||||
$entity = $this->entityManager->getStorage('entity_test_constraints')->create();
|
||||
$entity->user_id->target_id = 0;
|
||||
$violations = $entity->validate();
|
||||
$this->assertEqual($violations->count(), 0, 'Validation passed.');
|
||||
$entity->save();
|
||||
$entity->changed->value = REQUEST_TIME - 86400;
|
||||
$violations = $entity->validate();
|
||||
$this->assertEqual($violations->count(), 1, 'Validation failed.');
|
||||
$this->assertEqual($violations[0]->getMessage(), t('The content has either been modified by another user, or you have already submitted modifications. As a result, your changes cannot be saved.'));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Entity;
|
||||
|
||||
use Drupal\Core\Entity\TypedData\EntityDataDefinition;
|
||||
use Drupal\Core\Entity\TypedData\EntityDataDefinitionInterface;
|
||||
use Drupal\Core\Field\BaseFieldDefinition;
|
||||
use Drupal\Core\Field\FieldDefinitionInterface;
|
||||
use Drupal\Core\TypedData\ComplexDataDefinitionInterface;
|
||||
use Drupal\Core\TypedData\DataReferenceDefinition;
|
||||
use Drupal\Core\TypedData\DataReferenceDefinitionInterface;
|
||||
use Drupal\Core\TypedData\ListDataDefinitionInterface;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests deriving metadata of entity and field data types.
|
||||
*
|
||||
* @group Entity
|
||||
*/
|
||||
class EntityTypedDataDefinitionTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* The typed data manager to use.
|
||||
*
|
||||
* @var \Drupal\Core\TypedData\TypedDataManager
|
||||
*/
|
||||
protected $typedDataManager;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('filter', 'text', 'node', 'user');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setup();
|
||||
$this->typedDataManager = $this->container->get('typed_data_manager');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests deriving metadata about fields.
|
||||
*/
|
||||
public function testFields() {
|
||||
$field_definition = BaseFieldDefinition::create('integer');
|
||||
// Fields are lists of complex data.
|
||||
$this->assertTrue($field_definition instanceof ListDataDefinitionInterface);
|
||||
$this->assertFalse($field_definition instanceof ComplexDataDefinitionInterface);
|
||||
$field_item_definition = $field_definition->getItemDefinition();
|
||||
$this->assertFalse($field_item_definition instanceof ListDataDefinitionInterface);
|
||||
$this->assertTrue($field_item_definition instanceof ComplexDataDefinitionInterface);
|
||||
|
||||
// Derive metadata about field item properties.
|
||||
$this->assertEqual(array_keys($field_item_definition->getPropertyDefinitions()), array('value'));
|
||||
$this->assertEqual($field_item_definition->getPropertyDefinition('value')->getDataType(), 'integer');
|
||||
$this->assertEqual($field_item_definition->getMainPropertyName(), 'value');
|
||||
$this->assertNull($field_item_definition->getPropertyDefinition('invalid'));
|
||||
|
||||
// Test accessing field item property metadata via the field definition.
|
||||
$this->assertTrue($field_definition instanceof FieldDefinitionInterface);
|
||||
$this->assertEqual(array_keys($field_definition->getPropertyDefinitions()), array('value'));
|
||||
$this->assertEqual($field_definition->getPropertyDefinition('value')->getDataType(), 'integer');
|
||||
$this->assertEqual($field_definition->getMainPropertyName(), 'value');
|
||||
$this->assertNull($field_definition->getPropertyDefinition('invalid'));
|
||||
|
||||
// Test using the definition factory for field item lists and field items.
|
||||
$field_item = $this->typedDataManager->createDataDefinition('field_item:integer');
|
||||
$this->assertFalse($field_item instanceof ListDataDefinitionInterface);
|
||||
$this->assertTrue($field_item instanceof ComplexDataDefinitionInterface);
|
||||
// Comparison should ignore the internal static cache, so compare the
|
||||
// serialized objects instead.
|
||||
$this->assertEqual(serialize($field_item_definition), serialize($field_item));
|
||||
|
||||
$field_definition2 = $this->typedDataManager->createListDataDefinition('field_item:integer');
|
||||
$this->assertTrue($field_definition2 instanceof ListDataDefinitionInterface);
|
||||
$this->assertFalse($field_definition2 instanceof ComplexDataDefinitionInterface);
|
||||
$this->assertEqual(serialize($field_definition), serialize($field_definition2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests deriving metadata about entities.
|
||||
*/
|
||||
public function testEntities() {
|
||||
$entity_definition = EntityDataDefinition::create('node');
|
||||
// Entities are complex data.
|
||||
$this->assertFalse($entity_definition instanceof ListDataDefinitionInterface);
|
||||
$this->assertTrue($entity_definition instanceof ComplexDataDefinitionInterface);
|
||||
|
||||
$field_definitions = $entity_definition->getPropertyDefinitions();
|
||||
// Comparison should ignore the internal static cache, so compare the
|
||||
// serialized objects instead.
|
||||
$this->assertEqual(serialize($field_definitions), serialize(\Drupal::entityManager()->getBaseFieldDefinitions('node')));
|
||||
$this->assertEqual($entity_definition->getPropertyDefinition('title')->getItemDefinition()->getDataType(), 'field_item:string');
|
||||
$this->assertNull($entity_definition->getMainPropertyName());
|
||||
$this->assertNull($entity_definition->getPropertyDefinition('invalid'));
|
||||
|
||||
$entity_definition2 = $this->typedDataManager->createDataDefinition('entity:node');
|
||||
$this->assertFalse($entity_definition2 instanceof ListDataDefinitionInterface);
|
||||
$this->assertTrue($entity_definition2 instanceof ComplexDataDefinitionInterface);
|
||||
$this->assertEqual(serialize($entity_definition), serialize($entity_definition2));
|
||||
|
||||
// Test that the definition factory creates the right definitions for all
|
||||
// entity data types variants.
|
||||
$this->assertEqual($this->typedDataManager->createDataDefinition('entity'), EntityDataDefinition::create());
|
||||
$this->assertEqual($this->typedDataManager->createDataDefinition('entity:node'), EntityDataDefinition::create('node'));
|
||||
|
||||
// Config entities don't support typed data.
|
||||
$entity_definition = EntityDataDefinition::create('node_type');
|
||||
$this->assertEqual(array(), $entity_definition->getPropertyDefinitions());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests deriving metadata from entity references.
|
||||
*/
|
||||
public function testEntityReferences() {
|
||||
$reference_definition = DataReferenceDefinition::create('entity');
|
||||
$this->assertTrue($reference_definition instanceof DataReferenceDefinitionInterface);
|
||||
|
||||
// Test retrieving metadata about the referenced data.
|
||||
$this->assertEqual($reference_definition->getTargetDefinition()->getDataType(), 'entity');
|
||||
$this->assertTrue($reference_definition->getTargetDefinition() instanceof EntityDataDefinitionInterface);
|
||||
|
||||
// Test that the definition factory creates the right definition object.
|
||||
$reference_definition2 = $this->typedDataManager->createDataDefinition('entity_reference');
|
||||
$this->assertTrue($reference_definition2 instanceof DataReferenceDefinitionInterface);
|
||||
$this->assertEqual($reference_definition2, $reference_definition);
|
||||
}
|
||||
|
||||
}
|
||||
103
core/tests/Drupal/KernelTests/Core/Entity/EntityUUIDTest.php
Normal file
103
core/tests/Drupal/KernelTests/Core/Entity/EntityUUIDTest.php
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Entity;
|
||||
|
||||
/**
|
||||
* Tests creation, saving, and loading of entity UUIDs.
|
||||
*
|
||||
* @group Entity
|
||||
*/
|
||||
class EntityUUIDTest extends EntityKernelTestBase {
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
foreach (entity_test_entity_types() as $entity_type_id) {
|
||||
// The entity_test schema is installed by the parent.
|
||||
if ($entity_type_id != 'entity_test') {
|
||||
$this->installEntitySchema($entity_type_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests UUID generation in entity CRUD operations.
|
||||
*/
|
||||
function testCRUD() {
|
||||
// All entity variations have to have the same results.
|
||||
foreach (entity_test_entity_types() as $entity_type) {
|
||||
$this->assertCRUD($entity_type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the UUID CRUD tests for the given entity type.
|
||||
*
|
||||
* @param string $entity_type
|
||||
* The entity type to run the tests with.
|
||||
*/
|
||||
protected function assertCRUD($entity_type) {
|
||||
// Verify that no UUID is auto-generated when passing one for creation.
|
||||
$uuid_service = $this->container->get('uuid');
|
||||
$uuid = $uuid_service->generate();
|
||||
$custom_entity = $this->container->get('entity_type.manager')
|
||||
->getStorage($entity_type)
|
||||
->create(array(
|
||||
'name' => $this->randomMachineName(),
|
||||
'uuid' => $uuid,
|
||||
));
|
||||
$this->assertIdentical($custom_entity->uuid(), $uuid);
|
||||
// Save this entity, so we have more than one later.
|
||||
$custom_entity->save();
|
||||
|
||||
// Verify that a new UUID is generated upon creating an entity.
|
||||
$entity = $this->container->get('entity_type.manager')
|
||||
->getStorage($entity_type)
|
||||
->create(array('name' => $this->randomMachineName()));
|
||||
$uuid = $entity->uuid();
|
||||
$this->assertTrue($uuid);
|
||||
|
||||
// Verify that the new UUID is different.
|
||||
$this->assertNotEqual($custom_entity->uuid(), $uuid);
|
||||
|
||||
// Verify that the UUID is retained upon saving.
|
||||
$entity->save();
|
||||
$this->assertIdentical($entity->uuid(), $uuid);
|
||||
|
||||
// Verify that the UUID is retained upon loading.
|
||||
$entity_loaded = entity_load($entity_type, $entity->id(), TRUE);
|
||||
$this->assertIdentical($entity_loaded->uuid(), $uuid);
|
||||
|
||||
// Verify that \Drupal::entityManager()->loadEntityByUuid() loads the same entity.
|
||||
$entity_loaded_by_uuid = \Drupal::entityManager()->loadEntityByUuid($entity_type, $uuid, TRUE);
|
||||
$this->assertIdentical($entity_loaded_by_uuid->uuid(), $uuid);
|
||||
$this->assertEqual($entity_loaded_by_uuid->id(), $entity_loaded->id());
|
||||
|
||||
// Creating a duplicate needs to result in a new UUID.
|
||||
$entity_duplicate = $entity->createDuplicate();
|
||||
foreach ($entity->getFields() as $property => $value) {
|
||||
switch($property) {
|
||||
case 'uuid':
|
||||
$this->assertNotNull($entity_duplicate->uuid());
|
||||
$this->assertNotNull($entity->uuid());
|
||||
$this->assertNotEqual($entity_duplicate->uuid(), $entity->uuid());
|
||||
break;
|
||||
case 'id':
|
||||
$this->assertNull($entity_duplicate->id());
|
||||
$this->assertNotNull($entity->id());
|
||||
$this->assertNotEqual($entity_duplicate->id(), $entity->id());
|
||||
break;
|
||||
case 'revision_id':
|
||||
$this->assertNull($entity_duplicate->getRevisionId());
|
||||
$this->assertNotNull($entity->getRevisionId());
|
||||
$this->assertNotEqual($entity_duplicate->getRevisionId(), $entity->getRevisionId());
|
||||
$this->assertNotEqual($entity_duplicate->{$property}->getValue(), $entity->{$property}->getValue());
|
||||
break;
|
||||
default:
|
||||
$this->assertEqual($entity_duplicate->{$property}->getValue(), $entity->{$property}->getValue());
|
||||
}
|
||||
}
|
||||
$entity_duplicate->save();
|
||||
$this->assertNotEqual($entity->id(), $entity_duplicate->id());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,203 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Entity;
|
||||
|
||||
use Drupal\Core\Entity\Plugin\Validation\Constraint\CompositeConstraintBase;
|
||||
|
||||
/**
|
||||
* Tests the Entity Validation API.
|
||||
*
|
||||
* @group Entity
|
||||
*/
|
||||
class EntityValidationTest extends EntityKernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('filter', 'text');
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $entityName;
|
||||
|
||||
/**
|
||||
* @var \Drupal\user\Entity\User
|
||||
*/
|
||||
protected $entityUser;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $entityFieldText;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create the test field.
|
||||
module_load_install('entity_test');
|
||||
entity_test_install();
|
||||
|
||||
// Install required default configuration for filter module.
|
||||
$this->installConfig(array('system', 'filter'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a test entity.
|
||||
*
|
||||
* @param string $entity_type
|
||||
* An entity type.
|
||||
*
|
||||
* @return \Drupal\Core\Entity\EntityInterface
|
||||
* The created test entity.
|
||||
*/
|
||||
protected function createTestEntity($entity_type) {
|
||||
$this->entityName = $this->randomMachineName();
|
||||
$this->entityUser = $this->createUser();
|
||||
|
||||
// Pass in the value of the name field when creating. With the user
|
||||
// field we test setting a field after creation.
|
||||
$entity = $this->container->get('entity_type.manager')
|
||||
->getStorage($entity_type)
|
||||
->create();
|
||||
$entity->user_id->target_id = $this->entityUser->id();
|
||||
$entity->name->value = $this->entityName;
|
||||
|
||||
// Set a value for the test field.
|
||||
if ($entity->hasField('field_test_text')) {
|
||||
$this->entityFieldText = $this->randomMachineName();
|
||||
$entity->field_test_text->value = $this->entityFieldText;
|
||||
}
|
||||
|
||||
return $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests validating test entity types.
|
||||
*/
|
||||
public function testValidation() {
|
||||
// Ensure that the constraint manager is marked as cached cleared.
|
||||
|
||||
// Use the protected property on the cache_clearer first to check whether
|
||||
// the constraint manager is added there.
|
||||
|
||||
// Ensure that the proxy class is initialized, which has the necessary
|
||||
// method calls attached.
|
||||
\Drupal::service('plugin.cache_clearer');
|
||||
$plugin_cache_clearer = \Drupal::service('drupal.proxy_original_service.plugin.cache_clearer');
|
||||
$get_cached_discoveries = function () {
|
||||
return $this->cachedDiscoveries;
|
||||
};
|
||||
$get_cached_discoveries = $get_cached_discoveries->bindTo($plugin_cache_clearer, $plugin_cache_clearer);
|
||||
$cached_discoveries = $get_cached_discoveries();
|
||||
$cached_discovery_classes = [];
|
||||
foreach ($cached_discoveries as $cached_discovery) {
|
||||
$cached_discovery_classes[] = get_class($cached_discovery);
|
||||
}
|
||||
$this->assertTrue(in_array('Drupal\Core\Validation\ConstraintManager', $cached_discovery_classes));
|
||||
|
||||
// All entity variations have to have the same results.
|
||||
foreach (entity_test_entity_types() as $entity_type) {
|
||||
$this->checkValidation($entity_type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the validation test set for a defined entity type.
|
||||
*
|
||||
* @param string $entity_type
|
||||
* The entity type to run the tests with.
|
||||
*/
|
||||
protected function checkValidation($entity_type) {
|
||||
$entity = $this->createTestEntity($entity_type);
|
||||
$violations = $entity->validate();
|
||||
$this->assertEqual($violations->count(), 0, 'Validation passes.');
|
||||
|
||||
// Test triggering a fail for each of the constraints specified.
|
||||
$test_entity = clone $entity;
|
||||
$test_entity->id->value = -1;
|
||||
$violations = $test_entity->validate();
|
||||
$this->assertEqual($violations->count(), 1, 'Validation failed.');
|
||||
$this->assertEqual($violations[0]->getMessage(), t('%name: The integer must be larger or equal to %min.', array('%name' => 'ID', '%min' => 0)));
|
||||
|
||||
$test_entity = clone $entity;
|
||||
$test_entity->uuid->value = $this->randomString(129);
|
||||
$violations = $test_entity->validate();
|
||||
$this->assertEqual($violations->count(), 1, 'Validation failed.');
|
||||
$this->assertEqual($violations[0]->getMessage(), t('%name: may not be longer than @max characters.', array('%name' => 'UUID', '@max' => 128)));
|
||||
|
||||
$test_entity = clone $entity;
|
||||
$langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('langcode');
|
||||
$test_entity->{$langcode_key}->value = $this->randomString(13);
|
||||
$violations = $test_entity->validate();
|
||||
// This should fail on AllowedValues and Length constraints.
|
||||
$this->assertEqual($violations->count(), 2, 'Validation failed.');
|
||||
$this->assertEqual($violations[0]->getMessage(), t('This value is too long. It should have %limit characters or less.', array('%limit' => '12')));
|
||||
$this->assertEqual($violations[1]->getMessage(), t('The value you selected is not a valid choice.'));
|
||||
|
||||
$test_entity = clone $entity;
|
||||
$test_entity->type->value = NULL;
|
||||
$violations = $test_entity->validate();
|
||||
$this->assertEqual($violations->count(), 1, 'Validation failed.');
|
||||
$this->assertEqual($violations[0]->getMessage(), t('This value should not be null.'));
|
||||
|
||||
$test_entity = clone $entity;
|
||||
$test_entity->name->value = $this->randomString(33);
|
||||
$violations = $test_entity->validate();
|
||||
$this->assertEqual($violations->count(), 1, 'Validation failed.');
|
||||
$this->assertEqual($violations[0]->getMessage(), t('%name: may not be longer than @max characters.', array('%name' => 'Name', '@max' => 32)));
|
||||
|
||||
// Make sure the information provided by a violation is correct.
|
||||
$violation = $violations[0];
|
||||
$this->assertEqual($violation->getRoot()->getValue(), $test_entity, 'Violation root is entity.');
|
||||
$this->assertEqual($violation->getPropertyPath(), 'name.0.value', 'Violation property path is correct.');
|
||||
$this->assertEqual($violation->getInvalidValue(), $test_entity->name->value, 'Violation contains invalid value.');
|
||||
|
||||
$test_entity = clone $entity;
|
||||
$test_entity->set('user_id', 9999);
|
||||
$violations = $test_entity->validate();
|
||||
$this->assertEqual($violations->count(), 1, 'Validation failed.');
|
||||
$this->assertEqual($violations[0]->getMessage(), t('The referenced entity (%type: %id) does not exist.', array('%type' => 'user', '%id' => 9999)));
|
||||
|
||||
$test_entity = clone $entity;
|
||||
$test_entity->field_test_text->format = $this->randomString(33);
|
||||
$violations = $test_entity->validate();
|
||||
$this->assertEqual($violations->count(), 1, 'Validation failed.');
|
||||
$this->assertEqual($violations[0]->getMessage(), t('The value you selected is not a valid choice.'));
|
||||
|
||||
// Make sure the information provided by a violation is correct.
|
||||
$violation = $violations[0];
|
||||
$this->assertEqual($violation->getRoot()->getValue(), $test_entity, 'Violation root is entity.');
|
||||
$this->assertEqual($violation->getPropertyPath(), 'field_test_text.0.format', 'Violation property path is correct.');
|
||||
$this->assertEqual($violation->getInvalidValue(), $test_entity->field_test_text->format, 'Violation contains invalid value.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests composite constraints.
|
||||
*/
|
||||
public function testCompositeConstraintValidation() {
|
||||
$entity = $this->createTestEntity('entity_test_composite_constraint');
|
||||
$violations = $entity->validate();
|
||||
$this->assertEqual($violations->count(), 0);
|
||||
|
||||
// Trigger violation condition.
|
||||
$entity->name->value = 'test';
|
||||
$entity->type->value = 'test2';
|
||||
$violations = $entity->validate();
|
||||
$this->assertEqual($violations->count(), 1);
|
||||
|
||||
// Make sure we can determine this is composite constraint.
|
||||
$constraint = $violations[0]->getConstraint();
|
||||
$this->assertTrue($constraint instanceof CompositeConstraintBase, 'Constraint is composite constraint.');
|
||||
$this->assertEqual('type', $violations[0]->getPropertyPath());
|
||||
|
||||
/** @var CompositeConstraintBase $constraint */
|
||||
$this->assertEqual($constraint->coversFields(), ['name', 'type'], 'Information about covered fields can be retrieved.');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,212 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\KernelTests\Core\Entity;
|
||||
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\field\Tests\EntityReference\EntityReferenceTestTrait;
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\user\Entity\Role;
|
||||
use Drupal\user\RoleInterface;
|
||||
|
||||
/**
|
||||
* Tests the entity view builder.
|
||||
*
|
||||
* @group Entity
|
||||
*/
|
||||
class EntityViewBuilderTest extends EntityKernelTestBase {
|
||||
|
||||
use EntityReferenceTestTrait;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->installConfig(array('user', 'entity_test'));
|
||||
|
||||
// Give anonymous users permission to view test entities.
|
||||
Role::load(RoleInterface::ANONYMOUS_ID)
|
||||
->grantPermission('view test entity')
|
||||
->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests entity render cache handling.
|
||||
*/
|
||||
public function testEntityViewBuilderCache() {
|
||||
/** @var \Drupal\Core\Render\RendererInterface $renderer */
|
||||
$renderer = $this->container->get('renderer');
|
||||
$cache_contexts_manager = \Drupal::service("cache_contexts_manager");
|
||||
$cache = \Drupal::cache();
|
||||
|
||||
// Force a request via GET so we can get drupal_render() cache working.
|
||||
$request = \Drupal::request();
|
||||
$request_method = $request->server->get('REQUEST_METHOD');
|
||||
$request->setMethod('GET');
|
||||
|
||||
$entity_test = $this->createTestEntity('entity_test');
|
||||
|
||||
// Test that new entities (before they are saved for the first time) do not
|
||||
// generate a cache entry.
|
||||
$build = $this->container->get('entity.manager')->getViewBuilder('entity_test')->view($entity_test, 'full');
|
||||
$this->assertTrue(isset($build['#cache']) && array_keys($build['#cache']) == ['tags', 'contexts', 'max-age'], 'The render array element of new (unsaved) entities is not cached, but does have cache tags set.');
|
||||
|
||||
// Get a fully built entity view render array.
|
||||
$entity_test->save();
|
||||
$build = $this->container->get('entity.manager')->getViewBuilder('entity_test')->view($entity_test, 'full');
|
||||
$cid_parts = array_merge($build['#cache']['keys'], $cache_contexts_manager->convertTokensToKeys(['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme', 'user.permissions'])->getKeys());
|
||||
$cid = implode(':', $cid_parts);
|
||||
$bin = $build['#cache']['bin'];
|
||||
|
||||
// Mock the build array to not require the theme registry.
|
||||
unset($build['#theme']);
|
||||
$build['#markup'] = 'entity_render_test';
|
||||
|
||||
// Test that a cache entry is created.
|
||||
$renderer->renderRoot($build);
|
||||
$this->assertTrue($this->container->get('cache.' . $bin)->get($cid), 'The entity render element has been cached.');
|
||||
|
||||
// Re-save the entity and check that the cache entry has been deleted.
|
||||
$cache->set('kittens', 'Kitten data', Cache::PERMANENT, $build['#cache']['tags']);
|
||||
$entity_test->save();
|
||||
$this->assertFalse($this->container->get('cache.' . $bin)->get($cid), 'The entity render cache has been cleared when the entity was saved.');
|
||||
$this->assertFalse($cache->get('kittens'), 'The entity saving has invalidated cache tags.');
|
||||
|
||||
// Rebuild the render array (creating a new cache entry in the process) and
|
||||
// delete the entity to check the cache entry is deleted.
|
||||
unset($build['#printed']);
|
||||
$renderer->renderRoot($build);
|
||||
$this->assertTrue($this->container->get('cache.' . $bin)->get($cid), 'The entity render element has been cached.');
|
||||
$entity_test->delete();
|
||||
$this->assertFalse($this->container->get('cache.' . $bin)->get($cid), 'The entity render cache has been cleared when the entity was deleted.');
|
||||
|
||||
// Restore the previous request method.
|
||||
$request->setMethod($request_method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests entity render cache with references.
|
||||
*/
|
||||
public function testEntityViewBuilderCacheWithReferences() {
|
||||
/** @var \Drupal\Core\Render\RendererInterface $renderer */
|
||||
$renderer = $this->container->get('renderer');
|
||||
$cache_contexts_manager = \Drupal::service("cache_contexts_manager");
|
||||
|
||||
// Force a request via GET so we can get drupal_render() cache working.
|
||||
$request = \Drupal::request();
|
||||
$request_method = $request->server->get('REQUEST_METHOD');
|
||||
$request->setMethod('GET');
|
||||
|
||||
// Create an entity reference field and an entity that will be referenced.
|
||||
$this->createEntityReferenceField('entity_test', 'entity_test', 'reference_field', 'Reference', 'entity_test');
|
||||
entity_get_display('entity_test', 'entity_test', 'full')->setComponent('reference_field', [
|
||||
'type' => 'entity_reference_entity_view',
|
||||
'settings' => ['link' => FALSE],
|
||||
])->save();
|
||||
$entity_test_reference = $this->createTestEntity('entity_test');
|
||||
$entity_test_reference->save();
|
||||
|
||||
// Get a fully built entity view render array for the referenced entity.
|
||||
$build = $this->container->get('entity.manager')->getViewBuilder('entity_test')->view($entity_test_reference, 'full');
|
||||
$cid_parts = array_merge($build['#cache']['keys'], $cache_contexts_manager->convertTokensToKeys(['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme', 'user.permissions'])->getKeys());
|
||||
$cid_reference = implode(':', $cid_parts);
|
||||
$bin_reference = $build['#cache']['bin'];
|
||||
|
||||
// Mock the build array to not require the theme registry.
|
||||
unset($build['#theme']);
|
||||
$build['#markup'] = 'entity_render_test';
|
||||
$renderer->renderRoot($build);
|
||||
|
||||
// Test that a cache entry was created for the referenced entity.
|
||||
$this->assertTrue($this->container->get('cache.' . $bin_reference)->get($cid_reference), 'The entity render element for the referenced entity has been cached.');
|
||||
|
||||
// Create another entity that references the first one.
|
||||
$entity_test = $this->createTestEntity('entity_test');
|
||||
$entity_test->reference_field->entity = $entity_test_reference;
|
||||
$entity_test->save();
|
||||
|
||||
// Get a fully built entity view render array.
|
||||
$build = $this->container->get('entity.manager')->getViewBuilder('entity_test')->view($entity_test, 'full');
|
||||
$cid_parts = array_merge($build['#cache']['keys'], $cache_contexts_manager->convertTokensToKeys(['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme', 'user.permissions'])->getKeys());
|
||||
$cid = implode(':', $cid_parts);
|
||||
$bin = $build['#cache']['bin'];
|
||||
|
||||
// Mock the build array to not require the theme registry.
|
||||
unset($build['#theme']);
|
||||
$build['#markup'] = 'entity_render_test';
|
||||
$renderer->renderRoot($build);
|
||||
|
||||
// Test that a cache entry is created.
|
||||
$this->assertTrue($this->container->get('cache.' . $bin)->get($cid), 'The entity render element has been cached.');
|
||||
|
||||
// Save the entity and verify that both cache entries have been deleted.
|
||||
$entity_test_reference->save();
|
||||
$this->assertFalse($this->container->get('cache.' . $bin)->get($cid), 'The entity render cache has been cleared when the entity was deleted.');
|
||||
$this->assertFalse($this->container->get('cache.' . $bin_reference)->get($cid_reference), 'The entity render cache for the referenced entity has been cleared when the entity was deleted.');
|
||||
|
||||
// Restore the previous request method.
|
||||
$request->setMethod($request_method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests entity render cache toggling.
|
||||
*/
|
||||
public function testEntityViewBuilderCacheToggling() {
|
||||
$entity_test = $this->createTestEntity('entity_test');
|
||||
$entity_test->save();
|
||||
|
||||
// Test a view mode in default conditions: render caching is enabled for
|
||||
// the entity type and the view mode.
|
||||
$build = $this->container->get('entity.manager')->getViewBuilder('entity_test')->view($entity_test, 'full');
|
||||
$this->assertTrue(isset($build['#cache']) && array_keys($build['#cache']) == ['tags', 'contexts', 'max-age', 'keys', 'bin'] , 'A view mode with render cache enabled has the correct output (cache tags, keys, contexts, max-age and bin).');
|
||||
|
||||
// Test that a view mode can opt out of render caching.
|
||||
$build = $this->container->get('entity.manager')->getViewBuilder('entity_test')->view($entity_test, 'test');
|
||||
$this->assertTrue(isset($build['#cache']) && array_keys($build['#cache']) == ['tags', 'contexts', 'max-age'], 'A view mode with render cache disabled has the correct output (only cache tags, contexts and max-age).');
|
||||
|
||||
// Test that an entity type can opt out of render caching completely.
|
||||
$entity_test_no_cache = $this->createTestEntity('entity_test_label');
|
||||
$entity_test_no_cache->save();
|
||||
$build = $this->container->get('entity.manager')->getViewBuilder('entity_test_label')->view($entity_test_no_cache, 'full');
|
||||
$this->assertTrue(isset($build['#cache']) && array_keys($build['#cache']) == ['tags', 'contexts', 'max-age'], 'An entity type can opt out of render caching regardless of view mode configuration, but always has cache tags, contexts and max-age set.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests weighting of display components.
|
||||
*/
|
||||
public function testEntityViewBuilderWeight() {
|
||||
/** @var \Drupal\Core\Render\RendererInterface $renderer */
|
||||
$renderer = $this->container->get('renderer');
|
||||
|
||||
// Set a weight for the label component.
|
||||
entity_get_display('entity_test', 'entity_test', 'full')
|
||||
->setComponent('label', array('weight' => 20))
|
||||
->save();
|
||||
|
||||
// Create and build a test entity.
|
||||
$entity_test = $this->createTestEntity('entity_test');
|
||||
$view = $this->container->get('entity.manager')->getViewBuilder('entity_test')->view($entity_test, 'full');
|
||||
$renderer->renderRoot($view);
|
||||
|
||||
// Check that the weight is respected.
|
||||
$this->assertEqual($view['label']['#weight'], 20, 'The weight of a display component is respected.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an entity for testing.
|
||||
*
|
||||
* @param string $entity_type
|
||||
* The entity type.
|
||||
*
|
||||
* @return \Drupal\Core\Entity\EntityInterface
|
||||
* The created entity.
|
||||
*/
|
||||
protected function createTestEntity($entity_type) {
|
||||
$data = array(
|
||||
'bundle' => $entity_type,
|
||||
'name' => $this->randomMachineName(),
|
||||
);
|
||||
return $this->container->get('entity.manager')->getStorage($entity_type)->create($data);
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Reference in a new issue