From d314eb43cc70d79bef5343248ae4cb0ff0d0cb09 Mon Sep 17 00:00:00 2001
From: Sonia Zorba <sonia.zorba@inaf.it>
Date: Tue, 13 Aug 2019 12:01:31 +0200
Subject: [PATCH] Created GMS CLI client application

---
 .gitignore                                    |   2 +
 gms-client/gms-cli/.gitignore                 |  31 +++
 gms-client/gms-cli/gms.properties             |   3 +
 gms-client/gms-cli/pom.xml                    |  48 +++++
 .../main/java/it/inaf/ia2/gms/cli/CLI.java    | 132 +++++++++++++
 .../inaf/ia2/gms/cli/GmsCliApplication.java   |  13 ++
 .../src/main/resources/application.properties |   2 +
 .../ia2/gms/cli/GmsCliApplicationTests.java   |  16 ++
 gms-client/gms-client-lib/pom.xml             |  17 ++
 .../it/inaf/ia2/gms/client/GmsClient.java     |  57 ++++--
 .../it/inaf/ia2/gms/client/GmsClientTest.java | 183 ++++++++++++++++++
 .../controller/WebServiceControllerTest.java  |  19 +-
 12 files changed, 499 insertions(+), 24 deletions(-)
 create mode 100644 gms-client/gms-cli/.gitignore
 create mode 100644 gms-client/gms-cli/gms.properties
 create mode 100644 gms-client/gms-cli/pom.xml
 create mode 100644 gms-client/gms-cli/src/main/java/it/inaf/ia2/gms/cli/CLI.java
 create mode 100644 gms-client/gms-cli/src/main/java/it/inaf/ia2/gms/cli/GmsCliApplication.java
 create mode 100644 gms-client/gms-cli/src/main/resources/application.properties
 create mode 100644 gms-client/gms-cli/src/test/java/it/inaf/ia2/gms/cli/GmsCliApplicationTests.java
 create mode 100644 gms-client/gms-client-lib/src/test/java/it/inaf/ia2/gms/client/GmsClientTest.java

diff --git a/.gitignore b/.gitignore
index 3f9862f..847b248 100644
--- a/.gitignore
+++ b/.gitignore
@@ -57,3 +57,5 @@ nbactions.xml
 /gms-ui/target/
 /gms/nbactions-release-profile.xml
 
