Merge branch 'develop' into 0.1-release
diff --git a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/controller/LoadBalanceController.java b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/controller/LoadBalanceController.java
index b314aed..4325731 100644
--- a/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/controller/LoadBalanceController.java
+++ b/dubbo-admin-backend/src/main/java/org/apache/dubbo/admin/controller/LoadBalanceController.java
@@ -120,10 +120,10 @@
 
     @RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
     public boolean deleteLoadBalance(@PathVariable String id, @PathVariable String env) {
-        id = id.replace(Constants.ANY_VALUE, Constants.PATH_SEPARATOR);
         if (id == null) {
             throw new IllegalArgumentException("Argument of id is null!");
         }
+        id = id.replace(Constants.ANY_VALUE, Constants.PATH_SEPARATOR);
         overrideService.deleteBalance(id);
         return true;
     }
diff --git a/dubbo-admin-backend/src/test/java/org/apache/dubbo/admin/AbstractSpringIntegrationTest.java b/dubbo-admin-backend/src/test/java/org/apache/dubbo/admin/AbstractSpringIntegrationTest.java
index 5640c30..074373f 100644
--- a/dubbo-admin-backend/src/test/java/org/apache/dubbo/admin/AbstractSpringIntegrationTest.java
+++ b/dubbo-admin-backend/src/test/java/org/apache/dubbo/admin/AbstractSpringIntegrationTest.java
@@ -21,43 +21,39 @@
 import org.apache.curator.framework.CuratorFrameworkFactory;
 import org.apache.curator.retry.RetryOneTime;
 import org.apache.curator.test.TestingServer;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
 import org.junit.runner.RunWith;
-import org.springframework.beans.factory.annotation.Value;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.boot.test.web.client.TestRestTemplate;
+import org.springframework.boot.web.server.LocalServerPort;
 import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.ContextConfiguration;
 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
-import org.springframework.web.client.RestTemplate;
-
-import java.io.IOException;
 
 @ActiveProfiles("test")
 @RunWith(SpringJUnit4ClassRunner.class)
