Added tests for VC sharing and for OAuth2 client using VC services

Change-Id: Idbbaf350caf053732668cd613d0d36b627eeb1c9
diff --git a/full/Changes b/full/Changes
index 7c673dd..6f89192 100644
--- a/full/Changes
+++ b/full/Changes
@@ -4,6 +4,7 @@
   for mounting into docker
 - Added an error for missing redirect uri in a token request 
   when it has been included in the authorization request.
+- Added tests for VC sharing and for OAuth2 client using VC services    
 
 # version 0.69.4
 
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2AccessTokenTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2AccessTokenTest.java
index 935b819..e5abc6a 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2AccessTokenTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2AccessTokenTest.java
@@ -5,22 +5,18 @@
 import static org.junit.Assert.assertTrue;
 
 import java.io.IOException;
-import java.net.URI;
 
 import javax.ws.rs.client.Entity;
 import javax.ws.rs.core.Form;
-import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
 
 import org.apache.http.entity.ContentType;
 import org.apache.oltu.oauth2.common.message.types.GrantType;
 import org.junit.Test;
-import org.springframework.util.MultiValueMap;
-import org.springframework.web.util.UriComponentsBuilder;
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.google.common.net.HttpHeaders;
-import javax.ws.rs.core.Response;
 
 import de.ids_mannheim.korap.authentication.http.HttpAuthorizationHandler;
 import de.ids_mannheim.korap.config.Attributes;
@@ -68,12 +64,7 @@
         Response response =
                 requestAuthorizationCode("code", confidentialClientId, "",
                         OAuth2Scope.VC_INFO.toString(), "", userAuthHeader);
-        assertEquals(Status.TEMPORARY_REDIRECT.getStatusCode(),
-                response.getStatus());
-        URI redirectUri = response.getLocation();
-        MultiValueMap<String, String> params = UriComponentsBuilder
-                .fromUri(redirectUri).build().getQueryParams();
-        String code = params.getFirst("code");
+        String code = parseAuthorizationCode(response);
         
         response = requestTokenWithAuthorizationCodeAndForm(
                 confidentialClientId, clientSecret, code);
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2RClientTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2RClientTest.java
index 358cf18..61aa2f4 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2RClientTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/OAuth2RClientTest.java
@@ -7,14 +7,9 @@
 import java.net.URI;
 
 import javax.ws.rs.ProcessingException;
-import javax.ws.rs.client.Client;
-import javax.ws.rs.client.ClientBuilder;
-import javax.ws.rs.client.WebTarget;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
 
-import org.glassfish.jersey.client.ClientConfig;
-import org.glassfish.jersey.client.ClientProperties;
 import org.junit.Test;
 import org.springframework.util.MultiValueMap;
 import org.springframework.web.util.UriComponentsBuilder;
@@ -22,7 +17,6 @@
 import com.fasterxml.jackson.databind.JsonNode;
 
 import de.ids_mannheim.korap.authentication.http.HttpAuthorizationHandler;
