Issue #3361534 by Wim Leers, borisson_, longwave, catch: KernelTestBase::$strictConfigSchema = TRUE and BrowserTestBase::$strictConfigSchema = TRUE do not actually strictly validate

merge-requests/4485/head
Lauri Eskola 2023-07-26 20:30:33 +03:00
parent 29dc14b207
commit f13ad693c8
No known key found for this signature in database
GPG Key ID: 382FC0F5B0DF53F8
42 changed files with 451 additions and 112 deletions

View File

@ -99,7 +99,9 @@ machine_name:
type: string
label: 'Machine name'
constraints:
Regex: '/^[a-z0-9_]+$/'
Regex:
pattern: '/^[a-z0-9_]+$/'
message: "The %value machine name is not valid."
Length:
# @see \Drupal\Core\Config\Entity\ConfigEntityStorage::MAX_ID_LENGTH
max: 166
@ -233,6 +235,7 @@ theme_settings:
label: 'Logo path'
url:
type: uri
nullable: true
label: 'URL'
use_default:
type: boolean

View File

@ -9,6 +9,7 @@ use Drupal\Core\TypedData\Type\BooleanInterface;
use Drupal\Core\TypedData\Type\StringInterface;
use Drupal\Core\TypedData\Type\FloatInterface;
use Drupal\Core\TypedData\Type\IntegerInterface;
use Symfony\Component\Validator\ConstraintViolation;
/**
* Provides a trait for checking configuration schema.
@ -57,6 +58,34 @@ trait SchemaCheckTrait {
$errors[] = $this->checkValue($key, $value);
}
$errors = array_merge(...$errors);
// Also perform explicit validation. Note this does NOT require every node
// in the config schema tree to have validation constraints defined.
$violations = $this->schema->validate();
$ignored_validation_constraint_messages = [
// @see \Drupal\Core\Config\Plugin\Validation\Constraint\ConfigExistsConstraint::$message
// @todo Remove this in https://www.drupal.org/project/drupal/issues/3362453
"The '.*' config does not exist.",
// @see \Drupal\Core\Extension\Plugin\Validation\Constraint\ExtensionExistsConstraint::$moduleMessage
// @see \Drupal\Core\Extension\Plugin\Validation\Constraint\ExtensionExistsConstraint::$themeMessage
// @todo Remove this in https://www.drupal.org/project/drupal/issues/3362456
"Module '.*' is not installed.",
"Theme '.*' is not installed.",
// @see \Drupal\Core\Plugin\Plugin\Validation\Constraint\PluginExistsConstraint::$unknownPluginMessage
// @todo Remove this in https://www.drupal.org/project/drupal/issues/3362457
"The '.*' plugin does not exist.",
// @see "machine_name" in core.data_types.schema.yml
// @todo Remove this in https://www.drupal.org/project/drupal/issues/3372972
"The <em class=\"placeholder\">.*<\/em> machine name is not valid.",
];
$filtered_violations = array_filter(
iterator_to_array($violations),
fn (ConstraintViolation $v) => preg_match(sprintf("/^(%s)$/", implode('|', $ignored_validation_constraint_messages)), (string) $v->getMessage()) !== 1
);
$validation_errors = array_map(
fn (ConstraintViolation $v) => sprintf("[%s] %s", $v->getPropertyPath(), (string) $v->getMessage()),
$filtered_violations
);
$errors = array_merge($errors, $validation_errors);
if (empty($errors)) {
return TRUE;
}

View File

@ -11,7 +11,9 @@ block.block.*:
# @see https://www.drupal.org/project/drupal/issues/2685917
# @see https://www.drupal.org/project/drupal/issues/2043527
constraints:
Regex: '/^[a-z0-9_.]+$/'
Regex:
pattern: '/^[a-z0-9_.]+$/'
message: "The %value machine name is not valid."
theme:
type: string
label: 'Theme'

View File

@ -580,6 +580,7 @@ class BlockTest extends BlockTestBase {
$block = Block::create([
'id' => $this->randomMachineName(),
'plugin' => 'system_powered_by_block',
'theme' => 'stark',
]);
$block->setVisibilityConfig('user_role', [

View File

@ -69,7 +69,7 @@ class BlockContentDeletionTest extends KernelTestBase {
$block_content->save();
$plugin_id = 'block_content' . PluginBase::DERIVATIVE_SEPARATOR . $block_content->uuid();
$block = $this->placeBlock($plugin_id, ['region' => 'content']);
$block = $this->placeBlock($plugin_id, ['region' => 'content', 'theme' => 'stark']);
// Delete it via storage.
$storage = $this->container->get('entity_type.manager')->getStorage('block_content');

View File

@ -12,7 +12,6 @@ use Drupal\ckeditor5\Plugin\CKEditor5PluginManagerInterface;
use Drupal\Component\Serialization\Json;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Config\Schema\SchemaCheckTrait;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\SubformState;
@ -50,8 +49,6 @@ use Symfony\Component\Validator\ConstraintViolationListInterface;
*/
class CKEditor5 extends EditorBase implements ContainerFactoryPluginInterface {
use SchemaCheckTrait;
/**
* The CKEditor plugin manager.
*
@ -729,8 +726,6 @@ class CKEditor5 extends EditorBase implements ContainerFactoryPluginInterface {
// ::getGeneratedAllowedHtmlValue(), to update filter_html's
// "allowed_html".
$form_state->set('ckeditor5_validated_pair', $eventual_editor_and_format);
assert(TRUE === $this->checkConfigSchema(\Drupal::getContainer()->get('config.typed'), 'editor.editor.id_does_not_matter', $submitted_editor->toArray()), 'Schema errors: ' . print_r($this->checkConfigSchema(\Drupal::getContainer()->get('config.typed'), 'editor.editor.id_does_not_matter', $submitted_editor->toArray()), TRUE));
}
/**

View File

@ -224,7 +224,11 @@ class ImageUploadTest extends BrowserTestBase {
'drupalInsertImage',
],
],
'plugins' => [],
'plugins' => [
'ckeditor5_imageResize' => [
'allow_resize' => FALSE,
],
],
],
'image_upload' => $upload_config,
]);

View File

@ -82,6 +82,16 @@ class CKEditor5PluginManagerTest extends KernelTestBase {
$this->typedConfig = $this->container->get('config.typed');
}
/**
* {@inheritdoc}
*/
protected function enableModules(array $modules) {
parent::enableModules($modules);
// Ensure the CKEditor 5 plugin manager instance on the test reflects the
// status after the module is installed.
$this->manager = $this->container->get('plugin.manager.ckeditor5.plugin');
}
/**
* Mocks a module providing a CKEditor 5 plugin in VFS.
*

View File

@ -541,6 +541,13 @@ class SmartDefaultSettingsTest extends KernelTestBase {
// Ensure that the result of ::computeSmartDefaultSettings() always complies
// with the config schema.
// TRICKY: because we're validating using `editor.editor.*` as the config
// name, TextEditorObjectDependentValidatorTrait will load the stored filter
// format. That has not yet been updated at this point, so in order for
// validation to pass, it must first be saved.
// @see \Drupal\ckeditor5\Plugin\Validation\Constraint\TextEditorObjectDependentValidatorTrait::createTextEditorObjectFromContext()
// @todo Remove this work-around in https://www.drupal.org/project/drupal/issues/3231354
$updated_text_editor->getFilterFormat()->save();
$this->assertConfigSchema(
$this->typedConfig,
$updated_text_editor->getConfigDependencyName(),

View File

@ -595,20 +595,15 @@ class ValidatorsTest extends KernelTestBase {
->set('name', $this->randomString())
->save();
$this->assertConfigSchema(
$this->typedConfig,
$text_editor->getConfigDependencyName(),
$text_editor->toArray()
);
// TRICKY: only validate the Editor entity in isolation if we expect NO
// violations: when violations are expected, this would just find the very
// violations that the next assertion is checking.
// @todo Remove in https://www.drupal.org/project/drupal/issues/3361534, which moves this into ::assertConfigSchema()
// TRICKY: only assert config schema (and validation constraints) if we
// expect NO violations: when violations are expected, this would just find
// the very violations that the next assertion is checking.
if (empty($expected_violations)) {
$this->assertSame([], array_map(
fn ($v) => sprintf("[%s] %s", $v->getPropertyPath(), (string) $v->getMessage()),
iterator_to_array($this->typedConfig->createFromNameAndData($text_editor->getConfigDependencyName(), $text_editor->toArray())->validate())
));
$this->assertConfigSchema(
$this->typedConfig,
$text_editor->getConfigDependencyName(),
$text_editor->toArray()
);
}
$this->assertSame($expected_violations, $this->validatePairToViolationsArray($text_editor, $text_format, TRUE));

View File

@ -6,3 +6,4 @@ giraffe:
hum1: hum1
hum2: hum2
uuid: '7C30C50E-641A-4E34-A7F1-46BCFB9BE5A3'
langcode: en

View File

@ -11,6 +11,9 @@ use Drupal\Tests\system\Functional\Module\ModuleTestBase;
/**
* Tests the largest configuration import possible with all available modules.
*
* Note that the use of SchemaCheckTestTrait means that the schema conformance
* of all default configuration is also tested.
*
* @group config
*/
class ConfigImportAllTest extends ModuleTestBase {

View File

@ -204,7 +204,7 @@ class ImageFieldDefaultImagesTest extends ImageFieldTestBase {
// Remove the field default from articles.
$default_image_settings = $field->getSetting('default_image');
$default_image_settings['uuid'] = 0;
$default_image_settings['uuid'] = \Drupal::service('uuid')->generate();
$field->setSetting('default_image', $default_image_settings);
$field->save();

View File

@ -120,6 +120,10 @@ language.content_settings.*.*:
default_langcode:
type: langcode
label: 'Default language'
constraints:
Choice:
# The "default language" has a different list of allowed values.
callback: '\Drupal\language\Entity\ContentLanguageSettings::getAllValidDefaultLangcodes'
language_alterable:
type: boolean
label: 'Allow to alter the language'

View File

@ -214,4 +214,25 @@ class ContentLanguageSettings extends ConfigEntityBase implements ContentLanguag
return $this;
}
/**
* Returns all valid values for the `default_langcode` property.
*
* @return string[]
* All possible valid default langcodes. This includes all langcodes in the
* standard list of human languages, along with special langcodes like
* `site_default`, `current_interface` and `authors_default`.
*
* @see \Drupal\language\Element\LanguageConfiguration::getDefaultOptions()
* @see \Drupal\Core\TypedData\Plugin\DataType\LanguageReference::getAllValidLangcodes()
*/
public static function getAllValidDefaultLangcodes(): array {
$language_manager = \Drupal::service('language_manager');
return array_unique([
...array_keys($language_manager->getLanguages(LanguageInterface::STATE_ALL)),
LanguageInterface::LANGCODE_SITE_DEFAULT,
'current_interface',
'authors_default',
]);
}
}

View File

@ -42,6 +42,15 @@ use Drupal\block\Entity\Block;
*/
class LanguageUILanguageNegotiationTest extends BrowserTestBase {
/**
* {@inheritdoc}
*/
protected static $configSchemaCheckerExclusions = [
// Necessary to allow setting `selected_langcode` to NULL.
// @see testUILanguageNegotiation()
'language.negotiation',
];
/**
* The admin user for testing.
*

View File

@ -1,4 +1,4 @@
icon_base_uri: 'public://media-icons/generic'
iframe_domain: ''
iframe_domain: ~
oembed_providers_url: 'https://oembed.com/providers.json'
standalone_url: false

View File

@ -7,6 +7,7 @@ media.settings:
label: 'Full URI to a folder where the media icons will be installed'
iframe_domain:
type: uri
nullable: true
label: 'Domain from which to serve oEmbed content in an iframe'
oembed_providers_url:
type: uri

View File

@ -33,3 +33,15 @@ function media_post_update_oembed_loading_attribute(array &$sandbox = NULL): voi
return $media_config_updater->processOembedEagerLoadField($view_display);
});
}
/**
* Updates media.settings:iframe_domain config if it's still at the default.
*/
function media_post_update_set_blank_iframe_domain_to_null() {
$media_settings = \Drupal::configFactory()->getEditable('media.settings');
if ($media_settings->get('iframe_domain') === '') {
$media_settings
->set('iframe_domain', NULL)
->save(TRUE);
}
}

View File

@ -117,8 +117,13 @@ class MediaSettingsForm extends ConfigFormBase {
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$iframe_domain = $form_state->getValue('iframe_domain');
// The empty string is not a valid URI, but NULL is allowed.
if ($iframe_domain === '') {
$iframe_domain = NULL;
}
$this->config('media.settings')
->set('iframe_domain', $form_state->getValue('iframe_domain'))
->set('iframe_domain', $iframe_domain)
->set('standalone_url', $form_state->getValue('standalone_url'))
->save();

View File

@ -50,3 +50,21 @@ $connection->update('key_value')
->condition('name', 'existing_updates')
->execute();
// Create media.settings.
$connection->insert('config')
->fields([
'collection',
'name',
'data',
])
->values([
'collection' => '',
'name' => 'media.settings',
'data' => serialize([
'icon_base_uri' => 'public://media-icons/generic',
'iframe_domain' => '',
'oembed_providers_url' => 'https://oembed.com/providers.json',
'standalone_url' => FALSE,
]),
])
->execute();

View File

@ -2,6 +2,8 @@
namespace Drupal\Tests\media\Functional;
use Drupal\Core\Url;
/**
* Testing the media settings.
*
@ -19,7 +21,10 @@ class MediaSettingsTest extends MediaFunctionalTestBase {
*/
protected function setUp(): void {
parent::setUp();
$this->drupalLogin($this->createUser(['administer site configuration']));
$this->drupalLogin($this->createUser([
'administer site configuration',
'administer media',
]));
}
/**
@ -37,4 +42,25 @@ class MediaSettingsTest extends MediaFunctionalTestBase {
$assert_session->pageTextContains('It is potentially insecure to display oEmbed content in a frame');
}
/**
* Tests that the media settings form stores a `null` iFrame domain.
*/
public function testSettingsForm(): void {
$assert_session = $this->assertSession();
$this->assertNull($this->config('media.settings')->get('iframe_domain'));
$this->drupalGet(Url::fromRoute('media.settings'));
$assert_session->fieldExists('iframe_domain');
// Explicitly submitting an empty string does not result in the
// `iframe_domain` property getting set to the empty string: it is converted
// to `null` to comply with the config schema.
// @see \Drupal\media\Form\MediaSettingsForm::submitForm()
$this->submitForm([
'iframe_domain' => '',
], 'Save configuration');
$assert_session->statusMessageContains('The configuration options have been saved.', 'status');
$this->assertNull($this->config('media.settings')->get('iframe_domain'));
}
}

View File

@ -0,0 +1,60 @@
<?php
namespace Drupal\Tests\media\Functional\Update;
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
use Drupal\Tests\UpdatePathTestTrait;
/**
* Tests update of media.settings:iframe_domain if it's still the default of "".
*
* @group system
* @covers \media_post_update_set_blank_iframe_domain_to_null
*/
class MediaSettingsDefaultIframeDomainUpdateTest extends UpdatePathTestBase {
use UpdatePathTestTrait;
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected function setDatabaseDumpFiles() {
$this->databaseDumpFiles = [
DRUPAL_ROOT . '/core/modules/system/tests/fixtures/update/drupal-9.4.0.bare.standard.php.gz',
__DIR__ . '/../../../fixtures/update/media.php',
];
}
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
// Because the test manually installs media module, the entity type config
// must be manually installed similar to kernel tests.
$entity_type_manager = \Drupal::entityTypeManager();
$media = $entity_type_manager->getDefinition('media');
\Drupal::service('entity_type.listener')->onEntityTypeCreate($media);
$media_type = $entity_type_manager->getDefinition('media_type');
\Drupal::service('entity_type.listener')->onEntityTypeCreate($media_type);
}
/**
* Tests update of media.settings:iframe_domain.
*/
public function testUpdate() {
$iframe_domain_before = $this->config('media.settings')->get('iframe_domain');
$this->assertSame('', $iframe_domain_before);
$this->runUpdates();
$iframe_domain_after = $this->config('media.settings')->get('iframe_domain');
$this->assertNull($iframe_domain_after);
}
}

View File

@ -11,7 +11,9 @@ shortcut.set.*:
# dashes but not underscores.
# @see \Drupal\shortcut\ShortcutSetForm::form()
constraints:
Regex: '/^[a-z0-9-]+$/'
Regex:
pattern: '/^[a-z0-9-]+$/'
message: "The %value machine name is not valid."
Length:
max: 23
label:

View File

@ -10,5 +10,5 @@ features:
node_user_picture: true
logo:
path: ''
url: ''
url: ~
use_default: true

View File

@ -215,7 +215,9 @@ system.menu.*:
# underscores.
# @see \Drupal\menu_ui\MenuForm::form()
constraints:
Regex: '/^[a-z0-9-]+$/'
Regex:
pattern: '/^[a-z0-9-]+$/'
message: "The %value machine name is not valid."
Length:
max: 32
label:

View File

@ -146,3 +146,15 @@ function system_post_update_add_description_to_entity_form_mode(array &$sandbox
$config_entity_updater->update($sandbox, 'entity_form_mode', $callback);
}
/**
* Updates system.theme.global:logo.url config if it's still at the default.
*/
function system_post_update_set_blank_log_url_to_null() {
$global_theme_settings = \Drupal::configFactory()->getEditable('system.theme.global');
if ($global_theme_settings->get('logo.url') === '') {
$global_theme_settings
->set('logo.url', NULL)
->save(TRUE);
}
}

View File

@ -0,0 +1,45 @@
<?php
namespace Drupal\Tests\system\Functional\Update;
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
use Drupal\Tests\UpdatePathTestTrait;
/**
* Tests update of system.theme.global:logo.url if it's still the default of "".
*
* @group system
* @covers \system_post_update_set_blank_log_url_to_null
*/
class GlobalThemeSettingsDefaultLogoUrlUpdateTest extends UpdatePathTestBase {
use UpdatePathTestTrait;
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected function setDatabaseDumpFiles() {
$this->databaseDumpFiles = [
DRUPAL_ROOT . '/core/modules/system/tests/fixtures/update/drupal-9.4.0.bare.standard.php.gz',
];
}
/**
* Tests update of system.theme.global:logo.url.
*/
public function testUpdate() {
$logo_url_before = $this->config('system.theme.global')->get('logo.url');
$this->assertSame('', $logo_url_before);
$this->runUpdates();
$logo_url_after = $this->config('system.theme.global')->get('logo.url');
$this->assertNull($logo_url_after);
}
}

View File

@ -2,7 +2,7 @@ check:
disabled_extensions: false
interval_days: 1
fetch:
url: ''
url: ~
max_attempts: 2
timeout: 30
notification:

View File

@ -20,6 +20,7 @@ update.settings:
mapping:
url:
type: uri
nullable: true
label: 'URL for fetching available update data'
max_attempts:
type: integer

View File

@ -0,0 +1,110 @@
<?php
namespace Drupal\Tests\update\Functional\Update;
use Drupal\Core\Database\Database;
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
use Drupal\Tests\UpdatePathTestTrait;
/**
* Tests update of update.settings:fetch.url if it's still the default of "".
*
* @group system
* @covers \update_post_update_set_blank_fetch_url_to_null
*/
class UpdateSettingsDefaultFetchUrlUpdateTest extends UpdatePathTestBase {
use UpdatePathTestTrait;
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* {@inheritdoc}
*/
protected function setDatabaseDumpFiles() {
$this->databaseDumpFiles = [
DRUPAL_ROOT . '/core/modules/system/tests/fixtures/update/drupal-9.4.0.bare.standard.php.gz',
];
}
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$connection = Database::getConnection();
// Set the schema version.
$connection->merge('key_value')
->fields([
'value' => 'i:8001;',
'name' => 'update',
'collection' => 'system.schema',
])
->condition('collection', 'system.schema')
->condition('name', 'update')
->execute();
// Update core.extension.
$extensions = $connection->select('config')
->fields('config', ['data'])
->condition('collection', '')
->condition('name', 'core.extension')
->execute()
->fetchField();
$extensions = unserialize($extensions);
$extensions['module']['update'] = 0;
$connection->update('config')
->fields(['data' => serialize($extensions)])
->condition('collection', '')
->condition('name', 'core.extension')
->execute();
// Create update.settings config.
$default_update_settings = [
'check' => [
'disabled_extensions' => FALSE,
'interval_days' => 1,
],
'fetch' => [
'url' => '',
'max_attempts' => 2,
'timeout' => 30,
],
'notification' => [
'emails' => [],
'threshold' => 'all',
],
];
$connection->insert('config')
->fields([
'collection',
'name',
'data',
])
->values([
'collection' => '',
'name' => 'update.settings',
'data' => serialize($default_update_settings),
])
->execute();
}
/**
* Tests update of update.settings:fetch.url.
*/
public function testUpdate() {
$fetch_url_before = $this->config('update.settings')->get('fetch.url');
$this->assertSame('', $fetch_url_before);
$this->runUpdates();
$fetch_url_after = $this->config('update.settings')->get('fetch.url');
$this->assertNull($fetch_url_after);
}
}

