Merge "Support highlights in matchinfo"
diff --git a/full/Changes b/full/Changes
index 24636ce..809ebc0 100644
--- a/full/Changes
+++ b/full/Changes
@@ -1,10 +1,23 @@
 version 0.60.1
-14/03/2018
+20/03/2018
 	- added admin-related SQL codes (margaretha)
 	- updated AdminDao (margaretha)
 	- added optional username query parameter to group list controller (margaretha) 
+	- fixed non hierarchical URI of kustvakt conf files (margaretha)
+	- added delete group member triggers (margaretha)
+	- added list user-group by username and status for system admin (margaretha)
+	- added user-group status in user-group DTO (margaretha)
+	- added check for hidden groups in user-group tests (margaretha)
+	- added database trigger test on deleting members when deleting group (margaretha)
+	- renamed VC type PREDEFINED to SYSTEM (margaretha)
+	- added VC list controller for system admin (margaretha)
+	- added VC controller tests with for system admin (margaretha) 
+	- added hidden access removal when deleting published VC (margaretha)
+	- added check for hidden groups in VC controller tests (margaretha)
+	- added search user-group controller (margaretha)
+	- removed createdBy from VirtualCorpusJson (margaretha)
 	
-version 0.60 release
+version 0.60
 14/03/2018
 	- set up mail settings using localhost port 25 (margaretha)
 	- added mail template in kustvakt configuration (margaretha)
@@ -76,8 +89,8 @@
 	- removed deprecated loader codes and tests (margaretha)
 	- removed old Spring java configurations (margaretha)
 	- implemented entity classes for the new database (margaretha)
-	- added MySQL codes regarding virtual corpus and for testing (margaretha)
-	- added dao methods regarding virtual corpus (margaretha)
+	- added MySQL codes regarding VC and for testing (margaretha)
+	- added dao methods regarding VC (margaretha)
 	- added similar SQL codes (to MySQL) for sqlite (margaretha)
 	- added dao methods regarding user groups (margaretha)
 	- restructured web-service codes into controller and logic/business-service(margaretha)
diff --git a/full/src/main/java/de/ids_mannheim/korap/constant/VirtualCorpusType.java b/full/src/main/java/de/ids_mannheim/korap/constant/VirtualCorpusType.java
index 5abd54c..229eec2 100644
--- a/full/src/main/java/de/ids_mannheim/korap/constant/VirtualCorpusType.java
+++ b/full/src/main/java/de/ids_mannheim/korap/constant/VirtualCorpusType.java
@@ -9,7 +9,7 @@
  */
 public enum VirtualCorpusType {
     // available for all
-    PREDEFINED, 
+    SYSTEM, 
     // available to project group members
     PROJECT, 
     // available only for the creator
diff --git a/full/src/main/java/de/ids_mannheim/korap/dao/UserGroupDao.java b/full/src/main/java/de/ids_mannheim/korap/dao/UserGroupDao.java
index 43eee36..bee11e3 100644
--- a/full/src/main/java/de/ids_mannheim/korap/dao/UserGroupDao.java
+++ b/full/src/main/java/de/ids_mannheim/korap/dao/UserGroupDao.java
@@ -259,6 +259,60 @@
 
     }
 
+    /** This is an admin function. It retrieves all groups given the userId 
+     * and status.
+     * 
+     * @param userId
+     * @param status
+     * @return a list of {@link UserGroup}s
+     * @throws KustvaktException
+     */
+    public List<UserGroup> retrieveGroupByStatus (String userId,
+            UserGroupStatus status) throws KustvaktException {
+
+        CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
+        CriteriaQuery<UserGroup> query =
+                criteriaBuilder.createQuery(UserGroup.class);
+
+        Root<UserGroup> root = query.from(UserGroup.class);
+
+        Predicate restrictions = null;
+
+        if (userId != null && !userId.isEmpty()) {
+
+            ListJoin<UserGroup, UserGroupMember> members =
+                    root.join(UserGroup_.members);
+            restrictions = criteriaBuilder.and(
+                    criteriaBuilder.equal(members.get(UserGroupMember_.userId),
+                            userId));
+            
+            if (status != null){
+                restrictions = criteriaBuilder.and(restrictions, criteriaBuilder
+                        .equal(root.get(UserGroup_.status), status));
+            }
+        }
+        else if (status != null) {
+                restrictions = criteriaBuilder
+                        .equal(root.get(UserGroup_.status), status);
+                
+        }
+
+        query.select(root);
+        if (restrictions!=null){
+            query.where(restrictions);
+        }
+        Query q = entityManager.createQuery(query);
+
+        try {
+            return q.getResultList();
+        }
+        catch (NoResultException e) {
+            throw new KustvaktException(StatusCodes.NO_RESULT_FOUND,
+                    "No group found for status " + status, status.toString());
+        }
+
+    }
+
     public void addVCToGroup (VirtualCorpus virtualCorpus, String createdBy,
             VirtualCorpusAccessStatus status, UserGroup group) {
         VirtualCorpusAccess accessGroup = new VirtualCorpusAccess();
diff --git a/full/src/main/java/de/ids_mannheim/korap/dao/UserGroupMemberDao.java b/full/src/main/java/de/ids_mannheim/korap/dao/UserGroupMemberDao.java
index ae58ec6..b4f2227 100644
--- a/full/src/main/java/de/ids_mannheim/korap/dao/UserGroupMemberDao.java
+++ b/full/src/main/java/de/ids_mannheim/korap/dao/UserGroupMemberDao.java
@@ -143,17 +143,25 @@
 
     public List<UserGroupMember> retrieveMemberByGroupId (int groupId)
             throws KustvaktException {
+        return retrieveMemberByGroupId(groupId, false);
+    }
+
+    public List<UserGroupMember> retrieveMemberByGroupId (int groupId,
+            boolean isAdmin) throws KustvaktException {
         CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
         CriteriaQuery<UserGroupMember> query =
                 criteriaBuilder.createQuery(UserGroupMember.class);
 
         Root<UserGroupMember> root = query.from(UserGroupMember.class);
 
-        Predicate predicate = criteriaBuilder.and(
-                criteriaBuilder.equal(root.get(UserGroupMember_.group),
-                        groupId),
-                criteriaBuilder.notEqual(root.get(UserGroupMember_.status),
-                        GroupMemberStatus.DELETED));
+        Predicate predicate = criteriaBuilder.and(criteriaBuilder
+                .equal(root.get(UserGroupMember_.group), groupId));
+
+        if (!isAdmin) {
+            predicate = criteriaBuilder.and(predicate,
+                    criteriaBuilder.notEqual(root.get(UserGroupMember_.status),
+                            GroupMemberStatus.DELETED));
+        }
 
         query.select(root);
         query.where(predicate);
diff --git a/full/src/main/java/de/ids_mannheim/korap/dao/VirtualCorpusDao.java b/full/src/main/java/de/ids_mannheim/korap/dao/VirtualCorpusDao.java
index c0fd186..4b26b2f 100644
--- a/full/src/main/java/de/ids_mannheim/korap/dao/VirtualCorpusDao.java
+++ b/full/src/main/java/de/ids_mannheim/korap/dao/VirtualCorpusDao.java
@@ -97,23 +97,51 @@
         entityManager.merge(vc);
     }
 
-    public void deleteVirtualCorpus (int id) throws KustvaktException {
-        VirtualCorpus vc = retrieveVCById(id);
+    public void deleteVirtualCorpus (VirtualCorpus vc) throws KustvaktException {
+        if (!entityManager.contains(vc)){
+            vc = entityManager.merge(vc);
+        }
         entityManager.remove(vc);
+        
     }
 
-    // for admins
-    public List<VirtualCorpus> retrieveVCByType (VirtualCorpusType type)
-            throws KustvaktException {
-        ParameterChecker.checkObjectValue(type, "type");
+    /** System admin function. 
+     * 
+     *  Retrieves virtual corpus by creator and type. If type is not specified, 
+     *  retrieves virtual corpora of all types. If createdBy is not specified,
+     *  retrieves virtual corpora of all users.
+     * 
+     * @param type {@link VirtualCorpusType}
+     * @param createdBy username of the virtual corpus creator
+     * @return a list of {@link VirtualCorpus}
+     * @throws KustvaktException
+     */
+    public List<VirtualCorpus> retrieveVCByType (VirtualCorpusType type,
+            String createdBy) throws KustvaktException {
 
         CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
         CriteriaQuery<VirtualCorpus> query =
                 criteriaBuilder.createQuery(VirtualCorpus.class);
         Root<VirtualCorpus> virtualCorpus = query.from(VirtualCorpus.class);
+
+        Predicate conditions = null;
+        if (createdBy != null && !createdBy.isEmpty()) {
+            conditions = criteriaBuilder.equal(
+                    virtualCorpus.get(VirtualCorpus_.createdBy), createdBy);
+            if (type != null) {
+                conditions = criteriaBuilder.and(conditions, criteriaBuilder
+                        .equal(virtualCorpus.get(VirtualCorpus_.type), type));
+            }
+        }
+        else if (type != null) {
+            conditions = criteriaBuilder
+                    .equal(virtualCorpus.get(VirtualCorpus_.type), type);
+        }
+
         query.select(virtualCorpus);
-        query.where(criteriaBuilder
-                .equal(virtualCorpus.get(VirtualCorpus_.type), type));
+        if (conditions != null) {
+            query.where(conditions);
+        }
         Query q = entityManager.createQuery(query);
         return q.getResultList();
     }
@@ -193,15 +221,16 @@
         Join<VirtualCorpus, VirtualCorpusAccess> access =
                 virtualCorpus.join(VirtualCorpus_.virtualCorpusAccess);
 
-//        Predicate corpusStatus = builder.and(
-//                builder.notEqual(access.get(VirtualCorpusAccess_.status),
-//                        VirtualCorpusAccessStatus.HIDDEN),
-//                builder.notEqual(access.get(VirtualCorpusAccess_.status),
-//                        VirtualCorpusAccessStatus.DELETED));
+        //        Predicate corpusStatus = builder.and(
+        //                builder.notEqual(access.get(VirtualCorpusAccess_.status),
+        //                        VirtualCorpusAccessStatus.HIDDEN),
+        //                builder.notEqual(access.get(VirtualCorpusAccess_.status),
+        //                        VirtualCorpusAccessStatus.DELETED));
 
-        Predicate corpusStatus = builder.notEqual(access.get(VirtualCorpusAccess_.status),
-                VirtualCorpusAccessStatus.DELETED);
-        
+        Predicate corpusStatus =
+                builder.notEqual(access.get(VirtualCorpusAccess_.status),
+                        VirtualCorpusAccessStatus.DELETED);
+
         Predicate userGroupStatus =
                 builder.notEqual(access.get(VirtualCorpusAccess_.userGroup)
                         .get(UserGroup_.status), UserGroupStatus.DELETED);
@@ -235,7 +264,7 @@
                 builder.equal(virtualCorpus.get(VirtualCorpus_.createdBy),
                         userId),
                 builder.equal(virtualCorpus.get(VirtualCorpus_.type),
-                        VirtualCorpusType.PREDEFINED));
+                        VirtualCorpusType.SYSTEM));
 
 
         query.select(virtualCorpus);
@@ -248,7 +277,7 @@
         Set<VirtualCorpus> vcSet = new HashSet<VirtualCorpus>();
         vcSet.addAll(vcList);
         vcSet.addAll(groupVC);
-        
+
         List<VirtualCorpus> merger = new ArrayList<VirtualCorpus>(vcSet.size());
         merger.addAll(vcSet);
         Collections.sort(merger);