-@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+@SpringBootTest(classes = DubboAdminApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+@ContextConfiguration(classes = DubboAdminApplication.class)
 public abstract class AbstractSpringIntegrationTest {
-  protected RestTemplate restTemplate = (new TestRestTemplate()).getRestTemplate();
+  @Autowired
+  protected TestRestTemplate restTemplate;
 
-  protected static TestingServer zkServer;
-  protected static CuratorFramework zkClient;
+  protected static final TestingServer zkServer;
+  protected static final CuratorFramework zkClient;
 
-  @Value("${local.server.port}")
+  static {
+    try {
+      zkServer = new TestingServer(2182, true);
+      zkClient = CuratorFrameworkFactory.newClient(zkServer.getConnectString(), new RetryOneTime(2000));
+      zkClient.start();
+    } catch (Exception e) {
+      throw new ExceptionInInitializerError(e);
+    }
+  }
+
+  @LocalServerPort
   protected int port;
 
-  @BeforeClass
-  public static void beforeClass() throws Exception {
-    zkServer = new TestingServer(2182, true);
-    zkClient = CuratorFrameworkFactory.newClient(zkServer.getConnectString(), new RetryOneTime(2000));
-    zkClient.start();
-  }
-
-  @AfterClass
-  public static void afterClass() throws IOException {
-    zkClient.close();
-    zkServer.stop();
-  }
-
   protected String url(final String path) {
     return "http://localhost:" + port + path;
   }
diff --git a/dubbo-admin-backend/src/test/java/org/apache/dubbo/admin/controller/LoadBalanceControllerTest.java b/dubbo-admin-backend/src/test/java/org/apache/dubbo/admin/controller/LoadBalanceControllerTest.java
new file mode 100644
index 0000000..fe8878b
--- /dev/null
+++ b/dubbo-admin-backend/src/test/java/org/apache/dubbo/admin/controller/LoadBalanceControllerTest.java
@@ -0,0 +1,143 @@
+/*
+ * 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.dubbo.admin.controller;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.dubbo.admin.AbstractSpringIntegrationTest;
+import org.apache.dubbo.admin.model.dto.BalancingDTO;
+import org.apache.dubbo.admin.service.OverrideService;
+import org.apache.dubbo.admin.service.ProviderService;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+
+public class LoadBalanceControllerTest extends AbstractSpringIntegrationTest {
+
+    private final String env = "whatever";
+
+    @MockBean
+    private OverrideService overrideService;
+    @MockBean
+    private ProviderService providerService;
+    @Autowired
+    private ObjectMapper objectMapper;
+
+    @Test
+    public void createLoadbalance() throws IOException {
+        BalancingDTO balancingDTO = new BalancingDTO();
+        ResponseEntity<String> response;
+
+        // service and application are all blank
+        response = restTemplate.postForEntity(url("/api/{env}/rules/balancing"), balancingDTO, String.class, env);
+        assertFalse("should return a fail response, when service and application are all blank", (Boolean) objectMapper.readValue(response.getBody(), Map.class).get("success"));
+        // dubbo version is 2.6
+        balancingDTO.setApplication("test application");
+        balancingDTO.setService("test service");
+        when(providerService.findVersionInApplication("test application")).thenReturn("2.6");
+        response = restTemplate.postForEntity(url("/api/{env}/rules/balancing"), balancingDTO, String.class, env);
+        assertFalse("should return a fail response, when dubbo version is 2.6", (Boolean) objectMapper.readValue(response.getBody(), Map.class).get("success"));
+        // dubbo version is 2.7
+        when(providerService.findVersionInApplication("test application")).thenReturn("2.7");
+        response = restTemplate.postForEntity(url("/api/{env}/rules/balancing"), balancingDTO, String.class, env);
+        assertEquals(HttpStatus.CREATED, response.getStatusCode());
+        assertTrue(Boolean.valueOf(response.getBody()));
+    }
+
+    @Test
+    public void updateLoadbalance() throws IOException {
+        String id = "1";
+        BalancingDTO balancingDTO = new BalancingDTO();
+        URI uri;
+        ResponseEntity<String> response;
+        // unknown id
+        response = restTemplate.exchange(url("/api/{env}/rules/balancing/{id}"), HttpMethod.PUT, new HttpEntity<>(balancingDTO, null), String.class, env, id);
+        assertFalse("should return a fail response, when id is null", (Boolean) objectMapper.readValue(response.getBody(), Map.class).get("success"));
+        // valid id
+        BalancingDTO balancing = mock(BalancingDTO.class);
+        when(overrideService.findBalance(id)).thenReturn(balancing);
+        assertTrue(restTemplate.exchange(url("/api/{env}/rules/balancing/{id}"), HttpMethod.PUT, new HttpEntity<>(balancingDTO, null), Boolean.class, env, id).getBody());
+        verify(overrideService).saveBalance(any(BalancingDTO.class));
+    }
+
+    @Test
+    public void searchLoadbalances() throws IOException {
+        String service = "test service", application = "test application";
+        ResponseEntity<String> response;
+        // service and application are all blank
+        response = restTemplate.getForEntity(url("/api/{env}/rules/balancing"), String.class, env);
+        assertFalse("should return a fail response, when service and application are all blank", (Boolean) objectMapper.readValue(response.getBody(), Map.class).get("success"));
+        // service is valid
+        response = restTemplate.getForEntity(url("/api/{env}/rules/balancing?service={service}&application={application}"), String.class, env, service, null);
+        assertEquals(HttpStatus.OK, response.getStatusCode());
+        verify(overrideService).findBalance(service);
+        // application is valid
+        response = restTemplate.getForEntity(url("/api/{env}/rules/balancing?service={service}&application={application}"), String.class, env, null, application);
+        assertEquals(HttpStatus.OK, response.getStatusCode());
+        verify(overrideService).findBalance(application);
+        // findBalance return a notnull
+        BalancingDTO balancingDTO = new BalancingDTO();
+        when(overrideService.findBalance(anyString())).thenReturn(balancingDTO);
+        response = restTemplate.getForEntity(url("/api/{env}/rules/balancing?service={service}&application={application}"), String.class, env, null, application);
+        assertEquals(HttpStatus.OK, response.getStatusCode());
+        assertEquals(1, objectMapper.readValue(response.getBody(), List.class).size());
+    }
+
+    @Test
+    public void detailLoadBalance() throws IOException {
+        String id = "1";
+        ResponseEntity<String> response;
+        // when balancing is not exist
+        response = restTemplate.getForEntity(url("/api/{env}/rules/balancing/{id}"), String.class, env, id);
+        assertFalse("should return a fail response, when id is null", (Boolean) objectMapper.readValue(response.getBody(), Map.class).get("success"));
+        // when balancing is not null
+        BalancingDTO balancingDTO = new BalancingDTO();
+        when(overrideService.findBalance(id)).thenReturn(balancingDTO);
+        response = restTemplate.getForEntity(url("/api/{env}/rules/balancing/{id}"), String.class, env, id);
+        assertEquals(HttpStatus.OK, response.getStatusCode());
+    }
+
+    @Test
+    public void deleteLoadBalance() {
+        String id = "1";
+        URI uri;
+        ResponseEntity<String> response;
+
+        response = restTemplate.exchange(url("/api/{env}/rules/balancing/{id}"), HttpMethod.DELETE, new HttpEntity<>(null), String.class, env, id);
+        assertEquals(HttpStatus.OK, response.getStatusCode());
+        assertTrue(Boolean.valueOf(response.getBody()));
+    }
+}