Added VC list and search user-group controllers for system admin.

Change-Id: I6c99443e428c69c6ce3e9c2760b8193ce2613009
diff --git a/full/Changes b/full/Changes
index c7aea38..247ac09 100644
--- a/full/Changes
+++ b/full/Changes
@@ -9,6 +9,12 @@
 	- 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)
 	
 version 0.60 release
 14/03/2018
@@ -82,8 +88,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/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/service/UserGroupService.java b/full/src/main/java/de/ids_mannheim/korap/service/UserGroupService.java
index 4450551..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
@@ -131,7 +131,8 @@
             List<UserGroupMember> members;
             UserGroupDto groupDto;
             for (UserGroup group : userGroups) {
-                members = groupMemberDao.retrieveMemberByGroupId(group.getId(), true);
+                members = groupMemberDao.retrieveMemberByGroupId(group.getId(),
+                        true);
                 groupDto = converter.createUserGroupDto(group, members, null,
                         null);
                 dtos.add(groupDto);
@@ -478,5 +479,20 @@
         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 32312cd..429440d 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;
 
@@ -19,6 +20,7 @@
 import de.ids_mannheim.korap.dao.AdminDao;
 import de.ids_mannheim.korap.dao.VirtualCorpusAccessDao;
 import de.ids_mannheim.korap.dao.VirtualCorpusDao;
+import de.ids_mannheim.korap.dto.UserGroupDto;
 import de.ids_mannheim.korap.dto.VirtualCorpusAccessDto;
 import de.ids_mannheim.korap.dto.VirtualCorpusDto;
 import de.ids_mannheim.korap.dto.converter.VirtualCorpusAccessConverter;
@@ -74,12 +76,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 +140,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,
@@ -187,7 +227,7 @@
         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);
@@ -393,7 +433,7 @@
                     // skip adding user to hidden group
                 }
             }
-            // else VirtualCorpusType.PREDEFINED
+            // else VirtualCorpusType.SYSTEM
         }
 
         String json = vc.getCorpusQuery();
@@ -414,4 +454,6 @@
         }
         return false;
     }
+
+
 }
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 b4a5fc7..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;
@@ -22,6 +23,7 @@
 
 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;
@@ -108,6 +110,30 @@
         }
     }
 
+    /** 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..94cc93d 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,9 @@
     @Autowired
     private VirtualCorpusService service;
 
-    /** Creates a user VC, also for system admins
+    /** Creates a user virtual corpus, also for system admins
+     * 
+     *  EM: should system admins be able to create VC for other users?
      * 
      * @param securityContext
      * @param vc a JSON object describing the virtual corpus
@@ -91,7 +94,8 @@
         return Response.ok().build();
     }
 
-    /** Only the VC owner and system admins can edit VC.
+    /** Only the virtual corpus owner and system admins can edit 
+     * a virtual corpus.
      * 
      * @param securityContext
      * @param vc a JSON object describing the virtual corpus
@@ -115,11 +119,11 @@
         return Response.ok().build();
     }
 
-    /** Searches for a specific VC.
+    /** Searches for a specific virtual corpus.
      * 
      * @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 +143,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 +172,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 +195,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/resources/db/insert/V3.1__insert_virtual_corpus.sql b/full/src/main/resources/db/insert/V3.1__insert_virtual_corpus.sql
index e09d2fb..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
@@ -70,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/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/web/controller/VirtualCorpusControllerAdminTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusControllerAdminTest.java
new file mode 100644
index 0000000..225d83e
--- /dev/null
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusControllerAdminTest.java
@@ -0,0 +1,314 @@
+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\","
+                + "\"createdBy\": \"admin\",\"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\",\"createdBy\": "
+                        + "\"" + username
+                        + "\",\"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..bbe68bd 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,7 +293,7 @@
     }
 
     @Test
-    public void testCreateDeleteVC () throws KustvaktException {
+    public void testCreatePrivateVC () throws KustvaktException {
         String json =
                 "{\"name\": \"new vc\",\"type\": \"PRIVATE\",\"createdBy\": "
                         + "\"VirtualCorpusControllerTest\",\"corpusQuery\": \"corpusSigle=GOE\"}";
@@ -260,22 +305,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,32 +317,15 @@
         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 {
+    public void testCreatePublishVC () throws KustvaktException {
         String json =
                 "{\"name\": \"new published vc\",\"type\": \"PUBLISHED\",\"createdBy\": "
                         + "\"VirtualCorpusControllerTest\",\"corpusQuery\": \"corpusSigle=GOE\"}";
@@ -323,41 +340,56 @@
         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\","
+                + "\"createdBy\": \"VirtualCorpusControllerTest\","
+                + "\"corpusQuery\": \"corpusSigle=GOE\"}";
 
         InputStream is = getClass().getClassLoader()
                 .getResourceAsStream("test-user.token");
@@ -388,6 +420,32 @@
     }
 
     @Test
+    public void testCreateSystemVC () throws KustvaktException {
+        String json = "{\"name\": \"new vc\",\"type\": \"SYSTEM\","
+                + "\"createdBy\": \"VirtualCorpusControllerTest\","
+                + "\"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\": "
@@ -519,17 +577,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 +594,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 +630,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 +644,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 +666,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 +722,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 +743,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 +755,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 +824,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());
+    }
 }