diff --git a/full/src/main/java/de/ids_mannheim/korap/dto/UserGroupDto.java b/full/src/main/java/de/ids_mannheim/korap/dto/UserGroupDto.java
index 8d64c09..69900d8 100644
--- a/full/src/main/java/de/ids_mannheim/korap/dto/UserGroupDto.java
+++ b/full/src/main/java/de/ids_mannheim/korap/dto/UserGroupDto.java
@@ -5,6 +5,7 @@
 import com.fasterxml.jackson.annotation.JsonInclude;
 
 import de.ids_mannheim.korap.constant.GroupMemberStatus;
+import de.ids_mannheim.korap.constant.UserGroupStatus;
 import lombok.Getter;
 import lombok.Setter;
 
@@ -21,6 +22,7 @@
     private int id;
     private String name;
     private String owner;
+    private UserGroupStatus status;
     
     @JsonInclude(JsonInclude.Include.NON_EMPTY)
     private List<UserGroupMemberDto> members;
diff --git a/full/src/main/java/de/ids_mannheim/korap/dto/converter/UserGroupConverter.java b/full/src/main/java/de/ids_mannheim/korap/dto/converter/UserGroupConverter.java
index 9dd4df9..0ee2912 100644
--- a/full/src/main/java/de/ids_mannheim/korap/dto/converter/UserGroupConverter.java
+++ b/full/src/main/java/de/ids_mannheim/korap/dto/converter/UserGroupConverter.java
@@ -29,14 +29,17 @@
         UserGroupDto dto = new UserGroupDto();
         dto.setId(group.getId());
         dto.setName(group.getName());
+        dto.setStatus(group.getStatus());
         dto.setOwner(group.getCreatedBy());
         dto.setUserMemberStatus(userMemberStatus);
 
-        List<String> roles = new ArrayList<>(userRoles.size());
-        for (Role r : userRoles) {
-            roles.add(r.getName());
+        if (userRoles != null) {
+            List<String> roles = new ArrayList<>(userRoles.size());
+            for (Role r : userRoles) {
+                roles.add(r.getName());
+            }
+            dto.setUserRoles(roles);
         }
-        dto.setUserRoles(roles);
 
         if (members != null) {
             ArrayList<UserGroupMemberDto> memberDtos =
diff --git a/full/src/main/java/de/ids_mannheim/korap/service/UserGroupService.java b/full/src/main/java/de/ids_mannheim/korap/service/UserGroupService.java
index 147be56..e79db77 100644
--- a/full/src/main/java/de/ids_mannheim/korap/service/UserGroupService.java
+++ b/full/src/main/java/de/ids_mannheim/korap/service/UserGroupService.java
@@ -67,21 +67,8 @@
      * 
      * @see {@link PredefinedRole}
      */
-    public List<UserGroupDto> retrieveUserGroup (String username,
-            String contextUsername) throws KustvaktException {
-
-        boolean isAdmin = adminDao.isAdmin(contextUsername);
-
-        if (username != null) {
-            if (!username.equals(contextUsername) && !isAdmin) {
-                throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
-                        "Unauthorized operation for user: " + contextUsername,
-                        contextUsername);
-            }
-        }
-        else {
-            username = contextUsername;
-        }
+    public List<UserGroupDto> retrieveUserGroup (String username)
+            throws KustvaktException {
 
         List<UserGroup> userGroups =
                 userGroupDao.retrieveGroupByUserId(username);
@@ -124,10 +111,41 @@
         return userGroupDao.retrieveGroupById(groupId);
     }
 
-    public UserGroup retrieveHiddenGroup (int vcId) throws KustvaktException {
+    public UserGroup retrieveHiddenUserGroupByVC (int vcId)
+            throws KustvaktException {
         return userGroupDao.retrieveHiddenGroupByVC(vcId);
     }
 
+    public List<UserGroupDto> retrieveUserGroupByStatus (String username,
+            String contextUsername, UserGroupStatus status)
+            throws KustvaktException {
+
+        boolean isAdmin = adminDao.isAdmin(contextUsername);
+
+        if (isAdmin) {
+            List<UserGroup> userGroups =
+                    userGroupDao.retrieveGroupByStatus(username, status);
+            Collections.sort(userGroups);
+            ArrayList<UserGroupDto> dtos = new ArrayList<>(userGroups.size());
+
+            List<UserGroupMember> members;
+            UserGroupDto groupDto;
+            for (UserGroup group : userGroups) {
+                members = groupMemberDao.retrieveMemberByGroupId(group.getId(),
+                        true);
+                groupDto = converter.createUserGroupDto(group, members, null,
+                        null);
+                dtos.add(groupDto);
+            }
+            return dtos;
+        }
+        else {
+            throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
+                    "Unauthorized operation for user: " + contextUsername,
+                    contextUsername);
+        }
+    }
+
     public List<UserGroupMember> retrieveVCAccessAdmins (UserGroup userGroup)
             throws KustvaktException {
         List<UserGroupMember> groupAdmins = groupMemberDao.retrieveMemberByRole(
@@ -460,4 +478,21 @@
 
         groupMemberDao.deleteMember(member, deletedBy, isSoftDelete);
     }
+
+    public UserGroupDto searchById (String username, int groupId)
+            throws KustvaktException {
+        if (adminDao.isAdmin(username)) {
+            UserGroup userGroup = userGroupDao.retrieveGroupById(groupId, true);
+            UserGroupDto groupDto = converter.createUserGroupDto(userGroup,
+                    userGroup.getMembers(), null, null);
+            return groupDto;
+        }
+        else {
+            throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
+                    "Unauthorized operation for user: " + username, username);
+        }
+
+    }
+
+
 }
diff --git a/full/src/main/java/de/ids_mannheim/korap/service/VirtualCorpusService.java b/full/src/main/java/de/ids_mannheim/korap/service/VirtualCorpusService.java
index 6ea1002..0643baa 100644
--- a/full/src/main/java/de/ids_mannheim/korap/service/VirtualCorpusService.java
+++ b/full/src/main/java/de/ids_mannheim/korap/service/VirtualCorpusService.java
@@ -1,6 +1,7 @@
 package de.ids_mannheim.korap.service;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 
@@ -74,12 +75,42 @@
         return createVCDtos(vcList);
     }
 
-    public List<VirtualCorpusDto> listVCByUser (String username)
-            throws KustvaktException {
-        List<VirtualCorpus> vcList = vcDao.retrieveVCByUser(username);
+    public List<VirtualCorpusDto> listVCByUser (String contextUsername,
+            String createdBy) throws KustvaktException {
+
+        boolean isAdmin = adminDao.isAdmin(contextUsername);
+
+        if (createdBy != null) {
+            if (!createdBy.equals(contextUsername) && !isAdmin) {
+                throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
+                        "Unauthorized operation for user: " + contextUsername,
+                        contextUsername);
+            }
+        }
+        else {
+            createdBy = contextUsername;
+        }
+        List<VirtualCorpus> vcList = vcDao.retrieveVCByUser(createdBy);
         return createVCDtos(vcList);
     }
 