-import de.ids_mannheim.korap.config.Attributes;
 import de.ids_mannheim.korap.exceptions.KustvaktException;
 import de.ids_mannheim.korap.oauth2.constant.OAuth2ClientType;
 import de.ids_mannheim.korap.utils.JsonUtils;
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/UserControllerTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/UserControllerTest.java
index c8ad119..ffbc647 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/UserControllerTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/UserControllerTest.java
@@ -53,14 +53,7 @@
             throws KustvaktException {
         Response response = requestAuthorizationCode("code", clientId, "",
                 "user_info", "", userAuthHeader);
-
-        assertEquals(Status.TEMPORARY_REDIRECT.getStatusCode(),
-                response.getStatus());
-
-        URI redirectUri = response.getLocation();
-        MultiValueMap<String, String> params = UriComponentsBuilder
-                .fromUri(redirectUri).build().getQueryParams();
-        String code = params.getFirst("code");
+        String code = parseAuthorizationCode(response);
 
         response =
                 requestTokenWithAuthorizationCodeAndForm(clientId, null, code);
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusClientTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusClientTest.java
new file mode 100644
index 0000000..3315d57
--- /dev/null
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusClientTest.java
@@ -0,0 +1,82 @@
+package de.ids_mannheim.korap.web.controller;
+
+import static org.junit.Assert.assertEquals;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.junit.Test;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+import de.ids_mannheim.korap.authentication.http.HttpAuthorizationHandler;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.utils.JsonUtils;
+
+public class VirtualCorpusClientTest extends VirtualCorpusTestBase {
+
+    private String username = "VirtualCorpusClientTest";
+
+    @Test
+    public void testVirtualCorpusWithClient () throws KustvaktException {
+        // create client
+        Response response = registerConfidentialClient(username);
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+        JsonNode node = JsonUtils.readTree(response.readEntity(String.class));
+        String clientId = node.at("/client_id").asText();
+        String clientSecret = node.at("/client_secret").asText();
+
+        // obtain authorization
+        String userAuthHeader = HttpAuthorizationHandler
+                .createBasicAuthorizationHeaderValue(username, "password");
+        response = requestAuthorizationCode("code", clientId, clientRedirectUri,
+                "create_vc vc_info delete_vc edit_vc", "myState",
+                userAuthHeader);
+        String code = parseAuthorizationCode(response);
+
+        response = requestTokenWithAuthorizationCodeAndForm(clientId,
+                clientSecret, code, clientRedirectUri);
+        node = JsonUtils.readTree(response.readEntity(String.class));
+        String accessToken = node.at("/access_token").asText();
+
+        String accessTokenHeader = "Bearer " + accessToken;
+
+        // create VC
+        String vcName = "vc-client1";
+        String vcJson =
+                "{\"type\": \"PRIVATE\"" + ",\"queryType\": \"VIRTUAL_CORPUS\""
+                        + ",\"corpusQuery\": \"creationDate since 1820\"}";
+        createVC(accessTokenHeader, username, vcName, vcJson);
+
+        vcName = "vc-client2";
+        vcJson = "{\"type\": \"PRIVATE\"" + ",\"queryType\": \"VIRTUAL_CORPUS\""
+                + ",\"corpusQuery\": \"creationDate until 1820\"}";
+        createVC(accessTokenHeader, username, vcName, vcJson);
+
+        // list vc
+        node = listVCWithAuthHeader(accessTokenHeader);
+        assertEquals(3, node.size());
+
+        // delete vc
+        deleteVC(vcName, username, username);
+
+        // list vc
+        node = listVCWithAuthHeader(accessTokenHeader);
+        assertEquals(2, node.size());
+
+        // delete client
+        deregisterClient(username, clientId);
+
+        testSearchWithRevokedAccessToken(accessToken);
+
+        // obtain authorization from another client
+        response = requestTokenWithPassword(superClientId,
+                this.clientSecret, username, "pass");
+        node = JsonUtils.readTree(response.readEntity(String.class));
+        accessToken = node.at("/access_token").asText();
+        
+        // checking vc should still be available after client deregistration
+        node = listVCWithAuthHeader("Bearer "+accessToken);
+        assertEquals(2, node.size());
+    }
+}
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
index 53a2083..2738f0e 100644
--- 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
@@ -334,7 +334,7 @@
         node2 = testAdminListVC_UsingAdminToken(vcCreator,ResourceType.PROJECT);
         assertEquals(1, node2.size());
         
-        testEditVCType(admin, vcCreator, vcName, ResourceType.PRIVATE);
+        editVCType(admin, vcCreator, vcName, ResourceType.PRIVATE);
         
         node2 = testAdminListVC_UsingAdminToken(vcCreator,ResourceType.PROJECT);
         assertEquals(0, node2.size());
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 d470a6d..7bb8ee6 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
@@ -57,23 +57,15 @@
             }
         }
     }
-
-    private JsonNode testListVC (String username)
-            throws ProcessingException,
-            KustvaktException {
-        Response response = target().path(API_VERSION).path("vc")
-                .request()
-                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
-                        .createBasicAuthorizationHeaderValue(username, "pass"))
-                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
-                .get();
-
-        assertEquals(Status.OK.getStatusCode(), response.getStatus());
-        String entity = response.readEntity(String.class);
-        // System.out.println(entity);
-        return JsonUtils.readTree(entity);
+    
+    private String authHeader;
+    
+    public VirtualCorpusControllerTest () throws KustvaktException {
+        authHeader = HttpAuthorizationHandler
+                .createBasicAuthorizationHeaderValue(testUser, "pass");
     }
 
