This is an automated email from the ASF dual-hosted git repository.

apolovtsev pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git


The following commit(s) were added to refs/heads/main by this push:
     new 15ae1059e18 IGNITE-28091 Validate configuration in 
ConfigurationExtension (#7727)
15ae1059e18 is described below

commit 15ae1059e18c48cee28bb17a5316ad1eaab00e2e
Author: Alexander Polovtcev <[email protected]>
AuthorDate: Mon Mar 9 13:01:26 2026 +0200

    IGNITE-28091 Validate configuration in ConfigurationExtension (#7727)
---
 .../ignite/client/handler/ItClientHandlerTest.java |   3 +-
 .../ignite/client/ClientAuthenticationTest.java    |  19 +--
 modules/configuration/build.gradle                 |   8 ++
 .../internal/configuration/CompoundModule.java     |   0
 .../testframework/ConfigurationExtensionTest.java  | 156 +++++++++++++++++++--
 .../InvalidDefaultConfigurationSchema.java}        |   8 +-
 .../MissingDefaultConfigurationSchema.java}        |   7 +-
 .../ValidatedConfigurationSchema.java}             |  13 +-
 .../testframework/BasicConfigurationSchema.java    |   4 +-
 .../testframework/ConfigurationExtension.java      | 151 ++++++++++++++------
 .../testframework/InjectConfiguration.java         |  34 +++--
 .../testframework/JunitExtensionTestUtils.java     |   6 +-
 .../eventlog/impl/ItEventLogConfigurationTest.java |   3 +-
 .../ConfigurationBasedChannelRegistryTest.java     |   3 +-
 .../impl/ConfigurationBasedSinkRegistryTest.java   |   3 +-
 .../ignite/internal/eventlog/impl/LogSinkTest.java |   3 +-
 .../internal/eventlog/impl/WebhookSinkTest.java    |   6 +-
 .../lowwatermark/LowWatermarkImplTest.java         |   2 +-
 .../exporters/otlp/OtlpPushMetricExporterTest.java |   6 +-
 .../ignite/internal/metrics/MetricManagerTest.java |   6 +-
 .../metrics/exporters/jmx/JmxExporterTest.java     |   3 +-
 .../metrics/exporters/log/LogPushExporterTest.java |   5 +-
 .../PlacementDriverManagerTest.java                |   2 +-
 .../placementdriver/LeaseNegotiationTest.java      |   2 +-
 .../internal/placementdriver/LeaseUpdaterTest.java |   2 +-
 .../segstore/RaftLogGarbageCollectorTest.java      |   2 +-
 .../segstore/SegmentFileManagerGetEntryTest.java   |   2 +-
 .../storage/segstore/SegmentFileManagerTest.java   |   2 +-
 .../SegstoreLogStorageConcurrencyTest.java         |   2 +-
 .../ignite/internal/rest/RestComponentTest.java    |   3 +-
 .../SecurityConfigurationModule.java               |   6 +-
 .../AuthenticationProvidersValidatorImplTest.java  |   7 +-
 32 files changed, 363 insertions(+), 116 deletions(-)

diff --git 
a/modules/client-handler/src/integrationTest/java/org/apache/ignite/client/handler/ItClientHandlerTest.java
 
b/modules/client-handler/src/integrationTest/java/org/apache/ignite/client/handler/ItClientHandlerTest.java
index 943ceb20877..1d8a197991c 100644
--- 
a/modules/client-handler/src/integrationTest/java/org/apache/ignite/client/handler/ItClientHandlerTest.java
+++ 
b/modules/client-handler/src/integrationTest/java/org/apache/ignite/client/handler/ItClientHandlerTest.java
@@ -19,6 +19,7 @@ package org.apache.ignite.client.handler;
 
 import static org.apache.ignite.client.handler.ItClientHandlerTestUtils.MAGIC;
 import static 
org.apache.ignite.configuration.annotation.ConfigurationType.DISTRIBUTED;
+import static 
org.apache.ignite.internal.security.authentication.SecurityConfigurationModule.DEFAULT_PROVIDER_NAME;
 import static 
org.apache.ignite.internal.testframework.matchers.CompletableFutureMatcher.willCompleteSuccessfully;
 import static 
org.apache.ignite.lang.ErrorGroups.Authentication.INVALID_CREDENTIALS_ERR;
 import static 