+    public List<VirtualCorpusDto> listVCByType (String username,
+            String createdBy, VirtualCorpusType type) throws KustvaktException {
+
+        boolean isAdmin = adminDao.isAdmin(username);
+
+        if (isAdmin) {
+            List<VirtualCorpus> virtualCorpora =
+                    vcDao.retrieveVCByType(type, createdBy);
+            Collections.sort(virtualCorpora);
+            return createVCDtos(virtualCorpora);
+        }
+        else {
+            throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
+                    "Unauthorized operation for user: " + username, username);
+        }
+    }
+
     private ArrayList<VirtualCorpusDto> createVCDtos (
             List<VirtualCorpus> vcList) throws KustvaktException {
         ArrayList<VirtualCorpusDto> dtos = new ArrayList<>(vcList.size());
@@ -108,7 +139,15 @@
         VirtualCorpus vc = vcDao.retrieveVCById(vcId);
 
         if (vc.getCreatedBy().equals(username) || adminDao.isAdmin(username)) {
-            vcDao.deleteVirtualCorpus(vcId);
+
+            if (vc.getType().equals(VirtualCorpusType.PUBLISHED)) {
+                VirtualCorpusAccess access =
+                        accessDao.retrieveHiddenAccess(vcId);
+                accessDao.deleteAccess(access, "system");
+                userGroupService.deleteAutoHiddenGroup(
+                        access.getUserGroup().getId(), "system");
+            }
+            vcDao.deleteVirtualCorpus(vc);
         }
         else {
             throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
@@ -184,10 +223,9 @@
         ParameterChecker.checkStringValue(vc.getName(), "name");
         ParameterChecker.checkObjectValue(vc.getType(), "type");
         ParameterChecker.checkStringValue(vc.getCorpusQuery(), "corpusQuery");
-        ParameterChecker.checkStringValue(vc.getCreatedBy(), "createdBy");
 
 
-        if (vc.getType().equals(VirtualCorpusType.PREDEFINED)
+        if (vc.getType().equals(VirtualCorpusType.SYSTEM)
                 && !adminDao.isAdmin(username)) {
             throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
                     "Unauthorized operation for user: " + username, username);
@@ -198,7 +236,7 @@
 
         int vcId = vcDao.createVirtualCorpus(vc.getName(), vc.getType(),
                 requiredAccess, koralQuery, vc.getDefinition(),
-                vc.getDescription(), vc.getStatus(), vc.getCreatedBy());
+                vc.getDescription(), vc.getStatus(), username);
 
         if (vc.getType().equals(VirtualCorpusType.PUBLISHED)) {
             publishVC(vcId);
@@ -383,7 +421,7 @@
             else if (VirtualCorpusType.PUBLISHED.equals(type)) {
                 // add user in the VC's auto group 
                 UserGroup userGroup =
-                        userGroupService.retrieveHiddenGroup(vcId);
+                        userGroupService.retrieveHiddenUserGroupByVC(vcId);
                 try {
                     userGroupService.inviteGroupMember(username, userGroup,
                             "system", GroupMemberStatus.ACTIVE);
@@ -393,7 +431,7 @@
                     // skip adding user to hidden group
                 }
             }
-            // else VirtualCorpusType.PREDEFINED
+            // else VirtualCorpusType.SYSTEM
         }
 
         String json = vc.getCorpusQuery();
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/UserGroupController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/UserGroupController.java
index a838c13..6157b24 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/UserGroupController.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/UserGroupController.java
@@ -8,6 +8,7 @@
 import javax.ws.rs.GET;
 import javax.ws.rs.POST;
 import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Context;
@@ -20,7 +21,9 @@
 
 import com.sun.jersey.spi.container.ResourceFilters;
 
+import de.ids_mannheim.korap.constant.UserGroupStatus;
 import de.ids_mannheim.korap.dto.UserGroupDto;
+import de.ids_mannheim.korap.dto.VirtualCorpusDto;
 import de.ids_mannheim.korap.exceptions.KustvaktException;
 import de.ids_mannheim.korap.service.UserGroupService;
 import de.ids_mannheim.korap.user.TokenContext;
@@ -55,19 +58,21 @@
     private UserGroupService service;
 
     /** Returns all user-groups in which a user is an active or a pending member.
+     *  Not suitable for system-admin, instead use {@link UserGroupController#
+     *  getUserGroupBySystemAdmin(SecurityContext, String, UserGroupStatus)} 
      * 
      * @param securityContext
      * @return a list of user-groups
+     * 
      */
     @GET
     @Path("list")
-    public Response getUserGroup (@Context SecurityContext securityContext,
-            @QueryParam("username") String username) {
+    public Response getUserGroup (@Context SecurityContext securityContext) {
         TokenContext context =
                 (TokenContext) securityContext.getUserPrincipal();
         try {
             List<UserGroupDto> dtos =
-                    service.retrieveUserGroup(username, context.getUsername());
+                    service.retrieveUserGroup(context.getUsername());
             String result = JsonUtils.toJSON(dtos);
             return Response.ok(result).build();
         }
@@ -76,7 +81,59 @@
         }
     }
 
+    
+    /** Lists user-groups for system-admin purposes. If username parameter 
+     *  is not specified, list user-groups of all users. If status is not
+     *  specified, list user-groups of all statuses.
+     * 
+     * @param securityContext
+     * @param username username
+     * @param status {@link UserGroupStatus}
+     * @return a list of user-groups
+     */
+    @GET
+    @Path("list/system-admin")
+    public Response getUserGroupBySystemAdmin (
+            @Context SecurityContext securityContext,
+            @QueryParam("username") String username,
+            @QueryParam("status") UserGroupStatus status) {
+        TokenContext context =
+                (TokenContext) securityContext.getUserPrincipal();
+        try {
+            List<UserGroupDto> dtos = service.retrieveUserGroupByStatus(
+                    username, context.getUsername(), status);
+            String result = JsonUtils.toJSON(dtos);
+            return Response.ok(result).build();
+        }
+        catch (KustvaktException e) {
+            throw responseHandler.throwit(e);
+        }
+    }
 
+    /** Searches for a specific user-group for system admins.
+     * 
+     * @param securityContext
+     * @param groupId group id
+     * @return a user-group
+     */
+    @GET
+    @Path("search/{groupId}")
+    public Response searchUserGroup (@Context SecurityContext securityContext,
+            @PathParam("groupId") int groupId) {
+        String result;
+        TokenContext context =
+                (TokenContext) securityContext.getUserPrincipal();
+        try {
+            UserGroupDto dto =
+                    service.searchById(context.getUsername(), groupId);
+            result = JsonUtils.toJSON(dto);
+        }
+        catch (KustvaktException e) {
+            throw responseHandler.throwit(e);
+        }
+        return Response.ok(result).build();
+    }
+    
     /** Creates a user group where the user in token context is the 
      * group owner, and assigns the listed group members with status 
      * GroupMemberStatus.PENDING. 
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/VirtualCorpusController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/VirtualCorpusController.java
index ef012d6..a0f937f 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/VirtualCorpusController.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/VirtualCorpusController.java
@@ -24,6 +24,7 @@
 import com.sun.jersey.spi.container.ResourceFilters;
 
 import de.ids_mannheim.korap.constant.VirtualCorpusAccessStatus;
+import de.ids_mannheim.korap.constant.VirtualCorpusType;
 import de.ids_mannheim.korap.dto.VirtualCorpusAccessDto;
 import de.ids_mannheim.korap.dto.VirtualCorpusDto;
 import de.ids_mannheim.korap.exceptions.KustvaktException;
@@ -65,7 +66,10 @@
     @Autowired
     private VirtualCorpusService service;
 
-    /** Creates a user VC, also for system admins
+    // EM: should system admins be able to create VC for other users?
+    /** Creates a user virtual corpus, also for system admins
+     * 
+     * @see VirtualCorpusJson
      * 
      * @param securityContext
      * @param vc a JSON object describing the virtual corpus
@@ -91,7 +95,11 @@
         return Response.ok().build();
     }
 
-    /** Only the VC owner and system admins can edit VC.
+    /** Edits a virtual corpus attributes including name, type and corpus 
+     *  query. Only the virtual corpus owner and system admins can edit 
+     *  a virtual corpus.
+     * 
+     * @see VirtualCorpusJson
      * 
      * @param securityContext
      * @param vc a JSON object describing the virtual corpus
@@ -115,11 +123,11 @@
         return Response.ok().build();
     }
 
-    /** Searches for a specific VC.
+    /** Searches for a specific VC given the VC id. 
      * 
      * @param securityContext
      * @param vcId a virtual corpus id
-     * @return a list of VC
+     * @return a list of virtual corpora
      */
     @GET
     @Path("search/{vcId}")
@@ -139,20 +147,27 @@
         return Response.ok(result).build();
     }
 
-    /** Lists not only private VC but all VC available to a user.
+    /** Lists not only private virtual corpora but all virtual corpora 
+     *  available to a user.
+     *  
+     *  Users, except system admins, cannot list virtual corpora of 
+     *  other users. Thus, createdBy parameter is only relevant for 
+     *  requests from system admins.
      * 
      * @param securityContext
-     * @return a list of VC
+     * @param createdBy username of virtual corpus creator (optional)
+     * @return a list of virtual corpora
      */
     @GET
     @Path("list")
-    public Response listVCByUser (@Context SecurityContext securityContext) {
+    public Response listVCByUser (@Context SecurityContext securityContext,
+            @QueryParam("createdBy") String createdBy) {
         String result;
         TokenContext context =
                 (TokenContext) securityContext.getUserPrincipal();
         try {
             List<VirtualCorpusDto> dtos =
-                    service.listVCByUser(context.getUsername());
+                    service.listVCByUser(context.getUsername(), createdBy);
             result = JsonUtils.toJSON(dtos);
         }
         catch (KustvaktException e) {
@@ -161,10 +176,11 @@
         return Response.ok(result).build();
     }
 
-    /** Lists all VC created by a user
+    /** Lists all virtual corpora created by a user
      * 
      * @param securityContext
-     * @return a list of VC created by the user in the security context.
+     * @return a list of virtual corpora created by the user 
+     * in the security context.
      */
     @GET
     @Path("list/user")
@@ -183,6 +199,37 @@
         return Response.ok(result).build();
     }
 
+    /** Lists virtual corpora by creator and type. This is a controller for 
+     *  system admin requiring valid system admin authentication. 
+     *  
+     *  If type is not specified, retrieves virtual corpora of all types. 
+     *  If createdBy is not specified, retrieves virtual corpora of all 
+     *  users.
+     *  
+     * @param securityContext
+     * @param createdBy username of virtual corpus creator
+     * @param type {@link VirtualCorpusType}
+     * @return a list of virtual corpora
+     */
+    @GET
+    @Path("list/system-admin")
+    public Response listVCByStatus (@Context SecurityContext securityContext,
+            @QueryParam("createdBy") String createdBy,
+            @QueryParam("type") VirtualCorpusType type) {
+        String result;
+        TokenContext context =
+                (TokenContext) securityContext.getUserPrincipal();
+        try {
+            List<VirtualCorpusDto> dtos = service
+                    .listVCByType(context.getUsername(), createdBy, type);
+            result = JsonUtils.toJSON(dtos);
+        }
+        catch (KustvaktException e) {
+            throw responseHandler.throwit(e);
+        }
+        return Response.ok(result).build();
+    }
+
     /** Only the VC owner and system admins can delete VC. VCA admins 
      *  can delete VC-accesses e.g. of project VC, but not the VC 
      *  themselves. 
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/input/VirtualCorpusJson.java b/full/src/main/java/de/ids_mannheim/korap/web/input/VirtualCorpusJson.java
index ac09516..a67876a 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/input/VirtualCorpusJson.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/input/VirtualCorpusJson.java
@@ -23,7 +23,6 @@
     private String name;
     private VirtualCorpusType type;
     private String corpusQuery;
-    private String createdBy;
     
     // required in editing VCs
     private int id;
diff --git a/full/src/main/resources/db/insert/V3.1__insert_virtual_corpus.sql b/full/src/main/resources/db/insert/V3.1__insert_virtual_corpus.sql
index c708076..7d11ce6 100644
--- a/full/src/main/resources/db/insert/V3.1__insert_virtual_corpus.sql
+++ b/full/src/main/resources/db/insert/V3.1__insert_virtual_corpus.sql
@@ -13,8 +13,8 @@
 --INSERT INTO user_group(name,status,created_by) 
 --	VALUES ("all users","HIDDEN","system");
 
-INSERT INTO user_group(name,status,created_by) 
-	VALUES ("deleted group","DELETED","dory");
+INSERT INTO user_group(name,status,created_by, deleted_by) 
+	VALUES ("deleted group","DELETED","dory", "dory");
 
 
 
@@ -54,6 +54,11 @@
 		(SELECT id from user_group where name = "auto group"),
 		"ACTIVE","system";
 
+INSERT INTO user_group_member(user_id, group_id, status, created_by)
+	SELECT "dory",
+		(SELECT id from user_group where name = "deleted group"),
+		"ACTIVE","dory";
+
 		
 -- virtual corpora
 INSERT INTO virtual_corpus(name, type, required_access, created_by, description, status, corpus_query) 
@@ -65,7 +70,7 @@
 	'{"collection": { "@type": "koral:docGroup", "operands": [ { "@type": "koral:doc", "key": "corpusSigle", "match": "match:eq", "value": "GOE" }, { "@type": "koral:doc", "key": "creationDate", "match": "match:leq", "type": "type:date", "value": "1810" } ], "operation": "operation:and" }}');
 
 INSERT INTO virtual_corpus(name, type, required_access, created_by, description, status, corpus_query) 
-	VALUES ("system VC", "PREDEFINED", "ALL", "system", "test vc", "experimental",
+	VALUES ("system VC", "SYSTEM", "ALL", "system", "test vc", "experimental",
 	'{"collection":{"@type":"koral:doc","value":"GOE","match":"match:eq","key":"corpusSigle"}}');
 
 INSERT INTO virtual_corpus(name, type, required_access, created_by, description, status, corpus_query) 
diff --git a/full/src/main/resources/db/new-mysql/V1.2__create_admin_table.sql b/full/src/main/resources/db/new-mysql/V1.2__create_admin_table.sql
index f51cd1a..7c8ce73 100644
--- a/full/src/main/resources/db/new-mysql/V1.2__create_admin_table.sql
+++ b/full/src/main/resources/db/new-mysql/V1.2__create_admin_table.sql
@@ -1,5 +1,5 @@
 CREATE TABLE IF NOT EXISTS admin (
-	id INTEGER PRIMARY KEY AUTOINCREMENT,
+	id INTEGER PRIMARY KEY AUTO_INCREMENT,
 	user_id varchar(100) NOT NULL,
 	UNIQUE INDEX unique_index (user_id)
 );
\ No newline at end of file
diff --git a/full/src/main/resources/db/new-mysql/V1.3__triggers.sql b/full/src/main/resources/db/new-mysql/V1.3__triggers.sql
new file mode 100644
index 0000000..7bc25dd
--- /dev/null
+++ b/full/src/main/resources/db/new-mysql/V1.3__triggers.sql
@@ -0,0 +1,14 @@
+delimiter |
+
+CREATE TRIGGER delete_member AFTER UPDATE ON user_group
+	FOR EACH ROW 
+	BEGIN
+		UPDATE user_group_member 
+		SET status = "DELETED"
+		WHERE NEW.status = "DELETED" 
+			AND  OLD.status != "DELETED" 
+			AND group_id = NEW.id;
+	END;
+|
+	
+delimiter ;
\ No newline at end of file
diff --git a/full/src/main/resources/db/new-sqlite/V1.2__triggers.sql b/full/src/main/resources/db/new-sqlite/V1.2__triggers.sql
index 890e792..63dfeae 100644
--- a/full/src/main/resources/db/new-sqlite/V1.2__triggers.sql
+++ b/full/src/main/resources/db/new-sqlite/V1.2__triggers.sql
@@ -1,9 +1,22 @@
 CREATE TRIGGER insert_member_status AFTER INSERT ON user_group_member
      BEGIN
-      UPDATE user_group_member SET status_date = DATETIME('now', 'localtime')  WHERE rowid = new.rowid;
+      UPDATE user_group_member 
+      SET status_date = DATETIME('now', 'localtime')  
+      WHERE rowid = new.rowid;
      END;
 
 CREATE TRIGGER update_member_status AFTER UPDATE ON user_group_member	
      BEGIN
-      UPDATE user_group_member SET status_date = (datetime('now','localtime'))  WHERE rowid = old.rowid;
+      UPDATE user_group_member 
+      SET status_date = (datetime('now','localtime'))  
+      WHERE rowid = old.rowid;
      END;   
+
+CREATE TRIGGER delete_member AFTER UPDATE ON user_group
+	WHEN new.status = "DELETED" AND  old.status <> "DELETED"
+	BEGIN
+		UPDATE user_group_member 
+		SET status = "DELETED"
+		WHERE group_id = new.id;
+	END;

+	
\ No newline at end of file
diff --git a/full/src/test/java/de/ids_mannheim/korap/dao/UserGroupDaoTest.java b/full/src/test/java/de/ids_mannheim/korap/dao/UserGroupDaoTest.java
index d40ee83..4bb8ba0 100644
--- a/full/src/test/java/de/ids_mannheim/korap/dao/UserGroupDaoTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/dao/UserGroupDaoTest.java
@@ -150,6 +150,6 @@
         assertEquals(1, vc.size());
         
         // delete vc
-        virtualCorpusDao.deleteVirtualCorpus(virtualCorpus.getId());
+        virtualCorpusDao.deleteVirtualCorpus(virtualCorpus);
     }
 }
diff --git a/full/src/test/java/de/ids_mannheim/korap/dao/VirtualCorpusDaoTest.java b/full/src/test/java/de/ids_mannheim/korap/dao/VirtualCorpusDaoTest.java
index d13216b..022f56c 100644
--- a/full/src/test/java/de/ids_mannheim/korap/dao/VirtualCorpusDaoTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/dao/VirtualCorpusDaoTest.java
@@ -32,7 +32,7 @@
     @Test
     public void testListVCByType () throws KustvaktException {
         List<VirtualCorpus> vcList =
-                dao.retrieveVCByType(VirtualCorpusType.PUBLISHED);
+                dao.retrieveVCByType(VirtualCorpusType.PUBLISHED, null);
         assertEquals(1, vcList.size());
 
         VirtualCorpus vc = vcList.get(0);
@@ -42,20 +42,20 @@
     }
 
     @Test
-    public void testPredefinedVC () throws KustvaktException {
+    public void testSystemVC () throws KustvaktException {
         // insert vc
-        int id = dao.createVirtualCorpus("predefined VC",
-                VirtualCorpusType.PREDEFINED, User.CorpusAccess.FREE,
-                "corpusSigle=GOE", "definition", "description", "experimental",
-                "test class");
+        int id = dao.createVirtualCorpus("system VC", VirtualCorpusType.SYSTEM,
+                User.CorpusAccess.FREE, "corpusSigle=GOE", "definition",
+                "description", "experimental", "test class");
 
         // select vc
         List<VirtualCorpus> vcList =
-                dao.retrieveVCByType(VirtualCorpusType.PREDEFINED);
+                dao.retrieveVCByType(VirtualCorpusType.SYSTEM, null);
         assertEquals(2, vcList.size());
 
+        VirtualCorpus vc = dao.retrieveVCById(id);
         // delete vc
-        dao.deleteVirtualCorpus(id);
+        dao.deleteVirtualCorpus(vc);
 
         // check if vc has been deleted
         thrown.expect(KustvaktException.class);
@@ -64,9 +64,8 @@
 
 
     @Test
-    public void retrievePredefinedVC () throws KustvaktException {
-        List<VirtualCorpus> vc =
-                dao.retrieveVCByType(VirtualCorpusType.PREDEFINED);
+    public void retrieveSystemVC () throws KustvaktException {
+        List<VirtualCorpus> vc = dao.retrieveVCByType(VirtualCorpusType.SYSTEM, null);
         assertEquals(1, vc.size());
     }
 
@@ -77,12 +76,12 @@
     @Test
     public void retrieveVCByUserDory () throws KustvaktException {
         List<VirtualCorpus> virtualCorpora = dao.retrieveVCByUser("dory");
-//        System.out.println(virtualCorpora);
+        //        System.out.println(virtualCorpora);
         assertEquals(4, virtualCorpora.size());
         // ordered by id
         Iterator<VirtualCorpus> i = virtualCorpora.iterator();
         assertEquals("dory VC", i.next().getName());
-        assertEquals("group VC", i.next().getName());   
+        assertEquals("group VC", i.next().getName());
         assertEquals("system VC", i.next().getName());
         assertEquals("published VC", i.next().getName());
     }
diff --git a/full/src/test/java/de/ids_mannheim/korap/service/VirtualCorpusServiceTest.java b/full/src/test/java/de/ids_mannheim/korap/service/VirtualCorpusServiceTest.java
index e9511a4..6ae2e3d 100644
--- a/full/src/test/java/de/ids_mannheim/korap/service/VirtualCorpusServiceTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/service/VirtualCorpusServiceTest.java
@@ -32,7 +32,6 @@
 
         VirtualCorpusJson vc = new VirtualCorpusJson();
         vc.setCorpusQuery("corpusSigle=GOE");
-        vc.setCreatedBy(username);
         vc.setName("new published vc");
         vc.setType(VirtualCorpusType.PUBLISHED);
         int vcId = vcService.storeVC(vc, "VirtualCorpusServiceTest");
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/SearchControllerTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/SearchControllerTest.java
index 5814029..ecede59 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/SearchControllerTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/SearchControllerTest.java
@@ -17,28 +17,21 @@
 
 import de.ids_mannheim.korap.authentication.http.HttpAuthorizationHandler;
 import de.ids_mannheim.korap.config.Attributes;
+import de.ids_mannheim.korap.config.SpringJerseyTest;
 import de.ids_mannheim.korap.exceptions.KustvaktException;
 import de.ids_mannheim.korap.query.serialize.QuerySerializer;
 import de.ids_mannheim.korap.utils.JsonUtils;
-import de.ids_mannheim.korap.web.FastJerseyTest;
 
 /**
- * @author hanl, margaretha
- * @lastUpdate 30/05/2017
+ * @author margaretha, hanl
+ * @lastUpdate 22/03/2018
  *
  */
-public class SearchControllerTest extends FastJerseyTest {
+public class SearchControllerTest extends SpringJerseyTest {
 
     @Autowired
-    HttpAuthorizationHandler handler;
+    private HttpAuthorizationHandler handler;
     
-    @Override
-    public void initMethod () throws KustvaktException {
-//        helper().runBootInterfaces();
-//        helper().setupAccount();
-    }
-
-
 
     @Test
     public void testSearchQueryPublicCorpora () throws KustvaktException{
@@ -46,8 +39,8 @@
                 .path("search").queryParam("q", "[orth=der]")
                 .queryParam("ql", "poliqarp")
                 .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class);
-//        assertEquals(ClientResponse.Status.OK.getStatusCode(),
-//                response.getStatus());
+        assertEquals(ClientResponse.Status.OK.getStatusCode(),
+                response.getStatus());
         String ent = response.getEntity(String.class);
 //        System.out.println(ent);
         JsonNode node = JsonUtils.readTree(ent);
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/UserGroupControllerAdminTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/UserGroupControllerAdminTest.java
index a9127cc..a80397c 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/UserGroupControllerAdminTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/UserGroupControllerAdminTest.java
@@ -2,6 +2,8 @@
 
 import static org.junit.Assert.assertEquals;
 
+import javax.ws.rs.core.MediaType;
+
 import org.junit.Test;
 import org.springframework.beans.factory.annotation.Autowired;
 
@@ -15,41 +17,80 @@
 import de.ids_mannheim.korap.authentication.http.HttpAuthorizationHandler;
 import de.ids_mannheim.korap.config.Attributes;
 import de.ids_mannheim.korap.config.SpringJerseyTest;
+import de.ids_mannheim.korap.constant.GroupMemberStatus;
+import de.ids_mannheim.korap.constant.PredefinedRole;
 import de.ids_mannheim.korap.exceptions.KustvaktException;
 import de.ids_mannheim.korap.utils.JsonUtils;
+import de.ids_mannheim.korap.web.input.UserGroupJson;
 
 public class UserGroupControllerAdminTest extends SpringJerseyTest {
     @Autowired
     private HttpAuthorizationHandler handler;
 
-    @Test
-    public void testListDoryGroups () throws KustvaktException {
+    private String adminUsername = "admin";
+    private String testUsername = "UserGroupControllerAdminTest";
+
+    private JsonNode listGroup (String username)
+            throws UniformInterfaceException, ClientHandlerException,
+            KustvaktException {
         ClientResponse response = resource().path("group").path("list")
-                .queryParam("username", "dory")
+                .queryParam("username", username)
                 .header(Attributes.AUTHORIZATION,
-                        handler.createBasicAuthorizationHeaderValue("admin",
-                                "pass"))
+                        handler.createBasicAuthorizationHeaderValue(
+                                testUsername, "pass"))
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .get(ClientResponse.class);
 
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
         String entity = response.getEntity(String.class);
-        //        System.out.println(entity);
         JsonNode node = JsonUtils.readTree(entity);
-        JsonNode group = node.get(1);
-        assertEquals(2, group.at("/id").asInt());
-        assertEquals("dory group", group.at("/name").asText());
-        assertEquals("dory", group.at("/owner").asText());
-        assertEquals(3, group.at("/members").size());
+        return node;
     }
 
     @Test
+    public void testListDoryGroups () throws KustvaktException {
+        ClientResponse response = resource().path("group").path("list")
+                .path("system-admin").queryParam("username", "dory")
+                .header(Attributes.AUTHORIZATION,
+                        handler.createBasicAuthorizationHeaderValue(
+                                adminUsername, "pass"))
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                .get(ClientResponse.class);
+
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+        String entity = response.getEntity(String.class);
+//        System.out.println(entity);
+        JsonNode node = JsonUtils.readTree(entity);
+        assertEquals(3, node.size());
+    }
+
+    @Test
+    public void testListDoryActiveGroups () throws KustvaktException {
+        ClientResponse response = resource().path("group").path("list")
+                .path("system-admin").queryParam("username", "dory")
+                .queryParam("status", "ACTIVE")
+                .header(Attributes.AUTHORIZATION,
+                        handler.createBasicAuthorizationHeaderValue(
+                                adminUsername, "pass"))
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                .get(ClientResponse.class);
+
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+        String entity = response.getEntity(String.class);
+//        System.out.println(entity);
+        JsonNode node = JsonUtils.readTree(entity);
+        assertEquals(2, node.size());
+    }
+    
+    
+    // same as list user-groups of the admin
+    @Test
     public void testListWithoutUsername () throws UniformInterfaceException,
             ClientHandlerException, KustvaktException {
         ClientResponse response = resource().path("group").path("list")
                 .header(Attributes.AUTHORIZATION,
-                        handler.createBasicAuthorizationHeaderValue("admin",
-                                "pass"))
+                        handler.createBasicAuthorizationHeaderValue(
+                                adminUsername, "pass"))
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .get(ClientResponse.class);
 
@@ -58,5 +99,144 @@
         assertEquals("[]", entity);
     }
 
+    @Test
+    public void testListByStatusAll () throws UniformInterfaceException,
+            ClientHandlerException, KustvaktException {
+        ClientResponse response =
+                resource().path("group").path("list").path("system-admin")
+                        .header(Attributes.AUTHORIZATION,
+                                handler.createBasicAuthorizationHeaderValue(
+                                        adminUsername, "pass"))
+                        .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                        .get(ClientResponse.class);
+
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+        String entity = response.getEntity(String.class);
+
+        JsonNode node = JsonUtils.readTree(entity);
+        assertEquals(4, node.size());
+    }
+
+    @Test
+    public void testListByStatusHidden () throws UniformInterfaceException,
+            ClientHandlerException, KustvaktException {
+        ClientResponse response = resource().path("group").path("list")
+                .path("system-admin").queryParam("status", "HIDDEN")
+                .header(Attributes.AUTHORIZATION,
+                        handler.createBasicAuthorizationHeaderValue(
+                                adminUsername, "pass"))
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                .get(ClientResponse.class);
+
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+        String entity = response.getEntity(String.class);
+        JsonNode node = JsonUtils.readTree(entity);
+        assertEquals(1, node.size());
+        assertEquals(3, node.at("/0/id").asInt());
+    }
+
+    @Test
+    public void testUserGroup () throws UniformInterfaceException,
+            ClientHandlerException, KustvaktException {
+
+        UserGroupJson json = new UserGroupJson();
+        json.setName("admin test group");
+        json.setMembers(new String[] { "marlin", "nemo" });
+
+        ClientResponse response = resource().path("group").path("create")
+                .type(MediaType.APPLICATION_JSON)
+                .header(Attributes.AUTHORIZATION,
+                        handler.createBasicAuthorizationHeaderValue(
+                                testUsername, "password"))
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32").entity(json)
+                .post(ClientResponse.class);
+
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+
+        // list user group
+        JsonNode node = listGroup(testUsername);
+        assertEquals(1, node.size());
+        node = node.get(0);
+        assertEquals("admin test group", node.get("name").asText());
+
+        String groupId = node.get("id").asText();
+        testInviteMember(groupId);
+        testDeleteMember(groupId);
+        testDeleteGroup(groupId);
+    }
+
+    private void testDeleteGroup (String groupId)
+            throws UniformInterfaceException, ClientHandlerException,
+            KustvaktException {
+        //delete group
+        ClientResponse response = resource().path("group").path("delete")
+                .queryParam("groupId", groupId)
+                .header(Attributes.AUTHORIZATION,
+                        handler.createBasicAuthorizationHeaderValue(
+                                adminUsername, "pass"))
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                .delete(ClientResponse.class);
+
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+
+        // check group
+        JsonNode node = listGroup(testUsername);
+        assertEquals(0, node.size());
+    }
+
+    private void testDeleteMember (String groupId)
+            throws UniformInterfaceException, ClientHandlerException,
+            KustvaktException {
+        // delete marlin from group
+        ClientResponse response = resource().path("group").path("member")
+                .path("delete").queryParam("memberId", "marlin")
+                .queryParam("groupId", groupId)
+                .header(Attributes.AUTHORIZATION,
+                        handler.createBasicAuthorizationHeaderValue(
+                                adminUsername, "pass"))
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                .delete(ClientResponse.class);
+
+        // check group member
+        JsonNode node = listGroup(testUsername);
+        node = node.get(0);
+        assertEquals(3, node.get("members").size());
+        assertEquals("nemo", node.at("/members/1/userId").asText());
+        assertEquals(GroupMemberStatus.PENDING.name(),
+                node.at("/members/1/status").asText());
+    }
+
+    private void testInviteMember (String groupId)
+            throws UniformInterfaceException, ClientHandlerException,
+            KustvaktException {
+        String[] members = new String[] { "darla" };
+
+        UserGroupJson userGroup = new UserGroupJson();
+        userGroup.setMembers(members);
+        userGroup.setId(Integer.parseInt(groupId));
+
+        ClientResponse response = resource().path("group").path("member")
+                .path("invite").type(MediaType.APPLICATION_JSON)
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                .header(Attributes.AUTHORIZATION,
+                        handler.createBasicAuthorizationHeaderValue(
+                                adminUsername, "pass"))
+                .entity(userGroup).post(ClientResponse.class);
+
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+
+        // list group
+        JsonNode node = listGroup(testUsername);
+        node = node.get(0);
+        assertEquals(4, node.get("members").size());
+
+        assertEquals("darla", node.at("/members/3/userId").asText());
+        assertEquals(GroupMemberStatus.PENDING.name(),
+                node.at("/members/3/status").asText());
+        assertEquals(PredefinedRole.USER_GROUP_MEMBER.name(),
+                node.at("/members/3/roles/0").asText());
+        assertEquals(PredefinedRole.VC_ACCESS_MEMBER.name(),
+                node.at("/members/3/roles/1").asText());
+    }
 
 }
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/UserGroupControllerTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/UserGroupControllerTest.java
index f748b69..89d7ecd 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/UserGroupControllerTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/UserGroupControllerTest.java
@@ -31,6 +31,7 @@
     @Autowired
     private HttpAuthorizationHandler handler;
     private String username = "UserGroupControllerTest";
+    private String admin = "admin";
 
     private JsonNode retrieveUserGroups (String username)
             throws UniformInterfaceException, ClientHandlerException,
@@ -122,25 +123,6 @@
         assertEquals("Unauthorized operation for user: guest",
                 node.at("/errors/0/1").asText());
     }
-    
-    @Test
-    public void testListGroupOtherUser() throws KustvaktException {
-        ClientResponse response = resource().path("group").path("list")
-                .queryParam("username", "dory")
-                .header(Attributes.AUTHORIZATION,
-                        handler.createBasicAuthorizationHeaderValue("marlin",
-                                "pass"))
-                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
-                .get(ClientResponse.class);
-        assertEquals(Status.UNAUTHORIZED.getStatusCode(), response.getStatus());
-        
-        String entity = response.getEntity(String.class);
-        JsonNode node = JsonUtils.readTree(entity);
-        assertEquals(StatusCodes.AUTHORIZATION_FAILED,
-                node.at("/errors/0/0").asInt());
-        assertEquals("Unauthorized operation for user: marlin",
-                node.at("/errors/0/1").asText());
-    }
 
     @Test
     public void testCreateUserGroup () throws UniformInterfaceException,
@@ -319,14 +301,25 @@
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
 
         // check group
-        response = resource().path("group").path("list")
+        response = resource().path("group").path("list").path("system-admin")
+                .queryParam("username", username)
+                .queryParam("status", "DELETED")
                 .header(Attributes.AUTHORIZATION,
-                        handler.createBasicAuthorizationHeaderValue(username,
+                        handler.createBasicAuthorizationHeaderValue(admin,
                                 "pass"))
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .get(ClientResponse.class);
         String entity = response.getEntity(String.class);
-        assertEquals("[]", entity);
+
+        JsonNode node = JsonUtils.readTree(entity);
+        assertEquals(1, node.size());
+        assertEquals(groupId, node.at("/0/id").asText());
+
+        // check group members
+        for (int i=0; i< node.at("/0/members").size(); i++){
+            assertEquals(GroupMemberStatus.DELETED.name(),
+                    node.at("/0/members/"+i+"/status").asText());    
+        }
     }
 
     @Test
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusControllerAdminTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusControllerAdminTest.java
new file mode 100644
index 0000000..d8ff61b
--- /dev/null
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusControllerAdminTest.java
@@ -0,0 +1,312 @@
+package de.ids_mannheim.korap.web.controller;
+
+import static org.junit.Assert.assertEquals;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+
+import org.apache.http.entity.ContentType;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.google.common.net.HttpHeaders;
+import com.sun.jersey.api.client.ClientHandlerException;
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.ClientResponse.Status;
+import com.sun.jersey.core.util.MultivaluedMapImpl;
+import com.sun.jersey.api.client.UniformInterfaceException;
+
+import de.ids_mannheim.korap.authentication.http.HttpAuthorizationHandler;
+import de.ids_mannheim.korap.config.Attributes;
+import de.ids_mannheim.korap.config.SpringJerseyTest;
+import de.ids_mannheim.korap.constant.VirtualCorpusType;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.utils.JsonUtils;
+
+public class VirtualCorpusControllerAdminTest extends SpringJerseyTest {
+
+    @Autowired
+    private HttpAuthorizationHandler handler;
+
+    private String admin = "admin";
+    private String username = "VirtualCorpusControllerAdminTest";
+
+    @Test
+    public void testSearchPrivateVC () throws UniformInterfaceException,
+            ClientHandlerException, KustvaktException {
+        ClientResponse response = resource().path("vc").path("search").path("1")
+                .header(Attributes.AUTHORIZATION,
+                        handler.createBasicAuthorizationHeaderValue(admin,
+                                "pass"))
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                .get(ClientResponse.class);
+
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+
+        String entity = response.getEntity(String.class);
+        JsonNode node = JsonUtils.readTree(entity);
+
+        assertEquals(1, node.at("/id").asInt());
+        assertEquals("dory VC", node.at("/name").asText());
+    }
+
+    @Test
+    public void testSearchProjectVC () throws UniformInterfaceException,
+            ClientHandlerException, KustvaktException {
+
+        ClientResponse response = resource().path("vc").path("search").path("2")
+                .header(Attributes.AUTHORIZATION,
+                        handler.createBasicAuthorizationHeaderValue(admin,
+                                "pass"))
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                .get(ClientResponse.class);
+        String entity = response.getEntity(String.class);
+
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+
+        JsonNode node = JsonUtils.readTree(entity);
+        assertEquals("group VC", node.at("/name").asText());
+        assertEquals(VirtualCorpusType.PROJECT.displayName(),
+                node.at("/type").asText());
+    }
+
+    @Test
+    public void testListDoryVC () throws UniformInterfaceException,
+            ClientHandlerException, KustvaktException {
+        ClientResponse response = resource().path("vc").path("list")
+                .queryParam("createdBy", "dory")
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                .header(Attributes.AUTHORIZATION, handler
+                        .createBasicAuthorizationHeaderValue(admin, "pass"))
+                .get(ClientResponse.class);
+
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+
+        String entity = response.getEntity(String.class);
+        JsonNode node = JsonUtils.readTree(entity);
+        assertEquals(4, node.size());
+    }
+
+    private JsonNode testListSystemVC () throws UniformInterfaceException,
+            ClientHandlerException, KustvaktException {
+        ClientResponse response = resource().path("vc").path("list")
+                .path("system-admin").queryParam("type", "SYSTEM")
+                .queryParam("createdBy", admin)
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                .header(Attributes.AUTHORIZATION, handler
+                        .createBasicAuthorizationHeaderValue(admin, "pass"))
+                .get(ClientResponse.class);
+
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+
+        String entity = response.getEntity(String.class);
+        return JsonUtils.readTree(entity);
+    }
+
+    @Test
+    public void testCreateSystemVC () throws KustvaktException {
+        String json = "{\"name\": \"new system vc\",\"type\": \"SYSTEM\","
+                + "\"corpusQuery\": \"creationDate since 1820\"}";
+
+        ClientResponse response = resource().path("vc").path("create")
+                .header(Attributes.AUTHORIZATION,
+                        handler.createBasicAuthorizationHeaderValue(admin,
+                                "pass"))
+                .header(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON)
+                .entity(json).post(ClientResponse.class);
+
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+
+        JsonNode node = testListSystemVC();
+        assertEquals(1, node.size());
+
+        String vcId = node.at("/0/id").asText();
+
+        testDeleteSystemVC(vcId);
+    }
+
+    private void testDeleteSystemVC (String vcId)
+            throws UniformInterfaceException, ClientHandlerException,
+            KustvaktException {
+        ClientResponse response =
+                resource().path("vc").path("delete").path(vcId)
+                        .header(Attributes.AUTHORIZATION,
+                                handler.createBasicAuthorizationHeaderValue(
+                                        admin, "pass"))
+                        .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                        .delete(ClientResponse.class);
+
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+
+        JsonNode node = testListSystemVC();
+        assertEquals(0, node.size());
+    }
+
+    @Test
+    public void testPrivateVC () throws UniformInterfaceException,
+            ClientHandlerException, KustvaktException {
+        String json = "{\"name\": \"new vc\",\"type\": \"PRIVATE\","
+                + "\"corpusQuery\": \"corpusSigle=GOE\"}";
+
+        ClientResponse response = resource().path("vc").path("create")
+                .header(Attributes.AUTHORIZATION,
+                        handler.createBasicAuthorizationHeaderValue(username,
+                                "pass"))
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                .header(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON)
+                .post(ClientResponse.class, json);
+
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+
+        JsonNode node = testListUserVC();
+        assertEquals(1, node.size());
+
+        String vcId = node.at("/0/id").asText();
+        testEditPrivateVC(vcId);
+        testDeletePrivateVC(vcId);
+    }
+
+    private JsonNode testListUserVC () throws UniformInterfaceException,
+            ClientHandlerException, KustvaktException {
+        ClientResponse response = resource().path("vc").path("list")
+                .path("system-admin").queryParam("createdBy", username)
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                .header(Attributes.AUTHORIZATION, handler
+                        .createBasicAuthorizationHeaderValue(admin, "pass"))
+                .get(ClientResponse.class);
+
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+
+        String entity = response.getEntity(String.class);
+        return JsonUtils.readTree(entity);
+    }
+
+    private void testEditPrivateVC (String vcId)
+            throws UniformInterfaceException, ClientHandlerException,
+            KustvaktException {
+
+        String json = "{\"id\": \"" + vcId + "\", \"name\": \"edited vc\"}";
+
+        ClientResponse response = resource().path("vc").path("edit")
+                .header(Attributes.AUTHORIZATION,
+                        handler.createBasicAuthorizationHeaderValue(admin,
+                                "pass"))
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                .header(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON)
+                .post(ClientResponse.class, json);
+
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+
+        JsonNode node = testListUserVC();
+        assertEquals("edited vc", node.at("/0/name").asText());
+    }
+
+    private void testDeletePrivateVC (String vcId)
+            throws UniformInterfaceException, ClientHandlerException,
+            KustvaktException {
+        ClientResponse response =
+                resource().path("vc").path("delete").path(vcId)
+                        .header(Attributes.AUTHORIZATION,
+                                handler.createBasicAuthorizationHeaderValue(
+                                        admin, "pass"))
+                        .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                        .delete(ClientResponse.class);
+
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+
+        JsonNode node = testListUserVC();
+        assertEquals(0, node.size());
+    }
+
+
+    private String testlistAccessByVC (String vcId) throws KustvaktException {
+        ClientResponse response = resource().path("vc").path("access")
+                .path("list").queryParam("vcId", vcId)
+                .header(Attributes.AUTHORIZATION,
+                        handler.createBasicAuthorizationHeaderValue(admin,
+                                "pass"))
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                .get(ClientResponse.class);
+
+        String entity = response.getEntity(String.class);
+        JsonNode node = JsonUtils.readTree(entity);
+        assertEquals(1, node.size());
+        node = node.get(0);
+
+        assertEquals(admin, node.at("/createdBy").asText());
+        assertEquals(5, node.at("/vcId").asInt());
+        assertEquals("marlin VC", node.at("/vcName").asText());
+        assertEquals(1, node.at("/userGroupId").asInt());
+        assertEquals("marlin group", node.at("/userGroupName").asText());
+
+        return node.at("/accessId").asText();
+    }
+
+    private void testlistAccessByGroup (String groupId)
+            throws KustvaktException {
+        ClientResponse response = resource().path("vc").path("access")
+                .path("list").path("byGroup").queryParam("groupId", groupId)
+                .header(Attributes.AUTHORIZATION,
+                        handler.createBasicAuthorizationHeaderValue(admin,
+                                "pass"))
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                .get(ClientResponse.class);
+        String entity = response.getEntity(String.class);
+        JsonNode node = JsonUtils.readTree(entity);
+        assertEquals(2, node.size());
+    }
+
+    @Test
+    public void testVCSharing () throws UniformInterfaceException,
+            ClientHandlerException, KustvaktException {
+
+        String vcId = "5";
+        String groupId = "1";
+
+        testCreateVCAccess(vcId, groupId);
+        testlistAccessByGroup(groupId);
+
+        String accessId = testlistAccessByVC(vcId);
+        testDeleteVCAccess(accessId);
+    }
+
+    private void testCreateVCAccess (String vcId, String groupId)
+            throws UniformInterfaceException, ClientHandlerException,
+            KustvaktException {
+        MultivaluedMap<String, String> form = new MultivaluedMapImpl();
+        // marlin vc
+        form.add("vcId", vcId);
+        // marlin group
+        form.add("groupId", groupId);
+
+        ClientResponse response;
+        // share VC
+        response = resource().path("vc").path("access").path("share")
+                .type(MediaType.APPLICATION_FORM_URLENCODED)
+                .header(Attributes.AUTHORIZATION,
+                        handler.createBasicAuthorizationHeaderValue(admin,
+                                "pass"))
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32").entity(form)
+                .post(ClientResponse.class);
+
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+
+    }
+
+    private void testDeleteVCAccess (String accessId)
+            throws UniformInterfaceException, ClientHandlerException,
+            KustvaktException {
+
+        ClientResponse response = resource().path("vc").path("access")
+                .path("delete").path(accessId)
+                .header(Attributes.AUTHORIZATION,
+                        handler.createBasicAuthorizationHeaderValue("dory",
+                                "pass"))
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                .delete(ClientResponse.class);
+
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+
+    }
+}
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusControllerTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusControllerTest.java
index e36fc0f..b61c972 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusControllerTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusControllerTest.java
@@ -60,24 +60,96 @@
         }
     }
 