+    
     private JsonNode testListOwnerVC (String username)
             throws ProcessingException,
             KustvaktException {
@@ -91,38 +83,11 @@
         return JsonUtils.readTree(entity);
     }
 
-    private void testDeleteVC (String vcName, String vcCreator, String username)
-            throws KustvaktException {
-        Response response = target().path(API_VERSION).path("vc")
-                .path("~" + vcCreator).path(vcName)
-                .request()
-                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
-                        .createBasicAuthorizationHeaderValue(username, "pass"))
-                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
-                .delete();
-
-        assertEquals(Status.OK.getStatusCode(), response.getStatus());
-    }
-
-    private JsonNode testlistAccessByGroup (String username, String groupName)
-            throws KustvaktException {
-        Response response = target().path(API_VERSION).path("vc")
-                .path("access").queryParam("groupName", groupName)
-                .request()
-                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
-                        .createBasicAuthorizationHeaderValue(username, "pass"))
-                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
-                .get();
-        String entity = response.readEntity(String.class);
-        JsonNode node = JsonUtils.readTree(entity);
-        return node;
-    }
-
     @Test
     public void testRetrieveSystemVCInfo () throws
             ProcessingException, KustvaktException {
 
-        JsonNode node = testSearchVC(testUser, "system", "system-vc");
+        JsonNode node = retrieveVCInfo(testUser, "system", "system-vc");
         assertEquals("system-vc", node.at("/name").asText());
         assertEquals(ResourceType.SYSTEM.displayName(),
                 node.at("/type").asText());
@@ -154,7 +119,7 @@
             throws ProcessingException,
             KustvaktException {
 
-        JsonNode node = testSearchVC("dory", "dory", "dory-vc");
+        JsonNode node = retrieveVCInfo("dory", "dory", "dory-vc");
         assertEquals("dory-vc", node.at("/name").asText());
         assertEquals(ResourceType.PRIVATE.displayName(),
                 node.at("/type").asText());
@@ -167,8 +132,7 @@
         Response response = target().path(API_VERSION).path("vc")
                 .path("~dory").path("dory-vc")
                 .request()
-                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
-                        .createBasicAuthorizationHeaderValue(testUser, "pass"))
+                .header(Attributes.AUTHORIZATION, authHeader)
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .get();
         String entity = response.readEntity(String.class);
@@ -186,7 +150,7 @@
     public void testRetrieveProjectVCInfo () throws
             ProcessingException, KustvaktException {
 
-        JsonNode node = testSearchVC("nemo", "dory", "group-vc");
+        JsonNode node = retrieveVCInfo("nemo", "dory", "group-vc");
         assertEquals("group-vc", node.at("/name").asText());
         assertEquals(ResourceType.PROJECT.displayName(),
                 node.at("/type").asText());
@@ -219,7 +183,7 @@
     public void testRetrievePublishedVCInfo () throws
             ProcessingException, KustvaktException {
 
-        JsonNode node = testSearchVC("gill", "marlin", "published-vc");
+        JsonNode node = retrieveVCInfo("gill", "marlin", "published-vc");
         assertEquals("published-vc", node.at("/name").asText());
         assertEquals(ResourceType.PUBLISHED.displayName(),
                 node.at("/type").asText());
@@ -246,7 +210,7 @@
     @Test
     public void testListAvailableVCNemo () throws
             ProcessingException, KustvaktException {
-        JsonNode node = testListVC("nemo");
+        JsonNode node = listVC("nemo");
         assertEquals(3, node.size());
 
     }
@@ -254,7 +218,7 @@
     @Test
     public void testListAvailableVCPearl () throws
             ProcessingException, KustvaktException {
-        JsonNode node = testListVC("pearl");
+        JsonNode node = listVC("pearl");
         assertEquals(2, node.size());
 
     }
@@ -262,7 +226,7 @@
     @Test
     public void testListAvailableVCDory () throws
             ProcessingException, KustvaktException {
-        JsonNode node = testListVC("dory");
+        JsonNode node = listVC("dory");
         assertEquals(4, node.size());
     }
 
@@ -331,27 +295,18 @@
                 + ",\"queryType\": \"VIRTUAL_CORPUS\""
                 + ",\"corpusQuery\": \"corpusSigle=GOE\"}";
 
-        Response response = target().path(API_VERSION).path("vc")
-                .path("~"+testUser).path("new_vc")
-                .request()
-                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
-                        .createBasicAuthorizationHeaderValue(testUser, "pass"))
-                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
-                .header(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON)
-                .put(Entity.json(json));
-
-        assertEquals(Status.CREATED.getStatusCode(), response.getStatus());
+        createVC(authHeader,testUser, "new_vc", json);
 
         // list user VC
-        JsonNode node = testListVC(testUser);
+        JsonNode node = listVC(testUser);
         assertEquals(2, node.size());
         assertEquals("new_vc", node.get(1).get("name").asText());
 
         // delete new VC
-        testDeleteVC("new_vc", testUser, testUser);
+        deleteVC("new_vc", testUser, testUser);
 
         // list VC
-        node = testListVC(testUser);
+        node = listVC(testUser);
         assertEquals(1, node.size());
     }
 
@@ -362,25 +317,15 @@
                 + ",\"corpusQuery\": \"corpusSigle=GOE\"}";
 
         String vcName = "new-published-vc";
-
-        Response response = target().path(API_VERSION).path("vc")
-                .path("~"+testUser).path(vcName)
-                .request()
-                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
-                        .createBasicAuthorizationHeaderValue(testUser, "pass"))
-                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
-                .header(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON)
-                .put(Entity.json(json));
-
-        assertEquals(Status.CREATED.getStatusCode(), response.getStatus());
-
+        createVC(authHeader, testUser, vcName, json);
+        
         // test list owner vc
         JsonNode node = testListOwnerVC(testUser);
         assertEquals(1, node.size());
         assertEquals(vcName, node.get(0).get("name").asText());
 
         // EM: check hidden access
