You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by aw...@apache.org on 2019/09/03 07:26:02 UTC

[fineract-cn-group] 36/46: Document Group management API for savings and loans in groups

This is an automated email from the ASF dual-hosted git repository.

awasum pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract-cn-group.git

commit f252c32b55580b544a110a177fdf3ccad9e2a48c
Author: Isaac Kamga <u2...@gmail.com>
AuthorDate: Fri Sep 7 10:18:18 2018 +0100

    Document Group management API for savings and loans in groups
---
 .gitignore                                         |   3 +
 component-test/build.gradle                        |  15 +-
 .../fineract/cn/group/AbstractGroupTest.java       |  93 +++
 .../fineract/cn/group/GroupApiDocumentation.java   | 717 +++++++++++++++++++++
 .../fineract/cn/group/SuiteTestEnvironment.java    |  45 ++
 .../org/apache/fineract/cn/group/TestGroup.java    | 105 +--
 .../fineract/cn/group/TestGroupDefinition.java     |  79 +--
 .../cn/group/util/GroupDefinitionGenerator.java    |   4 +-
 .../fineract/cn/group/util/GroupGenerator.java     |  32 +-
 9 files changed, 911 insertions(+), 182 deletions(-)

diff --git a/.gitignore b/.gitignore
index 5f934cb..728f592 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,9 @@
 .settings
 **/build/
 **/target/
+api/out/
+component-test/out/
+service/out
 
 # Ignore Gradle GUI config
 gradle-app.setting