org.apache.ignite.lang.ErrorGroups.Authentication.UNSUPPORTED_AUTHENTICATION_TYPE_ERR;
@@ -592,7 +593,7 @@ public class ItClientHandlerTest extends 
BaseIgniteAbstractTest {
     private void setupAuthentication(String username, String password) {
         securityConfiguration.change(change -> {
             change.changeEnabled(true);
-            change.changeAuthentication().changeProviders().create("basic", 
authenticationProviderChange -> {
+            
change.changeAuthentication().changeProviders().update(DEFAULT_PROVIDER_NAME, 
authenticationProviderChange -> {
                 
authenticationProviderChange.convert(BasicAuthenticationProviderChange.class)
                         .changeUsers(users -> users.create(username, user -> 
user.changePassword(password)));
             });
diff --git 
a/modules/client/src/test/java/org/apache/ignite/client/ClientAuthenticationTest.java
 
b/modules/client/src/test/java/org/apache/ignite/client/ClientAuthenticationTest.java
index cca0f44ae48..96704bffb46 100644
--- 
a/modules/client/src/test/java/org/apache/ignite/client/ClientAuthenticationTest.java
+++ 
b/modules/client/src/test/java/org/apache/ignite/client/ClientAuthenticationTest.java
@@ -18,14 +18,17 @@
 package org.apache.ignite.client;
 
 import static 
org.apache.ignite.configuration.annotation.ConfigurationType.DISTRIBUTED;
+import static 
org.apache.ignite.internal.security.authentication.SecurityConfigurationModule.DEFAULT_PASSWORD;
+import static 
org.apache.ignite.internal.security.authentication.SecurityConfigurationModule.DEFAULT_USERNAME;
+import static 
org.apache.ignite.internal.testframework.matchers.CompletableFutureMatcher.willCompleteSuccessfully;
 import static org.apache.ignite.internal.util.IgniteUtils.closeAll;
+import static org.hamcrest.MatcherAssert.assertThat;
 
 import java.util.UUID;
 import org.apache.ignite.client.fakes.FakeIgnite;
 import org.apache.ignite.internal.configuration.ClusterConfiguration;
 import 
org.apache.ignite.internal.configuration.testframework.ConfigurationExtension;
 import 
org.apache.ignite.internal.configuration.testframework.InjectConfiguration;
-import 
org.apache.ignite.internal.security.authentication.basic.BasicAuthenticationProviderChange;
 import org.apache.ignite.internal.security.configuration.SecurityConfiguration;
 import 
org.apache.ignite.internal.security.configuration.SecurityExtensionConfiguration;
 import org.apache.ignite.internal.testframework.BaseIgniteAbstractTest;
@@ -85,9 +88,9 @@ public class ClientAuthenticationTest extends 
BaseIgniteAbstractTest {
 
     @Test
     public void testAuthnOnClientAuthnOnServer() {
-        server = startServer(false);
+        server = startServer(true);
 
-        client = 
startClient(BasicAuthenticator.builder().username("usr").password("pwd").build());
+        client = 
startClient(BasicAuthenticator.builder().username(DEFAULT_USERNAME).password(DEFAULT_PASSWORD).build());
     }
 
     private IgniteClient startClient(@Nullable IgniteClientAuthenticator 
authenticator) {
@@ -111,15 +114,7 @@ public class ClientAuthenticationTest extends 
BaseIgniteAbstractTest {
                 null);
 
         if (basicAuthn) {
-            securityConfiguration.change(securityChange -> {
-                securityChange.changeEnabled(true);
-                
securityChange.changeAuthentication().changeProviders().create("basic", change 
->
-                        change.convert(BasicAuthenticationProviderChange.class)
-                                .changeUsers(users -> users.create("usr", user 
->
-                                        user.changePassword("pwd"))
-                                )
-                );
-            }).join();
+            assertThat(securityConfiguration.change(securityChange -> 
securityChange.changeEnabled(true)), willCompleteSuccessfully());
         }
 
         return server;
diff --git a/modules/configuration/build.gradle 
b/modules/configuration/build.gradle
index f0f77c6676c..f4e10cd6a73 100644
--- a/modules/configuration/build.gradle
+++ b/modules/configuration/build.gradle
@@ -34,9 +34,17 @@ dependencies {
     testAnnotationProcessor 
project(':ignite-configuration-annotation-processor')
     testImplementation project(':ignite-core')
     testImplementation testFixtures(project(':ignite-core'))
+    testImplementation libs.junit.testkit
 
     testFixturesAnnotationProcessor 
project(':ignite-configuration-annotation-processor')
     testFixturesImplementation project(':ignite-core')
     testFixturesImplementation testFixtures(project(':ignite-core'))
     testFixturesImplementation libs.typesafe.config
 }
+
+// Exclude tests from inner static classes that use JUnit Platform Test Kit.
+test {
+    filter {
+        excludeTestsMatching "*ConfigurationExtensionTest\$*"
+    }
+}
diff --git 
a/modules/runner/src/main/java/org/apache/ignite/internal/configuration/CompoundModule.java
 
b/modules/configuration/src/main/java/org/apache/ignite/internal/configuration/CompoundModule.java
similarity index 100%
rename from 
modules/runner/src/main/java/org/apache/ignite/internal/configuration/CompoundModule.java
rename to 
modules/configuration/src/main/java/org/apache/ignite/internal/configuration/CompoundModule.java
diff --git 
a/modules/configuration/src/test/java/org/apache/ignite/internal/configuration/testframework/ConfigurationExtensionTest.java
 
b/modules/configuration/src/test/java/org/apache/ignite/internal/configuration/testframework/ConfigurationExtensionTest.java
index 48c4a323d5a..71f66ee669e 100644
--- 
a/modules/configuration/src/test/java/org/apache/ignite/internal/configuration/testframework/ConfigurationExtensionTest.java
+++ 
b/modules/configuration/src/test/java/org/apache/ignite/internal/configuration/testframework/ConfigurationExtensionTest.java
@@ -17,21 +17,30 @@
 
 package org.apache.ignite.internal.configuration.testframework;
 
-import static java.util.concurrent.TimeUnit.SECONDS;
+import static 
org.apache.ignite.internal.testframework.JunitExtensionTestUtils.assertExecutesWithFailure;
+import static 
org.apache.ignite.internal.testframework.matchers.CompletableFutureExceptionMatcher.willThrowFast;
 import static 
org.apache.ignite.internal.testframework.matchers.CompletableFutureMatcher.willCompleteSuccessfully;
 import static 
org.apache.ignite.internal.util.CompletableFutures.nullCompletedFuture;
 import static org.hamcrest.CoreMatchers.instanceOf;
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.hasSize;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import 
org.apache.ignite.configuration.validation.ConfigurationValidationException;
 import org.apache.ignite.internal.configuration.sample.DiscoveryConfiguration;
 import 
org.apache.ignite.internal.configuration.sample.ExtendedDiscoveryConfiguration;
 import 
org.apache.ignite.internal.configuration.sample.ExtendedDiscoveryConfigurationSchema;
 import org.apache.ignite.internal.testframework.BaseIgniteAbstractTest;
+import org.apache.ignite.internal.testframework.JunitExtensionTestUtils;
+import org.apache.ignite.internal.util.ExceptionUtils;
+import org.assertj.core.api.Condition;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
@@ -56,25 +65,23 @@ class ConfigurationExtensionTest extends 
BaseIgniteAbstractTest {
 
     /** Test that contains injected parameter. */
     @Test
-    public void injectConfiguration(
-            @InjectConfiguration("mock.joinTimeout=100") 
DiscoveryConfiguration paramCfg
-    ) throws Exception {
+    void injectConfiguration(@InjectConfiguration("mock.joinTimeout=100") 
DiscoveryConfiguration paramCfg) {
         assertEquals(5000, fieldCfg.joinTimeout().value());
 
         assertEquals(100, paramCfg.joinTimeout().value());
 
-        paramCfg.change(d -> d.changeJoinTimeout(200)).get(1, SECONDS);
+        assertThat(paramCfg.change(d -> d.changeJoinTimeout(200)), 
willCompleteSuccessfully());
 
         assertEquals(200, paramCfg.joinTimeout().value());
 
-        paramCfg.joinTimeout().update(300).get(1, SECONDS);
+        assertThat(paramCfg.joinTimeout().update(300), 
willCompleteSuccessfully());
 
         assertEquals(300, paramCfg.joinTimeout().value());
     }
 
     /** Tests that notifications work on injected configuration instance. */
     @Test
-    public void notifications() throws Exception {
+    void notifications() {
         List<String> log = new ArrayList<>();
 
         fieldCfg.listen(ctx -> {
@@ -95,35 +102,33 @@ class ConfigurationExtensionTest extends 
BaseIgniteAbstractTest {
             return nullCompletedFuture();
         });
 
-        fieldCfg.change(change -> change.changeJoinTimeout(1000_000)).get(1, 
SECONDS);
+        assertThat(fieldCfg.change(change -> 
change.changeJoinTimeout(1000_000)), willCompleteSuccessfully());
 
         assertEquals(List.of("update", "join"), log);
 
         log.clear();
 
-        fieldCfg.failureDetectionTimeout().update(2000_000).get(1, SECONDS);
+        assertThat(fieldCfg.failureDetectionTimeout().update(2000_000), 
willCompleteSuccessfully());
 
         assertEquals(List.of("update", "failure"), log);
     }
 
     /** Tests that internal configuration extensions work properly on injected 
configuration instance. */
     @Test
-    public void internalConfiguration(
-            @InjectConfiguration(extensions = 
{ExtendedConfigurationSchema.class}) BasicConfiguration cfg
-    ) throws Exception {
+    void internalConfiguration(@InjectConfiguration(extensions = 
ExtendedConfigurationSchema.class) BasicConfiguration cfg) {
         assertThat(cfg, is(instanceOf(ExtendedConfiguration.class)));
 
         assertEquals(1, cfg.visible().value());
 
         assertEquals(2, ((ExtendedConfiguration) cfg).invisible().value());
 
-        cfg.change(change -> {
+        assertThat(cfg.change(change -> {
             assertThat(change, is(instanceOf(ExtendedChange.class)));
 
             change.changeVisible(3);
 
             ((ExtendedChange) change).changeInvisible(4);
-        }).get(1, SECONDS);
+        }), willCompleteSuccessfully());
 
         assertEquals(3, cfg.visible().value());
 
@@ -132,7 +137,7 @@ class ConfigurationExtensionTest extends 
BaseIgniteAbstractTest {
 
     /** Test UUID generation in mocks. */
     @Test
-    public void testInjectInternalId(
+    void testInjectInternalId(
             @InjectConfiguration(
                     extensions = ExtendedDiscoveryConfigurationSchema.class,
                     name = "test"
@@ -140,4 +145,125 @@ class ConfigurationExtensionTest extends 
BaseIgniteAbstractTest {
     ) {
         assertNotNull(((ExtendedDiscoveryConfiguration) 
discoveryConfig).id().value());
     }
+
+    /** Tests that changing a value to one within the valid range succeeds. */
+    @Test
+    void rangeValidationAcceptsValidChange(@InjectConfiguration 
ValidatedConfiguration cfg) {
+        assertThat(cfg.change(c -> c.changeRangeValue(80)), 
willCompleteSuccessfully());
+
+        assertEquals(80, cfg.rangeValue().value());
+    }
+
+    /** Tests that changing a value to one outside the valid range throws 
{@link ConfigurationValidationException}. */
+    @Test
+    void rangeValidationRejectsInvalidChange(@InjectConfiguration 
ValidatedConfiguration cfg) {
+        assertThat(cfg.change(c -> c.changeRangeValue(0)), 
willThrowFast(ConfigurationValidationException.class));
+    }
+
+    /** Tests that changing an {@code @Immutable} field throws {@link 
ConfigurationValidationException}. */
+    @Test
+    void immutableValidationRejectsChange(@InjectConfiguration 
ValidatedConfiguration cfg) {
+        assertThat(cfg.change(c -> c.changeConstValue("changed")), 
willThrowFast(ConfigurationValidationException.class));
+    }
+
+    /** Tests that a failed validation does not mutate the configuration 
value. */
+    @Test
+    void failedValidationLeavesValueUnchanged(@InjectConfiguration 
ValidatedConfiguration cfg) {
+        assertThat(cfg.change(c -> c.changeRangeValue(0)), 
willThrowFast(ConfigurationValidationException.class));
+
+        assertEquals(50, cfg.rangeValue().value());
+    }
+
+    /** Tests that all violated constraints are collected and reported 
together. */
+    @Test
+    void multipleValidationIssuesAreReported(@InjectConfiguration 
ValidatedConfiguration cfg) {
+        CompletableFuture<Void> future = cfg.change(c -> 
c.changeRangeValue(0).changeConstValue("changed"));
+
+        assertThat(future, 
willThrowFast(ConfigurationValidationException.class));
+
+        ExecutionException ex = assertThrows(ExecutionException.class, 
future::get);
+
+        assertThat(((ConfigurationValidationException) 
ex.getCause()).getIssues(), hasSize(2));
+    }
+
+    /**
+     * Tests that {@code @Value(hasDefault = true)} schema defaults are 
applied when no explicit HOCON values
+     * are provided.
+     */
+    @Test
+    void defaultValuesAreApplied(@InjectConfiguration ValidatedConfiguration 
cfg) {
+        assertEquals(50, cfg.rangeValue().value());
+        assertEquals("constant", cfg.constValue().value());
+    }
+
+    /**
+     * Tests that initial configuration values are validated at injection 
time, and that a value
+     * violating a constraint causes {@link ConfigurationValidationException} 
to be thrown.
+     */
+    @Test
+    void initialValidationRejectsInvalidValue() {
+        assertInjectionFails(InvalidInitialValueTest.class);
+    }
+
+    @Test
+    void missingDefaultValueIsRejectedByValidation() {
+        assertInjectionFails(MissingDefaultValueTest.class);
+    }
+
+    @Test
+    void invalidDefaultValueIsRejectedByValidation() {
+        assertInjectionFails(InvalidDefaultValueTest.class);
+    }
+
+    private static void assertInjectionFails(Class<?> testClass) {
+        assertExecutesWithFailure(
+                testClass,
+                new Condition<>(
+                        t -> ExceptionUtils.hasCause(t, 
ConfigurationValidationException.class),
+                        "ConfigurationValidationException"
+                )
+        );
+    }
+
+    /**
+     * A test class whose {@code @Value(hasDefault = true)} is not initialized.
+     * It is not a normal test and is only executed programmatically via 
{@link JunitExtensionTestUtils}.
+     */
+    @ExtendWith(ConfigurationExtension.class)
+    static class MissingDefaultValueTest extends BaseIgniteAbstractTest {
+        @SuppressWarnings("unused")
+        @InjectConfiguration
+        InvalidDefaultConfiguration cfg;
+
+        @Test
+        void test() {}
+    }
+
+    /**
+     * A test class whose field injection intentionally violates {@code 
@Range(min=1)}.
+     * It is not a normal test and is only executed programmatically via 
{@link JunitExtensionTestUtils}.
+     */
+    @ExtendWith(ConfigurationExtension.class)
+    static class InvalidInitialValueTest extends BaseIgniteAbstractTest {
+        @SuppressWarnings("unused")
+        @InjectConfiguration("mock.rangeValue=0")
+        ValidatedConfiguration cfg;
+
+        @Test
+        void test() {}
+    }
+
+    /**
+     * A test class whose {@code @Value(hasDefault = true)} is inialized with 
an invalid value.
+     * It is not a normal test and is only executed programmatically via 
{@link JunitExtensionTestUtils}.
+     */
+    @ExtendWith(ConfigurationExtension.class)
+    static class InvalidDefaultValueTest extends BaseIgniteAbstractTest {
+        @SuppressWarnings("unused")
+        @InjectConfiguration
+        InvalidDefaultConfiguration cfg;
+
+        @Test
+        void test() {}
+    }
 }
diff --git 
a/modules/configuration/src/testFixtures/java/org/apache/ignite/internal/configuration/testframework/BasicConfigurationSchema.java
 
b/modules/configuration/src/test/java/org/apache/ignite/internal/configuration/testframework/InvalidDefaultConfigurationSchema.java
similarity index 78%
copy from 
modules/configuration/src/testFixtures/java/org/apache/ignite/internal/configuration/testframework/BasicConfigurationSchema.java
copy to 
modules/configuration/src/test/java/org/apache/ignite/internal/configuration/testframework/InvalidDefaultConfigurationSchema.java
index 78f8a7dd7ec..223e1bc86ce 100644
--- 
a/modules/configuration/src/testFixtures/java/org/apache/ignite/internal/configuration/testframework/BasicConfigurationSchema.java
+++ 
b/modules/configuration/src/test/java/org/apache/ignite/internal/configuration/testframework/InvalidDefaultConfigurationSchema.java
@@ -19,12 +19,14 @@ package 
org.apache.ignite.internal.configuration.testframework;
 
 import org.apache.ignite.configuration.annotation.Config;
 import org.apache.ignite.configuration.annotation.Value;
+import org.apache.ignite.configuration.validation.Range;
 
 /**
- * Configuration schema for {@link ConfigurationExtensionTest#notifications()}.
+ * Configuration schema whose {@code @Value(hasDefault = true)} field is 
initialized with an invalid value.
  */
 @Config
-public class BasicConfigurationSchema {
+public class InvalidDefaultConfigurationSchema {
     @Value(hasDefault = true)
-    public int visible = 1;
+    @Range(min = 1, max = 100)
+    public int invalidDefaultValue = -1;
 }
diff --git 
a/modules/configuration/src/testFixtures/java/org/apache/ignite/internal/configuration/testframework/BasicConfigurationSchema.java
 
b/modules/configuration/src/test/java/org/apache/ignite/internal/configuration/testframework/MissingDefaultConfigurationSchema.java
similarity index 78%
copy from 
modules/configuration/src/testFixtures/java/org/apache/ignite/internal/configuration/testframework/BasicConfigurationSchema.java
copy to 
modules/configuration/src/test/java/org/apache/ignite/internal/configuration/testframework/MissingDefaultConfigurationSchema.java
index 78f8a7dd7ec..7ddf0e50163 100644
--- 
a/modules/configuration/src/testFixtures/java/org/apache/ignite/internal/configuration/testframework/BasicConfigurationSchema.java
+++ 
b/modules/configuration/src/test/java/org/apache/ignite/internal/configuration/testframework/MissingDefaultConfigurationSchema.java
@@ -21,10 +21,11 @@ import org.apache.ignite.configuration.annotation.Config;
 import org.apache.ignite.configuration.annotation.Value;
 
 /**
- * Configuration schema for {@link ConfigurationExtensionTest#notifications()}.
+ * Configuration schema whose {@code @Value(hasDefault = true)} field is not 
initialized.
+ * Used by {@link ConfigurationExtensionTest} to verify that schema defaults 
are subject to validation.
  */
 @Config
-public class BasicConfigurationSchema {
+public class MissingDefaultConfigurationSchema {
     @Value(hasDefault = true)
-    public int visible = 1;
+    public String defaultValue;
 }
diff --git 
a/modules/configuration/src/testFixtures/java/org/apache/ignite/internal/configuration/testframework/BasicConfigurationSchema.java
 
b/modules/configuration/src/test/java/org/apache/ignite/internal/configuration/testframework/ValidatedConfigurationSchema.java
similarity index 71%
copy from 
modules/configuration/src/testFixtures/java/org/apache/ignite/internal/configuration/testframework/BasicConfigurationSchema.java
copy to 
modules/configuration/src/test/java/org/apache/ignite/internal/configuration/testframework/ValidatedConfigurationSchema.java
index 78f8a7dd7ec..8d34afc6e04 100644
--- 
a/modules/configuration/src/testFixtures/java/org/apache/ignite/internal/configuration/testframework/BasicConfigurationSchema.java
+++ 
b/modules/configuration/src/test/java/org/apache/ignite/internal/configuration/testframework/ValidatedConfigurationSchema.java
@@ -19,12 +19,19 @@ package 
org.apache.ignite.internal.configuration.testframework;
 
 import org.apache.ignite.configuration.annotation.Config;
 import org.apache.ignite.configuration.annotation.Value;
+import org.apache.ignite.configuration.validation.Immutable;
+import org.apache.ignite.configuration.validation.Range;
 
 /**
- * Configuration schema for {@link ConfigurationExtensionTest#notifications()}.
+ * Configuration schema with validation annotations, used by {@link 
ConfigurationExtensionTest}.
  */
 @Config
-public class BasicConfigurationSchema {
+public class ValidatedConfigurationSchema {
+    @Range(min = 1, max = 100)
     @Value(hasDefault = true)
-    public int visible = 1;
+    public int rangeValue = 50;
+
+    @Immutable
+    @Value(hasDefault = true)
+    public String constValue = "constant";
 }
diff --git 
a/modules/configuration/src/testFixtures/java/org/apache/ignite/internal/configuration/testframework/BasicConfigurationSchema.java
 
b/modules/configuration/src/testFixtures/java/org/apache/ignite/internal/configuration/testframework/BasicConfigurationSchema.java
index 78f8a7dd7ec..b3fb2b6ab1e 100644
--- 
a/modules/configuration/src/testFixtures/java/org/apache/ignite/internal/configuration/testframework/BasicConfigurationSchema.java
+++ 
b/modules/configuration/src/testFixtures/java/org/apache/ignite/internal/configuration/testframework/BasicConfigurationSchema.java
@@ -17,13 +17,13 @@
 
 package org.apache.ignite.internal.configuration.testframework;
 
-import org.apache.ignite.configuration.annotation.Config;
+import org.apache.ignite.configuration.annotation.ConfigurationRoot;
 import org.apache.ignite.configuration.annotation.Value;
 
 /**
  * Configuration schema for {@link ConfigurationExtensionTest#notifications()}.
  */
-@Config
+@ConfigurationRoot(rootName = "basic")
 public class BasicConfigurationSchema {
     @Value(hasDefault = true)
     public int visible = 1;
diff --git 
a/modules/configuration/src/testFixtures/java/org/apache/ignite/internal/configuration/testframework/ConfigurationExtension.java
 
b/modules/configuration/src/testFixtures/java/org/apache/ignite/internal/configuration/testframework/ConfigurationExtension.java
index 3f13dbfb754..4dc605807f8 100644
--- 
a/modules/configuration/src/testFixtures/java/org/apache/ignite/internal/configuration/testframework/ConfigurationExtension.java
+++ 
b/modules/configuration/src/testFixtures/java/org/apache/ignite/internal/configuration/testframework/ConfigurationExtension.java
@@ -20,21 +20,27 @@ package 
org.apache.ignite.internal.configuration.testframework;
 import static java.lang.reflect.Modifier.isStatic;
 import static java.util.concurrent.CompletableFuture.allOf;
 import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static 
org.apache.ignite.configuration.annotation.ConfigurationType.DISTRIBUTED;
 import static 
org.apache.ignite.configuration.annotation.ConfigurationType.LOCAL;
 import static 
org.apache.ignite.internal.configuration.notifications.ConfigurationNotifier.notifyListeners;
+import static 
org.apache.ignite.internal.configuration.testframework.InjectConfiguration.MOCK_ROOT_NAME;
 import static 
org.apache.ignite.internal.configuration.util.ConfigurationUtil.findEx;
 import static 
org.apache.ignite.internal.configuration.util.ConfigurationUtil.polymorphicSchemaExtensions;
 import static 
org.apache.ignite.internal.configuration.util.ConfigurationUtil.schemaExtensions;
 import static 
org.apache.ignite.internal.configuration.util.ConfigurationUtil.touch;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 import com.typesafe.config.ConfigFactory;
 import com.typesafe.config.ConfigObject;
 import java.lang.reflect.Field;
 import java.lang.reflect.Parameter;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 import java.util.NoSuchElementException;
 import java.util.ServiceLoader;
+import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutorService;
@@ -45,7 +51,11 @@ import org.apache.ignite.configuration.ConfigurationModule;
 import org.apache.ignite.configuration.RootKey;
 import org.apache.ignite.configuration.SuperRootChange;
 import org.apache.ignite.configuration.annotation.ConfigurationType;
-import org.apache.ignite.configuration.annotation.PolymorphicConfigInstance;
+import 
org.apache.ignite.configuration.validation.ConfigurationValidationException;
+import org.apache.ignite.configuration.validation.ValidationIssue;
+import org.apache.ignite.configuration.validation.Validator;
+import org.apache.ignite.internal.configuration.CompoundModule;
+import org.apache.ignite.internal.configuration.ConfigurationTreeGenerator;
 import org.apache.ignite.internal.configuration.DynamicConfiguration;
 import org.apache.ignite.internal.configuration.DynamicConfigurationChanger;
 import org.apache.ignite.internal.configuration.RootInnerNode;
@@ -57,6 +67,9 @@ import 
org.apache.ignite.internal.configuration.hocon.HoconConverter;
 import org.apache.ignite.internal.configuration.tree.ConfigurationSource;
 import org.apache.ignite.internal.configuration.tree.InnerNode;
 import org.apache.ignite.internal.configuration.util.ConfigurationUtil;
+import 
org.apache.ignite.internal.configuration.validation.ConfigurationValidator;
+import 
org.apache.ignite.internal.configuration.validation.ConfigurationValidatorImpl;
+import org.jetbrains.annotations.Nullable;
 import org.junit.jupiter.api.extension.AfterAllCallback;
 import org.junit.jupiter.api.extension.AfterEachCallback;
 import org.junit.jupiter.api.extension.BeforeAllCallback;
@@ -86,35 +99,28 @@ public class ConfigurationExtension implements 
BeforeEachCallback, AfterEachCall
     /** Key to store {@link ExecutorService} in {@link 
ExtensionContext.Store}. */
     private static final Object POOL_KEY = new Object();
 
-    private static final List<ConfigurationModule> LOCAL_MODULES = new 
ArrayList<>();
-    private static final List<ConfigurationModule> DISTRIBUTED_MODULES = new 
ArrayList<>();
+    private static final ConfigurationModule LOCAL_MODULE;
 
-    /** All {@link ConfigurationExtension} classes in classpath. */
-    private static final List<Class<?>> EXTENSIONS;
-
-    /** All {@link PolymorphicConfigInstance} classes in classpath. */
-    private static final List<Class<?>> POLYMORPHIC_EXTENSIONS;
+    private static final ConfigurationModule DISTRIBUTED_MODULE;
 
     static {
         // Automatically find all @InternalConfiguration and 
@PolymorphicConfigInstance classes
         // to avoid configuring extensions manually in every test.
         ServiceLoader<ConfigurationModule> modules = 
ServiceLoader.load(ConfigurationModule.class);
 
-        List<Class<?>> extensions = new ArrayList<>();
-        List<Class<?>> polymorphicExtensions = new ArrayList<>();
+        List<ConfigurationModule> localModules = new ArrayList<>();
+        List<ConfigurationModule> distributedModules = new ArrayList<>();
 
         modules.forEach(configurationModule -> {
-            extensions.addAll(configurationModule.schemaExtensions());
-            
polymorphicExtensions.addAll(configurationModule.polymorphicSchemaExtensions());
             if (configurationModule.type() == LOCAL) {
-                LOCAL_MODULES.add(configurationModule);
+                localModules.add(configurationModule);
             } else {
-                DISTRIBUTED_MODULES.add(configurationModule);
+                distributedModules.add(configurationModule);
             }
         });
 
-        EXTENSIONS = List.copyOf(extensions);
-        POLYMORPHIC_EXTENSIONS = List.copyOf(polymorphicExtensions);
+        LOCAL_MODULE = new CompoundModule(LOCAL, localModules);
+        DISTRIBUTED_MODULE = new CompoundModule(DISTRIBUTED, 
distributedModules);
     }
 
     @Override
@@ -220,14 +226,15 @@ public class ConfigurationExtension implements 
BeforeEachCallback, AfterEachCall
         // classes, extension is designed to mock actual configurations from 
public API to configure Ignite components.
         Class<?> schemaClass = Class.forName(type.getCanonicalName() + 
"Schema");
 
-        List<Class<?>> extensions = EXTENSIONS;
-        List<Class<?>> polymorphicExtensions = POLYMORPHIC_EXTENSIONS;
+        Collection<Class<?>> extensions = 
configurationModule(annotation.type()).schemaExtensions();
 
         if (annotation.extensions().length > 0) {
             extensions = new ArrayList<>(extensions);
             extensions.addAll(List.of(annotation.extensions()));
         }
 
+        Collection<Class<?>> polymorphicExtensions = 
configurationModule(annotation.type()).polymorphicSchemaExtensions();
+
         if (annotation.polymorphicExtensions().length > 0) {
             polymorphicExtensions = new ArrayList<>(polymorphicExtensions);
             
polymorphicExtensions.addAll(List.of(annotation.polymorphicExtensions()));
@@ -240,22 +247,14 @@ public class ConfigurationExtension implements 
BeforeEachCallback, AfterEachCall
         );
 
         // RootKey must be mocked, there's no way to instantiate it using a 
public constructor.
-        RootKey<?, ?, ?> rootKey = new RootKey<>(
-                annotation.rootName().isBlank() ? "mock" : 
annotation.rootName(),
-                LOCAL,
-                schemaClass,
-                false
-        );
+        RootKey<?, ?, ?> rootKey = new RootKey<>(rootName(annotation), 
annotation.type(), schemaClass, false);
 
-        // Accept both "mock" (HOCON convention per @InjectConfiguration docs) 
and rootKey.key()
-        // (for patchConfigurationWithDynamicDefaults which uses the real root 
key).
-        SuperRoot superRoot = new SuperRoot(s ->
-                s.equals(rootKey.key()) || s.equals("mock")
-                        ? new RootInnerNode(rootKey, 
cgen.instantiateNode(schemaClass))
-                        : null
-        );
+        SuperRoot superRoot = createSuperRoot(rootKey, schemaClass, cgen);
+
+        // Use empty object as default.
+        String hoconStr = annotation.value().isBlank() ? rootKey.key() + " : 
{}" : annotation.value();
 
-        ConfigObject hoconCfg = 
ConfigFactory.parseString(annotation.value()).root();
+        ConfigObject hoconCfg = ConfigFactory.parseString(hoconStr).root();
 
         HoconConverter.hoconSource(hoconCfg).descend(superRoot);
 
@@ -265,7 +264,7 @@ public class ConfigurationExtension implements 
BeforeEachCallback, AfterEachCall
 
         ConfigurationUtil.addDefaults(superRoot);
 
-        if (!annotation.name().isEmpty()) {
+        if (!annotation.name().isBlank()) {
             InnerNode root = superRoot.getRoot(rootKey);
 
             root.internalId(UUID.randomUUID());
@@ -274,6 +273,10 @@ public class ConfigurationExtension implements 
BeforeEachCallback, AfterEachCall
 
         superRoot.makeImmutable();
 
+        ConfigurationValidator validator = configurationValidator(annotation, 
rootKey, schemaClass, cgen);
+
+        validateConfiguration(validator, superRoot);
+
         // Reference to the super root is required to make 
DynamicConfigurationChanger#change method atomic.
         var superRootRef = new AtomicReference<>(superRoot);
 
@@ -296,19 +299,19 @@ public class ConfigurationExtension implements 
BeforeEachCallback, AfterEachCall
 
                     ConfigurationUtil.dropNulls(copy);
 
+                    validateConfiguration(validator, sr, copy);
+
                     if (superRootRef.compareAndSet(sr, copy)) {
                         long storageRevision = 
storageRevisionCounter.incrementAndGet();
                         long notificationNumber = 
notificationListenerCounter.incrementAndGet();
 
-                        List<CompletableFuture<?>> futures = new ArrayList<>();
-
-                        futures.addAll(notifyListeners(
+                        Collection<CompletableFuture<?>> futures = 
notifyListeners(
                                 sr.getRoot(rootKey),
                                 copy.getRoot(rootKey),
                                 (DynamicConfiguration<InnerNode, ?>) 
cfgRef.get(),
                                 storageRevision,
                                 notificationNumber
-                        ));
+                        );
 
                         return 
allOf(futures.toArray(CompletableFuture[]::new));
                     }
@@ -353,8 +356,8 @@ public class ConfigurationExtension implements 
BeforeEachCallback, AfterEachCall
 
     private static void patchWithDynamicDefault(ConfigurationType type, 
SuperRoot superRoot) {
         SuperRootChangeImpl rootChange = new SuperRootChangeImpl(superRoot);
-        List<ConfigurationModule> modules = type == LOCAL ? LOCAL_MODULES : 
DISTRIBUTED_MODULES;
-        modules.forEach(module -> patchIfRootExists(module, rootChange));
+
+        patchIfRootExists(configurationModule(type), rootChange);
     }
 
     /**
@@ -369,4 +372,74 @@ public class ConfigurationExtension implements 
BeforeEachCallback, AfterEachCall
             // Module tried to access a root that doesn't exist in this 
SuperRoot - skip
         }
     }
+
+    private static void validateConfiguration(@Nullable ConfigurationValidator 
validator, SuperRoot configuration) {
+        if (validator == null) {
+            return;
+        }
+
+        List<ValidationIssue> validationIssues = 
validator.validate(configuration);
+
+        if (!validationIssues.isEmpty()) {
+            throw new ConfigurationValidationException(validationIssues);
+        }
+    }
+
+    private static void validateConfiguration(@Nullable ConfigurationValidator 
validator, SuperRoot curRoots, SuperRoot changes) {
+        if (validator == null) {
+            return;
+        }
+
+        List<ValidationIssue> validationIssues = validator.validate(curRoots, 
changes);
+
+        if (!validationIssues.isEmpty()) {
+            throw new ConfigurationValidationException(validationIssues);
+        }
+    }
+
+    @Nullable
+    private static ConfigurationValidator configurationValidator(
+            InjectConfiguration annotation,
+            RootKey<?, ?, ?> rootKey,
+            Class<?> schemaClass,
+            ConfigurationAsmGenerator cgen
+    ) {
+        if (!annotation.validate()) {
+            return null;
+        }
+
+        ConfigurationTreeGenerator mockGenerator = 
mockConfigurationTreeGenerator(rootKey, schemaClass, cgen);
+
+        Set<Validator<?, ?>> validators = 
configurationModule(annotation.type()).validators();
+
+        return ConfigurationValidatorImpl.withDefaultValidators(mockGenerator, 
validators);
+    }
+
+    private static SuperRoot createSuperRoot(RootKey<?, ?, ?> rootKey, 
Class<?> schemaClass, ConfigurationAsmGenerator cgen) {
+        // Accept both "mock" (HOCON convention per @InjectConfiguration docs) 
and rootKey.key()
+        // (for patchConfigurationWithDynamicDefaults which uses the real root 
key).
+        return new SuperRoot(s ->
+                s.equals(rootKey.key()) || s.equals(MOCK_ROOT_NAME)
+                        ? new RootInnerNode(rootKey, 
cgen.instantiateNode(schemaClass))
+                        : null
+        );
+    }
+
+    private static ConfigurationTreeGenerator mockConfigurationTreeGenerator(
+            RootKey<?, ?, ?> rootKey, Class<?> schemaClass, 
ConfigurationAsmGenerator cgen
+    ) {
+        ConfigurationTreeGenerator generator = 
mock(ConfigurationTreeGenerator.class);
+
+        when(generator.createSuperRoot()).thenAnswer(invocation -> 
createSuperRoot(rootKey, schemaClass, cgen));
+
+        return generator;
+    }
+
+    private static ConfigurationModule configurationModule(ConfigurationType 
type) {
+        return type == LOCAL ? LOCAL_MODULE : DISTRIBUTED_MODULE;
+    }
+
+    private static String rootName(InjectConfiguration annotation) {
+        return annotation.rootName().isBlank() ? MOCK_ROOT_NAME : 
annotation.rootName();
+    }
 }
diff --git 
a/modules/configuration/src/testFixtures/java/org/apache/ignite/internal/configuration/testframework/InjectConfiguration.java
 
b/modules/configuration/src/testFixtures/java/org/apache/ignite/internal/configuration/testframework/InjectConfiguration.java
index 9ee019512e9..e2aca6dd1e7 100644
--- 
a/modules/configuration/src/testFixtures/java/org/apache/ignite/internal/configuration/testframework/InjectConfiguration.java
+++ 
b/modules/configuration/src/testFixtures/java/org/apache/ignite/internal/configuration/testframework/InjectConfiguration.java
@@ -34,13 +34,13 @@ import org.intellij.lang.annotations.Language;
 
 /**
  * Annotation for injecting configuration instances into tests.
- * <p/>
- * This annotation should be used on either fields or method parameters of the 
{@code *Configuration} type.
- * <p/>
- * Injected instance is initialized with values passed in {@link #value()}, 
with schema defaults where explicit initial values are not
+ *
+ * <p>This annotation should be used on either fields or method parameters of 
the {@code *Configuration} type.
+ *
+ * <p>Injected instance is initialized with values passed in {@link #value()}, 
with schema defaults where explicit initial values are not
  * found.
- * <p/>
- * Although configuration instance is mutable, there's no {@link 
ConfigurationRegistry} and {@link ConfigurationChanger} underneath. Main
+ *
+ * <p>Although configuration instance is mutable, there's no {@link 
ConfigurationRegistry} and {@link ConfigurationChanger} underneath. Main
  * point of the extension is to provide mocks.
  *
  * @see ConfigurationExtension
@@ -48,21 +48,23 @@ import org.intellij.lang.annotations.Language;
 @Retention(RetentionPolicy.RUNTIME)
 @Target({ElementType.FIELD, ElementType.PARAMETER})
 public @interface InjectConfiguration {
+    String MOCK_ROOT_NAME = "mock";
+
     /**
-     * Configuration values to initialize the instance. Has HOCON syntax. Must 
have a root value {@code mock}.
-     * <p/>
-     * Examples:
+     * Configuration values to initialize the instance. Has HOCON syntax. Must 
have a root value {@link #MOCK_ROOT_NAME}.
+     *
+     * <p>Examples:
      * <ul>
      *     <li>{@code mock.timeout=1000}</li>
      *     <li>{@code mock{cfg1=50, cfg2=90}}</li>
      * </ul>
-     * <p/>
-     * Uses only default values by default.
+     *
+     * <p>Uses only default values by default.
      *
      * @return Initial configuration values in HOCON format.
      */
     @Language("HOCON")
-    String value() default "mock : {}";
+    String value() default "";
 
     /**
      * Name value to imitate named list elements. Default empty string value 
is treated like the absence of the name.
@@ -96,4 +98,12 @@ public @interface InjectConfiguration {
      * @return Array of configuration schema extensions.
      */
     Class<?>[] polymorphicExtensions() default {};
+
+    /**
+     * Flag indicating whether the provided configuration should be validated.
+     *
+     * <p>It may be useful to disable validation in tests if the developer 
needs to break the configuration contract on purpose,
+     * for example to set a smaller value than allowed by the corresponding 
schema.
+     */
+    boolean validate() default true;
 }
diff --git 
a/modules/core/src/testFixtures/java/org/apache/ignite/internal/testframework/JunitExtensionTestUtils.java
 
b/modules/core/src/testFixtures/java/org/apache/ignite/internal/testframework/JunitExtensionTestUtils.java
index f7e43846b78..723a5da3f09 100644
--- 
a/modules/core/src/testFixtures/java/org/apache/ignite/internal/testframework/JunitExtensionTestUtils.java
+++ 
b/modules/core/src/testFixtures/java/org/apache/ignite/internal/testframework/JunitExtensionTestUtils.java
@@ -32,7 +32,7 @@ import org.junit.platform.testkit.engine.EventType;
  *
  * @see <a 
href="https://junit.org/junit5/docs/current/user-guide/#testkit";>JUnit Platform 
Test Kit</a>
  */
-class JunitExtensionTestUtils {
+public class JunitExtensionTestUtils {
     /**
      * Executes the given test class on the Jupiter test engine.
      */
@@ -45,7 +45,7 @@ class JunitExtensionTestUtils {
     /**
      * Executes the given test class and checks that it has run all its tests 
successfully.
      */
-    static void assertExecutesSuccessfully(Class<?> testClass) {
+    public static void assertExecutesSuccessfully(Class<?> testClass) {
         execute(testClass)
                 .allEvents()
                 .assertThatEvents()
@@ -58,7 +58,7 @@ class JunitExtensionTestUtils {
      * Executes the given test class and checks that it fails with matching 
error conditions.
      */
     @SafeVarargs
-    static void assertExecutesWithFailure(Class<?> testClass, 
Condition<Throwable>... conditions) {
+    public static void assertExecutesWithFailure(Class<?> testClass, 
Condition<Throwable>... conditions) {
         execute(testClass)
                 .allEvents()
                 .assertThatEvents()
diff --git 
a/modules/eventlog/src/integrationTest/java/org/apache/ignite/internal/eventlog/impl/ItEventLogConfigurationTest.java
 
b/modules/eventlog/src/integrationTest/java/org/apache/ignite/internal/eventlog/impl/ItEventLogConfigurationTest.java
index e94929047f4..a7ad204b0bb 100644
--- 
a/modules/eventlog/src/integrationTest/java/org/apache/ignite/internal/eventlog/impl/ItEventLogConfigurationTest.java
+++ 
b/modules/eventlog/src/integrationTest/java/org/apache/ignite/internal/eventlog/impl/ItEventLogConfigurationTest.java
@@ -17,6 +17,7 @@
 
 package org.apache.ignite.internal.eventlog.impl;
 
+import static 
org.apache.ignite.configuration.annotation.ConfigurationType.DISTRIBUTED;
 import static 
org.apache.ignite.internal.eventlog.impl.TestEventTypes.TEST_EVENT_TYPE_1;
 import static 
org.apache.ignite.internal.testframework.matchers.CompletableFutureMatcher.willCompleteSuccessfully;
 import static org.hamcrest.MatcherAssert.assertThat;
@@ -44,7 +45,7 @@ class ItEventLogConfigurationTest extends 
BaseIgniteAbstractTest {
     private static final String IN_MEMORY_SINK_TYPE = "inMemory";
     private static final String TEST_SINK_NAME = "testSink";
 
-    @InjectConfiguration(polymorphicExtensions = 
InMemoryCollectionSinkConfigurationSchema.class)
+    @InjectConfiguration(polymorphicExtensions = 
InMemoryCollectionSinkConfigurationSchema.class, type = DISTRIBUTED)
     private EventLogConfiguration eventLogConfiguration;
 
     private EventLogImpl eventLog;
diff --git 
a/modules/eventlog/src/test/java/org/apache/ignite/internal/eventlog/impl/ConfigurationBasedChannelRegistryTest.java
 
b/modules/eventlog/src/test/java/org/apache/ignite/internal/eventlog/impl/ConfigurationBasedChannelRegistryTest.java
index 2a50c401d26..19d5ff4db83 100644
--- 
a/modules/eventlog/src/test/java/org/apache/ignite/internal/eventlog/impl/ConfigurationBasedChannelRegistryTest.java
+++ 
b/modules/eventlog/src/test/java/org/apache/ignite/internal/eventlog/impl/ConfigurationBasedChannelRegistryTest.java
@@ -17,6 +17,7 @@
 
 package org.apache.ignite.internal.eventlog.impl;
 
+import static 
org.apache.ignite.configuration.annotation.ConfigurationType.DISTRIBUTED;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.hasItem;
 import static org.hamcrest.Matchers.hasSize;
@@ -41,7 +42,7 @@ import org.junit.jupiter.api.extension.ExtendWith;
 class ConfigurationBasedChannelRegistryTest extends BaseIgniteAbstractTest {
     private static final String TEST_CHANNEL = "testChannel";
 
-    @InjectConfiguration
+    @InjectConfiguration(type = DISTRIBUTED)
     private EventLogConfiguration cfg;
 
     private ConfigurationBasedChannelRegistry registry;
diff --git 
a/modules/eventlog/src/test/java/org/apache/ignite/internal/eventlog/impl/ConfigurationBasedSinkRegistryTest.java
 
b/modules/eventlog/src/test/java/org/apache/ignite/internal/eventlog/impl/ConfigurationBasedSinkRegistryTest.java
index c3a5f56b4b9..9e6c3799405 100644
--- 
a/modules/eventlog/src/test/java/org/apache/ignite/internal/eventlog/impl/ConfigurationBasedSinkRegistryTest.java
+++ 
b/modules/eventlog/src/test/java/org/apache/ignite/internal/eventlog/impl/ConfigurationBasedSinkRegistryTest.java
@@ -17,6 +17,7 @@
 
 package org.apache.ignite.internal.eventlog.impl;
 
+import static 
org.apache.ignite.configuration.annotation.ConfigurationType.DISTRIBUTED;
 import static 
org.apache.ignite.internal.testframework.matchers.CompletableFutureMatcher.willCompleteSuccessfully;
 import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.CoreMatchers.nullValue;
@@ -41,7 +42,7 @@ class ConfigurationBasedSinkRegistryTest extends 
BaseIgniteAbstractTest {
     private static final String TEST_CHANNEL = "testChannel";
     private static final String TEST_SINK = "testSink";
 
-    @InjectConfiguration(polymorphicExtensions = 
InMemoryCollectionSinkConfigurationSchema.class)
+    @InjectConfiguration(polymorphicExtensions = 
InMemoryCollectionSinkConfigurationSchema.class, type = DISTRIBUTED)
     private EventLogConfiguration cfg;
 
     private InMemoryCollectionSink inMemoryCollectionSink;
diff --git 
a/modules/eventlog/src/test/java/org/apache/ignite/internal/eventlog/impl/LogSinkTest.java
 
b/modules/eventlog/src/test/java/org/apache/ignite/internal/eventlog/impl/LogSinkTest.java
index 468abd8b73b..72e24754829 100644
--- 
a/modules/eventlog/src/test/java/org/apache/ignite/internal/eventlog/impl/LogSinkTest.java
+++ 
b/modules/eventlog/src/test/java/org/apache/ignite/internal/eventlog/impl/LogSinkTest.java
@@ -17,6 +17,7 @@
 
 package org.apache.ignite.internal.eventlog.impl;
 
+import static 
org.apache.ignite.configuration.annotation.ConfigurationType.DISTRIBUTED;
 import static org.awaitility.Awaitility.await;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.hasItem;
@@ -44,7 +45,7 @@ import org.junit.jupiter.api.extension.ExtendWith;
 @ExtendWith(ConfigurationExtension.class)
 class LogSinkTest extends BaseIgniteAbstractTest {
 
-    @InjectConfiguration
+    @InjectConfiguration(type = DISTRIBUTED)
     private EventLogConfiguration cfg;
 
     private static Path eventlogPath;
diff --git 
a/modules/eventlog/src/test/java/org/apache/ignite/internal/eventlog/impl/WebhookSinkTest.java
 
b/modules/eventlog/src/test/java/org/apache/ignite/internal/eventlog/impl/WebhookSinkTest.java
index c541cb8d8b5..a8af06144e9 100644
--- 
a/modules/eventlog/src/test/java/org/apache/ignite/internal/eventlog/impl/WebhookSinkTest.java
+++ 
b/modules/eventlog/src/test/java/org/apache/ignite/internal/eventlog/impl/WebhookSinkTest.java
@@ -28,6 +28,7 @@ import static 
com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor;
 import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
 import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
 import static com.github.tomakehurst.wiremock.client.WireMock.verify;
+import static 
org.apache.ignite.configuration.annotation.ConfigurationType.DISTRIBUTED;
 import static 
org.apache.ignite.internal.rest.constants.MediaType.APPLICATION_JSON;
 import static 
org.apache.ignite.internal.testframework.matchers.CompletableFutureMatcher.willCompleteSuccessfully;
 import static org.awaitility.Awaitility.await;
@@ -62,7 +63,10 @@ import org.junit.jupiter.params.provider.ValueSource;
 class WebhookSinkTest extends BaseIgniteAbstractTest {
     private static final UUID CLUSTER_ID = UUID.randomUUID();
 
-    
@InjectConfiguration("mock{sinks.webhookSink{type=webhook,endpoint=\"http://localhost\"}}";)
+    @InjectConfiguration(
+            value = 
"mock{sinks.webhookSink{type=webhook,endpoint=\"http://localhost\"}}";,
+            type = DISTRIBUTED
+    )
     private EventLogConfiguration cfg;
 
     private WebhookSink sink;
diff --git 
a/modules/low-watermark/src/test/java/org/apache/ignite/internal/lowwatermark/LowWatermarkImplTest.java
 
b/modules/low-watermark/src/test/java/org/apache/ignite/internal/lowwatermark/LowWatermarkImplTest.java
index 2869a298f69..3d7a5385256 100644
--- 
a/modules/low-watermark/src/test/java/org/apache/ignite/internal/lowwatermark/LowWatermarkImplTest.java
+++ 
b/modules/low-watermark/src/test/java/org/apache/ignite/internal/lowwatermark/LowWatermarkImplTest.java
@@ -86,7 +86,7 @@ import org.mockito.InOrder;
 /** For {@link LowWatermarkImpl} testing. */
 @ExtendWith(ConfigurationExtension.class)
 public class LowWatermarkImplTest extends BaseIgniteAbstractTest {
-    @InjectConfiguration
+    @InjectConfiguration(validate = false)
     private LowWatermarkConfiguration lowWatermarkConfig;
 
     private final ClockService clockService = spy(new TestClockService(new 
HybridClockImpl()));
diff --git 
a/modules/metrics-exporter-otlp/src/test/java/org/apache/ignite/internal/metrics/exporters/otlp/OtlpPushMetricExporterTest.java
 
b/modules/metrics-exporter-otlp/src/test/java/org/apache/ignite/internal/metrics/exporters/otlp/OtlpPushMetricExporterTest.java
index db807fdc7e7..6ce1464cabb 100644
--- 
a/modules/metrics-exporter-otlp/src/test/java/org/apache/ignite/internal/metrics/exporters/otlp/OtlpPushMetricExporterTest.java
+++ 
b/modules/metrics-exporter-otlp/src/test/java/org/apache/ignite/internal/metrics/exporters/otlp/OtlpPushMetricExporterTest.java
@@ -18,6 +18,7 @@
 package org.apache.ignite.internal.metrics.exporters.otlp;
 
 import static io.opentelemetry.api.common.AttributeType.STRING;
+import static 
org.apache.ignite.configuration.annotation.ConfigurationType.DISTRIBUTED;
 import static 
org.apache.ignite.internal.testframework.matchers.CompletableFutureMatcher.willCompleteSuccessfully;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.jupiter.api.Assertions.assertArrayEquals;
@@ -79,7 +80,10 @@ import org.mockito.quality.Strictness;
 @ExtendWith(MockitoExtension.class)
 @MockitoSettings(strictness = Strictness.LENIENT)
 class OtlpPushMetricExporterTest extends BaseIgniteAbstractTest {
-    @InjectConfiguration("mock.exporters = {otlp = {exporterName = otlp, 
periodMillis = 300, endpoint = \"http://localhost:4317\"}}";)
+    @InjectConfiguration(
+            value = "mock.exporters = {otlp = {exporterName = otlp, 
periodMillis = 300, endpoint = \"http://localhost:4317\"}}";,
+            type = DISTRIBUTED
+    )
     private MetricConfiguration metricConfiguration;
 
     private static final UUID CLUSTER_ID = UUID.randomUUID();
diff --git 
a/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/MetricManagerTest.java
 
b/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/MetricManagerTest.java
index 97b4fa8e49d..3e2e894003a 100644
--- 
a/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/MetricManagerTest.java
+++ 
b/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/MetricManagerTest.java
@@ -17,6 +17,7 @@
 
 package org.apache.ignite.internal.metrics;
 
+import static 
org.apache.ignite.configuration.annotation.ConfigurationType.DISTRIBUTED;
 import static 
org.apache.ignite.internal.metrics.exporters.jmx.JmxExporter.JMX_EXPORTER_NAME;
 import static 
org.apache.ignite.internal.testframework.matchers.CompletableFutureMatcher.willSucceedFast;
 import static org.apache.ignite.internal.util.IgniteUtils.makeMbeanName;
@@ -45,7 +46,10 @@ public class MetricManagerTest extends 
BaseIgniteAbstractTest {
 
     private static final UUID NODE_ID = UUID.randomUUID();
 
-    @InjectConfiguration("mock.exporters = {" + JMX_EXPORTER_NAME + " = 
{exporterName = " + JMX_EXPORTER_NAME + "}}")
+    @InjectConfiguration(
+            value = "mock.exporters = {" + JMX_EXPORTER_NAME + " = 
{exporterName = " + JMX_EXPORTER_NAME + "}}",
+            type = DISTRIBUTED
+    )
     private MetricConfiguration jmxMetricConfiguration;
 
     @Test
diff --git 
a/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/exporters/jmx/JmxExporterTest.java
 
b/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/exporters/jmx/JmxExporterTest.java
index f1c0252864c..f322aa52284 100644
--- 
a/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/exporters/jmx/JmxExporterTest.java
+++ 
b/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/exporters/jmx/JmxExporterTest.java
@@ -18,6 +18,7 @@
 package org.apache.ignite.internal.metrics.exporters.jmx;
 
 import static java.util.stream.Collectors.toMap;
+import static 
org.apache.ignite.configuration.annotation.ConfigurationType.DISTRIBUTED;
 import static org.junit.jupiter.api.Assertions.assertArrayEquals;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -81,7 +82,7 @@ import org.junit.jupiter.api.extension.ExtendWith;
  */
 @ExtendWith({ConfigurationExtension.class})
 public class JmxExporterTest extends BaseIgniteAbstractTest {
-    @InjectConfiguration(value = "mock.exporters = {jmx = {exporterName = 
jmx}}")
+    @InjectConfiguration(value = "mock.exporters = {jmx = {exporterName = 
jmx}}", type = DISTRIBUTED)
     private MetricConfiguration metricConfiguration;
 
     private JmxExporterView jmxExporterConf;
diff --git 
a/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/exporters/log/LogPushExporterTest.java
 
b/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/exporters/log/LogPushExporterTest.java
index d1abfcf37d5..ebbced059a7 100644
--- 
a/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/exporters/log/LogPushExporterTest.java
+++ 
b/modules/metrics/src/test/java/org/apache/ignite/internal/metrics/exporters/log/LogPushExporterTest.java
@@ -17,6 +17,7 @@
 
 package org.apache.ignite.internal.metrics.exporters.log;
 
+import static 
org.apache.ignite.configuration.annotation.ConfigurationType.DISTRIBUTED;
 import static 
org.apache.ignite.internal.testframework.matchers.CompletableFutureMatcher.willCompleteSuccessfully;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.equalTo;
@@ -72,7 +73,7 @@ import org.junit.jupiter.params.provider.ValueSource;
  */
 @ExtendWith({ConfigurationExtension.class})
 public class LogPushExporterTest extends BaseIgniteAbstractTest {
-    @InjectConfiguration("mock.exporters {log {"
+    @InjectConfiguration(value = "mock.exporters {log {"
             + "exporterName = logPush, "
             + "periodMillis = 300, "
             + "oneLinePerMetricSource = false,"
@@ -85,7 +86,7 @@ public class LogPushExporterTest extends 
BaseIgniteAbstractTest {
             + "  \"similar.name.*\", "
             + "  \"ignored\""
             + "]"
-            + "}}")
+            + "}}", type = DISTRIBUTED)
     private MetricConfiguration metricConfiguration;
 
     private static final UUID CLUSTER_ID = UUID.randomUUID();
diff --git 
a/modules/placement-driver/src/integrationTest/java/org/apache/ignite/internal/placementdriver/PlacementDriverManagerTest.java
 
b/modules/placement-driver/src/integrationTest/java/org/apache/ignite/internal/placementdriver/PlacementDriverManagerTest.java
index d78d82bc742..a89a9ef5960 100644
--- 
a/modules/placement-driver/src/integrationTest/java/org/apache/ignite/internal/placementdriver/PlacementDriverManagerTest.java
+++ 
b/modules/placement-driver/src/integrationTest/java/org/apache/ignite/internal/placementdriver/PlacementDriverManagerTest.java
@@ -153,7 +153,7 @@ public class PlacementDriverManagerTest extends 
BasePlacementDriverTest {
     @InjectConfiguration
     private SystemDistributedConfiguration systemDistributedConfiguration;
 
-    @InjectConfiguration
+    @InjectConfiguration(validate = false)
     private ReplicationConfiguration replicationConfiguration;
 
     private MetaStorageManagerImpl metaStorageManager;
diff --git 
a/modules/placement-driver/src/test/java/org/apache/ignite/internal/placementdriver/LeaseNegotiationTest.java
 
b/modules/placement-driver/src/test/java/org/apache/ignite/internal/placementdriver/LeaseNegotiationTest.java
index 8c445c7a91c..581337823fd 100644
--- 
a/modules/placement-driver/src/test/java/org/apache/ignite/internal/placementdriver/LeaseNegotiationTest.java
+++ 
b/modules/placement-driver/src/test/java/org/apache/ignite/internal/placementdriver/LeaseNegotiationTest.java
@@ -125,7 +125,7 @@ public class LeaseNegotiationTest extends 
BaseIgniteAbstractTest {
 
     private final long assignmentsTimestamp = new HybridTimestamp(0, 
1).longValue();
 
-    @InjectConfiguration("mock.leaseAgreementAcceptanceTimeLimitMillis = 2000")
+    @InjectConfiguration(value = "mock.leaseAgreementAcceptanceTimeLimitMillis 
= 2000", validate = false)
     private ReplicationConfiguration replicationConfiguration;
 
     private static ZonePartitionId replicationGroupId(int objectId, int 
partId) {
diff --git 
a/modules/placement-driver/src/test/java/org/apache/ignite/internal/placementdriver/LeaseUpdaterTest.java
 
b/modules/placement-driver/src/test/java/org/apache/ignite/internal/placementdriver/LeaseUpdaterTest.java
index 3a8e71fc845..e23632ddef1 100644
--- 
a/modules/placement-driver/src/test/java/org/apache/ignite/internal/placementdriver/LeaseUpdaterTest.java
+++ 
b/modules/placement-driver/src/test/java/org/apache/ignite/internal/placementdriver/LeaseUpdaterTest.java
@@ -110,7 +110,7 @@ public class LeaseUpdaterTest extends 
BaseIgniteAbstractTest {
     private final LogicalNode stableNode = new LogicalNode(randomUUID(), 
"test-node-stable", NetworkAddress.from("127.0.0.1:10000"));
     private final LogicalNode pendingNode = new LogicalNode(randomUUID(), 
"test-node-pending", NetworkAddress.from("127.0.0.1:10001"));
 
-    @InjectConfiguration("mock.leaseExpirationIntervalMillis = " + 
TEST_LEASE_INTERVAL_MILLIS)
+    @InjectConfiguration(value = "mock.leaseExpirationIntervalMillis = " + 
TEST_LEASE_INTERVAL_MILLIS, validate = false)
     private ReplicationConfiguration replicationConfiguration;
 
     @Mock
diff --git 
a/modules/raft/src/test/java/org/apache/ignite/internal/raft/storage/segstore/RaftLogGarbageCollectorTest.java
 
b/modules/raft/src/test/java/org/apache/ignite/internal/raft/storage/segstore/RaftLogGarbageCollectorTest.java
index 69b6e0c9f95..d17429bbb10 100644
--- 
a/modules/raft/src/test/java/org/apache/ignite/internal/raft/storage/segstore/RaftLogGarbageCollectorTest.java
+++ 
b/modules/raft/src/test/java/org/apache/ignite/internal/raft/storage/segstore/RaftLogGarbageCollectorTest.java
@@ -80,7 +80,7 @@ class RaftLogGarbageCollectorTest extends IgniteAbstractTest {
     @InjectConfiguration
     private RaftConfiguration raftConfiguration;
 
-    @InjectConfiguration("mock.segmentFileSizeBytes=" + FILE_SIZE)
+    @InjectConfiguration(value = "mock.segmentFileSizeBytes=" + FILE_SIZE, 
validate = false)
     private LogStorageConfiguration storageConfiguration;
 
     private SegmentFileManager fileManager;
diff --git 
a/modules/raft/src/test/java/org/apache/ignite/internal/raft/storage/segstore/SegmentFileManagerGetEntryTest.java
 
b/modules/raft/src/test/java/org/apache/ignite/internal/raft/storage/segstore/SegmentFileManagerGetEntryTest.java
index 27b182a7124..6828daf5e4d 100644
--- 
a/modules/raft/src/test/java/org/apache/ignite/internal/raft/storage/segstore/SegmentFileManagerGetEntryTest.java
+++ 
b/modules/raft/src/test/java/org/apache/ignite/internal/raft/storage/segstore/SegmentFileManagerGetEntryTest.java
@@ -79,7 +79,7 @@ class SegmentFileManagerGetEntryTest extends 
IgniteAbstractTest {
     @BeforeEach
     void setUp(
             @InjectConfiguration RaftConfiguration raftConfiguration,
-            @InjectConfiguration("mock.segmentFileSizeBytes=" + FILE_SIZE)
+            @InjectConfiguration(value = "mock.segmentFileSizeBytes=" + 
FILE_SIZE, validate = false)
             LogStorageConfiguration storageConfiguration
     ) throws IOException {
         fileManager = new SegmentFileManager(
diff --git 
a/modules/raft/src/test/java/org/apache/ignite/internal/raft/storage/segstore/SegmentFileManagerTest.java
 
b/modules/raft/src/test/java/org/apache/ignite/internal/raft/storage/segstore/SegmentFileManagerTest.java
index a11a96588db..6ff0d7c9460 100644
--- 
a/modules/raft/src/test/java/org/apache/ignite/internal/raft/storage/segstore/SegmentFileManagerTest.java
+++ 
b/modules/raft/src/test/java/org/apache/ignite/internal/raft/storage/segstore/SegmentFileManagerTest.java
@@ -101,7 +101,7 @@ class SegmentFileManagerTest extends IgniteAbstractTest {
     @InjectConfiguration
     private RaftConfiguration raftConfiguration;
 
-    @InjectConfiguration("mock.segmentFileSizeBytes=" + FILE_SIZE)
+    @InjectConfiguration(value = "mock.segmentFileSizeBytes=" + FILE_SIZE, 
validate = false)
     private LogStorageConfiguration storageConfiguration;
 
     private SegmentFileManager fileManager;
diff --git 
a/modules/raft/src/test/java/org/apache/ignite/internal/raft/storage/segstore/SegstoreLogStorageConcurrencyTest.java
 
b/modules/raft/src/test/java/org/apache/ignite/internal/raft/storage/segstore/SegstoreLogStorageConcurrencyTest.java
index ca7ce4c605d..b6237aa569c 100644
--- 
a/modules/raft/src/test/java/org/apache/ignite/internal/raft/storage/segstore/SegstoreLogStorageConcurrencyTest.java
+++ 
b/modules/raft/src/test/java/org/apache/ignite/internal/raft/storage/segstore/SegstoreLogStorageConcurrencyTest.java
@@ -53,7 +53,7 @@ class SegstoreLogStorageConcurrencyTest extends 
IgniteAbstractTest {
     @BeforeEach
     void setUp(
             @InjectConfiguration RaftConfiguration raftConfiguration,
-            @InjectConfiguration("mock.segmentFileSizeBytes=" + SEGMENT_SIZE)
+            @InjectConfiguration(value = "mock.segmentFileSizeBytes=" + 
SEGMENT_SIZE, validate = false)
             LogStorageConfiguration storageConfiguration
     ) throws IOException {
         segmentFileManager = new SegmentFileManager(
diff --git 
a/modules/rest/src/test/java/org/apache/ignite/internal/rest/RestComponentTest.java
 
b/modules/rest/src/test/java/org/apache/ignite/internal/rest/RestComponentTest.java
index 1071664048e..47f7a7102a0 100644
--- 
a/modules/rest/src/test/java/org/apache/ignite/internal/rest/RestComponentTest.java
+++ 
b/modules/rest/src/test/java/org/apache/ignite/internal/rest/RestComponentTest.java
@@ -20,6 +20,7 @@ package org.apache.ignite.internal.rest;
 import static io.micronaut.http.HttpStatus.CONFLICT;
 import static io.micronaut.http.HttpStatus.NOT_FOUND;
 import static io.micronaut.http.HttpStatus.OK;
+import static 
org.apache.ignite.configuration.annotation.ConfigurationType.DISTRIBUTED;
 import static 
org.apache.ignite.configuration.annotation.ConfigurationType.LOCAL;
 import static org.apache.ignite.internal.rest.RestState.INITIALIZATION;
 import static org.apache.ignite.internal.rest.RestState.INITIALIZED;
@@ -82,7 +83,7 @@ public class RestComponentTest extends BaseIgniteAbstractTest 
{
 
     private HttpClient client;
 
-    @InjectConfiguration
+    @InjectConfiguration(type = DISTRIBUTED, validate = false)
     private SecurityConfiguration securityConfiguration;
 
     @BeforeEach
diff --git 
a/modules/security/src/main/java/org/apache/ignite/internal/security/authentication/SecurityConfigurationModule.java
 
b/modules/security/src/main/java/org/apache/ignite/internal/security/authentication/SecurityConfigurationModule.java
index 0fa05a6edc0..a75b9fabefd 100644
--- 
a/modules/security/src/main/java/org/apache/ignite/internal/security/authentication/SecurityConfigurationModule.java
+++ 
b/modules/security/src/main/java/org/apache/ignite/internal/security/authentication/SecurityConfigurationModule.java
@@ -38,11 +38,11 @@ import 
org.apache.ignite.internal.security.configuration.SecurityExtensionConfig
  */
 @AutoService(ConfigurationModule.class)
 public class SecurityConfigurationModule implements ConfigurationModule {
-    static final String DEFAULT_PROVIDER_NAME = "default";
+    public static final String DEFAULT_PROVIDER_NAME = "default";
 
-    static final String DEFAULT_USERNAME = "ignite";
+    public static final String DEFAULT_USERNAME = "ignite";
 
-    static final String DEFAULT_PASSWORD = "ignite";
+    public static final String DEFAULT_PASSWORD = "ignite";
 
     @Override
     public ConfigurationType type() {
diff --git 
a/modules/security/src/test/java/org/apache/ignite/internal/security/authentication/AuthenticationProvidersValidatorImplTest.java
 
b/modules/security/src/test/java/org/apache/ignite/internal/security/authentication/AuthenticationProvidersValidatorImplTest.java
index 6735611d1fd..ce945539f34 100644
--- 
a/modules/security/src/test/java/org/apache/ignite/internal/security/authentication/AuthenticationProvidersValidatorImplTest.java
+++ 
b/modules/security/src/test/java/org/apache/ignite/internal/security/authentication/AuthenticationProvidersValidatorImplTest.java
@@ -23,6 +23,7 @@ import static org.mockito.Mockito.mock;
 
 import java.util.function.Consumer;
 import org.apache.ignite.configuration.NamedListView;
+import org.apache.ignite.configuration.annotation.ConfigurationType;
 import org.apache.ignite.configuration.validation.ValidationContext;
 import org.apache.ignite.internal.configuration.ClusterConfiguration;
 import 
org.apache.ignite.internal.configuration.testframework.ConfigurationExtension;
@@ -44,7 +45,11 @@ import org.junit.jupiter.api.extension.ExtendWith;
 
 @ExtendWith(ConfigurationExtension.class)
 class AuthenticationProvidersValidatorImplTest extends BaseIgniteAbstractTest {
-    @InjectConfiguration(polymorphicExtensions = 
CustomAuthenticationProviderConfigurationSchema.class)
+    @InjectConfiguration(
+            polymorphicExtensions = 
CustomAuthenticationProviderConfigurationSchema.class,
+            type = ConfigurationType.DISTRIBUTED,
+            validate = false
+    )
     private SecurityConfiguration securityConfiguration;
 
     @Test

Reply via email to