diff --git a/tests/base/pom.xml b/tests/base/pom.xml
index 34b15bb65948..f6b8fe78906c 100755
--- a/tests/base/pom.xml
+++ b/tests/base/pom.xml
@@ -72,6 +72,12 @@
org.keycloak.test
keycloak-test-framework-db-postgres
+
+ org.keycloak.tests
+ keycloak-tests-utils
+ ${project.version}
+ test
+
org.junit.platform
junit-platform-suite
diff --git a/tests/base/src/test/java/org/keycloak/test/admin/userprofile/UserProfileAdminTest.java b/tests/base/src/test/java/org/keycloak/test/admin/userprofile/UserProfileAdminTest.java
new file mode 100644
index 000000000000..e77fb75e1608
--- /dev/null
+++ b/tests/base/src/test/java/org/keycloak/test/admin/userprofile/UserProfileAdminTest.java
@@ -0,0 +1,181 @@
+package org.keycloak.test.admin.userprofile;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
+import org.keycloak.admin.client.resource.UserProfileResource;
+import org.keycloak.models.UserModel;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.UserProfileAttributeGroupMetadata;
+import org.keycloak.representations.idm.UserProfileMetadata;
+import org.keycloak.representations.userprofile.config.UPAttribute;
+import org.keycloak.representations.userprofile.config.UPConfig;
+import org.keycloak.representations.userprofile.config.UPGroup;
+import org.keycloak.test.framework.annotations.InjectRealm;
+import org.keycloak.test.framework.annotations.KeycloakIntegrationTest;
+import org.keycloak.test.framework.injection.LifeCycle;
+import org.keycloak.test.framework.realm.ManagedRealm;
+import org.keycloak.test.utils.JsonTestUtils;
+import org.keycloak.userprofile.config.UPConfigUtils;
+
+import java.util.List;
+import java.util.Map;
+
+@KeycloakIntegrationTest
+@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
+public class UserProfileAdminTest {
+
+ @InjectRealm(lifecycle = LifeCycle.CLASS)
+ private ManagedRealm realm;
+
+ @Test
+ @Order(1)
+ public void testDefaultConfigIfNoneSet() {
+ JsonTestUtils.assertJsonEquals(UPConfigUtils.readSystemDefaultConfig(), realm.admin().users().userProfile().getConfiguration());
+ }
+
+ @Test
+ public void testSetDefaultConfig() {
+ UPConfig config = UPConfigUtils.parseSystemDefaultConfig().addOrReplaceAttribute(new UPAttribute("test"));
+ UserProfileResource userProfile = realm.admin().users().userProfile();
+ userProfile.update(config);
+ // TODO
+ /*getCleanup().addCleanup(() -> testRealm().users().userProfile().update(null));*/
+
+ JsonTestUtils.assertJsonEquals(config, userProfile.getConfiguration());
+ }
+
+ @Test
+ public void testEmailRequiredIfEmailAsUsernameEnabled() {
+ RealmRepresentation realmRep = realm.admin().toRepresentation();
+ realmRep.setRegistrationEmailAsUsername(true);
+ realm.admin().update(realmRep);
+ // TODO
+ /*getCleanup().addCleanup(() -> {
+ realmRep.setRegistrationEmailAsUsername(registrationEmailAsUsername);
+ realm.update(realmRep);
+ });*/
+ UserProfileResource userProfile = realm.admin().users().userProfile();
+ UserProfileMetadata metadata = userProfile.getMetadata();
+ Assertions.assertTrue(metadata.getAttributeMetadata(UserModel.EMAIL).isRequired());
+ }
+
+ @Test
+ public void testEmailNotRequiredIfEmailAsUsernameDisabled() {
+ RealmRepresentation realmRep = realm.admin().toRepresentation();
+ realmRep.setRegistrationEmailAsUsername(false);
+ realm.admin().update(realmRep);
+ // TODO
+ /*getCleanup().addCleanup(() -> {
+ realmRep.setRegistrationEmailAsUsername(registrationEmailAsUsername);
+ realm.update(realmRep);
+ });*/
+ UserProfileResource userProfile = realm.admin().users().userProfile();
+ UserProfileMetadata metadata = userProfile.getMetadata();
+ Assertions.assertFalse(metadata.getAttributeMetadata(UserModel.EMAIL).isRequired());
+ }
+
+ @Test
+ public void testUsernameRequiredAndWritableIfEmailAsUsernameDisabledAndEditUsernameAllowed() {
+ RealmRepresentation realmRep = realm.admin().toRepresentation();
+ realmRep.setRegistrationEmailAsUsername(false);
+ realm.admin().update(realmRep);
+ // TODO
+ /*getCleanup().addCleanup(() -> {
+ realmRep.setRegistrationEmailAsUsername(registrationEmailAsUsername);
+ realm.update(realmRep);
+ });*/
+ realmRep.setEditUsernameAllowed(true);
+ realm.admin().update(realmRep);
+ // TODO
+ /*getCleanup().addCleanup(() -> {
+ realmRep.setEditUsernameAllowed(editUsernameAllowed);
+ realm.update(realmRep);
+ });*/
+ UserProfileResource userProfile = realm.admin().users().userProfile();
+ UserProfileMetadata metadata = userProfile.getMetadata();
+ Assertions.assertTrue(metadata.getAttributeMetadata(UserModel.USERNAME).isRequired());
+ Assertions.assertFalse(metadata.getAttributeMetadata(UserModel.USERNAME).isReadOnly());
+ }
+
+ @Test
+ public void testUsernameRequiredAndWritableIfEmailAsUsernameDisabledAndEditUsernameDisabled() {
+ RealmRepresentation realmRep = realm.admin().toRepresentation();
+ realmRep.setRegistrationEmailAsUsername(false);
+ realm.admin().update(realmRep);
+ // TODO
+ /*getCleanup().addCleanup(() -> {
+ realmRep.setRegistrationEmailAsUsername(registrationEmailAsUsername);
+ realm.update(realmRep);
+ });*/
+ realmRep.setEditUsernameAllowed(false);
+ realm.admin().update(realmRep);
+ // TODO
+ /*getCleanup().addCleanup(() -> {
+ realmRep.setEditUsernameAllowed(editUsernameAllowed);
+ realm.update(realmRep);
+ });*/
+ UserProfileResource userProfile = realm.admin().users().userProfile();
+ UserProfileMetadata metadata = userProfile.getMetadata();
+ Assertions.assertTrue(metadata.getAttributeMetadata(UserModel.USERNAME).isRequired());
+ Assertions.assertFalse(metadata.getAttributeMetadata(UserModel.USERNAME).isReadOnly());
+ }
+
+ @Test
+ public void testUsernameNotRequiredIfEmailAsUsernameEnabled() {
+ RealmRepresentation realmRep = realm.admin().toRepresentation();
+ realmRep.setRegistrationEmailAsUsername(true);
+ realm.admin().update(realmRep);
+ // TODO
+ /*getCleanup().addCleanup(() -> {
+ realmRep.setRegistrationEmailAsUsername(registrationEmailAsUsername);
+ realm.update(realmRep);
+ });*/
+ UserProfileResource userProfile = realm.admin().users().userProfile();
+ UserProfileMetadata metadata = userProfile.getMetadata();
+ Assertions.assertFalse(metadata.getAttributeMetadata(UserModel.USERNAME).isRequired());
+ Assertions.assertTrue(metadata.getAttributeMetadata(UserModel.USERNAME).isReadOnly());
+ }
+
+ @Test
+ public void testGroupsMetadata() {
+ UPConfig config = realm.admin().users().userProfile().getConfiguration();
+
+ for (int i = 0; i < 3; i++) {
+ UPGroup group = new UPGroup();
+ group.setName("name-" + i);
+ group.setDisplayHeader("displayHeader-" + i);
+ group.setDisplayDescription("displayDescription-" + i);
+ group.setAnnotations(Map.of("k1", "v1", "k2", "v2", "k3", "v3"));
+ config.addGroup(group);
+ }
+
+ UPAttribute firstName = config.getAttribute(UserModel.FIRST_NAME);
+ firstName.setGroup(config.getGroups().get(0).getName());
+ UserProfileResource userProfile = realm.admin().users().userProfile();
+ userProfile.update(config);
+ // TODO
+ /*getCleanup().addCleanup(() -> testRealm().users().userProfile().update(null));*/
+
+ UserProfileMetadata metadata = realm.admin().users().userProfile().getMetadata();
+ List groups = metadata.getGroups();
+ Assertions.assertNotNull(groups);
+ Assertions.assertFalse(groups.isEmpty());
+ Assertions.assertEquals(config.getGroups().size(), groups.size());
+ for (UPGroup group : config.getGroups()) {
+ UserProfileAttributeGroupMetadata mGroup = metadata.getAttributeGroupMetadata(group.getName());
+ Assertions.assertNotNull(mGroup);
+ Assertions.assertEquals(group.getName(), mGroup.getName());
+ Assertions.assertEquals(group.getDisplayHeader(), mGroup.getDisplayHeader());
+ Assertions.assertEquals(group.getDisplayDescription(), mGroup.getDisplayDescription());
+ if (group.getAnnotations() == null) {
+ Assertions.assertEquals(group.getAnnotations(), mGroup.getAnnotations());
+ } else {
+ Assertions.assertEquals(group.getAnnotations().size(), mGroup.getAnnotations().size());
+ }
+ }
+ Assertions.assertEquals(config.getGroups().get(0).getName(), metadata.getAttributeMetadata(UserModel.FIRST_NAME).getGroup());
+ }
+}
diff --git a/tests/pom.xml b/tests/pom.xml
index 99d04ebd5035..b12aea28a007 100755
--- a/tests/pom.xml
+++ b/tests/pom.xml
@@ -34,6 +34,7 @@
base
+ utils
diff --git a/tests/utils/pom.xml b/tests/utils/pom.xml
new file mode 100755
index 000000000000..77ca7c3475c1
--- /dev/null
+++ b/tests/utils/pom.xml
@@ -0,0 +1,56 @@
+
+
+
+
+
+ keycloak-tests-parent
+ org.keycloak.tests
+ 999.0.0-SNAPSHOT
+ ../pom.xml
+
+ 4.0.0
+
+ keycloak-tests-utils
+ Keycloak Testsuite Utilities
+ jar
+ Keycloak Testsuite Utilities
+
+
+
+
+ org.keycloak.test
+ keycloak-test-framework-bom
+ ${project.version}
+ import
+ pom
+
+
+
+
+
+
+ org.junit.jupiter
+ junit-jupiter
+
+
+ org.keycloak
+ keycloak-junit5
+
+
+
diff --git a/tests/utils/src/main/java/org/keycloak/test/utils/Assert.java b/tests/utils/src/main/java/org/keycloak/test/utils/Assert.java
new file mode 100644
index 000000000000..ed42efdd8808
--- /dev/null
+++ b/tests/utils/src/main/java/org/keycloak/test/utils/Assert.java
@@ -0,0 +1,177 @@
+package org.keycloak.test.utils;
+
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.hamcrest.MatcherAssert;
+import org.junit.jupiter.api.Assertions;
+import org.keycloak.common.util.MultivaluedHashMap;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.ClientScopeRepresentation;
+import org.keycloak.representations.idm.ComponentRepresentation;
+import org.keycloak.representations.idm.ConfigPropertyRepresentation;
+import org.keycloak.representations.idm.GroupRepresentation;
+import org.keycloak.representations.idm.IdentityProviderRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.representations.idm.UserFederationProviderFactoryRepresentation;
+import org.keycloak.representations.idm.UserProfileAttributeMetadata;
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.representations.info.ThemeInfoRepresentation;
+
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.greaterThanOrEqualTo;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.lessThanOrEqualTo;
+
+/**
+ * @author Stian Thorgersen
+ */
+public class Assert extends Assertions {
+
+ public static final Long DEFAULT_NUMBER_DEVIATION = 20L;
+
+ public static void assertNames(Set actual, String... expected) {
+ Arrays.sort(expected);
+ String[] actualNames = names(new LinkedList