diff --git a/component-test/build.gradle b/component-test/build.gradle
index a3a8ccb..e4edf5a 100644
--- a/component-test/build.gradle
+++ b/component-test/build.gradle
@@ -26,6 +26,7 @@ buildscript {
 
     dependencies {
         classpath ("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
+        classpath("org.asciidoctor:asciidoctor-gradle-plugin:1.5.3")
     }
 }
 
@@ -35,6 +36,7 @@ plugins {
 }
 
 apply from: '../shared.gradle'
+apply plugin: 'org.asciidoctor.convert'
 
 dependencies {
     compile(
@@ -43,14 +45,23 @@ dependencies {
             [group: 'org.apache.fineract.cn', name: 'api', version: versions.frameworkapi],
             [group: 'org.apache.fineract.cn', name: 'test', version: versions.frameworktest],
             [group: 'org.apache.fineract.cn.anubis', name: 'test', version: versions.frameworkanubis],
-            [group: 'org.springframework.boot', name: 'spring-boot-starter-test']
+            [group: 'org.springframework.boot', name: 'spring-boot-starter-test'],
+            [group: 'org.springframework.restdocs', name: 'spring-restdocs-mockmvc'],
+            [group: 'junit', name: 'junit', version: '4.12']
     )
 }
 
+asciidoctor {
+    sourceDir 'build/doc/asciidoc/'
+    outputDir 'build/doc/html5'
+    options backend: "html", doctype: "book"
+    attributes "source-highlighter": "highlightjs",
+            'snippets': file('build/doc/generated-snippets/')
+}
 publishing {
     publications {
         mavenJava(MavenPublication) {
             from components.java
         }
     }
-}
+}
\ No newline at end of file
diff --git a/component-test/src/main/java/org/apache/fineract/cn/group/AbstractGroupTest.java b/component-test/src/main/java/org/apache/fineract/cn/group/AbstractGroupTest.java
new file mode 100644
index 0000000..12d8b83
--- /dev/null
+++ b/component-test/src/main/java/org/apache/fineract/cn/group/AbstractGroupTest.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.cn.group;
+
+import org.apache.fineract.cn.anubis.test.v1.TenantApplicationSecurityEnvironmentTestRule;
+import org.apache.fineract.cn.api.context.AutoUserContext;
+import org.apache.fineract.cn.group.api.v1.EventConstants;
+import org.apache.fineract.cn.group.api.v1.client.GroupManager;
+import org.apache.fineract.cn.test.listener.EnableEventRecording;
+import org.apache.fineract.cn.test.listener.EventRecorder;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.runner.RunWith;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.cloud.netflix.feign.EnableFeignClients;
+import org.springframework.cloud.netflix.ribbon.RibbonClient;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.test.context.junit4.SpringRunner;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT,
+        classes = {AbstractGroupTest.TestConfiguration.class})
+public class AbstractGroupTest extends SuiteTestEnvironment {
+
+    @Rule
+    public final TenantApplicationSecurityEnvironmentTestRule tenantApplicationSecurityEnvironment
+            = new TenantApplicationSecurityEnvironmentTestRule(testEnvironment, this::waitForInitialize);
+    @Autowired
+    GroupManager testSubject;
+
+    @Autowired
+    EventRecorder eventRecorder;
+
+    AutoUserContext userContext;
+
+    @Before
+    public void prepTest() {
+        userContext = this.tenantApplicationSecurityEnvironment.createAutoUserContext(TEST_USER);
+    }
+
+    @After
+    public void cleanTest() {
+        userContext.close();
+    }
+
+    public boolean waitForInitialize() {
+        try {
+            return this.eventRecorder.wait(EventConstants.INITIALIZE, EventConstants.INITIALIZE);
+        } catch (final InterruptedException e) {
+            throw new IllegalStateException(e);
+        }
+    }
+
+    @Configuration
+    @EnableEventRecording
+    @EnableFeignClients(basePackages = {"org.apache.fineract.cn.group.api.v1.client"})
+    @RibbonClient(name = APP_NAME)
+    @Import({GroupConfiguration.class})
+    @ComponentScan("org.apache.fineract.cn.group.listener")
+    public static class TestConfiguration {
+        public TestConfiguration() {
+            super();
+        }
+
+        @Bean()
+        public Logger logger() {
+            return LoggerFactory.getLogger("test-logger");
+        }
+    }
+}
diff --git a/component-test/src/main/java/org/apache/fineract/cn/group/GroupApiDocumentation.java b/component-test/src/main/java/org/apache/fineract/cn/group/GroupApiDocumentation.java
new file mode 100644
index 0000000..1ecd27b
--- /dev/null
+++ b/component-test/src/main/java/org/apache/fineract/cn/group/GroupApiDocumentation.java
@@ -0,0 +1,717 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.cn.group;
+
+import com.google.gson.Gson;
+import org.apache.commons.lang3.RandomStringUtils;
+import org.apache.fineract.cn.group.api.v1.EventConstants;
+import org.apache.fineract.cn.group.api.v1.domain.*;
+import org.apache.fineract.cn.group.util.GroupDefinitionGenerator;
+import org.apache.fineract.cn.group.util.GroupGenerator;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.restdocs.JUnitRestDocumentation;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.web.context.WebApplicationContext;
+
+import java.time.Clock;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
+import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
+import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.*;
+import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest;
+import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse;
+import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint;
+import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
+import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields;
+import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+public class GroupApiDocumentation extends AbstractGroupTest {
+
+  @Rule
+  public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation("build/doc/generated-snippets/test-group");
+
+  @Autowired
+  private WebApplicationContext context;
+
+  private MockMvc mockMvc;
+
+  @Before
+  public void setUp ( ) {
+
+    this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
+            .apply(documentationConfiguration(this.restDocumentation))
+            .alwaysDo(document("{method-name}", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint())))
+            .build();
+  }
+
+  @Test
+  public void documentCreateGroup ( ) throws Exception {
+
+    final Cycle groupCycle = new Cycle();
+    groupCycle.setNumberOfMeetings(25);
+    groupCycle.setFrequency(Cycle.Frequency.MONTHLY.name());
+    groupCycle.setAdjustment(Cycle.Adjustment.NEXT_BUSINESS_DAY.name());
+
+    final GroupDefinition randomGroupDefinition = GroupDefinitionGenerator.createRandomGroupDefinition();
+    randomGroupDefinition.setIdentifier("grpDef101");
+    randomGroupDefinition.setDescription("Group Defininition 101");
+    randomGroupDefinition.setMinimalSize(Integer.valueOf(5));
+    randomGroupDefinition.setMaximalSize(Integer.valueOf(10));
+    randomGroupDefinition.setCycle(groupCycle);
+
+    this.testSubject.createGroupDefinition(randomGroupDefinition);
+    this.eventRecorder.wait(EventConstants.POST_GROUP_DEFINITION, randomGroupDefinition.getIdentifier());
+
+    Set <String> leadership = new HashSet <>();
+    leadership.add("Nditapa");
+    leadership.add("Imele");
+
+    Set <String> membership = new HashSet <>();
+    membership.add("Kamga");
+    membership.add("Ayuk");
+    membership.add("Awasum");
+
+    final Address address = new Address();
+    address.setStreet("Hospital");
+    address.setCity("Muyuka");
+    address.setRegion("SWR");
+    address.setPostalCode("8050");
+    address.setCountry("Cameroon");
+    address.setCountryCode("CM");
+
+    final Group randomGroup = GroupGenerator.createRandomGroup(randomGroupDefinition.getIdentifier());
+    randomGroup.setIdentifier("group001");
+    randomGroup.setGroupDefinitionIdentifier(randomGroupDefinition.getIdentifier());
+    randomGroup.setName("ARPOT Group");
+    randomGroup.setOffice("Bambui");
+    randomGroup.setAssignedEmployee("Tabah Tih");
+    randomGroup.setWeekday(Group.Weekday.WEDNESDAY.getValue());
+    randomGroup.setLeaders(leadership);
+    randomGroup.setMembers(membership);
+    randomGroup.setAddress(address);
+
+    Gson serialize = new Gson();
+    this.mockMvc.perform(post("/groups")
+            .accept(MediaType.APPLICATION_JSON_VALUE)
+            .contentType(MediaType.APPLICATION_JSON_VALUE)
+            .content(serialize.toJson(randomGroup)))
+            .andExpect(status().isAccepted())
+            .andDo(document("document-create-group", preprocessRequest(prettyPrint()),
+                    requestFields(
+                            fieldWithPath("identifier").description("Group Id"),
+                            fieldWithPath("groupDefinitionIdentifier").description("Group definition Id"),
+                            fieldWithPath("name").description("Group's name"),
+                            fieldWithPath("leaders").type("Set<String>").description("Group's leaders"),
+                            fieldWithPath("members").type("Set<String>").description("Group's members"),
+                            fieldWithPath("office").description("Group's office"),
+                            fieldWithPath("assignedEmployee").description("Assigned Employee"),
+                            fieldWithPath("weekday").type("Integer").description("Weekday for group meeting " +
+                                    " \n + " +
+                                    " *enum* _Weekday_ { + \n" +
+                                    "    MONDAY(1), + \n" +
+                                    "    TUESDAY(2), + \n" +
+                                    "    WEDNESDAY(3), + \n" +
+                                    "    THURSDAY(4), + \n" +
+                                    "    FRIDAY(5), + \n" +
+                                    "    SATURDAY(6), + \n" +
+                                    "    SUNDAY(7) + \n " +
+                                    " } \n +"),
+                            fieldWithPath("address.street").description("Office street"),
+                            fieldWithPath("address.city").description("Office city"),
+                            fieldWithPath("address.region").description("Office region"),
+                            fieldWithPath("address.postalCode").description("Office postal code"),
+                            fieldWithPath("address.countryCode").description("Office country code"),
+                            fieldWithPath("address.country").description("Office country")
+                    )));
+  }
+
+  @Test
+  public void documentFindGroup ( ) throws Exception {
+
+    final Cycle groupCycle = new Cycle();
+    groupCycle.setNumberOfMeetings(12);
+    groupCycle.setFrequency(Cycle.Frequency.MONTHLY.name());
+    groupCycle.setAdjustment(Cycle.Adjustment.NEXT_BUSINESS_DAY.name());
+
+    final GroupDefinition randomGroupDefinition = GroupDefinitionGenerator.createRandomGroupDefinition();
+    randomGroupDefinition.setDescription("Group Defininition 202");
+    randomGroupDefinition.setMinimalSize(Integer.valueOf(5));
+    randomGroupDefinition.setMaximalSize(Integer.valueOf(10));
+    randomGroupDefinition.setCycle(groupCycle);
+
+    this.testSubject.createGroupDefinition(randomGroupDefinition);
+    this.eventRecorder.wait(EventConstants.POST_GROUP_DEFINITION, randomGroupDefinition.getIdentifier());
+
+    Set <String> leadership = new HashSet <>();
+    leadership.add("Ndi");
+    leadership.add("Mele");
+
+    Set <String> membership = new HashSet <>();
+    membership.add("Kamgue");
+    membership.add("Etta");
+    membership.add("Awasom");
+
+    final Address address = new Address();
+    address.setStreet("Ghana Street");
+    address.setCity("Bamenda");
+    address.setRegion("NWR");
+    address.setPostalCode("8050");
+    address.setCountry("Cameroon");
+    address.setCountryCode("CM");
+
+    final Group randomGroup = GroupGenerator.createRandomGroup(randomGroupDefinition.getIdentifier());
+    randomGroup.setGroupDefinitionIdentifier(randomGroupDefinition.getIdentifier());
+    randomGroup.setName("SDLE Group");
+    randomGroup.setOffice("Bambui");
+    randomGroup.setAssignedEmployee("Tabah Tih");
+    randomGroup.setWeekday(Group.Weekday.SATURDAY.getValue());
+    randomGroup.setLeaders(leadership);
+    randomGroup.setMembers(membership);
+    randomGroup.setAddress(address);
+
+    this.testSubject.createGroup(randomGroup);
+    this.eventRecorder.wait(EventConstants.POST_GROUP, randomGroup.getIdentifier());
+
+    this.mockMvc.perform(get("/groups/" + randomGroup.getIdentifier())
+            .accept(MediaType.ALL_VALUE)
+            .contentType(MediaType.APPLICATION_JSON_VALUE))
+            .andExpect(status().isOk())
+            .andDo(document("document-find-group", preprocessResponse(prettyPrint()),
+                    responseFields(
+                            fieldWithPath("identifier").description("Group Id"),
+                            fieldWithPath("groupDefinitionIdentifier").description("Group definition Id"),
+                            fieldWithPath("name").description("Group's name"),
+                            fieldWithPath("leaders").type("Set<String>").description("Group's leaders"),
+                            fieldWithPath("members").type("Set<String>").description("Group's members"),
+                            fieldWithPath("office").description("Group's office"),
+                            fieldWithPath("assignedEmployee").description("Assigned Employee"),
+                            fieldWithPath("weekday").type("Integer").description("Weekday for group meeting " +
+                                    " \n + " +
+                                    " *enum* _Weekday_ { + \n" +
+                                    "    MONDAY(1), + \n" +
+                                    "    TUESDAY(2), + \n" +
+                                    "    WEDNESDAY(3), + \n" +
+                                    "    THURSDAY(4), + \n" +
+                                    "    FRIDAY(5), + \n" +
+                                    "    SATURDAY(6), + \n" +
+                                    "    SUNDAY(7) + \n " +
+                                    " } \n +"),
+                            fieldWithPath("address.street").description("Office street"),
+                            fieldWithPath("address.city").description("Office city"),
+                            fieldWithPath("address.region").description("Office region"),
+                            fieldWithPath("address.postalCode").description("Office postal code"),
+                            fieldWithPath("address.countryCode").description("Office country code"),
+                            fieldWithPath("address.country").description("Office country"),
+                            fieldWithPath("status").description("Status " +
+                                    " + \n" +
+                                    "*enum* _Status_ { + \n" +
+                                    "    PENDING, + \n" +
+                                    "    ACTIVE, + \n" +
+                                    "    CLOSED + \n" +
+                                    "  }"),
+                            fieldWithPath("createdOn").description(""),
+                            fieldWithPath("createdBy").description(""),
+                            fieldWithPath("lastModifiedOn").description(""),
+                            fieldWithPath("lastModifiedBy").description("")
+                    )));
+  }
+
+  @Test
+  public void documentUpdateGroup ( ) throws Exception {
+
+    final GroupDefinition randomGroupDefinition = GroupDefinitionGenerator.createRandomGroupDefinition();
+    this.testSubject.createGroupDefinition(randomGroupDefinition);
+    this.eventRecorder.wait(EventConstants.POST_GROUP_DEFINITION, randomGroupDefinition.getIdentifier());
+
+    final Group randomGroup = GroupGenerator.createRandomGroup(randomGroupDefinition.getIdentifier());
+    this.testSubject.createGroup(randomGroup);
+    this.eventRecorder.wait(EventConstants.POST_GROUP, randomGroup.getIdentifier());
+
+    randomGroup.setName(randomGroup.getName() + "Updated");
+    randomGroup.setWeekday(Group.Weekday.SATURDAY.getValue());
+
+    Gson serialize = new Gson();
+    this.mockMvc.perform(put("/groups/" + randomGroup.getIdentifier())
+            .accept(MediaType.APPLICATION_JSON_VALUE)
+            .contentType(MediaType.APPLICATION_JSON_VALUE)
+            .content(serialize.toJson(randomGroup)))
+            .andExpect(status().isAccepted())
+            .andDo(document("document-update-group", preprocessRequest(prettyPrint()),
+                    requestFields(
+                            fieldWithPath("identifier").description("Group Id"),
+                            fieldWithPath("groupDefinitionIdentifier").description("Group Definition Id"),
+                            fieldWithPath("name").description("Group's name"),
+                            fieldWithPath("leaders").type("Set<String>").description("Group's leaders"),
+                            fieldWithPath("members").type("Set<String>").description("Group's members"),
+                            fieldWithPath("office").description("Group's office"),
+                            fieldWithPath("assignedEmployee").description("Assigned Employee"),
+                            fieldWithPath("weekday").type("Integer").description("Weekday for group meeting " +
+                                    " \n + " +
+                                    " *enum* _Weekday_ { + \n" +
+                                    "    MONDAY(1), + \n" +
+                                    "    TUESDAY(2), + \n" +
+                                    "    WEDNESDAY(3), + \n" +
+                                    "    THURSDAY(4), + \n" +
+                                    "    FRIDAY(5), + \n" +
+                                    "    SATURDAY(6), + \n" +
+                                    "    SUNDAY(7) + \n " +
+                                    " } \n +"),
+                            fieldWithPath("address.street").description("Office street"),
+                            fieldWithPath("address.city").description("Office city"),
+                            fieldWithPath("address.region").description("Office region"),
+                            fieldWithPath("address.postalCode").description("Office postal code"),
+                            fieldWithPath("address.countryCode").description("Office country code"),
+                            fieldWithPath("address.country").description("Office country")
+                    )));
+  }
+
+  @Test
+  public void documentActivateGroup ( ) throws Exception {
+
+    final GroupDefinition randomGroupDefinition = GroupDefinitionGenerator.createRandomGroupDefinition();
+    this.testSubject.createGroupDefinition(randomGroupDefinition);
+    this.eventRecorder.wait(EventConstants.POST_GROUP_DEFINITION, randomGroupDefinition.getIdentifier());
+
+    final Group randomGroup = GroupGenerator.createRandomGroup(randomGroupDefinition.getIdentifier());
+    this.testSubject.createGroup(randomGroup);
+    this.eventRecorder.wait(EventConstants.POST_GROUP, randomGroup.getIdentifier());
+
+    final Group fetchedGroup = this.testSubject.findGroup(randomGroup.getIdentifier());
+    Assert.assertEquals(Group.Status.PENDING.name(), fetchedGroup.getStatus());
+
+    final GroupCommand activate = new GroupCommand();
+    activate.setAction(GroupCommand.Action.ACTIVATE.name());
+    activate.setNote("Activate Note" + RandomStringUtils.randomAlphanumeric(3));
+    activate.setCreatedBy(SuiteTestEnvironment.TEST_USER);
+    activate.setCreatedOn(ZonedDateTime.now(Clock.systemUTC()).format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
+
+    Gson serialize = new Gson();
+    this.mockMvc.perform(post("/groups/" + randomGroup.getIdentifier() + "/commands")
+            .accept(MediaType.APPLICATION_JSON_VALUE)
+            .contentType(MediaType.APPLICATION_JSON_VALUE)
+            .content(serialize.toJson(activate)))
+            .andExpect(status().isAccepted())
+            .andDo(document("document-activate-group", preprocessRequest(prettyPrint()),
+                    requestFields(
+                            fieldWithPath("action").description("Action " +
+                                    "" +
+                                    "*enum* _Action_ { + \n" +
+                                    "    ACTIVATE, + \n" +
+                                    "    CLOSE, + \n" +
+                                    "    REOPEN + \n" +
+                                    "  }"),
+                            fieldWithPath("note").description("Group NOte"),
+                            fieldWithPath("createdBy").description("Assigned Employee to Group"),
+                            fieldWithPath("createdOn").type("String").description("Date when group was created")
+                    )));
+  }
+
+  @Test
+  public void documentCloseGroup ( ) throws Exception {
+
+    final GroupDefinition randomGroupDefinition = GroupDefinitionGenerator.createRandomGroupDefinition();
+    this.testSubject.createGroupDefinition(randomGroupDefinition);
+    this.eventRecorder.wait(EventConstants.POST_GROUP_DEFINITION, randomGroupDefinition.getIdentifier());
+
+    final Group randomGroup = GroupGenerator.createRandomGroup(randomGroupDefinition.getIdentifier());
+    this.testSubject.createGroup(randomGroup);
+    this.eventRecorder.wait(EventConstants.POST_GROUP, randomGroup.getIdentifier());
+
+    final Group fetchedGroup = this.testSubject.findGroup(randomGroup.getIdentifier());
+    Assert.assertEquals(Group.Status.PENDING.name(), fetchedGroup.getStatus());
+
+    final GroupCommand activate = new GroupCommand();
+    activate.setAction(GroupCommand.Action.ACTIVATE.name());
+    activate.setNote("Activate Note" + RandomStringUtils.randomAlphanumeric(3));
+    activate.setCreatedBy(SuiteTestEnvironment.TEST_USER);
+    activate.setCreatedOn(ZonedDateTime.now(Clock.systemUTC()).format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
+
+    this.testSubject.processGroupCommand(randomGroup.getIdentifier(), activate);
+    this.eventRecorder.wait(EventConstants.ACTIVATE_GROUP, randomGroup.getIdentifier());
+
+    final GroupCommand close = new GroupCommand();
+    close.setAction(GroupCommand.Action.CLOSE.name());
+    close.setNote("Close Note" + RandomStringUtils.randomAlphanumeric(3));
+    close.setCreatedBy(SuiteTestEnvironment.TEST_USER);
+    close.setCreatedOn(ZonedDateTime.now(Clock.systemUTC()).format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
+
+    Gson serialize = new Gson();
+    this.mockMvc.perform(post("/groups/" + fetchedGroup.getIdentifier() + "/commands")
+            .accept(MediaType.APPLICATION_JSON_VALUE)
+            .contentType(MediaType.APPLICATION_JSON_VALUE)
+            .content(serialize.toJson(close)))
+            .andExpect(status().isAccepted())
+            .andDo(document("document-close-group", preprocessRequest(prettyPrint()),
+                    requestFields(
+                            fieldWithPath("action").description("Action " +
+                                    "" +
+                                    "*enum* _Action_ { + \n" +
+                                    "    ACTIVATE, + \n" +
+                                    "    CLOSE, + \n" +
+                                    "    REOPEN + \n" +
+                                    "  }"),
+                            fieldWithPath("note").description("Group NOte"),
+                            fieldWithPath("createdBy").description("Assigned Employee to Group"),
+                            fieldWithPath("createdOn").type("String").description("Date when group was created")
+                    )));
+  }
+
+  @Test
+  public void documentReopenGroup ( ) throws Exception {
+
+    final GroupDefinition randomGroupDefinition = GroupDefinitionGenerator.createRandomGroupDefinition();
+    this.testSubject.createGroupDefinition(randomGroupDefinition);
+    this.eventRecorder.wait(EventConstants.POST_GROUP_DEFINITION, randomGroupDefinition.getIdentifier());
+
+    final Group randomGroup = GroupGenerator.createRandomGroup(randomGroupDefinition.getIdentifier());
+    randomGroup.setGroupDefinitionIdentifier(randomGroupDefinition.getIdentifier());
+    this.testSubject.createGroup(randomGroup);
+    this.eventRecorder.wait(EventConstants.POST_GROUP, randomGroup.getIdentifier());
+
+    final Group fetchedGroup = this.testSubject.findGroup(randomGroup.getIdentifier());
+    Assert.assertEquals(Group.Status.PENDING.name(), fetchedGroup.getStatus());
+
+    final GroupCommand activate = new GroupCommand();
+    activate.setAction(GroupCommand.Action.ACTIVATE.name());
+    activate.setNote("Activate Note" + RandomStringUtils.randomAlphanumeric(3));
+    activate.setCreatedBy(SuiteTestEnvironment.TEST_USER);
+    activate.setCreatedOn(ZonedDateTime.now(Clock.systemUTC()).format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
+
+    this.testSubject.processGroupCommand(randomGroup.getIdentifier(), activate);
+    this.eventRecorder.wait(EventConstants.ACTIVATE_GROUP, randomGroup.getIdentifier());
+
+    final GroupCommand close = new GroupCommand();
+    close.setAction(GroupCommand.Action.CLOSE.name());
+    close.setNote("Close Note" + RandomStringUtils.randomAlphanumeric(3));
+    close.setCreatedBy(SuiteTestEnvironment.TEST_USER);
+    close.setCreatedOn(ZonedDateTime.now(Clock.systemUTC()).format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
+
+    this.testSubject.processGroupCommand(randomGroup.getIdentifier(), close);
+    this.eventRecorder.wait(EventConstants.CLOSE_GROUP, randomGroup.getIdentifier());
+
+    final GroupCommand reopen = new GroupCommand();
+    reopen.setAction(GroupCommand.Action.REOPEN.name());
+    reopen.setNote("Reopen Note" + RandomStringUtils.randomAlphanumeric(3));
+    reopen.setCreatedBy(SuiteTestEnvironment.TEST_USER);
+    reopen.setCreatedOn(ZonedDateTime.now(Clock.systemUTC()).format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
+
+    Gson serialize = new Gson();
+    this.mockMvc.perform(post("/groups/" + fetchedGroup.getIdentifier() + "/commands")
+            .accept(MediaType.APPLICATION_JSON_VALUE)
+            .contentType(MediaType.APPLICATION_JSON_VALUE)
+            .content(serialize.toJson(reopen)))
+            .andExpect(status().isAccepted())
+            .andDo(document("document-reopen-group", preprocessRequest(prettyPrint()),
+                    requestFields(
+                            fieldWithPath("action").description("Action " +
+                                    "" +
+                                    "*enum* _Action_ { + \n" +
+                                    "    ACTIVATE, + \n" +
+                                    "    CLOSE, + \n" +
+                                    "    REOPEN + \n" +
+                                    "  }"),
+                            fieldWithPath("note").description("Group NOte"),
+                            fieldWithPath("createdBy").description("Assigned Employee to Group"),
+                            fieldWithPath("createdOn").type("String").description("Date when group was created")
+                    )));
+  }
+
+  @Test
+  public void documentGetGroupCommands ( ) throws Exception {
+
+    final GroupDefinition randomGroupDefinition = GroupDefinitionGenerator.createRandomGroupDefinition();
+    this.testSubject.createGroupDefinition(randomGroupDefinition);
+    this.eventRecorder.wait(EventConstants.POST_GROUP_DEFINITION, randomGroupDefinition.getIdentifier());
+
+    final Group randomGroup = GroupGenerator.createRandomGroup(randomGroupDefinition.getIdentifier());
+    this.testSubject.createGroup(randomGroup);
+    this.eventRecorder.wait(EventConstants.POST_GROUP, randomGroup.getIdentifier());
+
+    final Group fetchedGroup = this.testSubject.findGroup(randomGroup.getIdentifier());
+    Assert.assertEquals(Group.Status.PENDING.name(), fetchedGroup.getStatus());
+
+    final GroupCommand activate = new GroupCommand();
+    activate.setAction(GroupCommand.Action.ACTIVATE.name());
+    activate.setNote("Activate Note " + RandomStringUtils.randomAlphanumeric(3));
+    activate.setCreatedBy(SuiteTestEnvironment.TEST_USER);
+    activate.setCreatedOn(ZonedDateTime.now(Clock.systemUTC()).format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
+
+    this.testSubject.processGroupCommand(randomGroup.getIdentifier(), activate);
+    this.eventRecorder.wait(EventConstants.ACTIVATE_GROUP, randomGroup.getIdentifier());
+
+    final Group activatedGroup = this.testSubject.findGroup(randomGroup.getIdentifier());
+    Assert.assertEquals(Group.Status.ACTIVE.name(), activatedGroup.getStatus());
+
+    this.mockMvc.perform(get("/groups/" + activatedGroup.getIdentifier() + "/commands")
+            .accept(MediaType.ALL_VALUE)
+            .contentType(MediaType.APPLICATION_JSON_VALUE))
+            .andExpect(status().isOk())
+            .andDo(document("document-get-group-commands", preprocessResponse(prettyPrint()),
+                    responseFields(
+                            fieldWithPath("[].action").description("Action " +
+                                    "" +
+                                    "*enum* _Action_ { + \n" +
+                                    "    ACTIVATE, + \n" +
+                                    "    CLOSE, + \n" +
+                                    "    REOPEN + \n" +
+                                    "  }"),
+                            fieldWithPath("[].note").description("Group NOte"),
+                            fieldWithPath("[].createdBy").description("Assigned Employee to Group"),
+                            fieldWithPath("[].createdOn").type("String").description("Date when group was created")
+                    )));
+  }
+
+  @Test
+  public void documentUpdateLeaders ( ) throws Exception {
+
+    final GroupDefinition randomGroupDefinition = GroupDefinitionGenerator.createRandomGroupDefinition();
+    this.testSubject.createGroupDefinition(randomGroupDefinition);
+    this.eventRecorder.wait(EventConstants.POST_GROUP_DEFINITION, randomGroupDefinition.getIdentifier());
+
+    final Group randomGroup = GroupGenerator.createRandomGroup(randomGroupDefinition.getIdentifier());
+    this.testSubject.createGroup(randomGroup);
+    this.eventRecorder.wait(EventConstants.POST_GROUP, randomGroup.getIdentifier());
+
+    randomGroup.getLeaders().add(RandomStringUtils.randomAlphanumeric(5));
+
+    Gson gson = new Gson();
+    this.mockMvc.perform(put("/groups/" + randomGroup.getIdentifier() + "/leaders")
+            .accept(MediaType.ALL_VALUE)
+            .contentType(MediaType.APPLICATION_JSON_VALUE)
+            .content(gson.toJson(randomGroup.getLeaders())))
+            .andExpect(status().isAccepted())
+            .andDo(document("document-update-leaders"));
+  }
+
+  @Test
+  public void documentUpdateMembers ( ) throws Exception {
+
+    final GroupDefinition randomGroupDefinition = GroupDefinitionGenerator.createRandomGroupDefinition();
+    this.testSubject.createGroupDefinition(randomGroupDefinition);
+    this.eventRecorder.wait(EventConstants.POST_GROUP_DEFINITION, randomGroupDefinition.getIdentifier());
+
+    final Group randomGroup = GroupGenerator.createRandomGroup(randomGroupDefinition.getIdentifier());
+    this.testSubject.createGroup(randomGroup);
+    this.eventRecorder.wait(EventConstants.POST_GROUP, randomGroup.getIdentifier());
+
+    randomGroup.getMembers().addAll(Arrays.asList(
+            "Member" + RandomStringUtils.randomAlphanumeric(3),
+            "Member" + RandomStringUtils.randomAlphanumeric(3)
+    ));
+
+    Gson gson = new Gson();
+    this.mockMvc.perform(put("/groups/" + randomGroup.getIdentifier() + "/members")
+            .accept(MediaType.ALL_VALUE)
+            .contentType(MediaType.APPLICATION_JSON_VALUE)
+            .content(gson.toJson(randomGroup.getMembers())))
+            .andExpect(status().isAccepted())
+            .andDo(document("document-update-members"));
+  }
+
+  @Test
+  public void documentUpdateAssignedEmployee ( ) throws Exception {
+
+    final GroupDefinition randomGroupDefinition = GroupDefinitionGenerator.createRandomGroupDefinition();
+    this.testSubject.createGroupDefinition(randomGroupDefinition);
+    this.eventRecorder.wait(EventConstants.POST_GROUP_DEFINITION, randomGroupDefinition.getIdentifier());
+
+    final Group randomGroup = GroupGenerator.createRandomGroup(randomGroupDefinition.getIdentifier());
+    this.testSubject.createGroup(randomGroup);
+    this.eventRecorder.wait(EventConstants.POST_GROUP, randomGroup.getIdentifier());
+
+    final AssignedEmployeeHolder anotherEmployee = new AssignedEmployeeHolder();
+    anotherEmployee.setIdentifier("Emply" + RandomStringUtils.randomAlphanumeric(3));
+
+    Gson gson = new Gson();
+    this.mockMvc.perform(put("/groups/" + randomGroup.getIdentifier() + "/employee")
+            .accept(MediaType.ALL_VALUE)
+            .contentType(MediaType.APPLICATION_JSON_VALUE)
+            .content(gson.toJson(anotherEmployee)))
+            .andExpect(status().isAccepted())
+            .andDo(document("document-update-assigned-employee", preprocessRequest(prettyPrint()),
+                    requestFields(
+                            fieldWithPath("identifier").description("Assigned employee identifier")
+                    )));
+  }
+
+  @Test
+  public void documentFetchMeetings ( ) throws Exception {
+
+    final GroupDefinition randomGroupDefinition = GroupDefinitionGenerator.createRandomGroupDefinition();
+    this.testSubject.createGroupDefinition(randomGroupDefinition);
+    this.eventRecorder.wait(EventConstants.POST_GROUP_DEFINITION, randomGroupDefinition.getIdentifier());
+
+    final Group randomGroup = GroupGenerator.createRandomGroup(randomGroupDefinition.getIdentifier());
+    this.testSubject.createGroup(randomGroup);
+    this.eventRecorder.wait(EventConstants.POST_GROUP, randomGroup.getIdentifier());
+
+    final Group fetchedGroup = this.testSubject.findGroup(randomGroup.getIdentifier());
+    Assert.assertEquals(Group.Status.PENDING.name(), fetchedGroup.getStatus());
+
+    final GroupCommand activate = new GroupCommand();
+    activate.setAction(GroupCommand.Action.ACTIVATE.name());
+    activate.setNote(RandomStringUtils.randomAlphanumeric(256));
+    activate.setCreatedBy(SuiteTestEnvironment.TEST_USER);
+    activate.setCreatedOn(ZonedDateTime.now(Clock.systemUTC()).format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
+
+    this.testSubject.processGroupCommand(randomGroup.getIdentifier(), activate);
+    this.eventRecorder.wait(EventConstants.ACTIVATE_GROUP, randomGroup.getIdentifier());
+
+    final Group activatedGroup = this.testSubject.findGroup(randomGroup.getIdentifier());
+    Assert.assertEquals(Group.Status.ACTIVE.name(), activatedGroup.getStatus());
+
+    this.mockMvc.perform(get("/groups/" + activatedGroup.getIdentifier() + "/meetings")
+            .param("upcoming", Boolean.FALSE.toString())
+            .accept(MediaType.ALL_VALUE)
+            .contentType(MediaType.APPLICATION_JSON_VALUE))
+            .andExpect(status().isOk())
+            .andDo(document("document-fetch-meetings"));
+  }
+
+  @Test
+  public void documentCloseMeeting ( ) throws Exception {
+
+    final GroupDefinition randomGroupDefinition = GroupDefinitionGenerator.createRandomGroupDefinition();
+    this.testSubject.createGroupDefinition(randomGroupDefinition);
+    this.eventRecorder.wait(EventConstants.POST_GROUP_DEFINITION, randomGroupDefinition.getIdentifier());
+
+    final Group randomGroup = GroupGenerator.createRandomGroup(randomGroupDefinition.getIdentifier());
+    this.testSubject.createGroup(randomGroup);
+    this.eventRecorder.wait(EventConstants.POST_GROUP, randomGroup.getIdentifier());
+
+    final Group fetchedGroup = this.testSubject.findGroup(randomGroup.getIdentifier());
+    Assert.assertEquals(Group.Status.PENDING.name(), fetchedGroup.getStatus());
+
+    final GroupCommand activate = new GroupCommand();
+    activate.setAction(GroupCommand.Action.ACTIVATE.name());
+    activate.setNote(RandomStringUtils.randomAlphanumeric(256));
+    activate.setCreatedBy(SuiteTestEnvironment.TEST_USER);
+    activate.setCreatedOn(ZonedDateTime.now(Clock.systemUTC()).format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
+
+    this.testSubject.processGroupCommand(randomGroup.getIdentifier(), activate);
+    this.eventRecorder.wait(EventConstants.ACTIVATE_GROUP, randomGroup.getIdentifier());
+
+    final Group activatedGroup = this.testSubject.findGroup(randomGroup.getIdentifier());
+    Assert.assertEquals(Group.Status.ACTIVE.name(), activatedGroup.getStatus());
+
+    final List <GroupCommand> groupCommands = this.testSubject.fetchGroupCommands(activatedGroup.getIdentifier());
+    Assert.assertTrue(groupCommands.size() == 1);
+    final GroupCommand groupCommand = groupCommands.get(0);
+    Assert.assertEquals(activate.getAction(), groupCommand.getAction());
+    Assert.assertEquals(activate.getNote(), groupCommand.getNote());
+    Assert.assertEquals(activate.getCreatedBy(), groupCommand.getCreatedBy());
+    Assert.assertNotNull(groupCommand.getCreatedOn());
+
+    final List <Meeting> meetings = this.testSubject.fetchMeetings(activatedGroup.getIdentifier(), Boolean.FALSE);
+    Assert.assertNotNull(meetings);
+    Assert.assertEquals(randomGroupDefinition.getCycle().getNumberOfMeetings(), Integer.valueOf(meetings.size()));
+
+    final Meeting meeting2signOff = meetings.get(0);
+    final SignOffMeeting signOffMeeting = new SignOffMeeting();
+    signOffMeeting.setCycle(meeting2signOff.getCurrentCycle());
+    signOffMeeting.setSequence(meeting2signOff.getMeetingSequence());
+    signOffMeeting.setDuration(120L);
+    signOffMeeting.setAttendees(meeting2signOff.getAttendees()
+            .stream()
+            .map(attendee -> {
+              attendee.setStatus(Attendee.Status.ATTENDED.name());
+              return attendee;
+            })
+            .collect(Collectors.toSet())
+    );
+
+    Gson gson = new Gson();
+    this.mockMvc.perform(put("/groups/" + randomGroup.getIdentifier() + "/meetings")
+            .accept(MediaType.ALL_VALUE)
+            .contentType(MediaType.APPLICATION_JSON_VALUE)
+            .content(gson.toJson(signOffMeeting)))
+            .andExpect(status().isAccepted())
+            .andDo(document("document-close-meeting", preprocessRequest(prettyPrint()),
+                    requestFields(
+                            fieldWithPath("cycle").type("Integer").description("Meetings in cycle"),
+                            fieldWithPath("sequence").type("Integer").description("Meeting sequence"),
+                            fieldWithPath("attendees").type("Set<String>").description("Set of attendees"),
+                            fieldWithPath("duration").type("Long").description("Duration of meeting")
+                    )));
+  }
+
+  @Test
+  public void documentCreateGroupDefinition ( ) throws Exception {
+
+    final GroupDefinition randomGroupDefinition = GroupDefinitionGenerator.createRandomGroupDefinition();
+
+    Gson serialize = new Gson();
+    this.mockMvc.perform(post("/definitions")
+            .accept(MediaType.APPLICATION_JSON_VALUE)
+            .contentType(MediaType.APPLICATION_JSON_VALUE)
+            .content(serialize.toJson(randomGroupDefinition)))
+            .andExpect(status().isAccepted())
+            .andDo(document("document-create-group-definition", preprocessRequest(prettyPrint()),
+                    requestFields(
+                            fieldWithPath("identifier").description("Group definition Identifier"),
+                            fieldWithPath("description").description("Group definition description"),
+                            fieldWithPath("minimalSize").type("Integer").description("Group's minimum size"),
+                            fieldWithPath("maximalSize").type("Integer").description("Group's maximum size"),
+                            fieldWithPath("cycle").type("Cycle").description("Group definition's cycle")
+                    )));
+  }
+
+  @Test
+  public void documentUpdateGroupDefinition ( ) throws Exception {
+
+    final GroupDefinition randomGroupDefinition = GroupDefinitionGenerator.createRandomGroupDefinition();
+    this.testSubject.createGroupDefinition(randomGroupDefinition);
+    this.eventRecorder.wait(EventConstants.POST_GROUP_DEFINITION, randomGroupDefinition.getIdentifier());
+
+    Gson serialize = new Gson();
+    this.mockMvc.perform(put("/definitions/" + randomGroupDefinition.getIdentifier())
+            .accept(MediaType.APPLICATION_JSON_VALUE)
+            .contentType(MediaType.APPLICATION_JSON_VALUE)
+            .content(serialize.toJson(randomGroupDefinition)))
+            .andExpect(status().isAccepted())
+            .andDo(document("document-update-group-definition", preprocessRequest(prettyPrint()),
+                    requestFields(
+                            fieldWithPath("identifier").type("String").description("Group definition Identifier"),
+                            fieldWithPath("description").type("String").description("Group definition description"),
+                            fieldWithPath("minimalSize").type("Integer").description("Group's minimum size"),
+                            fieldWithPath("maximalSize").type("Integer").description("Group's maximum size"),
+                            fieldWithPath("cycle").type("Cycle").description("Cycle of Meetings " +
+                                    " \n + " +
+                                    " *class* _Cycle_ { + \n" +
+                                    "    private Integer numberOfMeetings; + \n" +
+                                    "    private Frequency frequency; + \n" +
+                                    "    private Adjustment adjustment; + \n" +
+                                    " } \n +")
+                    )));
+  }
+}
diff --git a/component-test/src/main/java/org/apache/fineract/cn/group/SuiteTestEnvironment.java b/component-test/src/main/java/org/apache/fineract/cn/group/SuiteTestEnvironment.java
new file mode 100644
index 0000000..8cd3a8e
--- /dev/null
+++ b/component-test/src/main/java/org/apache/fineract/cn/group/SuiteTestEnvironment.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.cn.group;
+
+import org.apache.fineract.cn.test.env.TestEnvironment;
+import org.apache.fineract.cn.test.fixture.TenantDataStoreContextTestRule;
+import org.apache.fineract.cn.test.fixture.cassandra.CassandraInitializer;
+import org.apache.fineract.cn.test.fixture.mariadb.MariaDBInitializer;
+import org.junit.ClassRule;
+import org.junit.rules.RuleChain;
+import org.junit.rules.TestRule;
+
+public class SuiteTestEnvironment {
+    static final String APP_NAME = "group-v1";
+    static final String TEST_USER = "ranefer";
+
+    static final TestEnvironment testEnvironment = new TestEnvironment(APP_NAME);
+    static final CassandraInitializer cassandraInitializer = new CassandraInitializer();
+    static final MariaDBInitializer mariaDBInitializer = new MariaDBInitializer();
+    static final TenantDataStoreContextTestRule tenantDataStoreContext =
+            TenantDataStoreContextTestRule.forRandomTenantName(cassandraInitializer, mariaDBInitializer);
+
+    @ClassRule
+    public static TestRule orderClassRules = RuleChain
+            .outerRule(testEnvironment)
+            .around(cassandraInitializer)
+            .around(mariaDBInitializer)
+            .around(tenantDataStoreContext);
+}
\ No newline at end of file
diff --git a/component-test/src/main/java/org/apache/fineract/cn/group/TestGroup.java b/component-test/src/main/java/org/apache/fineract/cn/group/TestGroup.java
index 1b485f9..a78017a 100644
--- a/component-test/src/main/java/org/apache/fineract/cn/group/TestGroup.java
+++ b/component-test/src/main/java/org/apache/fineract/cn/group/TestGroup.java
@@ -29,12 +29,14 @@ import org.apache.fineract.cn.group.api.v1.domain.Meeting;
 import org.apache.fineract.cn.group.api.v1.domain.SignOffMeeting;
 import org.apache.fineract.cn.group.util.GroupDefinitionGenerator;
 import org.apache.fineract.cn.group.util.GroupGenerator;
+
 import java.time.Clock;
 import java.time.ZonedDateTime;
 import java.time.format.DateTimeFormatter;
 import java.util.Arrays;
 import java.util.List;
 import java.util.stream.Collectors;
+
 import org.apache.commons.lang3.RandomStringUtils;
 import org.apache.fineract.cn.anubis.test.v1.TenantApplicationSecurityEnvironmentTestRule;
 import org.apache.fineract.cn.api.context.AutoUserContext;
@@ -65,58 +67,10 @@ import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Import;
 import org.springframework.test.context.junit4.SpringRunner;
 
-@RunWith(SpringRunner.class)
-@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
-public class TestGroup {
-  private static final String APP_NAME = "group-v1";
-  private static final String TEST_USER = "ranefer";
-
-  private final static TestEnvironment testEnvironment = new TestEnvironment(APP_NAME);
-  private final static CassandraInitializer cassandraInitializer = new CassandraInitializer();
-  private final static MariaDBInitializer mariaDBInitializer = new MariaDBInitializer();
-  private final static TenantDataStoreContextTestRule tenantDataStoreContext = TenantDataStoreContextTestRule.forRandomTenantName(cassandraInitializer, mariaDBInitializer);
-
-  @ClassRule
-  public static TestRule orderClassRules = RuleChain
-          .outerRule(testEnvironment)
-          .around(cassandraInitializer)
-          .around(mariaDBInitializer)
-          .around(tenantDataStoreContext);
-
-  @Rule
-  public final TenantApplicationSecurityEnvironmentTestRule tenantApplicationSecurityEnvironment
-          = new TenantApplicationSecurityEnvironmentTestRule(testEnvironment, this::waitForInitialize);
-  @Autowired
-  private GroupManager testSubject;
-  @Autowired
-  private EventRecorder eventRecorder;
-
-  private AutoUserContext userContext;
-
-  public TestGroup() {
-    super();
-  }
-
-  @Before
-  public void prepTest() {
-    userContext = this.tenantApplicationSecurityEnvironment.createAutoUserContext(TestGroup.TEST_USER);
-  }
-
-  @After
-  public void cleanTest() {
-    userContext.close();
-  }
-
-  public boolean waitForInitialize() {
-    try {
-      return this.eventRecorder.wait(EventConstants.INITIALIZE, EventConstants.INITIALIZE);
-    } catch (final InterruptedException e) {
-      throw new IllegalStateException(e);
-    }
-  }
+public class TestGroup extends AbstractGroupTest {
 
   @Test
-  public void shouldCreateGroup() throws Exception {
+  public void shouldCreateGroup ( ) throws Exception {
     final GroupDefinition randomGroupDefinition = GroupDefinitionGenerator.createRandomGroupDefinition();
 
     this.testSubject.createGroupDefinition(randomGroupDefinition);
@@ -147,7 +101,7 @@ public class TestGroup {
   }
 
   @Test
-  public void shouldActivateCommand() throws Exception {
+  public void shouldActivateCommand ( ) throws Exception {
     final GroupDefinition randomGroupDefinition = GroupDefinitionGenerator.createRandomGroupDefinition();
     this.testSubject.createGroupDefinition(randomGroupDefinition);
     this.eventRecorder.wait(EventConstants.POST_GROUP_DEFINITION, randomGroupDefinition.getIdentifier());
@@ -171,7 +125,7 @@ public class TestGroup {
     final Group activatedGroup = this.testSubject.findGroup(randomGroup.getIdentifier());
     Assert.assertEquals(Group.Status.ACTIVE.name(), activatedGroup.getStatus());
 
-    final List<GroupCommand> groupCommands = this.testSubject.fetchGroupCommands(activatedGroup.getIdentifier());
+    final List <GroupCommand> groupCommands = this.testSubject.fetchGroupCommands(activatedGroup.getIdentifier());
     Assert.assertTrue(groupCommands.size() == 1);
     final GroupCommand groupCommand = groupCommands.get(0);
     Assert.assertEquals(activate.getAction(), groupCommand.getAction());
@@ -179,7 +133,7 @@ public class TestGroup {
     Assert.assertEquals(activate.getCreatedBy(), groupCommand.getCreatedBy());
     Assert.assertNotNull(groupCommand.getCreatedOn());
 
-    final List<Meeting> meetings = this.testSubject.fetchMeetings(activatedGroup.getIdentifier(), Boolean.FALSE);
+    final List <Meeting> meetings = this.testSubject.fetchMeetings(activatedGroup.getIdentifier(), Boolean.FALSE);
     Assert.assertNotNull(meetings);
     Assert.assertEquals(randomGroupDefinition.getCycle().getNumberOfMeetings(), Integer.valueOf(meetings.size()));
 
@@ -189,12 +143,12 @@ public class TestGroup {
     signOffMeeting.setSequence(meeting2signOff.getMeetingSequence());
     signOffMeeting.setDuration(120L);
     signOffMeeting.setAttendees(meeting2signOff.getAttendees()
-        .stream()
-        .map(attendee -> {
-          attendee.setStatus(Attendee.Status.ATTENDED.name());
-          return attendee;
-        })
-        .collect(Collectors.toSet())
+            .stream()
+            .map(attendee -> {
+              attendee.setStatus(Attendee.Status.ATTENDED.name());
+              return attendee;
+            })
+            .collect(Collectors.toSet())
     );
 
     this.testSubject.closeMeeting(activatedGroup.getIdentifier(), signOffMeeting);
@@ -202,7 +156,7 @@ public class TestGroup {
   }
 
   @Test
-  public void shouldUpdateLeaders() throws Exception {
+  public void shouldUpdateLeaders ( ) throws Exception {
     final GroupDefinition randomGroupDefinition = GroupDefinitionGenerator.createRandomGroupDefinition();
     this.testSubject.createGroupDefinition(randomGroupDefinition);
     this.eventRecorder.wait(EventConstants.POST_GROUP_DEFINITION, randomGroupDefinition.getIdentifier());
@@ -221,7 +175,7 @@ public class TestGroup {
   }
 
   @Test
-  public void shouldUpdateMembers() throws Exception {
+  public void shouldUpdateMembers ( ) throws Exception {
     final GroupDefinition randomGroupDefinition = GroupDefinitionGenerator.createRandomGroupDefinition();
     this.testSubject.createGroupDefinition(randomGroupDefinition);
     this.eventRecorder.wait(EventConstants.POST_GROUP_DEFINITION, randomGroupDefinition.getIdentifier());
@@ -232,8 +186,8 @@ public class TestGroup {
 
     final int currentMembersSize = randomGroup.getMembers().size();
     randomGroup.getMembers().addAll(Arrays.asList(
-        RandomStringUtils.randomAlphanumeric(32),
-        RandomStringUtils.randomAlphanumeric(32)
+            RandomStringUtils.randomAlphanumeric(32),
+            RandomStringUtils.randomAlphanumeric(32)
     ));
     this.testSubject.updateMembers(randomGroup.getIdentifier(), randomGroup.getMembers());
     this.eventRecorder.wait(EventConstants.PUT_GROUP, randomGroup.getIdentifier());
@@ -243,7 +197,7 @@ public class TestGroup {
   }
 
   @Test
-  public void shouldUpdateAssignedEmployee() throws Exception {
+  public void shouldUpdateAssignedEmployee ( ) throws Exception {
     final GroupDefinition randomGroupDefinition = GroupDefinitionGenerator.createRandomGroupDefinition();
     this.testSubject.createGroupDefinition(randomGroupDefinition);
     this.eventRecorder.wait(EventConstants.POST_GROUP_DEFINITION, randomGroupDefinition.getIdentifier());
@@ -263,7 +217,7 @@ public class TestGroup {
   }
 
   @Test
-  public void shouldUpdateGroup() throws Exception{
+  public void shouldUpdateGroup ( ) throws Exception {
     final GroupDefinition randomGroupDefinition = GroupDefinitionGenerator.createRandomGroupDefinition();
     this.testSubject.createGroupDefinition(randomGroupDefinition);
     this.eventRecorder.wait(EventConstants.POST_GROUP_DEFINITION, randomGroupDefinition.getIdentifier());
@@ -277,28 +231,9 @@ public class TestGroup {
 
     this.testSubject.updateGroup(randomGroup.getIdentifier(), randomGroup);
 
-    this.eventRecorder.wait(EventConstants.PUT_GROUP,randomGroup.getIdentifier());
+    this.eventRecorder.wait(EventConstants.PUT_GROUP, randomGroup.getIdentifier());
 
     final Group updatedGroup = this.testSubject.findGroup(randomGroup.getIdentifier());
     Assert.assertEquals(randomGroup.getName(), updatedGroup.getName());
-
-
-  }
-
-  @Configuration
-  @EnableEventRecording
-  @EnableFeignClients(basePackages = {"org.apache.fineract.cn.group.api.v1.client"})
-  @RibbonClient(name = APP_NAME)
-  @Import({GroupConfiguration.class})
-  @ComponentScan("org.apache.fineract.cn.group.listener")
-  public static class TestConfiguration {
-    public TestConfiguration() {
-      super();
-    }
-
-    @Bean()
-    public Logger logger() {
-      return LoggerFactory.getLogger("test-logger");
-    }
   }
 }
diff --git a/component-test/src/main/java/org/apache/fineract/cn/group/TestGroupDefinition.java b/component-test/src/main/java/org/apache/fineract/cn/group/TestGroupDefinition.java
index e7a75f0..3a4b652 100644
--- a/component-test/src/main/java/org/apache/fineract/cn/group/TestGroupDefinition.java
+++ b/component-test/src/main/java/org/apache/fineract/cn/group/TestGroupDefinition.java
@@ -52,61 +52,10 @@ import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Import;
 import org.springframework.test.context.junit4.SpringRunner;
 
-@RunWith(SpringRunner.class)
-@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
-
-public class TestGroupDefinition {
-  private static final String APP_NAME = "group-v1";
-  private static final String TEST_USER = "ranefer";
-
-  private final static TestEnvironment testEnvironment = new TestEnvironment(APP_NAME);
-  private final static CassandraInitializer cassandraInitializer = new CassandraInitializer();
-  private final static MariaDBInitializer mariaDBInitializer = new MariaDBInitializer();
-  private final static TenantDataStoreContextTestRule tenantDataStoreContext = TenantDataStoreContextTestRule.forRandomTenantName(cassandraInitializer, mariaDBInitializer);
-
-  @ClassRule
-  public static TestRule orderClassRules = RuleChain
-          .outerRule(testEnvironment)
-          .around(cassandraInitializer)
-          .around(mariaDBInitializer)
-          .around(tenantDataStoreContext);
-
-  @Rule
-  public final TenantApplicationSecurityEnvironmentTestRule tenantApplicationSecurityEnvironment
-          = new TenantApplicationSecurityEnvironmentTestRule(testEnvironment, this::waitForInitialize);
-
-  @Autowired
-  private GroupManager testSubject;
-  @Autowired
-  private EventRecorder eventRecorder;
-
-  private AutoUserContext userContext;
-
-  public TestGroupDefinition() {
-    super();
-  }
-
-  @Before
-  public void prepTest() {
-    userContext = this.tenantApplicationSecurityEnvironment.createAutoUserContext(TestGroupDefinition.TEST_USER);
-  }
-
-  @After
-  public void cleanTest() {
-   //TenantContextHolder.clear();
-    userContext.close();
-  }
-
-  public boolean waitForInitialize() {
-    try {
-      return this.eventRecorder.wait(EventConstants.INITIALIZE, EventConstants.INITIALIZE);
-    } catch (final InterruptedException e) {
-      throw new IllegalStateException(e);
-    }
-  }
+public class TestGroupDefinition extends AbstractGroupTest {
 
   @Test
-  public void shouldCreateGroupDefinition() throws Exception {
+  public void shouldCreateGroupDefinition ( ) throws Exception {
     final GroupDefinition randomGroupDefinition = GroupDefinitionGenerator.createRandomGroupDefinition();
     this.testSubject.createGroupDefinition(randomGroupDefinition);
 
@@ -128,9 +77,8 @@ public class TestGroupDefinition {
     Assert.assertNull(fetchedGroupDefinition.getLastModifiedOn());
   }
 
-
   @Test
-  public void shouldUpdateGroupDefinition() throws Exception{
+  public void shouldUpdateGroupDefinition ( ) throws Exception {
     final GroupDefinition randomGroupDefinition = GroupDefinitionGenerator.createRandomGroupDefinition();
     this.testSubject.createGroupDefinition(randomGroupDefinition);
 
@@ -139,7 +87,7 @@ public class TestGroupDefinition {
     final GroupDefinition updatedGroupDefinition = GroupDefinitionGenerator.createRandomGroupDefinition();
     updatedGroupDefinition.setIdentifier(randomGroupDefinition.getIdentifier());
 
-    this.testSubject.updateGroupDefinition(updatedGroupDefinition.getIdentifier(),updatedGroupDefinition);
+    this.testSubject.updateGroupDefinition(updatedGroupDefinition.getIdentifier(), updatedGroupDefinition);
 
     this.eventRecorder.wait(EventConstants.PUT_GROUP_DEFINITION, randomGroupDefinition.getIdentifier());
 
@@ -153,23 +101,4 @@ public class TestGroupDefinition {
     Assert.assertEquals(updatedGroupDefinition.getCycle().getNumberOfMeetings(), fetchedGroupDefinition.getCycle().getNumberOfMeetings());
     Assert.assertEquals(updatedGroupDefinition.getCycle().getFrequency(), fetchedGroupDefinition.getCycle().getFrequency());
   }
-
-
-
-  @Configuration
-  @EnableEventRecording
-  @EnableFeignClients(basePackages = {"org.apache.fineract.cn.group.api.v1.client"})
-  @RibbonClient(name = APP_NAME)
-  @Import({GroupConfiguration.class})
-  @ComponentScan("org.apache.fineract.cn.group.listener")
-  public static class TestConfiguration {
-    public TestConfiguration() {
-      super();
-    }
-
-    @Bean()
-    public Logger logger() {
-      return LoggerFactory.getLogger("test-logger");
-    }
-  }
 }
diff --git a/component-test/src/main/java/org/apache/fineract/cn/group/util/GroupDefinitionGenerator.java b/component-test/src/main/java/org/apache/fineract/cn/group/util/GroupDefinitionGenerator.java
index 1ecebe8..b66a9df 100644
--- a/component-test/src/main/java/org/apache/fineract/cn/group/util/GroupDefinitionGenerator.java
+++ b/component-test/src/main/java/org/apache/fineract/cn/group/util/GroupDefinitionGenerator.java
@@ -30,8 +30,8 @@ public class GroupDefinitionGenerator {
 
   public static GroupDefinition createRandomGroupDefinition() {
     final GroupDefinition groupDefinition = new GroupDefinition();
-    groupDefinition.setIdentifier(RandomStringUtils.randomAlphanumeric(32));
-    groupDefinition.setDescription(RandomStringUtils.randomAlphabetic(2048));
+    groupDefinition.setIdentifier("grpDef" + RandomStringUtils.randomAlphanumeric(3));
+    groupDefinition.setDescription("Group Def Descr " + RandomStringUtils.randomAlphabetic(5));
     groupDefinition.setMinimalSize(10);
     groupDefinition.setMaximalSize(30);
     final Cycle cycle = new Cycle();
diff --git a/component-test/src/main/java/org/apache/fineract/cn/group/util/GroupGenerator.java b/component-test/src/main/java/org/apache/fineract/cn/group/util/GroupGenerator.java
index 4d63436..ed05cd1 100644
--- a/component-test/src/main/java/org/apache/fineract/cn/group/util/GroupGenerator.java
+++ b/component-test/src/main/java/org/apache/fineract/cn/group/util/GroupGenerator.java
@@ -33,30 +33,26 @@ public class GroupGenerator {
 
   public static Group createRandomGroup(final String definitionIdentifier) {
     final Group group = new Group();
-    group.setIdentifier(RandomStringUtils.randomAlphanumeric(32));
+    group.setIdentifier("grp" + RandomStringUtils.randomAlphanumeric(3));
     group.setGroupDefinitionIdentifier(definitionIdentifier);
-    group.setName(RandomStringUtils.randomAlphanumeric(256));
-    group.setOffice(RandomStringUtils.randomAlphanumeric(32));
-    group.setAssignedEmployee(RandomStringUtils.randomAlphanumeric(32));
-    group.setLeaders(new HashSet<>(Arrays.asList(
-        RandomStringUtils.randomAlphanumeric(32), RandomStringUtils.randomAlphanumeric(32)
+    group.setName("groupName" + RandomStringUtils.randomAlphanumeric(2));
+    group.setOffice("office" + RandomStringUtils.randomAlphanumeric(4));
+    group.setAssignedEmployee("employee" + RandomStringUtils.randomAlphanumeric(3));
+    group.setLeaders(new HashSet<>(Arrays.asList("Leader" +
+            RandomStringUtils.randomAlphanumeric(3), "Leader" + RandomStringUtils.randomAlphanumeric(3)
     )));
     group.setMembers(new HashSet<>(Arrays.asList(
-        RandomStringUtils.randomAlphanumeric(32), RandomStringUtils.randomAlphanumeric(32),
-        RandomStringUtils.randomAlphanumeric(32), RandomStringUtils.randomAlphanumeric(32),
-        RandomStringUtils.randomAlphanumeric(32), RandomStringUtils.randomAlphanumeric(32),
-        RandomStringUtils.randomAlphanumeric(32), RandomStringUtils.randomAlphanumeric(32),
-        RandomStringUtils.randomAlphanumeric(32), RandomStringUtils.randomAlphanumeric(32)
+            "Member" + RandomStringUtils.randomAlphanumeric(3), "Member" + RandomStringUtils.randomAlphanumeric(3)
     )));
     group.setWeekday(Group.Weekday.WEDNESDAY.getValue());
     final Address address = new Address();
-    address.setStreet(RandomStringUtils.randomAlphanumeric(256));
-    address.setCity(RandomStringUtils.randomAlphanumeric(256));
-    address.setRegion(RandomStringUtils.randomAlphanumeric(256));
-    address.setPostalCode(RandomStringUtils.randomAlphanumeric(2));
-    address.setCountry(RandomStringUtils.randomAlphanumeric(256));
-    address.setCountryCode(RandomStringUtils.randomAlphanumeric(2));
+    address.setStreet("street" + RandomStringUtils.randomAlphanumeric(4));
+    address.setCity("city" + RandomStringUtils.randomAlphanumeric(5));
+    address.setRegion("region" + RandomStringUtils.randomAlphanumeric(6));
+    address.setPostalCode(RandomStringUtils.randomAlphanumeric(4));
+    address.setCountry("country" + RandomStringUtils.randomAlphanumeric(6));
+    address.setCountryCode(RandomStringUtils.randomAlphanumeric(2).toUpperCase());
     group.setAddress(address);
     return group;
   }
-}
+}
\ No newline at end of file