From c8c88dd58c2ec739bf61ec9a45a4087e19cf780f Mon Sep 17 00:00:00 2001 From: Levente NAGY Date: Tue, 12 Sep 2017 15:09:08 +0200 Subject: [PATCH] KEYCLOAK 2538 - UI group pagination - TU + some code improvement + add mockito dependency --- model/jpa/pom.xml | 5 + .../models/jpa/JpaRealmProviderTest.java | 218 +++++++++++++++++ pom.xml | 7 + .../models/utils/ModelToRepresentation.java | 23 +- services/pom.xml | 5 + .../resources/admin/GroupsResource.java | 1 - .../resources/admin/GroupsResourceTest.java | 230 ++++++++++++++++++ 7 files changed, 469 insertions(+), 20 deletions(-) create mode 100644 model/jpa/src/test/java/org/keycloak/models/jpa/JpaRealmProviderTest.java create mode 100644 services/src/test/java/org/keycloak/services/resources/admin/GroupsResourceTest.java diff --git a/model/jpa/pom.xml b/model/jpa/pom.xml index 54166f5445..5994089985 100755 --- a/model/jpa/pom.xml +++ b/model/jpa/pom.xml @@ -111,6 +111,11 @@ junit test + + org.mockito + mockito-all + test + diff --git a/model/jpa/src/test/java/org/keycloak/models/jpa/JpaRealmProviderTest.java b/model/jpa/src/test/java/org/keycloak/models/jpa/JpaRealmProviderTest.java new file mode 100644 index 0000000000..fc8ffa4ae1 --- /dev/null +++ b/model/jpa/src/test/java/org/keycloak/models/jpa/JpaRealmProviderTest.java @@ -0,0 +1,218 @@ +/* + * 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. + */ + +package org.keycloak.models.jpa; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.keycloak.models.GroupModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.models.RealmProvider; +import org.keycloak.models.jpa.entities.GroupEntity; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.stubbing.Answer; + +import javax.persistence.EntityManager; +import javax.persistence.Query; +import javax.persistence.TypedQuery; +import java.util.*; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.*; + +/** + * @author NAGY Léventé + * @version $Revision: 1 $ + */ +@RunWith(MockitoJUnitRunner.class) +public class JpaRealmProviderTest { + + private JpaRealmProvider subject; + + private RealmModel realmModelMock; + private RealmProvider realmProviderMock; + private KeycloakSession sessionMock; + private EntityManager entityManagerMock; + + @Before + public void setup() { + realmModelMock = mock(RealmModel.class); + realmProviderMock = mock(RealmProvider.class); + sessionMock = mock(KeycloakSession.class); + entityManagerMock = mock(EntityManager.class); + + subject = new JpaRealmProvider(sessionMock, entityManagerMock); + + // Common behaviours + when(realmProviderMock.getGroupById(anyString(), any(RealmModel.class))).thenAnswer((Answer) invocationOnMock -> { + GroupEntity entity = new GroupEntity(); + entity.setId((String) invocationOnMock.getArguments()[0]); + entity.setName((String) invocationOnMock.getArguments()[0]); + return new GroupAdapter(realmModelMock, entityManagerMock, entity); + }); + } + + @Test + public void testGetGroupsCountAllGroups() { + // Given + Long result = 10L; + String idRealm = "idGroup"; + TypedQuery query = mock(TypedQuery.class); + + // When + when(entityManagerMock.createNamedQuery("getGroupCount", Long.class)).thenReturn(query); + when(realmModelMock.getId()).thenReturn(idRealm); + when(query.setParameter("realm", idRealm)).thenReturn(query); + when(query.getSingleResult()).thenReturn(result); + + // Then + Long countResult = subject.getGroupsCount(realmModelMock, false); + + assertEquals(result, countResult); + } + + @Test + public void testGetGroupsCountOnlyTopLevelGroups() { + // Given + Long result = 10L; + String idRealm = "idGroup"; + TypedQuery query = mock(TypedQuery.class); + + // When + when(entityManagerMock.createNamedQuery("getTopLevelGroupCount", Long.class)).thenReturn(query); + when(realmModelMock.getId()).thenReturn(idRealm); + when(query.setParameter("realm", idRealm)).thenReturn(query); + when(query.getSingleResult()).thenReturn(result); + + // Then + Long countResult = subject.getGroupsCount(realmModelMock, true); + + assertEquals(result, countResult); + } + + @Test + public void testSearchForGroupByNameWithAllParams() { + // Given + List result = Arrays.asList("idGroup1", "idGroup2", "idGroup3"); + String idRealm = "idGroup"; + TypedQuery query = mock(TypedQuery.class); + String search = "findMe"; + Integer first = 0; + Integer max = 10; + + // When + when(entityManagerMock.createNamedQuery("getGroupIdsByNameContaining", String.class)).thenReturn(query); + when(realmModelMock.getId()).thenReturn(idRealm); + when(query.setParameter("realm", idRealm)).thenReturn(query); + when(query.setParameter("search", search)).thenReturn(query); + when(query.setFirstResult(first)).thenReturn(query); + when(query.setMaxResults(max)).thenReturn(query); + when(query.getResultList()).thenReturn(result); + when(sessionMock.realms()).thenReturn(realmProviderMock); + + // Then + List searchResult = subject.searchForGroupByName(realmModelMock, search, first, max); + + assertEquals(result.size(), searchResult.size()); + } + + @Test + public void testSearchForGroupByNameWithNullQueryResult() { + // Given + String idRealm = "idGroup"; + TypedQuery query = mock(TypedQuery.class); + String search = "findMe"; + + // When + when(entityManagerMock.createNamedQuery("getGroupIdsByNameContaining", String.class)).thenReturn(query); + when(realmModelMock.getId()).thenReturn(idRealm); + when(query.setParameter("realm", idRealm)).thenReturn(query); + when(query.setParameter("search", search)).thenReturn(query); + when(query.getResultList()).thenReturn(null); + when(sessionMock.realms()).thenReturn(realmProviderMock); + + // Then + List searchResult = subject.searchForGroupByName(realmModelMock, search, null, null); + + assertEquals(Collections.EMPTY_LIST, searchResult); + } + + @Test + public void testSearchForGroupByNameWithNonTopLevelGroupInQueryResult() { + // Given + List result = Arrays.asList("idGroup1", "idGroup2", "idGroup3", "idGroup4"); + String idRealm = "idGroup"; + TypedQuery query = mock(TypedQuery.class); + String search = "findMe"; + Integer first = 0; + Integer max = 10; + + // When + when(entityManagerMock.createNamedQuery("getGroupIdsByNameContaining", String.class)).thenReturn(query); + when(realmModelMock.getId()).thenReturn(idRealm); + when(query.setParameter("realm", idRealm)).thenReturn(query); + when(query.setParameter("search", search)).thenReturn(query); + when(query.setFirstResult(first)).thenReturn(query); + when(query.setMaxResults(max)).thenReturn(query); + when(query.getResultList()).thenReturn(result); + when(sessionMock.realms()).thenReturn(realmProviderMock); + when(realmProviderMock.getGroupById(anyString(), any(RealmModel.class))).thenAnswer((Answer) invocationOnMock -> { + GroupEntity entity = new GroupEntity(); + entity.setId((String) invocationOnMock.getArguments()[0]); + entity.setName((String) invocationOnMock.getArguments()[0]); + if(Arrays.asList("idGroup2", "idGroup4").contains(invocationOnMock.getArguments()[0])) { + entity.setParent(new GroupEntity()); + entity.getParent().setId("idGroup5"); + entity.getParent().setName("idGroup5"); + } + return new GroupAdapter(realmModelMock, entityManagerMock, entity); + }); + + // Then + List searchResult = subject.searchForGroupByName(realmModelMock, search, first, max); + + assertEquals(3,searchResult.size()); + } + + @Test + public void testGetGroupsCountByNameContaining() { + // Given + List result = Arrays.asList("idGroup1", "idGroup2", "idGroup3", "idGroup4"); + String idRealm = "idGroup"; + TypedQuery query = mock(TypedQuery.class); + String search = "findMe"; + + // When + when(entityManagerMock.createNamedQuery("getGroupIdsByNameContaining", String.class)).thenReturn(query); + when(realmModelMock.getId()).thenReturn(idRealm); + when(query.setParameter("realm", idRealm)).thenReturn(query); + when(query.setParameter("search", search)).thenReturn(query); + when(query.getResultList()).thenReturn(result); + when(sessionMock.realms()).thenReturn(realmProviderMock); + + // Then + Long countResult = subject.getGroupsCountByNameContaining(realmModelMock, search); + + verify(query, never()).setFirstResult(anyInt()); + verify(query, never()).setFirstResult(anyInt()); + assertEquals(result.size(), countResult.intValue()); + } +} diff --git a/pom.xml b/pom.xml index 8b216dcbef..e437123938 100755 --- a/pom.xml +++ b/pom.xml @@ -108,6 +108,7 @@ 1.3 2.10 4.12 + 1.9.5 2.7.0.Final 2.35.0 1.4.01 @@ -367,6 +368,12 @@ ${junit.version} test + + org.mockito + mockito-all + test + ${mockito.version} + org.hamcrest hamcrest-all diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java b/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java index d11c2729db..225117d40e 100755 --- a/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java +++ b/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java @@ -76,12 +76,7 @@ public class ModelToRepresentation { } else { ClientModel client = (ClientModel)role.getContainer(); String clientId = client.getClientId(); - List currentClientRoles = clientRoleNames.get(clientId); - if (currentClientRoles == null) { - currentClientRoles = new ArrayList<>(); - clientRoleNames.put(clientId, currentClientRoles); - } - + List currentClientRoles = clientRoleNames.computeIfAbsent(clientId, k -> new ArrayList<>()); currentClientRoles.add(role.getName()); } } @@ -153,9 +148,7 @@ public class ModelToRepresentation { List reqActions = new ArrayList(); Set requiredActions = user.getRequiredActions(); - for (String ra : requiredActions){ - reqActions.add(ra); - } + reqActions.addAll(requiredActions); rep.setRequiredActions(reqActions); @@ -609,11 +602,7 @@ public class ModelToRepresentation { Map> grantedProtocolMappers = new HashMap>(); for (ProtocolMapperModel protocolMapper : model.getGrantedProtocolMappers()) { String protocol = protocolMapper.getProtocol(); - List currentProtocolMappers = grantedProtocolMappers.get(protocol); - if (currentProtocolMappers == null) { - currentProtocolMappers = new LinkedList(); - grantedProtocolMappers.put(protocol, currentProtocolMappers); - } + List currentProtocolMappers = grantedProtocolMappers.computeIfAbsent(protocol, k -> new LinkedList()); currentProtocolMappers.add(protocolMapper.getName()); } @@ -626,11 +615,7 @@ public class ModelToRepresentation { ClientModel client2 = (ClientModel) role.getContainer(); String clientId2 = client2.getClientId(); - List currentClientRoles = grantedClientRoles.get(clientId2); - if (currentClientRoles == null) { - currentClientRoles = new LinkedList(); - grantedClientRoles.put(clientId2, currentClientRoles); - } + List currentClientRoles = grantedClientRoles.computeIfAbsent(clientId2, k -> new LinkedList()); currentClientRoles.add(role.getName()); } } diff --git a/services/pom.xml b/services/pom.xml index 733b81277e..cbbf3da206 100755 --- a/services/pom.xml +++ b/services/pom.xml @@ -180,6 +180,11 @@ + + org.mockito + mockito-all + test + diff --git a/services/src/main/java/org/keycloak/services/resources/admin/GroupsResource.java b/services/src/main/java/org/keycloak/services/resources/admin/GroupsResource.java index ef704552e9..958b995786 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/GroupsResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/GroupsResource.java @@ -126,7 +126,6 @@ public class GroupsResource { try { response.put("count", results); } catch (JSONException e) { - e.printStackTrace(); return ErrorResponse.error("Cannot create response object", Response.Status.INTERNAL_SERVER_ERROR); } return Response.ok(response.toString(), MediaType.APPLICATION_JSON).build(); diff --git a/services/src/test/java/org/keycloak/services/resources/admin/GroupsResourceTest.java b/services/src/test/java/org/keycloak/services/resources/admin/GroupsResourceTest.java new file mode 100644 index 0000000000..9783bbb065 --- /dev/null +++ b/services/src/test/java/org/keycloak/services/resources/admin/GroupsResourceTest.java @@ -0,0 +1,230 @@ +/* + * 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. + */ +package org.keycloak.services.resources.admin; + + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.keycloak.events.admin.ResourceType; +import org.keycloak.models.GroupModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.representations.idm.GroupRepresentation; +import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator; +import org.keycloak.services.resources.admin.permissions.GroupPermissionEvaluator; +import twitter4j.JSONException; +import twitter4j.JSONObject; + +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import java.util.Collections; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * @author NAGY Léventé + * @version $Revision: 1 $ + */ +public class GroupsResourceTest { + + private GroupsResource subject; + + private RealmModel realmMock; + private KeycloakSession sessionMock; + private AdminPermissionEvaluator authMock; + private AdminEventBuilder adminEventBuilderMock; + + @Before + public void setup() { + realmMock = mock(RealmModel.class); + sessionMock = mock(KeycloakSession.class); + authMock = mock(AdminPermissionEvaluator.class); + when(authMock.groups()).thenReturn(mock(GroupPermissionEvaluator.class)); + + adminEventBuilderMock = mock(AdminEventBuilder.class); + when(adminEventBuilderMock.resource(ResourceType.GROUP)).thenReturn(adminEventBuilderMock); + + subject= new GroupsResource(realmMock, sessionMock, authMock, adminEventBuilderMock); + } + + @Test + public void testGetGroupWithAllParams() { + // Given + String search = "hello"; + Integer first = 0; + Integer max = 20; + String groupId = "groupId"; + String groupName = "groupName"; + GroupModel groupMock = mock(GroupModel.class); + List groupsList = Collections.singletonList(groupMock); + + // When + when(realmMock.searchForGroupByName(search, first, max)).thenReturn(groupsList); + when(groupMock.getSubGroups()).thenReturn(Collections.EMPTY_SET); + when(groupMock.getId()).thenReturn(groupId); + when(groupMock.getName()).thenReturn(groupName); + when(groupMock.getParent()).thenReturn(null); + + //Then + List result = subject.getGroups(search, first,max); + + Assert.assertEquals(groupsList.size(), result.size()); + Assert.assertEquals(groupId, result.get(0).getId()); + Assert.assertEquals(groupName, result.get(0).getName()); + Assert.assertTrue(result.get(0).getSubGroups().isEmpty()); + } + + @Test + public void testGetGroupWithoutSearch() { + // Given + Integer first = 0; + Integer max = 20; + String groupId = "groupId"; + String groupName = "groupName"; + GroupModel groupMock = mock(GroupModel.class); + List groupsList = Collections.singletonList(groupMock); + + // When + when(realmMock.getTopLevelGroups(first, max)).thenReturn(groupsList); + when(groupMock.getSubGroups()).thenReturn(Collections.EMPTY_SET); + when(groupMock.getId()).thenReturn(groupId); + when(groupMock.getName()).thenReturn(groupName); + when(groupMock.getParent()).thenReturn(null); + + //Then + List result = subject.getGroups(null, first,max); + + Assert.assertEquals(groupsList.size(), result.size()); + Assert.assertEquals(groupId, result.get(0).getId()); + Assert.assertEquals(groupName, result.get(0).getName()); + Assert.assertTrue(result.get(0).getSubGroups().isEmpty()); + } + + @Test + public void testGetGroupWithoutSearchAndPagination() { + // Given + String groupId = "groupId"; + String groupName = "groupName"; + GroupModel groupMock = mock(GroupModel.class); + List groupsList = Collections.singletonList(groupMock); + + // When + when(realmMock.getTopLevelGroups()).thenReturn(groupsList); + when(groupMock.getSubGroups()).thenReturn(Collections.EMPTY_SET); + when(groupMock.getId()).thenReturn(groupId); + when(groupMock.getName()).thenReturn(groupName); + when(groupMock.getParent()).thenReturn(null); + + //Then + List result = subject.getGroups(null, null, null); + + Assert.assertEquals(groupsList.size(), result.size()); + Assert.assertEquals(groupId, result.get(0).getId()); + Assert.assertEquals(groupName, result.get(0).getName()); + Assert.assertTrue(result.get(0).getSubGroups().isEmpty()); + } + + @Test + public void testGetGroupCountWithSearchAndTopLevelFlagTrue() { + // Given + String search = "search"; + Long countResult = 5L; + JSONObject response = new JSONObject(); + try { + response.put("count", countResult); + } catch (JSONException e) { + fail(e.getMessage()); + } + + // When + when(realmMock.getGroupsCountByNameContaining(search)).thenReturn(countResult); + + //Then + Response restResponse = subject.getGroupCount(search, "true"); + + assertEquals(response.toString(), restResponse.getEntity()); + assertEquals(MediaType.APPLICATION_JSON, restResponse.getMediaType().toString()); + } + + @Test + public void testGetGroupCountWithoutSearchAndTopLevelFlagTrue() { + // Given + Long countResult = 5L; + JSONObject response = new JSONObject(); + try { + response.put("count", countResult); + } catch (JSONException e) { + fail(e.getMessage()); + } + + // When + when(realmMock.getGroupsCount(true)).thenReturn(countResult); + + //Then + Response restResponse = subject.getGroupCount(null, "true"); + + assertEquals(response.toString(), restResponse.getEntity()); + assertEquals(MediaType.APPLICATION_JSON, restResponse.getMediaType().toString()); + } + + @Test + public void testGetGroupCountWithoutSearchAndTopLevelFlagFalse() { + // Given + Long countResult = 5L; + JSONObject response = new JSONObject(); + try { + response.put("count", countResult); + } catch (JSONException e) { + fail(e.getMessage()); + } + + // When + when(realmMock.getGroupsCount(false)).thenReturn(countResult); + + //Then + Response restResponse = subject.getGroupCount(null, "false"); + + assertEquals(response.toString(), restResponse.getEntity()); + assertEquals(MediaType.APPLICATION_JSON, restResponse.getMediaType().toString()); + } + + @Test + public void testGetGroupCountWithoutSearchAndTopLevelFlagNull() { + // Given + Long countResult = 5L; + JSONObject response = new JSONObject(); + try { + response.put("count", countResult); + } catch (JSONException e) { + fail(e.getMessage()); + } + + // When + when(realmMock.getGroupsCount(false)).thenReturn(countResult); + + //Then + Response restResponse = subject.getGroupCount(null, null); + + assertEquals(response.toString(), restResponse.getEntity()); + assertEquals(MediaType.APPLICATION_JSON, restResponse.getMediaType().toString()); + } +}