-        node = testlistAccessByGroup("admin", "");
+        node = listAccessByGroup("admin", "");
         node = node.get(node.size() - 1);
         assertEquals("system", node.at("/createdBy").asText());
         assertEquals(vcName, node.at("/queryName").asText());
@@ -394,7 +339,7 @@
         assertEquals("HIDDEN", node.at("/status").asText());
 
         // EM: delete vc
-        testDeleteVC(vcName, testUser, testUser);
+        deleteVC(vcName, testUser, testUser);
 
         // EM: check if the hidden groups are deleted as well
         node = testCheckHiddenGroup(groupName);
@@ -503,7 +448,7 @@
         assertEquals(Status.CREATED.getStatusCode(), response.getStatus());
         
         testListSystemVC();
-        testDeleteVC(vcName, "system","admin");
+        deleteVC(vcName, "system","admin");
     }        
     
     @Test
@@ -511,12 +456,11 @@
         String json = "{\"type\": \"SYSTEM\""
                 + ",\"queryType\": \"VIRTUAL_CORPUS\""
                 + ",\"corpusQuery\": \"creationDate since 1820\"}";
-
+        
         Response response = target().path(API_VERSION).path("vc")
                 .path("~"+testUser).path("new_vc")
                 .request()
-                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
-                        .createBasicAuthorizationHeaderValue(testUser, "pass"))
+                .header(Attributes.AUTHORIZATION, authHeader)
                 .header(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON)
                 .put(Entity.json(json));
 
@@ -541,8 +485,7 @@
         Response response = target().path(API_VERSION).path("vc")
                 .path("~"+testUser).path("new $vc")
                 .request()
-                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
-                        .createBasicAuthorizationHeaderValue(testUser, "pass"))
+                .header(Attributes.AUTHORIZATION, authHeader)
                 .header(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON)
                 .put(Entity.json(json));
         String entity = response.readEntity(String.class);
@@ -563,8 +506,7 @@
         Response response = target().path(API_VERSION).path("vc")
                 .path("~"+testUser).path("ne")
                 .request()
-                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
-                        .createBasicAuthorizationHeaderValue(testUser, "pass"))
+                .header(Attributes.AUTHORIZATION, authHeader)
                 .header(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON)
                 .put(Entity.json(json));
         String entity = response.readEntity(String.class);
@@ -609,8 +551,7 @@
         Response response = target().path(API_VERSION).path("vc")
                 .path("~"+testUser).path("new_vc")
                 .request()
-                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
-                        .createBasicAuthorizationHeaderValue(testUser, "pass"))
+                .header(Attributes.AUTHORIZATION, authHeader)
                 .header(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON)
                 .put(Entity.json(json));
         String entity = response.readEntity(String.class);
