Updated create update user-group API.

Change-Id: Ie9181c80165a1ca006038043cdf7d96bc8eddc12
diff --git a/full/Changes b/full/Changes
index 8d1347a..030fdfb 100644
--- a/full/Changes
+++ b/full/Changes
@@ -8,6 +8,8 @@
    - Added support for multiple cq parameters (margaretha, resolved #46)
 13/12/2019
    - Handled pipe errors and added tests (margaretha)
+19/12/2019
+   - Updated create update user-group API (margaretha)
     
 # version 0.62.2
 17/10/2019
diff --git a/full/src/main/java/de/ids_mannheim/korap/dao/UserGroupDao.java b/full/src/main/java/de/ids_mannheim/korap/dao/UserGroupDao.java
index 28b8650..7564121 100644
--- a/full/src/main/java/de/ids_mannheim/korap/dao/UserGroupDao.java
+++ b/full/src/main/java/de/ids_mannheim/korap/dao/UserGroupDao.java
@@ -55,14 +55,15 @@
     @Autowired
     private RoleDao roleDao;
 
-    public int createGroup (String name, String createdBy,
-            UserGroupStatus status) throws KustvaktException {
+    public int createGroup (String name, String description,
+            String createdBy, UserGroupStatus status) throws KustvaktException {
         ParameterChecker.checkStringValue(name, "name");
         ParameterChecker.checkStringValue(createdBy, "createdBy");
         ParameterChecker.checkObjectValue(status, "UserGroupStatus");
 
         UserGroup group = new UserGroup();
         group.setName(name);
+        group.setDescription(description);
         group.setStatus(status);
         group.setCreatedBy(createdBy);
         entityManager.persist(group);
@@ -112,13 +113,11 @@
         }
     }
 
-    public void editGroupName (int groupId, String name)
-            throws KustvaktException {
-        UserGroup group = retrieveGroupById(groupId);
-        group.setName(name);
+    public void updateGroup (UserGroup group) throws KustvaktException {
+        ParameterChecker.checkObjectValue(group, "user-group");
         entityManager.merge(group);
     }
-
+    
     /**
      * Retrieves the UserGroup by the given group id. This methods
      * does not
@@ -156,7 +155,7 @@
             return (UserGroup) q.getSingleResult();
         }
         catch (NoResultException e) {
-            throw new KustvaktException(StatusCodes.GROUP_NOT_FOUND,
+            throw new KustvaktException(StatusCodes.NO_RESOURCE_FOUND,
                     "Group with id " + groupId + " is not found",
                     String.valueOf(groupId), e);
         }
diff --git a/full/src/main/java/de/ids_mannheim/korap/dto/UserGroupDto.java b/full/src/main/java/de/ids_mannheim/korap/dto/UserGroupDto.java
index 69900d8..a2b2732 100644
--- a/full/src/main/java/de/ids_mannheim/korap/dto/UserGroupDto.java
+++ b/full/src/main/java/de/ids_mannheim/korap/dto/UserGroupDto.java
@@ -21,6 +21,7 @@
 
     private int id;
     private String name;
+    private String description;
     private String owner;
     private UserGroupStatus status;
     
diff --git a/full/src/main/java/de/ids_mannheim/korap/dto/converter/UserGroupConverter.java b/full/src/main/java/de/ids_mannheim/korap/dto/converter/UserGroupConverter.java
index cadedf9..5071635 100644
--- a/full/src/main/java/de/ids_mannheim/korap/dto/converter/UserGroupConverter.java
+++ b/full/src/main/java/de/ids_mannheim/korap/dto/converter/UserGroupConverter.java
@@ -33,6 +33,7 @@
         UserGroupDto dto = new UserGroupDto();
         dto.setId(group.getId());
         dto.setName(group.getName());
+        dto.setDescription(group.getDescription());
         dto.setStatus(group.getStatus());
         dto.setOwner(group.getCreatedBy());
         dto.setUserMemberStatus(userMemberStatus);
diff --git a/full/src/main/java/de/ids_mannheim/korap/entity/UserGroup.java b/full/src/main/java/de/ids_mannheim/korap/entity/UserGroup.java
index d3e8d47..0f2d7fd 100644
--- a/full/src/main/java/de/ids_mannheim/korap/entity/UserGroup.java
+++ b/full/src/main/java/de/ids_mannheim/korap/entity/UserGroup.java
@@ -40,8 +40,8 @@
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     private int id;
     // unique
-    @Column(name = "name")
     private String name;
+    private String description;
     @Column(name = "created_by")
     private String createdBy;
     @Column(name = "deleted_by")
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 090660e..7b0724f 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
@@ -212,11 +212,11 @@
      * 
      * 
      */
-    public boolean createUserGroup (String groupName, String members, String createdBy)
+    public boolean createUpdateUserGroup (String groupName, String description, String createdBy)
             throws KustvaktException {
         ParameterChecker.checkNameValue(groupName, "groupName");
         ParameterChecker.checkStringValue(createdBy, "createdBy");
-        
+
         if (!groupNamePattern.matcher(groupName).matches()) {
             throw new KustvaktException(StatusCodes.INVALID_ARGUMENT,
                     "User-group name must only contains letters, numbers, "
@@ -229,7 +229,6 @@
         try{
             userGroup = userGroupDao.retrieveGroupByName(groupName,false);
             groupExists = true;
-            
         }
         catch (KustvaktException e) {
             if (e.getStatusCode() != StatusCodes.NO_RESOURCE_FOUND){
@@ -239,7 +238,7 @@
         
         if (!groupExists){
             try {
-                userGroupDao.createGroup(groupName, createdBy,
+                userGroupDao.createGroup(groupName, description, createdBy,
                         UserGroupStatus.ACTIVE);
                 userGroup = userGroupDao.retrieveGroupByName(groupName,false);
             }
@@ -257,18 +256,10 @@
                 throw new KustvaktException(StatusCodes.DB_INSERT_FAILED,
                         cause.getMessage());
             }
-            
-            if (members != null && !members.isEmpty()){
-                String[] groupMembers = members.split(",");
-                for (String memberUsername : groupMembers) {
-                    if (memberUsername.equals(createdBy)) {
-                        // skip owner, already added while creating group.
-                        continue;
-                    }
-                    inviteGroupMember(memberUsername, userGroup, createdBy,
-                            GroupMemberStatus.PENDING);
-                }
-            }
+        }
+        else if (description != null) {
+            userGroup.setDescription(description);
+            userGroupDao.updateGroup(userGroup);
         }
         return groupExists;
     }
@@ -297,8 +288,8 @@
     public int createAutoHiddenGroup () throws KustvaktException {
         String code = random.createRandomCode();
         String groupName = "auto-"+code;
-        int groupId = userGroupDao.createGroup(groupName, "system",
-                UserGroupStatus.HIDDEN);
+        int groupId = userGroupDao.createGroup(groupName, "auto-hidden-group",
+                "system", UserGroupStatus.HIDDEN);
 
         return groupId;
     }
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 84c0afa..0d3154e 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
@@ -149,48 +149,33 @@
     }
 
     /**
-     * Creates a user group where the user in the token context is the
-     * group owner, and invites all users specified as members, see
-     * {@link #inviteGroupMembers(SecurityContext, String, UserGroupJson)}.
-     * The invited users are added as group members with status
-     * GroupMemberStatus.PENDING.
-     * 
-     * If a user accepts the invitation by using the service:
-     * {@link UserGroupController#subscribeToGroup(SecurityContext, String)},
-     * his GroupMemberStatus will be updated to
-     * GroupMemberStatus.ACTIVE.
-     * 
-     * If a user rejects the invitation by using the service:
-     * {@link UserGroupController#unsubscribeFromGroup(SecurityContext, String)},
-     * his GroupMemberStatus will be updated to
-     * GroupMemberStatus.DELETED.
+     * Creates a user group with the group owner as the only group
+     * member. The group owner is the authenticated user in the token
+     * context.
      * 
      * @param securityContext
-     * @param members
-     *            usernames of users to be invited as group members
-     *            (separated by comma)
+     * @param groupName the name of the group            
      * @return if a new group created, HTTP response status 201
      *         Created, otherwise 204 No Content.
      */
     @PUT
     @Path("@{groupName}")
     @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
-    public Response createUserGroup (@Context SecurityContext securityContext,
+    public Response createUpdateUserGroup (@Context SecurityContext securityContext,
             @PathParam("groupName") String groupName,
-            @FormParam("members") String members) {
+            @FormParam("description") String description) {
         TokenContext context =
                 (TokenContext) securityContext.getUserPrincipal();
         try {
             scopeService.verifyScope(context, OAuth2Scope.CREATE_USER_GROUP);
-            boolean groupExists = service.createUserGroup(groupName, members,
-                    context.getUsername());
+            boolean groupExists = service.createUpdateUserGroup(groupName,
+                    description, context.getUsername());
             if (groupExists) {
                 return Response.noContent().build();
             }
             else {
                 return Response.status(HttpStatus.SC_CREATED).build();
             }
-
         }
         catch (KustvaktException e) {
             throw kustvaktResponseHandler.throwit(e);
@@ -256,7 +241,21 @@
 
     /**
      * Invites users to join a user-group specified by the
-     * groupName.Only user-group admins and system admins are allowed.
+     * groupName. Only user-group admins and system admins are
+     * allowed to use this service.
+     *  
+     * The invited users are added as group members with status
+     * GroupMemberStatus.PENDING.
+     * 
+     * If a user accepts the invitation by using the service:
+     * {@link UserGroupController#subscribeToGroup(SecurityContext, String)},
+     * his GroupMemberStatus will be updated to
+     * GroupMemberStatus.ACTIVE.
+     * 
+     * If a user rejects the invitation by using the service:
+     * {@link UserGroupController#unsubscribeFromGroup(SecurityContext, String)},
+     * his GroupMemberStatus will be updated to
+     * GroupMemberStatus.DELETED.
      * 
      * @param securityContext
      * @param members
@@ -289,9 +288,12 @@
      * as well.
      * 
      * @param securityContext
-     * @param groupName the group name
-     * @param memberUsername the username of a group-member 
-     * @param roleIds the role ids for the member
+     * @param groupName
+     *            the group name
+     * @param memberUsername
+     *            the username of a group-member
+     * @param roleIds
+     *            the role ids for the member
      * @return
      */
     @POST
diff --git a/full/src/main/resources/db/mysql/V1.1__create_virtual_corpus_tables.sql b/full/src/main/resources/db/mysql/V1.1__create_virtual_corpus_tables.sql
index ac1f81d..590e318 100644
--- a/full/src/main/resources/db/mysql/V1.1__create_virtual_corpus_tables.sql
+++ b/full/src/main/resources/db/mysql/V1.1__create_virtual_corpus_tables.sql
@@ -1,13 +1,13 @@
 CREATE TABLE IF NOT EXISTS role (
   id INTEGER PRIMARY KEY AUTO_INCREMENT,
-  name varchar(100) NOT NULL,
+  name VARCHAR(100) NOT NULL,
   UNIQUE INDEX name_index(name)
 );
 
 
 CREATE TABLE IF NOT EXISTS privilege (
   id INTEGER PRIMARY KEY AUTO_INCREMENT,
-  name varchar(20) NOT NULL,
+  name VARCHAR(20) NOT NULL,
   role_id int NOT NULL,
   UNIQUE INDEX privilege_index(name, role_id),
   FOREIGN KEY (role_id) 
@@ -18,21 +18,22 @@
 
 CREATE TABLE IF NOT EXISTS user_group (
   id INTEGER PRIMARY KEY AUTO_INCREMENT,
-  name varchar(100) NOT NULL,
-  status varchar(100) NOT NULL,
-  created_by varchar(100) NOT NULL,
-  deleted_by varchar(100) DEFAULT NULL,
+  name VARCHAR(100) NOT NULL,
+  description VARCHAR(255) DEFAULT NULL,
+  status VARCHAR(100) NOT NULL,
+  created_by VARCHAR(100) NOT NULL,
+  deleted_by VARCHAR(100) DEFAULT NULL,
   INDEX status_index(status),
   UNIQUE INDEX unique_name(name);
 );
 
 CREATE TABLE IF NOT EXISTS user_group_member (
   id INTEGER PRIMARY KEY AUTO_INCREMENT,
-  user_id varchar(100) NOT NULL,
+  user_id VARCHAR(100) NOT NULL,
   group_id int(11) NOT NULL,
-  status varchar(100) NOT NULL,
-  created_by varchar(100) NOT NULL,
-  deleted_by varchar(100) DEFAULT NULL,
+  status VARCHAR(100) NOT NULL,
+  created_by VARCHAR(100) NOT NULL,
+  deleted_by VARCHAR(100) DEFAULT NULL,
   status_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
   UNIQUE INDEX unique_index (user_id,group_id),
   INDEX status_index(status),
@@ -56,14 +57,14 @@
 
 CREATE TABLE IF NOT EXISTS virtual_corpus (
   id INTEGER PRIMARY KEY AUTO_INCREMENT,
-  name varchar(255) NOT NULL,
-  type varchar(100) NOT NULL,
-  required_access varchar(100) NOT NULL,
-  created_by varchar(100) NOT NULL,
-  description varchar(255) DEFAULT NULL,
-  status varchar(100) DEFAULT NULL,
+  name VARCHAR(255) NOT NULL,
+  type VARCHAR(100) NOT NULL,
+  required_access VARCHAR(100) NOT NULL,
+  created_by VARCHAR(100) NOT NULL,
+  description VARCHAR(255) DEFAULT NULL,
+  status VARCHAR(100) DEFAULT NULL,
   corpus_query TEXT NOT NULL,
-  definition varchar(255) DEFAULT NULL,
+  definition VARCHAR(255) DEFAULT NULL,
   is_cached BOOLEAN DEFAULT 0,
   UNIQUE INDEX unique_index (name,created_by),
   INDEX owner_index (created_by),
@@ -74,10 +75,10 @@
   id INTEGER PRIMARY KEY AUTO_INCREMENT,
   virtual_corpus_id int(11) NOT NULL,
   user_group_id int(11) NOT NULL,
-  status varchar(100) NOT NULL,
-  created_by varchar(100) NOT NULL,
-  approved_by varchar(100) DEFAULT NULL,
-  deleted_by varchar(100) DEFAULT NULL,
+  status VARCHAR(100) NOT NULL,
+  created_by VARCHAR(100) NOT NULL,
+  approved_by VARCHAR(100) DEFAULT NULL,
+  deleted_by VARCHAR(100) DEFAULT NULL,
   UNIQUE INDEX unique_index (virtual_corpus_id,user_group_id),
   INDEX status_index(status),
   FOREIGN KEY (user_group_id) 
diff --git a/full/src/main/resources/db/mysql/V1.2__create_admin_table.sql b/full/src/main/resources/db/mysql/V1.2__create_admin_table.sql
index 7c8ce73..39a7114 100644
--- a/full/src/main/resources/db/mysql/V1.2__create_admin_table.sql
+++ b/full/src/main/resources/db/mysql/V1.2__create_admin_table.sql
@@ -1,5 +1,5 @@
 CREATE TABLE IF NOT EXISTS admin (
 	id INTEGER PRIMARY KEY AUTO_INCREMENT,
-	user_id varchar(100) NOT NULL,
+	user_id VARCHAR(100) NOT NULL,
 	UNIQUE INDEX unique_index (user_id)
 );
\ No newline at end of file
diff --git a/full/src/main/resources/db/sqlite/V1.1__create_virtual_corpus_tables.sql b/full/src/main/resources/db/sqlite/V1.1__create_virtual_corpus_tables.sql
index 0198da2..dbb9901 100644
--- a/full/src/main/resources/db/sqlite/V1.1__create_virtual_corpus_tables.sql
+++ b/full/src/main/resources/db/sqlite/V1.1__create_virtual_corpus_tables.sql
@@ -20,6 +20,7 @@
 CREATE TABLE IF NOT EXISTS user_group (
   id INTEGER PRIMARY KEY AUTOINCREMENT,
   name VARCHAR(100) NOT NULL,
+  description VARCHAR(255)DEFAULT NULL,
   status VARCHAR(100) NOT NULL,
   created_by VARCHAR(100) NOT NULL,
   deleted_by VARCHAR(100) DEFAULT NULL
diff --git a/full/src/main/resources/db/sqlite/V1.3__create_admin_table.sql b/full/src/main/resources/db/sqlite/V1.3__create_admin_table.sql
index f13861c..35b934b 100644
--- a/full/src/main/resources/db/sqlite/V1.3__create_admin_table.sql
+++ b/full/src/main/resources/db/sqlite/V1.3__create_admin_table.sql
@@ -1,6 +1,6 @@
 CREATE TABLE IF NOT EXISTS admin(
 	id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
-	user_id varchar(100) NOT NULL
+	user_id VARCHAR(100) NOT NULL
 );
 
 CREATE UNIQUE INDEX admin_index on admin(user_id);
\ No newline at end of file
diff --git a/full/src/test/java/de/ids_mannheim/korap/dao/UserGroupDaoTest.java b/full/src/test/java/de/ids_mannheim/korap/dao/UserGroupDaoTest.java
index d1fda36..1aa6ca2 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
@@ -51,7 +51,7 @@
         String groupName = "test group";
         String createdBy = "test class";
         // create group
-        int groupId = userGroupDao.createGroup(groupName, createdBy,
+        int groupId = userGroupDao.createGroup(groupName, null, createdBy,
                 UserGroupStatus.ACTIVE);
 
         // retrieve group
@@ -126,20 +126,6 @@
     }
 
     @Test
-    public void editExistingGroupName () throws KustvaktException {
-        UserGroup group = userGroupDao.retrieveGroupById(1);
-        String name = group.getName();
-        String newName = "new vc name";
-        userGroupDao.editGroupName(1, newName);
-        group = userGroupDao.retrieveGroupById(1);
-        assertEquals(newName, group.getName());
-
-        userGroupDao.editGroupName(1, name);
-        group = userGroupDao.retrieveGroupById(1);
-        assertEquals(name, group.getName());
-    }
-
-    @Test
     public void addVCToGroup () throws KustvaktException {
         // dory group
         int groupId = 2;
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/UserGroupControllerAdminTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/UserGroupControllerAdminTest.java
index 77765e1..bf78727 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/UserGroupControllerAdminTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/UserGroupControllerAdminTest.java
@@ -143,9 +143,6 @@
 
         String groupName = "admin-test-group";
 
-        MultivaluedMap<String, String> form = new MultivaluedMapImpl();
-        form.add("members", "marlin,nemo");
-
         ClientResponse response = resource().path(API_VERSION).path("group")
                 .path("@" + groupName)
                 .type(MediaType.APPLICATION_FORM_URLENCODED)
@@ -153,7 +150,7 @@
                         HttpAuthorizationHandler
                                 .createBasicAuthorizationHeaderValue(testUser,
                                         "password"))
-                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32").entity(form)
+                .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
                 .put(ClientResponse.class);
 
         assertEquals(Status.CREATED.getStatusCode(), response.getStatus());
@@ -164,8 +161,8 @@
         node = node.get(0);
         assertEquals(groupName, node.get("name").asText());
 
-        testMemberRole("marlin", groupName);
         testInviteMember(groupName);
+        testMemberRole("marlin", groupName);
         testDeleteMember(groupName);
         testDeleteGroup(groupName);
     }
@@ -314,7 +311,7 @@
             throws UniformInterfaceException, ClientHandlerException,
             KustvaktException {
         MultivaluedMap<String, String> form = new MultivaluedMapImpl();
-        form.add("members", "darla");
+        form.add("members", "marlin,nemo,darla");
 
         ClientResponse response = resource().path(API_VERSION).path("group")
                 .path("@" + groupName).path("invite")
@@ -333,8 +330,8 @@
 
         assertEquals("darla", node.at("/members/3/userId").asText());
         assertEquals(GroupMemberStatus.PENDING.name(),
-                node.at("/members/3/status").asText());
-        assertEquals(0, node.at("/members/3/roles").size());
+                node.at("/members/1/status").asText());
+        assertEquals(0, node.at("/members/1/roles").size());
     }
 
 }
diff --git a/full/src/test/java/de/ids_mannheim/korap/web/controller/UserGroupControllerTest.java b/full/src/test/java/de/ids_mannheim/korap/web/controller/UserGroupControllerTest.java
index 2770714..a03110d 100644
--- a/full/src/test/java/de/ids_mannheim/korap/web/controller/UserGroupControllerTest.java
+++ b/full/src/test/java/de/ids_mannheim/korap/web/controller/UserGroupControllerTest.java
@@ -139,7 +139,7 @@
 
     
     @Test
-    public void testCreateGroupEmptyMembers () throws UniformInterfaceException,
+    public void testCreateGroupEmptyDescription () throws UniformInterfaceException,
             ClientHandlerException, KustvaktException {
         String groupName = "empty_group";
         ClientResponse response = testCreateUserGroup(groupName,"");
@@ -150,20 +150,20 @@
 
     
     @Test
-    public void testCreateGroupMissingMembers () throws UniformInterfaceException,
+    public void testCreateGroupMissingDescription () throws UniformInterfaceException,
             ClientHandlerException, KustvaktException {
-        String groupName = "missing-member-group";
+        String groupName = "missing-desc-group";
 
-        ClientResponse response = testCreateGroupWithoutMembers(groupName);
+        ClientResponse response = testCreateGroupWithoutDescription(groupName);
         assertEquals(Status.CREATED.getStatusCode(), response.getStatus());
         deleteGroupByName(groupName);
     }
     
-    private ClientResponse testCreateUserGroup (String groupName, String members)
+    private ClientResponse testCreateUserGroup (String groupName, String description)
             throws UniformInterfaceException, ClientHandlerException,
             KustvaktException {
         MultivaluedMap<String, String> form = new MultivaluedMapImpl();
-        form.add("members", members);
+        form.add("description", description);
         
         ClientResponse response = resource().path(API_VERSION).path("group")
                 .path("@"+groupName).type(MediaType.APPLICATION_FORM_URLENCODED)
@@ -175,7 +175,7 @@
         return response;
     }
     
-    private ClientResponse testCreateGroupWithoutMembers (String groupName)
+    private ClientResponse testCreateGroupWithoutDescription (String groupName)
             throws UniformInterfaceException, ClientHandlerException,
             KustvaktException {
         ClientResponse response = resource().path(API_VERSION).path("group")
@@ -192,9 +192,8 @@
     public void testCreateGroupInvalidName () throws UniformInterfaceException,
             ClientHandlerException, KustvaktException {
         String groupName = "invalid-group-name$"; 
-        String members = "marlin,nemo";
 
-        ClientResponse response = testCreateUserGroup(groupName, members);
+        ClientResponse response = testCreateGroupWithoutDescription(groupName);
         assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
         
         JsonNode node = JsonUtils.readTree(response.getEntity(String.class));
@@ -208,9 +207,8 @@
     public void testCreateGroupNameTooShort () throws UniformInterfaceException,
             ClientHandlerException, KustvaktException {
         String groupName = "a"; 
-        String members = "marlin,nemo";
 
-        ClientResponse response = testCreateUserGroup(groupName, members);
+        ClientResponse response = testCreateGroupWithoutDescription(groupName);
         assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
         
         JsonNode node = JsonUtils.readTree(response.getEntity(String.class));
@@ -225,22 +223,24 @@
             ClientHandlerException, KustvaktException {
 
         String groupName = "new-user-group";
-        String members = "marlin,nemo";
-
-        ClientResponse response = testCreateUserGroup(groupName,members);
+        String description= "This is new-user-group.";
+        
+        ClientResponse response =
+                testCreateUserGroup(groupName, description);
         assertEquals(Status.CREATED.getStatusCode(), response.getStatus());
         // same name
-        response = testCreateGroupWithoutMembers(groupName);
+        response = testCreateGroupWithoutDescription(groupName);
         assertEquals(Status.NO_CONTENT.getStatusCode(), response.getStatus());
 
         // list user group
-        JsonNode node = retrieveUserGroups(username);;
+        JsonNode node = retrieveUserGroups(username);
         assertEquals(1, node.size());
         node = node.get(0);
         assertEquals("new-user-group", node.get("name").asText());
+        assertEquals(description, node.get("description").asText());
 
         assertEquals(username, node.get("owner").asText());
-        assertEquals(3, node.get("members").size());
+        assertEquals(1, node.get("members").size());
         assertEquals(username, node.at("/members/0/userId").asText());
         assertEquals(GroupMemberStatus.ACTIVE.name(),
                 node.at("/members/0/status").asText());
@@ -249,11 +249,7 @@
         assertEquals(PredefinedRole.VC_ACCESS_ADMIN.name(),
                 node.at("/members/0/roles/1").asText());
 
-        assertEquals("marlin", node.at("/members/1/userId").asText());
-        assertEquals(GroupMemberStatus.PENDING.name(),
-                node.at("/members/1/status").asText());
-        assertEquals(0, node.at("/members/1/roles").size());
-
+        testUpdateUserGroup(groupName);
         testInviteMember(groupName);
 
         testDeleteMemberUnauthorized(groupName);
@@ -264,12 +260,24 @@
         testUnsubscribeToDeletedGroup(groupName);
     }
 
+    private void testUpdateUserGroup (String groupName)
+            throws UniformInterfaceException, ClientHandlerException,
+            KustvaktException {
+        String description = "Description is updated.";
+        ClientResponse response = testCreateUserGroup(groupName, description);
+        assertEquals(Status.NO_CONTENT.getStatusCode(), response.getStatus());
+
+        JsonNode node = retrieveUserGroups(username);
+        assertEquals(1, node.size());
+        assertEquals(description, node.get(0).get("description").asText());
+    }
+
     private void testDeleteMember (String groupName)
             throws UniformInterfaceException, ClientHandlerException,
             KustvaktException {
-        // delete marlin from group
+        // delete darla from group
         ClientResponse response = resource().path(API_VERSION).path("group")
-                .path("@"+groupName).path("~marlin")
+                .path("@"+groupName).path("~darla")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue(username, "pass"))
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
@@ -284,7 +292,7 @@
         String entity = response.getEntity(String.class);
         JsonNode node = JsonUtils.readTree(entity);
         node = node.get(0);
-        assertEquals(3, node.get("members").size());
+        assertEquals(1, node.get("members").size());
     }
 
     private void testDeleteMemberUnauthorized (String groupName)
@@ -292,7 +300,7 @@
             KustvaktException {
         // nemo is a group member
         ClientResponse response = resource().path(API_VERSION).path("group")
-                .path("@"+groupName).path("~marlin")
+                .path("@"+groupName).path("~darla")
                 .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
                         .createBasicAuthorizationHeaderValue("nemo", "pass"))
                 .header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32")
@@ -473,12 +481,12 @@
 
         JsonNode node = JsonUtils.readTree(entity);
         node = node.get(0);
-        assertEquals(4, node.get("members").size());
+        assertEquals(2, node.get("members").size());
 
-        assertEquals("darla", node.at("/members/3/userId").asText());
+        assertEquals("darla", node.at("/members/1/userId").asText());
         assertEquals(GroupMemberStatus.PENDING.name(),
-                node.at("/members/3/status").asText());
-        assertEquals(0, node.at("/members/3/roles").size());
+                node.at("/members/1/status").asText());
+        assertEquals(0, node.at("/members/1/roles").size());
     }
 
     private void testInviteDeletedMember () throws UniformInterfaceException,