-
-    @Test
-    public void testSearchPredefinedVC () throws UniformInterfaceException,
-            ClientHandlerException, KustvaktException {
-
-        ClientResponse response = resource().path("vc").path("search").path("3")
-                .header(Attributes.AUTHORIZATION,
-                        handler.createBasicAuthorizationHeaderValue(
-                                "VirtualCorpusControllerTest", "pass"))
-                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
-                .get(ClientResponse.class);
+    private JsonNode testSearchVC (String username, String vcId)
+            throws UniformInterfaceException, ClientHandlerException,
+            KustvaktException {
+        ClientResponse response =
+                resource().path("vc").path("search").path(vcId)
+                        .header(Attributes.AUTHORIZATION,
+                                handler.createBasicAuthorizationHeaderValue(
+                                        username, "pass"))
+                        .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                        .get(ClientResponse.class);
         String entity = response.getEntity(String.class);
         //        System.out.println(entity);
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
 
+        return JsonUtils.readTree(entity);
+    }
+
+    private JsonNode testListVC (String username)
+            throws UniformInterfaceException, ClientHandlerException,
+            KustvaktException {
+        ClientResponse response = resource().path("vc").path("list")
+                .header(Attributes.AUTHORIZATION,
+                        handler.createBasicAuthorizationHeaderValue(username,
+                                "pass"))
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+
+                .get(ClientResponse.class);
+
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+        String entity = response.getEntity(String.class);
+        //                System.out.println(entity);
+        return JsonUtils.readTree(entity);
+    }
+
+
+    private JsonNode testListOwnerVC (String username)
+            throws UniformInterfaceException, ClientHandlerException,
+            KustvaktException {
+        ClientResponse response =
+                resource().path("vc").path("list").path("user")
+                        .header(Attributes.AUTHORIZATION,
+                                handler.createBasicAuthorizationHeaderValue(
+                                        username, "pass"))
+                        .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+
+                        .get(ClientResponse.class);
+
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+
+        String entity = response.getEntity(String.class);
+        //        System.out.println(entity);
+        return JsonUtils.readTree(entity);
+    }
+
+    private void testDeleteVC (String vcId, String username)
+            throws KustvaktException {
+        ClientResponse response =
+                resource().path("vc").path("delete").path(vcId)
+                        .header(Attributes.AUTHORIZATION,
+                                handler.createBasicAuthorizationHeaderValue(
+                                        username, "pass"))
+                        .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+
+                        .delete(ClientResponse.class);
+
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+    }
+
+    private JsonNode testlistAccessByVC (String username, String vcId)
+            throws KustvaktException {
+        ClientResponse response = resource().path("vc").path("access")
+                .path("list").queryParam("vcId", vcId)
+                .header(Attributes.AUTHORIZATION,
+                        handler.createBasicAuthorizationHeaderValue(username,
+                                "pass"))
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                .get(ClientResponse.class);
+        String entity = response.getEntity(String.class);
+        //                System.out.println(entity);
         JsonNode node = JsonUtils.readTree(entity);
+        return node;
+    }
+
+    @Test
+    public void testSearchSystemVC () throws UniformInterfaceException,
+            ClientHandlerException, KustvaktException {
+
+        JsonNode node = testSearchVC("VirtualCorpusControllerTest", "3");
         assertEquals("system VC", node.at("/name").asText());
-        assertEquals(VirtualCorpusType.PREDEFINED.displayName(),
+        assertEquals(VirtualCorpusType.SYSTEM.displayName(),
                 node.at("/type").asText());
     }
 
@@ -85,17 +157,7 @@
     public void testSearchOwnerPrivateVC () throws UniformInterfaceException,
             ClientHandlerException, KustvaktException {
 
-        ClientResponse response = resource().path("vc").path("search").path("1")
-                .header(Attributes.AUTHORIZATION,
-                        handler.createBasicAuthorizationHeaderValue("dory",
-                                "pass"))
-                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
-                .get(ClientResponse.class);
-        String entity = response.getEntity(String.class);
-
-        assertEquals(Status.OK.getStatusCode(), response.getStatus());
-
-        JsonNode node = JsonUtils.readTree(entity);
+        JsonNode node = testSearchVC("dory", "1");
         assertEquals("dory VC", node.at("/name").asText());
         assertEquals(VirtualCorpusType.PRIVATE.displayName(),
                 node.at("/type").asText());
@@ -127,17 +189,7 @@
     public void testSearchProjectVC () throws UniformInterfaceException,
             ClientHandlerException, KustvaktException {
 
-        ClientResponse response = resource().path("vc").path("search").path("2")
-                .header(Attributes.AUTHORIZATION,
-                        handler.createBasicAuthorizationHeaderValue("nemo",
-                                "pass"))
-                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
-                .get(ClientResponse.class);
-        String entity = response.getEntity(String.class);
-
-        assertEquals(Status.OK.getStatusCode(), response.getStatus());
-
-        JsonNode node = JsonUtils.readTree(entity);
+        JsonNode node = testSearchVC("nemo", "2");
         assertEquals("group VC", node.at("/name").asText());
         assertEquals(VirtualCorpusType.PROJECT.displayName(),
                 node.at("/type").asText());
@@ -168,17 +220,8 @@
     @Test
     public void testSearchPublishedVC () throws UniformInterfaceException,
             ClientHandlerException, KustvaktException {
-        ClientResponse response = resource().path("vc").path("search").path("4")
-                .header(Attributes.AUTHORIZATION,
-                        handler.createBasicAuthorizationHeaderValue("gill",
-                                "pass"))
-                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
-                .get(ClientResponse.class);
-        String entity = response.getEntity(String.class);
-        //        System.out.println(entity);
-        assertEquals(Status.OK.getStatusCode(), response.getStatus());
 
-        JsonNode node = JsonUtils.readTree(entity);
+        JsonNode node = testSearchVC("gill", "4");
         assertEquals("published VC", node.at("/name").asText());
         assertEquals(VirtualCorpusType.PUBLISHED.displayName(),
                 node.at("/type").asText());
@@ -187,53 +230,55 @@
     }
 
     @Test
-    public void testListVC () throws UniformInterfaceException,
+    public void testListNemoVC () throws UniformInterfaceException,
             ClientHandlerException, KustvaktException {
-        // dory
-        ClientResponse response = resource().path("vc").path("list")
-                .header(Attributes.AUTHORIZATION,
-                        handler.createBasicAuthorizationHeaderValue("dory",
-                                "pass"))
-                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
-
-                .get(ClientResponse.class);
-        String entity = response.getEntity(String.class);
-        assertEquals(Status.OK.getStatusCode(), response.getStatus());
-        //                System.out.println(entity);
-        JsonNode node = JsonUtils.readTree(entity);
-        assertEquals(4, node.size());
-
-        // nemo
-        response = resource().path("vc").path("list")
-                .header(Attributes.AUTHORIZATION,
-                        handler.createBasicAuthorizationHeaderValue("nemo",
-                                "pass"))
-                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
-                .get(ClientResponse.class);
-
-        entity = response.getEntity(String.class);
-        node = JsonUtils.readTree(entity);
+        JsonNode node = testListVC("nemo");
         assertEquals(3, node.size());
 
-        // pearl
-        response = resource().path("vc").path("list")
-                .header(Attributes.AUTHORIZATION,
-                        handler.createBasicAuthorizationHeaderValue("pearl",
-                                "pass"))
-                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
-                .get(ClientResponse.class);
-
-        entity = response.getEntity(String.class);
-        node = JsonUtils.readTree(entity);
-        assertEquals(2, node.size());
     }
 
     @Test
-    public void testListVCUnauthorized () throws UniformInterfaceException,
+    public void testListPearlVC () throws UniformInterfaceException,
+            ClientHandlerException, KustvaktException {
+        JsonNode node = testListVC("pearl");
+        assertEquals(2, node.size());
+
+    }
+
+    @Test
+    public void testListDoryVC () throws UniformInterfaceException,
+            ClientHandlerException, KustvaktException {
+        JsonNode node = testListVC("dory");
+        assertEquals(4, node.size());
+
+    }
+
+    @Test
+    public void testListVCByOtherUser () throws UniformInterfaceException,
+            ClientHandlerException, KustvaktException {
+        ClientResponse response = resource().path("vc").path("list")
+                .queryParam("createdBy", "dory")
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                .header(Attributes.AUTHORIZATION, handler
+                        .createBasicAuthorizationHeaderValue("pearl", "pass"))
+                .get(ClientResponse.class);
+        String entity = response.getEntity(String.class);
+        JsonNode node = JsonUtils.readTree(entity);
+
+        assertEquals(Status.UNAUTHORIZED.getStatusCode(), response.getStatus());
+        assertEquals(StatusCodes.AUTHORIZATION_FAILED,
+                node.at("/errors/0/0").asInt());
+        assertEquals("Unauthorized operation for user: pearl",
+                node.at("/errors/0/1").asText());
+
+        checkWWWAuthenticateHeader(response);
+    }
+
+    @Test
+    public void testListVCByGuest () throws UniformInterfaceException,
             ClientHandlerException, KustvaktException {
         ClientResponse response = resource().path("vc").path("list")
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
-
                 .get(ClientResponse.class);
         String entity = response.getEntity(String.class);
         JsonNode node = JsonUtils.readTree(entity);
@@ -248,10 +293,9 @@
     }
 
     @Test
-    public void testCreateDeleteVC () throws KustvaktException {
-        String json =
-                "{\"name\": \"new vc\",\"type\": \"PRIVATE\",\"createdBy\": "
-                        + "\"VirtualCorpusControllerTest\",\"corpusQuery\": \"corpusSigle=GOE\"}";
+    public void testCreatePrivateVC () throws KustvaktException {
+        String json = "{\"name\": \"new vc\",\"type\": \"PRIVATE\","
+                + "\"corpusQuery\": \"corpusSigle=GOE\"}";
 
         ClientResponse response = resource().path("vc").path("create")
                 .header(Attributes.AUTHORIZATION,
@@ -260,22 +304,11 @@
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .header(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON)
                 .post(ClientResponse.class, json);
-        String entity = response.getEntity(String.class);
-        //        System.out.println(entity);
+
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
 
-        // retrieve user VC
-        response = resource().path("vc").path("list")
-                .header(Attributes.AUTHORIZATION,
-                        handler.createBasicAuthorizationHeaderValue(
-                                "VirtualCorpusControllerTest", "pass"))
-                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
-
-                .get(ClientResponse.class);
-        entity = response.getEntity(String.class);
-        assertEquals(Status.OK.getStatusCode(), response.getStatus());
-        //                System.out.println(entity);
-        JsonNode node = JsonUtils.readTree(entity);
+        // list user VC
+        JsonNode node = testListVC("VirtualCorpusControllerTest");
         assertEquals(2, node.size());
         assertEquals("new vc", node.get(1).get("name").asText());
 
@@ -283,35 +316,17 @@
         vcId = node.get(1).get("id").asText();
 
         // delete new VC
-        resource().path("vc").path("delete").path(vcId)
-                .header(Attributes.AUTHORIZATION,
-                        handler.createBasicAuthorizationHeaderValue(
-                                "VirtualCorpusControllerTest", "pass"))
-                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
-                .delete(ClientResponse.class);
-        //        entity = response.getEntity(String.class);
-        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+        testDeleteVC(vcId, "VirtualCorpusControllerTest");
 
         // list VC
-        response = resource().path("vc").path("list")
-                .header(Attributes.AUTHORIZATION,
-                        handler.createBasicAuthorizationHeaderValue(
-                                "VirtualCorpusControllerTest", "pass"))
-                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
-
-                .get(ClientResponse.class);
-        entity = response.getEntity(String.class);
-        assertEquals(Status.OK.getStatusCode(), response.getStatus());
-        //        System.out.println(entity);
-        node = JsonUtils.readTree(entity);
+        node = testListVC("VirtualCorpusControllerTest");
         assertEquals(1, node.size());
     }
 
     @Test
-    public void testCreateDeletePublishVC () throws KustvaktException {
-        String json =
-                "{\"name\": \"new published vc\",\"type\": \"PUBLISHED\",\"createdBy\": "
-                        + "\"VirtualCorpusControllerTest\",\"corpusQuery\": \"corpusSigle=GOE\"}";
+    public void testCreatePublishVC () throws KustvaktException {
+        String json = "{\"name\": \"new published vc\",\"type\": \"PUBLISHED\""
+                + ",\"corpusQuery\": \"corpusSigle=GOE\"}";
         ClientResponse response = resource().path("vc").path("create")
                 .header(Attributes.AUTHORIZATION,
                         handler.createBasicAuthorizationHeaderValue(
@@ -323,41 +338,55 @@
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
 
         // test list owner vc
-        response = resource().path("vc").path("list").path("user")
-                .header(Attributes.AUTHORIZATION,
-                        handler.createBasicAuthorizationHeaderValue(
-                                "VirtualCorpusControllerTest", "pass"))
-                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
-
-                .get(ClientResponse.class);
-
-        String entity = response.getEntity(String.class);
-        //        System.out.println(entity);
-        JsonNode node = JsonUtils.readTree(entity);
+        JsonNode node = testListOwnerVC("VirtualCorpusControllerTest");
         assertEquals(1, node.size());
         assertEquals("new published vc", node.get(0).get("name").asText());
-        //EM: cannot explicitly checked hidden groups here
 
         String vcId = node.get(0).get("id").asText();
 
+        // EM: check hidden access
+        node = testlistAccessByVC("admin", vcId).get(0);
+        assertEquals("system", node.at("/createdBy").asText());
+        assertEquals(vcId, node.at("/vcId").asText());
+        assertEquals("auto-hidden-group", node.at("/userGroupName").asText());
+        assertEquals("new published vc", node.at("/vcName").asText());
+
+        String groupId = node.at("/userGroupId").asText();
+
+        // EM: check if hidden group has been created
+        node = testCheckHiddenGroup(groupId);
+        assertEquals("HIDDEN", node.at("/status").asText());
+
         //EM: delete vc
-        resource().path("vc").path("delete").path(vcId)
-                .header(Attributes.AUTHORIZATION,
-                        handler.createBasicAuthorizationHeaderValue(
-                                "VirtualCorpusControllerTest", "pass"))
-                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+        testDeleteVC(vcId, "VirtualCorpusControllerTest");
 
-                .delete(ClientResponse.class);
+        //EM: check if the hidden groups are deleted as well
+        node = testCheckHiddenGroup(groupId);
+        assertEquals(605, node.at("/errors/0/0").asInt());
+        assertEquals("Group with id 5 is not found",
+                node.at("/errors/0/1").asText());
+    }
 
-        //EM: check if the hidden groups are deleted as well (require system admin)
+    private JsonNode testCheckHiddenGroup (String groupId)
+            throws UniformInterfaceException, ClientHandlerException,
+            KustvaktException {
+        ClientResponse response =
+                resource().path("group").path("search").path(groupId)
+                        .header(Attributes.AUTHORIZATION,
+                                handler.createBasicAuthorizationHeaderValue(
+                                        "admin", "pass"))
+                        .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                        .get(ClientResponse.class);
+
+        String entity = response.getEntity(String.class);
+        return JsonUtils.readTree(entity);
     }
 
     @Test
     public void testCreateVCWithExpiredToken ()
             throws IOException, KustvaktException {
-        String json =
-                "{\"name\": \"new vc\",\"type\": \"PRIVATE\",\"createdBy\": "
-                        + "\"VirtualCorpusControllerTest\",\"corpusQuery\": \"corpusSigle=GOE\"}";
+        String json = "{\"name\": \"new vc\",\"type\": \"PRIVATE\","
+                + "\"corpusQuery\": \"corpusSigle=GOE\"}";
 
         InputStream is = getClass().getClassLoader()
                 .getResourceAsStream("test-user.token");
@@ -388,10 +417,34 @@
     }
 
     @Test
+    public void testCreateSystemVC () throws KustvaktException {
+        String json = "{\"name\": \"new vc\",\"type\": \"SYSTEM\","
+                + "\"corpusQuery\": \"creationDate since 1820\"}";
+
+        ClientResponse response = resource().path("vc").path("create")
+                .header(Attributes.AUTHORIZATION,
+                        handler.createBasicAuthorizationHeaderValue(
+                                "VirtualCorpusControllerTest", "pass"))
+                .header(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON)
+                .entity(json).post(ClientResponse.class);
+
+        assertEquals(Status.UNAUTHORIZED.getStatusCode(), response.getStatus());
+
+        String entity = response.getEntity(String.class);
+        JsonNode node = JsonUtils.readTree(entity);
+        assertEquals(StatusCodes.AUTHORIZATION_FAILED,
+                node.at("/errors/0/0").asInt());
+        assertEquals(
+                "Unauthorized operation for user: VirtualCorpusControllerTest",
+                node.at("/errors/0/1").asText());
+
+        checkWWWAuthenticateHeader(response);
+    }
+
+    @Test
     public void testCreateVCUnauthorized () throws KustvaktException {
-        String json =
-                "{\"name\": \"new vc\",\"type\": \"PRIVATE\",\"createdBy\": "
-                        + "\"VirtualCorpusControllerTest\",\"corpusQuery\": \"creationDate since 1820\"}";
+        String json = "{\"name\": \"new vc\",\"type\": \"PRIVATE\","
+                + "\"corpusQuery\": \"creationDate since 1820\"}";
 
         ClientResponse response = resource().path("vc").path("create")
                 .header(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON)
@@ -411,9 +464,7 @@
 
     @Test
     public void testCreateVCWithoutcorpusQuery () throws KustvaktException {
-        String json =
-                "{\"name\": \"new vc\",\"type\": \"PRIVATE\",\"createdBy\": "
-                        + "\"VirtualCorpusControllerTest\"}";
+        String json = "{\"name\": \"new vc\",\"type\": \"PRIVATE\"}";
 
         ClientResponse response = resource().path("vc").path("create")
                 .header(Attributes.AUTHORIZATION,
@@ -434,8 +485,8 @@
 
     @Test
     public void testCreateVCWithoutType () throws KustvaktException {
-        String json = "{\"name\": \"new vc\",\"createdBy\": "
-                + "\"VirtualCorpusControllerTest\",\"corpusQuery\": \"creationDate since 1820\"}";
+        String json = "{\"name\": \"new vc\",\"corpusQuery\": "
+                + "\"creationDate since 1820\"}";
 
         ClientResponse response = resource().path("vc").path("create")
                 .header(Attributes.AUTHORIZATION,
@@ -456,9 +507,8 @@
 
     @Test
     public void testCreateVCWithWrongType () throws KustvaktException {
-        String json =
-                "{\"name\": \"new vc\",\"type\": \"PRIVAT\",\"createdBy\": "
-                        + "\"VirtualCorpusControllerTest\",\"corpusQuery\": \"creationDate since 1820\"}";
+        String json = "{\"name\": \"new vc\",\"type\": \"PRIVAT\","
+                + "\"corpusQuery\": \"creationDate since 1820\"}";
 
         ClientResponse response = resource().path("vc").path("create")
                 .header(Attributes.AUTHORIZATION,
@@ -519,17 +569,7 @@
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
 
         // check VC
-        response = resource().path("vc").path("list")
-                .header(Attributes.AUTHORIZATION,
-                        handler.createBasicAuthorizationHeaderValue("dory",
-                                "pass"))
-                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
-
-                .get(ClientResponse.class);
-        String entity = response.getEntity(String.class);
-        assertEquals(Status.OK.getStatusCode(), response.getStatus());
-
-        JsonNode node = JsonUtils.readTree(entity);
+        JsonNode node = testListVC("dory");
         assertEquals("edited vc", node.get(0).get("name").asText());
 
         // 2nd edit
@@ -546,18 +586,7 @@
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
 
         // check VC
-        response = resource().path("vc").path("list")
-                .header(Attributes.AUTHORIZATION,
-                        handler.createBasicAuthorizationHeaderValue("dory",
-                                "pass"))
-                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
-
-                .get(ClientResponse.class);
-
-        assertEquals(Status.OK.getStatusCode(), response.getStatus());
-
-        entity = response.getEntity(String.class);
-        node = JsonUtils.readTree(entity);
+        node = testListVC("dory");
         assertEquals("dory VC", node.get(0).get("name").asText());
     }
 
@@ -593,7 +622,8 @@
     @Test
     public void testEditPublishVC () throws KustvaktException {
 
-        String json = "{\"id\": \"2\", \"type\": \"PUBLISHED\"}";
+        String vcId = "2";
+        String json = "{\"id\": \"" + vcId + "\", \"type\": \"PUBLISHED\"}";
 
         ClientResponse response = resource().path("vc").path("edit")
                 .header(Attributes.AUTHORIZATION,
@@ -606,23 +636,14 @@
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
 
         // check VC
-        response = resource().path("vc").path("list").path("user")
-                .header(Attributes.AUTHORIZATION,
-                        handler.createBasicAuthorizationHeaderValue("dory",
-                                "pass"))
-                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
-
-                .get(ClientResponse.class);
-        String entity = response.getEntity(String.class);
-        assertEquals(Status.OK.getStatusCode(), response.getStatus());
-
-        JsonNode node = JsonUtils.readTree(entity);
+        JsonNode node = testListOwnerVC("dory");
         JsonNode n = node.get(1);
         assertEquals(VirtualCorpusType.PUBLISHED.displayName(),
                 n.get("type").asText());
 
-        //check VC access
-        // need system admin account
+        //check hidden VC access
+        node = testlistAccessByVC("admin", vcId);
+        assertEquals(2, node.size());
 
         // edit 2nd
         json = "{\"id\": \"2\", \"type\": \"PROJECT\"}";
@@ -637,53 +658,19 @@
 
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
 
-        response = resource().path("vc").path("list").path("user")
-                .header(Attributes.AUTHORIZATION,
-                        handler.createBasicAuthorizationHeaderValue("dory",
-                                "pass"))
-                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
-
-                .get(ClientResponse.class);
-        entity = response.getEntity(String.class);
-        assertEquals(Status.OK.getStatusCode(), response.getStatus());
-
-        node = JsonUtils.readTree(entity);
+        node = testListOwnerVC("dory");
         assertEquals(VirtualCorpusType.PROJECT.displayName(),
                 node.get(1).get("type").asText());
-    }
 
-
-    @Test
-    public void testlistAccessByVC () throws KustvaktException {
-        ClientResponse response = resource().path("vc").path("access")
-                .path("list").queryParam("vcId", "2")
-                .header(Attributes.AUTHORIZATION,
-                        handler.createBasicAuthorizationHeaderValue("dory",
-                                "pass"))
-                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
-                .get(ClientResponse.class);
-        String entity = response.getEntity(String.class);
-        //                System.out.println(entity);
-        JsonNode node = JsonUtils.readTree(entity);
-        assertEquals(1, node.at("/0/accessId").asInt());
-        assertEquals(2, node.at("/0/vcId").asInt());
-        assertEquals("group VC", node.at("/0/vcName").asText());
-        assertEquals(2, node.at("/0/userGroupId").asInt());
-        assertEquals("dory group", node.at("/0/userGroupName").asText());
-
+        //check VC access
+        node = testlistAccessByVC("admin", vcId);
+        assertEquals(1, node.size());
     }
 
     @Test
     public void testlistAccessNonVCAAdmin () throws KustvaktException {
-        ClientResponse response = resource().path("vc").path("access")
-                .path("list").queryParam("vcId", "2")
-                .header(Attributes.AUTHORIZATION,
-                        handler.createBasicAuthorizationHeaderValue("nemo",
-                                "pass"))
-                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
-                .get(ClientResponse.class);
-        String entity = response.getEntity(String.class);
-        assertEquals("[]", entity);
+        JsonNode node = testlistAccessByVC("nemo", "2");
+        assertEquals(0, node.size());
     }
 
     @Test
@@ -727,9 +714,11 @@
     public void testCreateDeleteAccess () throws UniformInterfaceException,
             ClientHandlerException, KustvaktException {
 
+        String vcId = "5";
+
         MultivaluedMap<String, String> form = new MultivaluedMapImpl();
         // marlin vc
-        form.add("vcId", "5");
+        form.add("vcId", vcId);
         // marlin group
         form.add("groupId", "1");
 
@@ -746,18 +735,7 @@
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
 
         // list vc access by marlin
-        response = resource().path("vc").path("access").path("list")
-                .queryParam("vcId", "5")
-                .header(Attributes.AUTHORIZATION,
-                        handler.createBasicAuthorizationHeaderValue("marlin",
-                                "pass"))
-                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
-                .header(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON)
-                .get(ClientResponse.class);
-
-        String entity = response.getEntity(String.class);
-        //        System.out.println(entity);
-        JsonNode node = JsonUtils.readTree(entity);
+        JsonNode node = testlistAccessByVC("marlin", vcId);
         assertEquals(1, node.size());
         node = node.get(0);
         assertEquals(5, node.at("/vcId").asInt());
@@ -769,49 +747,14 @@
 
         // delete access
         // unauthorized
-        response = resource().path("vc").path("access").path("delete")
-                .path(accessId)
-                .header(Attributes.AUTHORIZATION,
-                        handler.createBasicAuthorizationHeaderValue(
-                                "VirtualCorpusControllerTest", "pass"))
-                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
-                .delete(ClientResponse.class);
-
-        entity = response.getEntity(String.class);
-        //        System.out.println(entity);
-        node = JsonUtils.readTree(entity);
-        assertEquals(Status.UNAUTHORIZED.getStatusCode(), response.getStatus());
-        assertEquals(StatusCodes.AUTHORIZATION_FAILED,
-                node.at("/errors/0/0").asInt());
-        assertEquals(
-                "Unauthorized operation for user: VirtualCorpusControllerTest",
-                node.at("/errors/0/1").asText());
+        testDeleteAccessUnauthorized(accessId);
 
         // delete access
         // dory is a vc-admin in marlin group
-        response = resource().path("vc").path("access").path("delete")
-                .path(accessId)
-                .header(Attributes.AUTHORIZATION,
-                        handler.createBasicAuthorizationHeaderValue("dory",
-                                "pass"))
-                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
-                .delete(ClientResponse.class);
-
-        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+        testDeleteAccess("dory", accessId);
 
         // list vc access by dory
-        response = resource().path("vc").path("access").path("list")
-                .queryParam("vcId", "5")
-                .header(Attributes.AUTHORIZATION,
-                        handler.createBasicAuthorizationHeaderValue("dory",
-                                "pass"))
-                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
-                .header(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON)
-                .get(ClientResponse.class);
-
-        entity = response.getEntity(String.class);
-        assertEquals("[]", entity);
-        node = JsonUtils.readTree(entity);
+        node = testlistAccessByVC("dory", vcId);
         assertEquals(0, node.size());
     }
 
@@ -873,4 +816,40 @@
         assertEquals("Unauthorized operation for user: nemo",
                 node.at("/errors/0/1").asText());
     }
+
+    private void testDeleteAccess (String username, String accessId)
+            throws UniformInterfaceException, ClientHandlerException,
+            KustvaktException {
+        ClientResponse response = resource().path("vc").path("access")
+                .path("delete").path(accessId)
+                .header(Attributes.AUTHORIZATION,
+                        handler.createBasicAuthorizationHeaderValue(username,
+                                "pass"))
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                .delete(ClientResponse.class);
+
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+    }
+
+    private void testDeleteAccessUnauthorized (String accessId)
+            throws UniformInterfaceException, ClientHandlerException,
+            KustvaktException {
+        ClientResponse response = resource().path("vc").path("access")
+                .path("delete").path(accessId)
+                .header(Attributes.AUTHORIZATION,
+                        handler.createBasicAuthorizationHeaderValue(
+                                "VirtualCorpusControllerTest", "pass"))
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
+                .delete(ClientResponse.class);
+
+        String entity = response.getEntity(String.class);
+        //        System.out.println(entity);
+        JsonNode node = JsonUtils.readTree(entity);
+        assertEquals(Status.UNAUTHORIZED.getStatusCode(), response.getStatus());
+        assertEquals(StatusCodes.AUTHORIZATION_FAILED,
+                node.at("/errors/0/0").asInt());
+        assertEquals(
+                "Unauthorized operation for user: VirtualCorpusControllerTest",
+                node.at("/errors/0/1").asText());
+    }
 }
diff --git a/lite/src/main/java/de/ids_mannheim/korap/server/KustvaktLiteServer.java b/lite/src/main/java/de/ids_mannheim/korap/server/KustvaktLiteServer.java
index 54e80f9..4be47cd 100644
--- a/lite/src/main/java/de/ids_mannheim/korap/server/KustvaktLiteServer.java
+++ b/lite/src/main/java/de/ids_mannheim/korap/server/KustvaktLiteServer.java
@@ -3,7 +3,6 @@
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.InputStream;
-import java.net.URL;
 import java.util.Properties;
 
 import de.ids_mannheim.korap.config.KustvaktConfiguration;