@@ -629,8 +570,7 @@
         Response response = target().path(API_VERSION).path("vc")
                 .path("~"+testUser).path("new_vc")
                 .request()
-                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
-                        .createBasicAuthorizationHeaderValue(testUser, "pass"))
+                .header(Attributes.AUTHORIZATION, authHeader)
                 .header(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON)
                 .put(Entity.json(""));
         String entity = response.readEntity(String.class);
@@ -652,8 +592,7 @@
         Response response = target().path(API_VERSION).path("vc")
                 .path("~"+testUser).path("new_vc")
                 .request()
-                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
-                        .createBasicAuthorizationHeaderValue(testUser, "pass"))
+                .header(Attributes.AUTHORIZATION, authHeader)
                 .header(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON)
                 .put(Entity.json(json));
         String entity = response.readEntity(String.class);
@@ -676,8 +615,7 @@
         Response response = target().path(API_VERSION).path("vc")
                 .path("~"+testUser).path("new_vc")
                 .request()
-                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
-                        .createBasicAuthorizationHeaderValue(testUser, "pass"))
+                .header(Attributes.AUTHORIZATION, authHeader)
                 .header(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON)
                 .put(Entity.json(json));
 
@@ -697,8 +635,7 @@
         Response response = target().path(API_VERSION).path("vc")
                 .path("~dory").path("dory-vc")
                 .request()
-                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
-                        .createBasicAuthorizationHeaderValue(testUser, "pass"))
+                .header(Attributes.AUTHORIZATION, authHeader)
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
 
                 .delete();
@@ -733,7 +670,7 @@
         assertEquals(Status.NO_CONTENT.getStatusCode(), response.getStatus());
 
         // check VC
-        JsonNode node = testListVC("dory");
+        JsonNode node = listVC("dory");
         assertEquals("edited vc", node.get(0).get("description").asText());
 
         // 2nd edit
@@ -751,7 +688,7 @@
         assertEquals(Status.NO_CONTENT.getStatusCode(), response.getStatus());
 
         // check VC
-        node = testListVC("dory");
+        node = listVC("dory");
         assertEquals("test vc", node.get(0).get("description").asText());
     }
 
@@ -805,8 +742,7 @@
         Response response = target().path(API_VERSION).path("vc")
                 .path("~dory").path("dory-vc")
                 .request()
-                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
-                        .createBasicAuthorizationHeaderValue(testUser, "pass"))
+                .header(Attributes.AUTHORIZATION, authHeader)
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .header(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON)
                 .put(Entity.json(json));
@@ -828,7 +764,7 @@
         String vcName = "group-vc";
 
         // check the vc type
-        JsonNode node = testSearchVC("dory", "dory", vcName);
+        JsonNode node = retrieveVCInfo("dory", "dory", vcName);
         assertEquals(ResourceType.PROJECT.displayName(),
                 node.get("type").asText());
 
@@ -851,7 +787,7 @@
                 n.get("type").asText());
 
         // check hidden VC access
-        node = testlistAccessByGroup("admin", "");
+        node = listAccessByGroup("admin", "");
         assertEquals(4, node.size());
         node = node.get(node.size() - 1);
         assertEquals(vcName, node.at("/queryName").asText());
@@ -877,13 +813,13 @@
                 node.get(1).get("type").asText());
 
         // check VC access