+/gms-client/gms-client-lib/target/
+/gms-client/gms-cli/target/
diff --git a/gms-client/gms-cli/.gitignore b/gms-client/gms-cli/.gitignore
new file mode 100644
index 0000000..a2a3040
--- /dev/null
+++ b/gms-client/gms-cli/.gitignore
@@ -0,0 +1,31 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**
+!**/src/test/**
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+
+### VS Code ###
+.vscode/
diff --git a/gms-client/gms-cli/gms.properties b/gms-client/gms-cli/gms.properties
new file mode 100644
index 0000000..6ffdc4f
--- /dev/null
+++ b/gms-client/gms-cli/gms.properties
@@ -0,0 +1,3 @@
+base_url=http://localhost:8081
+client_id=test
+client_secret=test
diff --git a/gms-client/gms-cli/pom.xml b/gms-client/gms-cli/pom.xml
new file mode 100644
index 0000000..2a8a09e
--- /dev/null
+++ b/gms-client/gms-cli/pom.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <version>2.1.7.RELEASE</version>
+        <relativePath/> <!-- lookup parent from repository -->
+    </parent>
+    <groupId>it.inaf.ia2</groupId>
+    <artifactId>gms-cli</artifactId>
+    <version>0.0.1-SNAPSHOT</version>
+    <name>gms-cli</name>
+    <description>GMS Command Line Client</description>
+
+    <properties>
+        <java.version>1.8</java.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>gms-client-lib</artifactId>
+            <version>1.0-SNAPSHOT</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/gms-client/gms-cli/src/main/java/it/inaf/ia2/gms/cli/CLI.java b/gms-client/gms-cli/src/main/java/it/inaf/ia2/gms/cli/CLI.java
new file mode 100644
index 0000000..6794f49
--- /dev/null
+++ b/gms-client/gms-cli/src/main/java/it/inaf/ia2/gms/cli/CLI.java
@@ -0,0 +1,132 @@
+package it.inaf.ia2.gms.cli;
+
+import it.inaf.ia2.gms.client.GmsClient;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.stereotype.Component;
+
+@Component
+public class CLI implements CommandLineRunner {
+
+    private final GmsClient client;
+
+    public CLI() throws IOException {
+
+        File config = new File("gms.properties");
+        if (!config.exists()) {
+            System.err.println("Unable to find the file gms.properties");
+            System.exit(1);
+        }
+
+        Properties properties = new Properties();
+        try (InputStream in = new FileInputStream(config)) {
+            properties.load(in);
+        }
+
+        String baseUrl = (String) properties.get("base_url");
+        if (baseUrl == null) {
+            System.err.println("Missing base_url in gms.properties");
+            System.exit(1);
+        }
+
+        String clientId = (String) properties.get("client_id");
+        if (clientId == null) {
+            System.err.println("Missing client_id in gms.properties");
+            System.exit(1);
+        }
+
+        String clientSecret = (String) properties.get("client_secret");
+        if (clientSecret == null) {
+            System.err.println("Missing client_secret in gms.properties");
+            System.exit(1);
+        }
+
+        client = new GmsClient(baseUrl, clientId, clientSecret);
+    }
+
+    @Override
+    public void run(String... args) throws Exception {
+        if (args.length < 2) {
+            displayUsage();
+        }
+
+        switch (args[0]) {
+            case "create-group":
+                client.createGroup(getNames(args, 1));
+                System.out.println("Group created");
+                break;
+            case "delete-group":
+                client.deleteGroup(getNames(args, 1));
+                System.out.println("Group deleted");
+                break;
+            case "add-member":
+                if (args.length < 3) {
+                    displayUsage();
+                }
+                client.addMember(getNames(args, args.length - 2), args[args.length - 1]);
+                System.out.println("Member added");
+                break;
+            case "remove-member":
+                if (args.length < 3) {
+                    displayUsage();
+                }
+                client.removeMember(getNames(args, args.length - 2), args[args.length - 1]);
+                System.out.println("Member removed");
+                break;
+            case "add-permission":
+                if (args.length < 4) {
+                    displayUsage();
+                }
+                client.addPermission(getNames(args, args.length - 3), args[args.length - 2], args[args.length - 1]);
+                System.out.println("Permission added");
+                break;
+            case "delete-permission":
+                if (args.length < 4) {
+                    displayUsage();
+                }
+                client.deletePermission(getNames(args, args.length - 3), args[args.length - 2], args[args.length - 1]);
+                System.out.println("Permission added");
+                break;
+            case "prepare-join":
+                if (args.length != 3) {
+                    displayUsage();
+                }
+                client.prepareToJoin(args[1], args[2]);
+                System.out.println("Join prepared");
+                break;
+            default:
+                displayUsage();
+                break;
+        }
+    }
+
+    private void displayUsage() {
+        System.out.println("java -jar gms-client.jar\n"
+                + "    create-group <name1 name2 name3>\n"
+                + "    delete-group <name1 name2 name3>\n"
+                + "    add-member <name1 name2 name3> <user_id>\n"
+                + "    remove-member <name1 name2 name3> <user_id>\n"
+                + "    add-permission <name1 name2 name3> <user_id> <permission>\n"
+                + "    delete-permission <name1 name2 name3> <user_id> <permission>\n"
+                + "    prepare-join <from_user_id> <to_user_id>");
+        System.exit(0);
+    }
+
+    private List<String> getNames(String[] args, int startIndex) {
+        return getNames(args, startIndex, args.length - 1);
+    }
+
+    private List<String> getNames(String[] args, int startIndex, int endIndex) {
+        List<String> names = new ArrayList<>();
+        for (int i = startIndex; i <= endIndex; i++) {
+            names.add(args[i]);
+        }
+        return names;
+    }
+}
diff --git a/gms-client/gms-cli/src/main/java/it/inaf/ia2/gms/cli/GmsCliApplication.java b/gms-client/gms-cli/src/main/java/it/inaf/ia2/gms/cli/GmsCliApplication.java
new file mode 100644
index 0000000..005a5b4
--- /dev/null
+++ b/gms-client/gms-cli/src/main/java/it/inaf/ia2/gms/cli/GmsCliApplication.java
@@ -0,0 +1,13 @@
+package it.inaf.ia2.gms.cli;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class GmsCliApplication {
+
+	public static void main(String[] args) {
+		SpringApplication.run(GmsCliApplication.class, args);
+	}
+
+}
diff --git a/gms-client/gms-cli/src/main/resources/application.properties b/gms-client/gms-cli/src/main/resources/application.properties
new file mode 100644
index 0000000..2a867fa
--- /dev/null
+++ b/gms-client/gms-cli/src/main/resources/application.properties
@@ -0,0 +1,2 @@
+spring.main.banner-mode=off
+logging.level.root=OFF
\ No newline at end of file
diff --git a/gms-client/gms-cli/src/test/java/it/inaf/ia2/gms/cli/GmsCliApplicationTests.java b/gms-client/gms-cli/src/test/java/it/inaf/ia2/gms/cli/GmsCliApplicationTests.java
new file mode 100644
index 0000000..55043e8
--- /dev/null
+++ b/gms-client/gms-cli/src/test/java/it/inaf/ia2/gms/cli/GmsCliApplicationTests.java
@@ -0,0 +1,16 @@
+package it.inaf.ia2.gms.cli;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest
+public class GmsCliApplicationTests {
+
+	@Test
+	public void contextLoads() {
+	}
+
+}
diff --git a/gms-client/gms-client-lib/pom.xml b/gms-client/gms-client-lib/pom.xml
index bbaa736..7ef5181 100644
--- a/gms-client/gms-client-lib/pom.xml
+++ b/gms-client/gms-client-lib/pom.xml
@@ -17,5 +17,22 @@
             <artifactId>spring-web</artifactId>
             <version>${spring.version}</version>
         </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+            <version>2.9.9</version>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.12</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <version>2.23.4</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 </project>
\ No newline at end of file
diff --git a/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/GmsClient.java b/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/GmsClient.java
index b9ddaf8..634640f 100644
--- a/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/GmsClient.java
+++ b/gms-client/gms-client-lib/src/main/java/it/inaf/ia2/gms/client/GmsClient.java
@@ -3,10 +3,13 @@ package it.inaf.ia2.gms.client;
 import it.inaf.ia2.gms.client.model.Group;
 import it.inaf.ia2.gms.client.model.Member;
 import it.inaf.ia2.gms.client.model.Permission;
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
 import org.springframework.http.HttpMethod;
 import org.springframework.web.client.RestTemplate;
 import org.springframework.web.util.UriComponentsBuilder;
@@ -16,32 +19,46 @@ public class GmsClient {
     private static final String WS = "ws";
 
     private final String baseUrl;
-    private final RestTemplate restTemplate;
+    private final String authHeader;
+    private RestTemplate restTemplate;
 
     public GmsClient(String baseUrl, String clientId, String clientSecret) {
         this.baseUrl = baseUrl;
+
+        String auth = clientId + ":" + clientSecret;
+        byte[] encodedAuth = Base64.getEncoder().encode(
+                auth.getBytes(StandardCharsets.UTF_8));
+        authHeader = "Basic " + new String(encodedAuth);
+
         restTemplate = new RestTemplate();
     }
 
+    /**
+     * For testing purpose.
+     */
+    protected void setRestTemplate(RestTemplate restTemplate) {
+        this.restTemplate = restTemplate;
+    }
+
     public Group createGroup(List<String> names) {
 
         String url = UriComponentsBuilder.fromHttpUrl(baseUrl)
                 .pathSegment(WS, "group")
                 .toUriString();
 
-        HttpEntity<List<String>> httpEntity = new HttpEntity<>(names);
+        HttpEntity<List<String>> httpEntity = getEntity(names);
 
         return restTemplate.exchange(url, HttpMethod.POST, httpEntity, Group.class).getBody();
     }
 