View File

@ -13,3 +13,15 @@ function update_remove_post_updates() {
'update_post_update_add_view_update_notifications_permission' => '10.0.0',
];
}
/**
* Updates update.settings:fetch.url config if it's still at the default.
*/
function update_post_update_set_blank_fetch_url_to_null() {
$update_settings = \Drupal::configFactory()->getEditable('update.settings');
if ($update_settings->get('fetch.url') === '') {
$update_settings
->set('fetch.url', NULL)
->save(TRUE);
}
}

View File

@ -37,6 +37,7 @@ class TestViewsTest extends KernelTestBase {
\Drupal::service('module_handler'),
\Drupal::service('class_resolver')
);
$typed_config->setValidationConstraintManager(\Drupal::service('validation.constraint'));
// Create a configuration storage with access to default configuration in
// every module, profile and theme.

View File

@ -26,7 +26,7 @@ class AreaEntityUITest extends UITestBase {
public function testUI() {
// Set up a block and an entity_test entity.
$block = Block::create(['id' => 'test_id', 'plugin' => 'system_main_block']);
$block = Block::create(['id' => 'test_id', 'plugin' => 'system_main_block', 'theme' => 'stark']);
$block->save();
$entity_test = EntityTest::create(['bundle' => 'entity_test']);

View File

@ -33,6 +33,9 @@ settings:
- heading4
- heading5
- heading6
ckeditor5_list:
reversed: false
startIndex: false
ckeditor5_sourceEditing:
allowed_tags:
- '<cite>'
@ -50,6 +53,8 @@ settings:
- '<h6 id>'
- '<img src alt data-entity-type data-entity-uuid data-align data-caption width height>'
- '<drupal-media data-view-mode title>'
media_media:
allow_view_mode_override: false
image_upload:
status: false
scheme: public

View File

@ -38,8 +38,13 @@ settings:
- heading4
- heading5
- heading6
ckeditor5_list:
reversed: false
startIndex: false
ckeditor5_sourceEditing:
allowed_tags: { }
media_media:
allow_view_mode_override: false
image_upload:
status: true
scheme: public

View File

@ -10,5 +10,5 @@ features:
node_user_picture: true
logo:
path: ''
url: ''
url: ~
use_default: true

View File

@ -10,5 +10,5 @@ features:
node_user_picture: false
logo:
path: ''
url: ''
url: ~
use_default: true

View File

@ -25,6 +25,11 @@ class TypedConfigTest extends KernelTestBase {
*/
protected static $modules = ['config_test'];
/**
* {@inheritdoc}
*/
protected static $configSchemaCheckerExclusions = ['config_test.validation'];
/**
* {@inheritdoc}
*/
@ -90,7 +95,7 @@ class TypedConfigTest extends KernelTestBase {
$typed_config_manager = \Drupal::service('config.typed');
$typed_config = $typed_config_manager->createFromNameAndData('config_test.validation', \Drupal::configFactory()->get('config_test.validation')->get());
$this->assertInstanceOf(TypedConfigInterface::class, $typed_config);
$this->assertEquals(['_core', 'llama', 'cat', 'giraffe', 'uuid'], array_keys($typed_config->getElements()));
$this->assertEquals(['_core', 'llama', 'cat', 'giraffe', 'uuid', 'langcode'], array_keys($typed_config->getElements()));
$this->assertSame('config_test.validation', $typed_config->getName());
$this->assertSame('config_test.validation', $typed_config->getPropertyPath());
$this->assertSame('config_test.validation.llama', $typed_config->get('llama')->getPropertyPath());

View File

@ -106,14 +106,18 @@ abstract class ConfigEntityValidationTestBase extends KernelTestBase {
$constraints = $this->getMachineNameConstraints();
$this->assertNotEmpty($constraints['Regex']);
$this->assertIsString($constraints['Regex']);
$this->assertIsArray($constraints['Regex']);
$this->assertArrayHasKey('pattern', $constraints['Regex']);
$this->assertIsString($constraints['Regex']['pattern']);
$this->assertArrayHasKey('message', $constraints['Regex']);
$this->assertIsString($constraints['Regex']['message']);
$id_key = $this->entity->getEntityType()->getKey('id');
if ($is_expected_to_be_valid) {
$expected_errors = [];
}
else {
$expected_errors = [$id_key => 'This value is not valid.'];
$expected_errors = [$id_key => sprintf('The <em class="placeholder">&quot;%s&quot;</em> machine name is not valid.', $machine_name)];
}
$this->entity->set(

View File

@ -1,76 +0,0 @@
<?php
namespace Drupal\KernelTests\Core\Config;
use Drupal\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
*/
protected static $modules = ['system', 'config_test'];
/**
* Config files to be ignored by this test.
*
* @var array
*/
protected $toSkip = [
// Skip files provided by the config_schema_test module since that module
// is explicitly for testing schema.
'config_schema_test.ignore',
'config_schema_test.noschema',
'config_schema_test.plugin_types',
'config_schema_test.someschema.somemodule.section_one.subsection',
'config_schema_test.someschema.somemodule.section_two.subsection',
'config_schema_test.someschema.with_parents',
'config_schema_test.someschema',
];
/**
* {@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) {
if (in_array($config_name, $this->toSkip)) {
continue;
}
$data = $default_config_storage->read($config_name);
$this->assertConfigSchema($typed_config, $config_name, $data);
}
}
}

View File

@ -56,9 +56,14 @@ class SchemaCheckTraitTest extends KernelTestBase {
$config_data['boolean'] = [];
$ret = $this->checkConfigSchema($this->typedConfig, 'config_test.types', $config_data);
$expected = [
// Storage type check errors.
// @see \Drupal\Core\Config\Schema\SchemaCheckTrait::checkValue()
'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)',
// Validation constraints violations.
// @see \Drupal\Core\TypedData\TypedDataInterface::validate()
'0' => '[boolean] This value should be of the correct primitive type.',
];
$this->assertEquals($expected, $ret);
}