-        node = testlistAccessByGroup("admin", "");
+        node = listAccessByGroup("admin", "");
         assertEquals(3, node.size());
     }
 
     @Test
     public void testlistAccessByNonVCAAdmin () throws KustvaktException {
-        JsonNode node = testlistAccessByGroup("nemo", "dory-group");
+        JsonNode node = listAccessByGroup("nemo", "dory-group");
         assertEquals(StatusCodes.AUTHORIZATION_FAILED,
                 node.at("/errors/0/0").asInt());
         assertEquals("Unauthorized operation for user: nemo",
@@ -939,7 +875,7 @@
         String groupName = "marlin-group";
 
         // check the vc type
-        JsonNode node = testSearchVC("marlin", "marlin", vcName);
+        JsonNode node = retrieveVCInfo("marlin", "marlin", vcName);
         assertEquals(vcName, node.at("/name").asText());
         assertEquals("private", node.at("/type").asText());
 
@@ -948,11 +884,11 @@
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
 
         // check the vc type
-        node = testSearchVC("marlin", "marlin", vcName);
+        node = retrieveVCInfo("marlin", "marlin", vcName);
         assertEquals("project", node.at("/type").asText());
 
         // list vc access by marlin
-        node = testlistAccessByGroup("marlin", groupName);
+        node = listAccessByGroup("marlin", groupName);
         assertEquals(2, node.size());
         node = node.get(1);
         assertEquals(5, node.at("/queryId").asInt());
@@ -971,23 +907,10 @@
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
 
         // list vc access by dory
-        node = testlistAccessByGroup("dory", groupName);
+        node = listAccessByGroup("dory", groupName);
         assertEquals(1, node.size());
 
-        testEditVCType("marlin", "marlin", vcName, ResourceType.PRIVATE);
-    }
-
-    private Response testShareVCByCreator (String vcCreator,
-            String vcName, String groupName) throws
-            ProcessingException, KustvaktException {
-
-        return target().path(API_VERSION).path("vc").path("~"+vcCreator)
-                .path(vcName).path("share").path("@"+groupName)
-                .request()
-                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
-                        .createBasicAuthorizationHeaderValue(vcCreator, "pass"))
-                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
-                .post(Entity.form(new Form()));
+        editVCType("marlin", "marlin", vcName, ResourceType.PRIVATE);
     }
 
     private void testShareVCNonUniqueAccess (String vcCreator, String vcName,
@@ -1093,8 +1016,7 @@
         Response response = target().path(API_VERSION).path("vc")
                 .path("access").path(accessId)
                 .request()
-                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
-                        .createBasicAuthorizationHeaderValue(testUser, "pass"))
+                .header(Attributes.AUTHORIZATION, authHeader)
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .delete();
 
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusSharingTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusSharingTest.java
new file mode 100644
index 0000000..701b8e5
--- /dev/null
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusSharingTest.java
@@ -0,0 +1,180 @@
+package de.ids_mannheim.korap.web.controller;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import javax.ws.rs.ProcessingException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Form;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.junit.Test;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+import de.ids_mannheim.korap.authentication.http.HttpAuthorizationHandler;
+import de.ids_mannheim.korap.config.Attributes;
+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.exceptions.StatusCodes;
+import de.ids_mannheim.korap.utils.JsonUtils;
+
+public class VirtualCorpusSharingTest extends VirtualCorpusTestBase {
+    private String testUser = "VirtualCorpusSharingTest";
+
+    @Test
+    public void testProjectVC () throws KustvaktException {
+        String json =
+                "{\"type\": \"PROJECT\"" + ",\"queryType\": \"VIRTUAL_CORPUS\""
+                        + ",\"corpusQuery\": \"corpusSigle=GOE\"}";
+        String vcName = "new_project_vc";
+        String authHeader = HttpAuthorizationHandler
+                .createBasicAuthorizationHeaderValue(testUser, "pass");
+
+        createVC(authHeader, testUser, vcName, json);
+
+        // retrieve vc info
+        JsonNode vcInfo = retrieveVCInfo(testUser, testUser, vcName);
+        assertEquals(vcName, vcInfo.get("name").asText());
+
+        // list user VC
+        JsonNode node = listVC(testUser);
+        assertEquals(2, node.size());
+        assertEquals(vcName, node.get(1).get("name").asText());
+
+        // search by non member
+        Response response = searchWithVCRef("dory", testUser, vcName);
+        assertEquals(Status.UNAUTHORIZED.getStatusCode(), response.getStatus());
+
+        // create user group
+        String groupName = "owidGroup";
+        String memberName = "darla";
+
+        response = createUserGroup(testUser, groupName, "Owid users");
+        assertEquals(Status.CREATED.getStatusCode(), response.getStatus());
+
+        listUserGroup(testUser, groupName);
+        testInviteMember(testUser, groupName, "darla");
+        subscribeToGroup(memberName, groupName);
+        checkMemberInGroup(memberName, testUser, groupName);
+
+        // share vc to group
+        testShareVCByCreator(testUser, vcName, groupName);
+        node = listAccessByGroup(testUser, groupName);
+
+        // search by member
+        response = searchWithVCRef(memberName, testUser, vcName);
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+        node = JsonUtils.readTree(response.readEntity(String.class));
+        assertTrue(node.at("/matches").size() > 0);
+
+        // delete project VC
+        deleteVC(vcName, testUser, testUser);
+
+        // list VC
+        node = listVC(testUser);
+        assertEquals(1, node.size());
+
+        // search by member
+        response = searchWithVCRef(memberName, testUser, vcName);
+        assertEquals(Status.NOT_FOUND.getStatusCode(), response.getStatus());
+        node = JsonUtils.readTree(response.readEntity(String.class));
+        assertEquals(StatusCodes.NO_RESOURCE_FOUND, node.at("/errors/0/0").asInt());
+        
+    }
+
+    private Response createUserGroup (String username, String groupName,
+            String description) throws ProcessingException, KustvaktException {
+        Form form = new Form();
+        form.param("description", description);
+
+        Response response = target().path(API_VERSION).path("group")
+                .path("@" + groupName).request()
+                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
+                        .createBasicAuthorizationHeaderValue(username, "pass"))
+                .put(Entity.form(form));
+
+        return response;
+    }
+
+    private JsonNode listUserGroup (String username, String groupName)
+            throws KustvaktException {
+        Response response = target().path(API_VERSION).path("group").request()
+                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
+                        .createBasicAuthorizationHeaderValue(username, "pass"))
+                .get();
+
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+
+        String entity = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(entity);
+        return node;
+    }
+
+    private void testInviteMember (String username, String groupName,
+            String memberName) throws ProcessingException, KustvaktException {
+        Form form = new Form();
+        form.param("members", memberName);
+
+        Response response = target().path(API_VERSION).path("group")
+                .path("@" + groupName).path("invite").request()
+                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
+                        .createBasicAuthorizationHeaderValue(username, "pass"))
+                .post(Entity.form(form));
+
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+
+        // list group
+        JsonNode node = listUserGroup(username, groupName);
+        node = node.get(0);
+        assertEquals(2, node.get("members").size());
+
+        assertEquals(memberName, node.at("/members/1/userId").asText());
+        assertEquals(GroupMemberStatus.PENDING.name(),
+                node.at("/members/1/status").asText());
+        assertEquals(0, node.at("/members/1/roles").size());
+    }
+
+    private void subscribeToGroup (String username, String groupName)
+            throws KustvaktException {
+        Response response = target().path(API_VERSION).path("group")
+                .path("@" + groupName).path("subscribe").request()
+                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
+                        .createBasicAuthorizationHeaderValue(username, "pass"))
+                .post(Entity.form(new Form()));
+
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+    }
+
+    private void checkMemberInGroup (String memberName, String testUser,
+            String groupName) throws KustvaktException {
+
+        JsonNode node = listUserGroup(testUser, groupName).get(0);
+        assertEquals(2, node.get("members").size());
+
+        assertEquals(memberName, node.at("/members/1/userId").asText());
+        assertEquals(GroupMemberStatus.ACTIVE.name(),
+                node.at("/members/1/status").asText());
+        assertEquals(PredefinedRole.VC_ACCESS_MEMBER.name(),
+                node.at("/members/1/roles/1").asText());
+        assertEquals(PredefinedRole.USER_GROUP_MEMBER.name(),
+                node.at("/members/1/roles/0").asText());
+    }
+
+    private Response searchWithVCRef (String username, String vcCreator,
+            String vcName) throws KustvaktException {
+        Response response = target().path(API_VERSION).path("search")
+                .queryParam("q", "[orth=der]").queryParam("ql", "poliqarp")
+                .queryParam("cq",
+                        "referTo \"" + vcCreator + "/" + vcName + "\"")
+                .request()
+                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
+                        .createBasicAuthorizationHeaderValue(username, "pass"))
+                .get();
+
+        return response;
+    }
+
+}
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusTestBase.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusTestBase.java
index c712030..b5454cf 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusTestBase.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/VirtualCorpusTestBase.java
@@ -2,33 +2,31 @@
 
 import static org.junit.Assert.assertEquals;
 