-    public void deleteGroupByPath(List<String> names) {
+    public void deleteGroup(List<String> names) {
 
         String url = UriComponentsBuilder.fromHttpUrl(baseUrl)
                 .pathSegment(WS, "group")
-                .queryParam("names", names)
+                .queryParam("names", names.toArray())
                 .toUriString();
 
-        restTemplate.delete(url);
+        restTemplate.exchange(url, HttpMethod.DELETE, getEntity(), Void.class);
     }
 
     public Member addMember(List<String> names, String userId) {
@@ -53,7 +70,7 @@ public class GmsClient {
         Map<String, Object> params = new HashMap<>();
         params.put("names", names);
         params.put("userId", userId);
-        HttpEntity<Map<String, Object>> httpEntity = new HttpEntity<>(params);
+        HttpEntity<Map<String, Object>> httpEntity = getEntity(params);
 
         return restTemplate.exchange(url, HttpMethod.POST, httpEntity, Member.class).getBody();
     }
@@ -62,11 +79,11 @@ public class GmsClient {
 
         String url = UriComponentsBuilder.fromHttpUrl(baseUrl)
                 .pathSegment(WS, "member")
-                .queryParam("names", names)
+                .queryParam("names", names.toArray())
                 .queryParam("userId", userId)
                 .toUriString();
 
-        restTemplate.delete(url);
+        restTemplate.exchange(url, HttpMethod.DELETE, getEntity(), Void.class);
     }
 
     public Permission addPermission(List<String> names, String userId, String permission) {
@@ -79,7 +96,7 @@ public class GmsClient {
         params.put("names", names);
         params.put("userId", userId);
         params.put("permission", permission);
-        HttpEntity<Map<String, Object>> httpEntity = new HttpEntity<>(params);
+        HttpEntity<Map<String, Object>> httpEntity = getEntity(params);
 
         return restTemplate.exchange(url, HttpMethod.POST, httpEntity, Permission.class).getBody();
     }
@@ -88,12 +105,12 @@ public class GmsClient {
 
         String url = UriComponentsBuilder.fromHttpUrl(baseUrl)
                 .pathSegment(WS, "permission")
-                .queryParam("names", names)
+                .queryParam("names", names.toArray())
                 .queryParam("userId", userId)
                 .queryParam("permission", permission)
                 .toUriString();
 
-        restTemplate.delete(url);
+        restTemplate.exchange(url, HttpMethod.DELETE, getEntity(), Void.class);
     }
 
     public void prepareToJoin(String fromUserId, String toUserId) {
@@ -105,8 +122,24 @@ public class GmsClient {
         Map<String, Object> params = new HashMap<>();
         params.put("fromUserId", fromUserId);
         params.put("toUserId", toUserId);
-        HttpEntity<Map<String, Object>> httpEntity = new HttpEntity<>(params);
+        HttpEntity<Map<String, Object>> httpEntity = getEntity(params);
 
         restTemplate.exchange(url, HttpMethod.POST, httpEntity, Void.class);
     }
+
+    private HttpEntity<?> getEntity() {
+        return new HttpEntity<>(getHeaders());
+    }
+
+    private <T> HttpEntity<T> getEntity(T body) {
+        return new HttpEntity<>(body, getHeaders());
+    }
+
+    private HttpHeaders getHeaders() {
+        return new HttpHeaders() {
+            {
+                set("Authorization", authHeader);
+            }
+        };
+    }
 }
diff --git a/gms-client/gms-client-lib/src/test/java/it/inaf/ia2/gms/client/GmsClientTest.java b/gms-client/gms-client-lib/src/test/java/it/inaf/ia2/gms/client/GmsClientTest.java
new file mode 100644
index 0000000..d18b185
--- /dev/null
+++ b/gms-client/gms-client-lib/src/test/java/it/inaf/ia2/gms/client/GmsClientTest.java
@@ -0,0 +1,183 @@
+package it.inaf.ia2.gms.client;
+
+import it.inaf.ia2.gms.client.model.Group;
+import it.inaf.ia2.gms.client.model.Member;
+import it.inaf.ia2.gms.client.model.Permission;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import static org.junit.Assert.assertEquals;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.client.RestTemplate;
+
+@RunWith(MockitoJUnitRunner.class)
+public class GmsClientTest {
+
+    private static final String BASE_URL = "http://base-url";
+
+    private RestTemplate restTemplate;
+    private GmsClient client;
+
+    @Before
+    public void setUp() {
+
+        restTemplate = mock(RestTemplate.class);
+
+        client = new GmsClient(BASE_URL, "test", "test");
+        client.setRestTemplate(restTemplate);
+
+        when(restTemplate.exchange(anyString(), any(HttpMethod.class), any(HttpEntity.class), any(Class.class)))
+                .thenReturn(new ResponseEntity<>(HttpStatus.I_AM_A_TEAPOT));
+    }
+
+    @Test
+    public void testCreateGroup() {
+
+        List<String> names = Arrays.asList("LBT", "INAF");
+
+        client.createGroup(names);
+
+        ArgumentCaptor<HttpEntity> entityCaptor = ArgumentCaptor.forClass(HttpEntity.class);
+        verify(restTemplate, times(1)).exchange(eq(BASE_URL + "/ws/group"),
+                eq(HttpMethod.POST), entityCaptor.capture(), eq(Group.class));
+
+        HttpEntity<?> entity = entityCaptor.getValue();
+        verifyAuthHeaders(entity);
+        verifyBody(entity, names);
+    }
+
+    @Test
+    public void testDeleteGroup() {
+
+        List<String> names = Arrays.asList("LBT", "INAF");
+
+        client.deleteGroup(names);
+
+        ArgumentCaptor<HttpEntity> entityCaptor = ArgumentCaptor.forClass(HttpEntity.class);
+        verify(restTemplate, times(1)).exchange(eq(BASE_URL + "/ws/group?names=LBT&names=INAF"),
+                eq(HttpMethod.DELETE), entityCaptor.capture(), eq(Void.class));
+
+        HttpEntity<?> entity = entityCaptor.getValue();
+        verifyAuthHeaders(entity);
+    }
+
+    @Test
+    public void testAddMember() {
+
+        List<String> names = Arrays.asList("LBT", "INAF");
+
+        client.addMember(names, "user_id");
+
+        ArgumentCaptor<HttpEntity> entityCaptor = ArgumentCaptor.forClass(HttpEntity.class);
+        verify(restTemplate, times(1)).exchange(eq(BASE_URL + "/ws/member"),
+                eq(HttpMethod.POST), entityCaptor.capture(), eq(Member.class));
+
+        HttpEntity<?> entity = entityCaptor.getValue();
+        verifyAuthHeaders(entity);
+
+        Map<String, Object> expectedBody = new HashMap<>();
+        expectedBody.put("names", names);
+        expectedBody.put("userId", "user_id");
+
+        verifyBody(entity, expectedBody);
+    }
+
+    @Test
+    public void testRemoveMember() {
+
+        List<String> names = Arrays.asList("LBT", "INAF");
+
+        client.removeMember(names, "user_id");
+
+        ArgumentCaptor<HttpEntity> entityCaptor = ArgumentCaptor.forClass(HttpEntity.class);
+        verify(restTemplate, times(1)).exchange(eq(BASE_URL + "/ws/member?names=LBT&names=INAF&userId=user_id"),
+                eq(HttpMethod.DELETE), entityCaptor.capture(), eq(Void.class));
+
+        HttpEntity<?> entity = entityCaptor.getValue();
+        verifyAuthHeaders(entity);
+    }
+
+    @Test
+    public void testAddPermission() {
+
+        List<String> names = Arrays.asList("LBT", "INAF");
+
+        client.addPermission(names, "user_id", "ADMIN");
+
+        ArgumentCaptor<HttpEntity> entityCaptor = ArgumentCaptor.forClass(HttpEntity.class);
+        verify(restTemplate, times(1)).exchange(eq(BASE_URL + "/ws/permission"),
+                eq(HttpMethod.POST), entityCaptor.capture(), eq(Permission.class));
+
+        HttpEntity<?> entity = entityCaptor.getValue();
+        verifyAuthHeaders(entity);
+
+        Map<String, Object> expectedBody = new HashMap<>();
+        expectedBody.put("names", names);
+        expectedBody.put("userId", "user_id");
+        expectedBody.put("permission", "ADMIN");
+
+        verifyBody(entity, expectedBody);
+    }
+
+    @Test
+    public void testDeletePermission() {
+
+        List<String> names = Arrays.asList("LBT", "INAF");
+
+        client.deletePermission(names, "user_id", "ADMIN");
+
+        ArgumentCaptor<HttpEntity> entityCaptor = ArgumentCaptor.forClass(HttpEntity.class);
+        verify(restTemplate, times(1)).exchange(eq(BASE_URL + "/ws/permission?names=LBT&names=INAF&userId=user_id&permission=ADMIN"),
+                eq(HttpMethod.DELETE), entityCaptor.capture(), eq(Void.class));
+
+        HttpEntity<?> entity = entityCaptor.getValue();
+        verifyAuthHeaders(entity);
+    }
+
+    @Test
+    public void testPrepareToJoin() {
+
+        String fromUserId = "from_user_id";
+        String toUserId = "to_user_id";
+
+        client.prepareToJoin(fromUserId, toUserId);
+
+        ArgumentCaptor<HttpEntity> entityCaptor = ArgumentCaptor.forClass(HttpEntity.class);
+        verify(restTemplate, times(1)).exchange(eq(BASE_URL + "/ws/prepare-join"),
+                eq(HttpMethod.POST), entityCaptor.capture(), eq(Void.class));
+
+        HttpEntity<?> entity = entityCaptor.getValue();
+        verifyAuthHeaders(entity);
+
+        Map<String, Object> expectedBody = new HashMap<>();
+        expectedBody.put("fromUserId", fromUserId);
+        expectedBody.put("toUserId", toUserId);
+
+        verifyBody(entity, expectedBody);
+    }
+
+    private void verifyAuthHeaders(HttpEntity<?> entity) {//
+        String authHeader = entity.getHeaders().getFirst("Authorization");
+        assertEquals("Basic dGVzdDp0ZXN0", authHeader);
+    }
+
+    private <T> void verifyBody(HttpEntity<?> entity, T body) {
+        assertEquals(entity.getBody(), body);
+    }
+}
diff --git a/gms/src/test/java/it/inaf/ia2/gms/controller/WebServiceControllerTest.java b/gms/src/test/java/it/inaf/ia2/gms/controller/WebServiceControllerTest.java
index 573b5f5..7ccbc78 100644
--- a/gms/src/test/java/it/inaf/ia2/gms/controller/WebServiceControllerTest.java
+++ b/gms/src/test/java/it/inaf/ia2/gms/controller/WebServiceControllerTest.java
@@ -11,10 +11,9 @@ import it.inaf.ia2.gms.persistence.model.PermissionEntity;
 import it.inaf.ia2.gms.service.GroupsService;
 import it.inaf.ia2.gms.service.MembersService;
 import it.inaf.ia2.gms.service.PermissionsService;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Optional;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
 import static org.hamcrest.CoreMatchers.is;
 import org.junit.Before;
 import org.junit.Test;
@@ -79,7 +78,7 @@ public class WebServiceControllerTest {
         when(groupsService.findGroupByParentAndName(eq(root), eq("LBT"))).thenReturn(Optional.of(lbt));
         when(groupsService.addGroup(eq(lbt), eq("INAF"))).thenReturn(inaf);
 
-        List<String> names = getNames("LBT", "INAF");
+        List<String> names = Arrays.asList("LBT", "INAF");
 
         mockMvc.perform(post("/ws/group")
                 .content(mapper.writeValueAsString(names))
@@ -94,7 +93,7 @@ public class WebServiceControllerTest {
     @Test
     public void testDeleteGroupByPath() throws Exception {
 
-        List<String> names = getNames("LBT", "INAF");
+        List<String> names = Arrays.asList("LBT", "INAF");
 
         GroupEntity inaf = getInafGroup();
 
@@ -109,7 +108,7 @@ public class WebServiceControllerTest {
     @Test
     public void testAddMember() throws Exception {
 
-        List<String> names = getNames("LBT", "INAF");
+        List<String> names = Arrays.asList("LBT", "INAF");
 
         GroupEntity inaf = getInafGroup();
 
@@ -137,7 +136,7 @@ public class WebServiceControllerTest {
     @Test
     public void testRemoveMember() throws Exception {
 
-        List<String> names = getNames("LBT", "INAF");
+        List<String> names = Arrays.asList("LBT", "INAF");
 
         GroupEntity inaf = getInafGroup();
 
@@ -152,7 +151,7 @@ public class WebServiceControllerTest {
     @Test
     public void testAddPermission() throws Exception {
 
-        List<String> names = getNames("LBT", "INAF");
+        List<String> names = Arrays.asList("LBT", "INAF");
 
         AddPermissionWsRequest request = new AddPermissionWsRequest();
         request.setNames(names);
@@ -186,7 +185,7 @@ public class WebServiceControllerTest {
     @Test
     public void testDeletePermission() throws Exception {
 
-        List<String> names = getNames("LBT", "INAF");
+        List<String> names = Arrays.asList("LBT", "INAF");
         GroupEntity inaf = getInafGroup();
         when(groupsService.findGroupByNames(names)).thenReturn(Optional.of(inaf));
 
@@ -212,10 +211,6 @@ public class WebServiceControllerTest {
         verify(membersService, times(1)).moveMemberships(request.getFromUserId(), request.getToUserId());
     }
 
-    private List<String> getNames(String... names) {
-        return Stream.of(names).collect(Collectors.toList());
-    }
-
     private GroupEntity getInafGroup() {
         GroupEntity inaf = new GroupEntity();
         inaf.setId("inaf_id");
-- 
GitLab