+import javax.ws.rs.ProcessingException;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Form;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
 import org.apache.http.entity.ContentType;
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.google.common.net.HttpHeaders;
-import javax.ws.rs.ProcessingException;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.core.Response.Status;
-import javax.ws.rs.client.Entity;
 
 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.ResourceType;
 import de.ids_mannheim.korap.exceptions.KustvaktException;
 import de.ids_mannheim.korap.utils.JsonUtils;
 
-public abstract class VirtualCorpusTestBase extends SpringJerseyTest{
-    
-    protected JsonNode testSearchVC (String username, String vcCreator, String vcName)
-            throws ProcessingException,
-            KustvaktException {
+public abstract class VirtualCorpusTestBase extends OAuth2TestBase {
+
+    protected JsonNode retrieveVCInfo (String username, String vcCreator,
+            String vcName) throws ProcessingException, KustvaktException {
         Response response = target().path(API_VERSION).path("vc")
-                .path("~"+vcCreator).path(vcName)
-                .request()
+                .path("~" + vcCreator).path(vcName).request()
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue(username, "pass"))
-                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .get();
         String entity = response.readEntity(String.class);
         // System.out.println(entity);
@@ -36,24 +34,88 @@
 
         return JsonUtils.readTree(entity);
     }
-    
-    protected void testEditVCType (String username, String vcCreator,
-            String vcName, ResourceType type)
-            throws KustvaktException {
+
+    protected void createVC (String authHeader,String username, String vcName, 
+            String vcJson) throws KustvaktException {
+        Response response = target().path(API_VERSION).path("vc")
+                .path("~" + username).path(vcName).request()
+                .header(Attributes.AUTHORIZATION, authHeader)
+                .header(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON)
+                .put(Entity.json(vcJson));
+
+        assertEquals(Status.CREATED.getStatusCode(), response.getStatus());
+    }
+
+    protected void editVCType (String username, String vcCreator, String vcName,
+            ResourceType type) throws KustvaktException {
         String json = "{\"type\": \"" + type + "\"}";
 
         Response response = target().path(API_VERSION).path("vc")
-                .path("~"+vcCreator).path(vcName)
-                .request()
+                .path("~" + vcCreator).path(vcName).request()
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue(username, "pass"))
-                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .header(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON)
                 .put(Entity.json(json));
 
         assertEquals(Status.NO_CONTENT.getStatusCode(), response.getStatus());
 
-        JsonNode node = testSearchVC(username, vcCreator, vcName);
+        JsonNode node = retrieveVCInfo(username, vcCreator, vcName);
         assertEquals(type.displayName(), node.at("/type").asText());
     }
+
+    protected JsonNode listVC (String username)
+            throws ProcessingException, KustvaktException {
+        Response response = target().path(API_VERSION).path("vc").request()
+                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
+                        .createBasicAuthorizationHeaderValue(username, "pass"))
+                .get();
+
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+        String entity = response.readEntity(String.class);
+        // System.out.println(entity);
+        return JsonUtils.readTree(entity);
+    }
+    
+    protected JsonNode listVCWithAuthHeader (String authHeader)
+            throws ProcessingException, KustvaktException {
+        Response response = target().path(API_VERSION).path("vc").request()
+                .header(Attributes.AUTHORIZATION, authHeader).get();
+
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+        String entity = response.readEntity(String.class);
+        return JsonUtils.readTree(entity);
+    }
+
+    protected Response testShareVCByCreator (String vcCreator, String vcName,
+            String groupName) throws ProcessingException, KustvaktException {
+
+        return target().path(API_VERSION).path("vc").path("~" + vcCreator)
+                .path(vcName).path("share").path("@" + groupName).request()
+                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
+                        .createBasicAuthorizationHeaderValue(vcCreator, "pass"))
+                .post(Entity.form(new Form()));
+    }
+
+    protected JsonNode listAccessByGroup (String username, String groupName)
+            throws KustvaktException {
+        Response response = target().path(API_VERSION).path("vc").path("access")
+                .queryParam("groupName", groupName).request()
+                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
+                        .createBasicAuthorizationHeaderValue(username, "pass"))
+                .get();
+        String entity = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(entity);
+        return node;
+    }
+
+    protected void deleteVC (String vcName, String vcCreator, String username)
+            throws KustvaktException {
+        Response response = target().path(API_VERSION).path("vc")
+                .path("~" + vcCreator).path(vcName).request()
+                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
+                        .createBasicAuthorizationHeaderValue(username, "pass"))
+                .delete();
+
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+    }
 }