content: package com.aliyuncs.fc;
  
  import com.aliyuncs.DefaultAcsClient;
  import com.aliyuncs.auth.BasicSessionCredentials;
  import com.aliyuncs.auth.InstanceProfileCredentialsProvider;
  import com.aliyuncs.fc.auth.AcsURLEncoder;
  import com.aliyuncs.fc.auth.SignURLConfig;
  import com.aliyuncs.fc.client.FunctionComputeClient;
  import com.aliyuncs.fc.client.PopClient;
  import com.aliyuncs.fc.config.Config;
  import com.aliyuncs.fc.constants.Const;
  import com.aliyuncs.fc.exceptions.ClientException;
  import com.aliyuncs.fc.exceptions.ErrorCodes;
  import com.aliyuncs.fc.model.*;
  import com.aliyuncs.fc.model.NasConfig.NasMountConfig;
  import com.aliyuncs.fc.request.*;
  import com.aliyuncs.fc.response.*;
  import com.aliyuncs.fc.utils.Util;
  import com.aliyuncs.http.MethodType;
  import com.aliyuncs.http.ProtocolType;
  import com.aliyuncs.profile.DefaultProfile;
  import com.aliyuncs.profile.IClientProfile;
  import com.aliyuncs.sts.model.v20150401.AssumeRoleRequest;
  import com.aliyuncs.sts.model.v20150401.AssumeRoleResponse;
  import com.aliyuncs.sts.model.v20150401.AssumeRoleResponse.Credentials;
  import com.google.common.base.Strings;
  import com.google.gson.Gson;
  import com.google.gson.JsonObject;
  import org.apache.commons.lang.StringUtils;
  import org.json.JSONException;
  import org.junit.*;
  
  import java.io.File;
  import java.io.IOException;
  import java.io.PrintWriter;
  import java.net.HttpURLConnection;
  import java.net.URL;
  import java.security.NoSuchAlgorithmException;
  import java.text.DateFormat;
  import java.text.ParseException;
  import java.text.SimpleDateFormat;
  import java.util.*;
  
  import static com.aliyuncs.fc.constants.Const.DEFAULT_REGEX;
  import static com.aliyuncs.fc.constants.Const.NONE;
  import static com.aliyuncs.fc.constants.HeaderKeys.OPENTRACING_SPANCONTEXT;
  import static com.aliyuncs.fc.constants.HeaderKeys.OPENTRACING_SPANCONTEXT_BAGGAGE_PREFIX;
  import static com.aliyuncs.fc.model.HttpAuthType.ANONYMOUS;
  import static com.aliyuncs.fc.model.HttpAuthType.FUNCTION;
  import static com.aliyuncs.fc.model.HttpMethod.*;
  import static java.util.Arrays.asList;
  import static java.util.Arrays.deepEquals;
  import static junit.framework.TestCase.assertEquals;
  import static junit.framework.TestCase.assertFalse;
  import static junit.framework.TestCase.assertNull;
  import static junit.framework.TestCase.fail;
  import static org.junit.Assert.assertNotNull;
  import static org.junit.Assert.assertTrue;
  import static org.junit.Assert.*;
  import static org.mockito.Mockito.mock;
  import static org.mockito.Mockito.when;
  
  /**
   * Validation for FunctionComputeClient, tests including create/list/get/update
   * service/function/trigger
   */
  public class FunctionComputeClientTest {
  
      public static final String STS_API_VERSION = "2015-04-01";
      private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
      private static final String VALIDATE_MSG = "cannot be blank";
      private static final String REGION = System.getenv("REGION");
      private static final String ENDPOINT = System.getenv("ENDPOINT");
      private static final String ROLE = System.getenv("ROLE");
      private static final String STS_ROLE = System.getenv("STS_ROLE");
      private static final String ACCESS_KEY = System.getenv("ACCESS_KEY");
      private static final String SECRET_KEY = System.getenv("SECRET_KEY");
      private static final String ACCOUNT_ID = System.getenv("ACCOUNT_ID");
      private static final String CODE_BUCKET = System.getenv("CODE_BUCKET");
      private static final String INVOCATION_ROLE = System.getenv("INVOCATION_ROLE");
      private static final String LOG_PROJECT = System.getenv("LOG_PROJECT");
      private static final String LOG_STORE = System.getenv("LOG_STORE");
      private static final String VPC_ID = System.getenv("VPC_ID");
      private static final String VSWITCH_IDS = System.getenv("VSWITCH_IDS");
      private static final String SECURITY_GROUP_ID = System.getenv("SECURITY_GROUP_ID");
      private static final String USER_ID = System.getenv("USER_ID");
      private static final String GROUP_ID = System.getenv("GROUP_ID");
      private static final String NAS_SERVER_ADDR = System.getenv("NAS_SERVER_ADDR");
      private static final String NAS_MOUNT_DIR = System.getenv("NAS_MOUNT_DIR");
      private static final String PUBLIC_KEY_CERTIFICATE_01 = System.getenv("PUBLIC_KEY_CERTIFICATE_01");
      private static final String PRIVATE_KEY_01 = System.getenv("PRIVATE_KEY_01");
      private static final String PUBLIC_KEY_CERTIFICATE_02 = System.getenv("PUBLIC_KEY_CERTIFICATE_02");
      private static final String PRIVATE_KEY_02 = System.getenv("PRIVATE_KEY_02");
      private static final String JAEGER_ENDPOINT = System.getenv("JAEGER_ENDPOINT");
  
      private static final String OSS_SOURCE_ARN =
              String.format("acs:oss:%s:%s:%s", REGION, ACCOUNT_ID, CODE_BUCKET);
      private static final String LOG_SOURCE_ARN =
              String.format("acs:log:%s:%s:project/%s", REGION, ACCOUNT_ID, LOG_PROJECT);
      private static final String CDN_SOURCE_ARN =
              String.format("acs:cdn:*:%s", ACCOUNT_ID);
      private static final String SERVICE_NAME = "testServiceJavaSDK";
      private static final String SERVICE_DESC_OLD = "service desc";
      private static final String SERVICE_DESC_NEW = "service desc updated";
      private static final String FUNCTION_NAME = "testFunction";
      private static final String FUNCTION_DESC_OLD = "function desc";
      private static final String FUNCTION_DESC_NEW = "function desc updated";
      private static final String TRIGGER_NAME = "testTrigger";
      private static final String TRIGGER_TYPE_OSS = "oss";
      private static final String TRIGGER_TYPE_HTTP = "http";
      private static final String TRIGGER_TYPE_LOG = "log";
      private static final String TRIGGER_TYPE_CDN = "cdn_events";
      private static final String TRIGGER_TYPE_TIMER = "timer";
      private static final String CUSTOMDOMAIN_NAME = String.format("java-sdk.cn-hongkong.%s.cname-test.functioncompute.com", ACCOUNT_ID);
      private static final String CERT_NAME = "CERT_NAME";
      private static final Gson gson = new Gson();
      private FunctionComputeClient client;
  
      @BeforeClass
      public static void setupSuite() {
          System.out.println("ENDPOINT: " + ENDPOINT);
          System.out.println("ROLE: " + ROLE);
          System.out.println("VPC_ID: " + VPC_ID);
          System.out.println("STS_ROLE: " + STS_ROLE);
      }
  
      @Before
      public void setup() {
          // Create or clean up everything under the test service
          client = new FunctionComputeClient(REGION, ACCOUNT_ID, ACCESS_KEY, SECRET_KEY);
          if (!Strings.isNullOrEmpty(ENDPOINT)) {
              client.setEndpoint(ENDPOINT);
          }
          GetServiceRequest getSReq = new GetServiceRequest(SERVICE_NAME);
          try {
              client.getService(getSReq);
              cleanUpAliases(SERVICE_NAME);
              cleanUpVersions(SERVICE_NAME);
              cleanUpFunctions(SERVICE_NAME);
              cleanupService(SERVICE_NAME);
              cleanUpFunctions(SERVICE_NAME + "-nas");
              cleanupService(SERVICE_NAME + "-nas");
          } catch (ClientException e) {
              if (!ErrorCodes.SERVICE_NOT_FOUND.equals(e.getErrorCode())) {
                  throw e;
              }
          }
      }
  
      public FunctionComputeClient overrideFCClient(boolean useSts, boolean useHttps)
              throws com.aliyuncs.exceptions.ClientException {
          if (useSts) {
              Credentials creds = getAssumeRoleCredentials(null);
              FunctionComputeClient fcClient = new FunctionComputeClient(
                      new Config(REGION, ACCOUNT_ID,
                              creds.getAccessKeyId(), creds.getAccessKeySecret(), creds.getSecurityToken(),
                              useHttps));
  
              if (!Strings.isNullOrEmpty(ENDPOINT)) {
                  fcClient.setEndpoint(ENDPOINT);
              }
  
              return fcClient;
          }
          return new FunctionComputeClient(new Config(REGION, ACCOUNT_ID,
                  ACCESS_KEY, SECRET_KEY, null, useHttps));
      }
  
      private void cleanupService(String serviceName) {
          DeleteServiceRequest request = new DeleteServiceRequest(serviceName);
          try {
              client.deleteService(request);
          } catch (ClientException e) {
              if (!ErrorCodes.SERVICE_NOT_FOUND.equals(e.getErrorCode())) {
                  throw e;
              }
          }
          System.out.println("Service " + serviceName + " is deleted");
      }
  
      private void cleanupProvision(String serviceName, String aliasName, String functionName) {
          Integer target = 0;
          PutProvisionConfigRequest provisionConfigRequest = new PutProvisionConfigRequest(serviceName, aliasName, functionName);
          provisionConfigRequest.setTarget(target);
          provisionConfigRequest.setScheduledActions(new ScheduledAction[0]);
          PutProvisionConfigResponse provisionConfigResponse = client.putProvisionConfig(provisionConfigRequest);
          assertEquals(HttpURLConnection.HTTP_OK, provisionConfigResponse.getStatus());
          assertEquals(target, provisionConfigResponse.getTarget());
  
          try {
              // retry 30s for release provision container,
              int retryTimes = 0;
              while (retryTimes < 30) {
                  // get provisionConfig
                  GetProvisionConfigRequest getProvisionConfigRequest = new GetProvisionConfigRequest(serviceName, aliasName, functionName);
                  GetProvisionConfigResponse getProvisionConfigResponse = client.getProvisionConfig(getProvisionConfigRequest);
                  if (getProvisionConfigResponse.getCurrent() != 0) {
                      Thread.sleep(1000); // sleep 1s
                      retryTimes++;
                      continue;
                  }
                  break;
              }
              assertEquals(true, retryTimes < 30);
          } catch (Exception e) {
              assertNull(e);
          }
      }
  
      private void cleanupCustomDomain(String customDomainName) {
          DeleteCustomDomainRequest request = new DeleteCustomDomainRequest(customDomainName);
          try {
              client.deleteCustomDomain(request);
          } catch (ClientException e) {
              if (!ErrorCodes.DOMAIN_NAME_NOT_FOUND.equals(e.getErrorCode())) {
                  throw e;
              }
          }
          System.out.println("CustomDomain " + customDomainName + " is deleted");
      }
  
      private TriggerMetadata[] listTriggers(String serviceName, String functionName) {
          ListTriggersRequest listReq = new ListTriggersRequest(serviceName,
                  functionName);
  
          ListTriggersResponse listResp = client.listTriggers(listReq);
  
          assertFalse(Strings.isNullOrEmpty(listResp.getRequestId()));
  
          return listResp.getTriggers();
      }
  
      private void cleanUpFunctions(String serviceName) {
          ListFunctionsRequest listFReq = new ListFunctionsRequest(serviceName);
          ListFunctionsResponse listFResp = client.listFunctions(listFReq);
          FunctionMetadata[] functions = listFResp.getFunctions();
  
          for (FunctionMetadata function : functions) {
              TriggerMetadata[] triggers = listTriggers(serviceName, function.getFunctionName());
              cleanUpTriggers(serviceName, function.getFunctionName(), triggers);
              System.out.println(
                      "All triggers for Function " + function.getFunctionName() + " are deleted");
              DeleteFunctionRequest deleteFReq = new DeleteFunctionRequest(serviceName,
                      function.getFunctionName());
              client.deleteFunction(deleteFReq);
          }
      }
  
      private void cleanUpTriggers(String serviceName, String functionName,
                                   TriggerMetadata[] triggers) {
          for (TriggerMetadata trigger : triggers) {
              DeleteTriggerResponse response = deleteTrigger(serviceName, functionName,
                      trigger.getTriggerName());
              assertTrue(response.isSuccess());
              System.out.println("Trigger " + trigger.getTriggerName() + " is deleted");
          }
      }
  
      private String cleanUpVersions(String serviceName) {
          ListVersionsRequest listVersionsReq = new ListVersionsRequest(serviceName);
          ListVersionsResponse listVersionResp = client.listVersions(listVersionsReq);
          VersionMetaData[] versions = listVersionResp.getVersions();
          for (VersionMetaData version : versions) {
              DeleteVersionRequest deleteVersionRequest = new DeleteVersionRequest(serviceName,
                      version.getVersionId());
              DeleteVersionResponse response = client.deleteVersion(deleteVersionRequest);
              assertTrue(response.isSuccess());
              System.out.println("Version " + version.getVersionId() + " is deleted");
          }
          return (versions.length > 0) ? versions[0].getVersionId() : "0";
      }
  
      private void cleanUpAliases(String serviceName) {
          ListAliasesRequest listAliasesRequest = new ListAliasesRequest(serviceName);
          ListAliasesResponse listAliasesResponse = client.listAliases(listAliasesRequest);
          AliasMetaData[] aliases = listAliasesResponse.getAliases();
          for (AliasMetaData alias : aliases) {
              DeleteAliasRequest deleteAliasRequest = new DeleteAliasRequest(serviceName,
                      alias.getAliasName());
              DeleteAliasResponse response = client.deleteAlias(deleteAliasRequest);
              assertTrue(response.isSuccess());
              System.out.println(alias.getAliasName() + " is deleted");
          }
      }
  
      private CreateFunctionResponse createFunction(String functionName) throws IOException {
          return createFunction(SERVICE_NAME, functionName);
      }
  
      private CreateFunctionResponse createFunction(String serviceName, String functionName) throws IOException {
          String source = "exports.handler = function(event, context, callback) {\n" +
                  "  callback(null, 'hello world');\n" +
                  "};";
  
          byte[] code = Util.createZipByteData("hello_world.js", source);
  
          CreateFunctionRequest createFuncReq = new CreateFunctionRequest(serviceName);
          createFuncReq.setFunctionName(functionName);
          createFuncReq.setDescription(FUNCTION_DESC_OLD);
          createFuncReq.setMemorySize(128);
          createFuncReq.setHandler("hello_world.handler");
          createFuncReq.setRuntime("nodejs4.4");
          Map<String, String> environmentVariables = new HashMap<String, String>();
          environmentVariables.put("testKey", "testValue");
          createFuncReq.setEnvironmentVariables(environmentVariables);
          createFuncReq.setCode(new Code().setZipFile(code));
          createFuncReq.setTimeout(10);
  
          CreateFunctionResponse response = client.createFunction(createFuncReq);
  
          assertFalse(Strings.isNullOrEmpty(response.getRequestId()));
          assertFalse(Strings.isNullOrEmpty(response.getFunctionId()));
          assertEquals(functionName, response.getFunctionName());
          assertEquals(FUNCTION_DESC_OLD, response.getDescription());
  
          environmentVariables = response.getEnvironmentVariables();
          assertEquals(1, environmentVariables.size());
          assertEquals("testValue", environmentVariables.get("testKey"));
          assertEquals(functionName, response.getFunctionName());
          assertEquals(FUNCTION_DESC_OLD, response.getDescription());
  
          return response;
      }
  
      private CreateServiceResponse createService(String serviceName) {
          return createService(serviceName, true);
      }
  
      private CreateServiceResponse createService(String serviceName, boolean check) {
          CreateServiceRequest createSReq = new CreateServiceRequest();
          createSReq.setServiceName(serviceName);
          createSReq.setDescription(SERVICE_DESC_OLD);
          createSReq.setRole(ROLE);
  
          CreateServiceResponse response = client.createService(createSReq);
  
          if (check) {
              assertEquals(serviceName, response.getServiceName());
              assertFalse(Strings.isNullOrEmpty(response.getRequestId()));
              assertFalse(Strings.isNullOrEmpty(response.getServiceId()));
              assertEquals(SERVICE_DESC_OLD, response.getDescription());
              assertEquals(ROLE, response.getRole());
          }
  
          return response;
      }
  
      private CreateServiceResponse createVPCService(String serviceName) {
          CreateServiceRequest createSReq = new CreateServiceRequest();
          createSReq.setServiceName(serviceName);
          createSReq.setDescription(SERVICE_DESC_OLD);
          createSReq.setRole(ROLE);
          createSReq
                  .setVpcConfig(new VpcConfig(VPC_ID, new String[]{VSWITCH_IDS}, SECURITY_GROUP_ID));
          createSReq.setNasConfig(new NasConfig(Integer.parseInt(USER_ID), Integer.parseInt(GROUP_ID),
                  new NasMountConfig[]{
                          new NasMountConfig(NAS_SERVER_ADDR, NAS_MOUNT_DIR)
                  }));
  
          CreateServiceResponse response = client.createService(createSReq);
  
          assertEquals(serviceName, response.getServiceName());
          assertFalse(Strings.isNullOrEmpty(response.getRequestId()));
          assertFalse(Strings.isNullOrEmpty(response.getServiceId()));
          assertEquals(SERVICE_DESC_OLD, response.getDescription());
          assertEquals(ROLE, response.getRole());
          assertEquals(VPC_ID, response.getVpcConfig().getVpcId());
          assertEquals(SECURITY_GROUP_ID, response.getVpcConfig().getSecurityGroupId());
          return response;
      }
  
      private CreateTriggerResponse createHttpTrigger(String triggerName, HttpAuthType authType,
                                                      HttpMethod[] methods) {
          return createHttpTriggerWithQualifier(triggerName, "", authType, methods);
      }
  
      private CreateTriggerResponse createHttpTriggerWithQualifier(String triggerName,
                                                                   String qualifier,
                                                                   HttpAuthType authType, HttpMethod[] methods) {
          CreateTriggerRequest createReq = new CreateTriggerRequest(SERVICE_NAME, FUNCTION_NAME);
          createReq.setTriggerName(triggerName);
          createReq.setTriggerType(TRIGGER_TYPE_HTTP);
          createReq.setTriggerConfig(new HttpTriggerConfig(authType, methods));
          if (!qualifier.isEmpty()) {
              createReq.setQualifier(qualifier);
          }
          return client.createTrigger(createReq);
      }
  
      private CreateTriggerResponse createOssTrigger(String triggerName, String prefix,
                                                     String suffix) {
          CreateTriggerRequest createTReq = new CreateTriggerRequest(SERVICE_NAME, FUNCTION_NAME);
          createTReq.setTriggerName(triggerName);
          createTReq.setTriggerType(TRIGGER_TYPE_OSS);
          createTReq.setInvocationRole(INVOCATION_ROLE);
          createTReq.setSourceArn(OSS_SOURCE_ARN);
          createTReq.setTriggerConfig(
                  new OSSTriggerConfig(new String[]{"oss:ObjectCreated:*"}, prefix, suffix));
          CreateTriggerResponse resp = client.createTrigger(createTReq);
          try {
              // Add some sleep since OSS notifications create is not strongly consistent
              Thread.sleep(5000);
          } catch (InterruptedException e) {
              throw new RuntimeException(e);
          }
          return resp;
      }
  
      private DeleteTriggerResponse deleteTrigger(String serviceName, String funcName,
                                                  String triggerName) {
          DeleteTriggerRequest req = new DeleteTriggerRequest(serviceName, funcName, triggerName);
          DeleteTriggerResponse resp = client.deleteTrigger(req);
          try {
              Thread.sleep(5000);
          } catch (InterruptedException e) {
              throw new RuntimeException(e);
          }
          return resp;
      }
  
      private UpdateTriggerResponse updateTrigger(UpdateTriggerRequest req) {
          UpdateTriggerResponse resp = client.updateTrigger(req);
          try {
              Thread.sleep(5000);
          } catch (InterruptedException e) {
              throw new RuntimeException(e);
          }
          return resp;
      }
  
      // fetch from OSS url and returns crc64 header value
      private String fetchFromURL(String urlString) {
          try {
              URL url = new URL(urlString);
              HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
              httpConn.setRequestMethod("GET");
              httpConn.setConnectTimeout(60 * 1000);
              httpConn.setReadTimeout(120 * 1000);
  
              httpConn.connect();
              assertEquals(200, httpConn.getResponseCode());
              String headerKey = "X-Oss-Hash-Crc64ecma";
              Map<String, List<String>> headers = httpConn.getHeaderFields();
              for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
                  String key = entry.getKey();
                  if (null == key || !key.equalsIgnoreCase(headerKey)) {
                      continue;
                  }
                  List<String> values = entry.getValue();
                  StringBuilder builder = new StringBuilder(values.get(0));
                  for (int i = 1; i < values.size(); i++) {
                      builder.append(",");
                      builder.append(values.get(i));
                  }
                  return builder.toString();
              }
          } catch (Exception e) {
              assertFalse(String.format("fetchFromURL %s error: %s", urlString, e.toString()), true);
          }
  
          return "";
      }
  
      @Test
      public void testNewRegions() {
          FunctionComputeClient clientHz = new FunctionComputeClient("cn-hangzhou", ACCOUNT_ID,
                  ACCESS_KEY, SECRET_KEY);
          ListServicesResponse lrHz = clientHz.listServices(new ListServicesRequest());
          assertTrue(lrHz.getStatus() == HttpURLConnection.HTTP_OK);
  
          FunctionComputeClient clientBj = new FunctionComputeClient("cn-beijing", ACCOUNT_ID,
                  ACCESS_KEY, SECRET_KEY);
          ListServicesResponse lrBj = clientBj.listServices(new ListServicesRequest());
          assertTrue(lrBj.getStatus() == HttpURLConnection.HTTP_OK);
      }
  
      @Test
      public void testCRUD()
              throws ClientException, JSONException, NoSuchAlgorithmException, InterruptedException, ParseException, IOException {
          testCRUDHelper(true);
      }
  
      @Test
      public void testCRUDStsToken() throws com.aliyuncs.exceptions.ClientException,
              ParseException, InterruptedException, IOException {
          client = overrideFCClient(true, false);
          testCRUDHelper(false);
      }
  
      @Test
      public void testCRUDStsTokenHttps() throws com.aliyuncs.exceptions.ClientException,
              ParseException, InterruptedException, IOException {
          client = overrideFCClient(true, true);
          testCRUDHelper(false);
      }
  
      private String generateNASPythonCode() {
          return "# -*- coding: utf-8 -*-\n"
                  + "import logging  \n"
                  + "import random\n"
                  + "import string\n"
                  + "import os.path\n"
                  + "import shutil\n"
                  + "from os import path\n"
                  + "\n"
                  + "def handler(event, context):\n"
                  + "  logger = logging.getLogger()\n"
                  + "  root_dir1 = \"" + NAS_MOUNT_DIR + "\"\n"
                  + "  logger.info('uid : ' + str(os.geteuid()))\n"
                  + "  logger.info('gid : ' + str(os.getgid()))\n"
                  + "  file_name = randomString(6)+'.txt'\n"
                  + "  dir1 = root_dir1 + '/rzhang-test/'\n"
                  + "  content = \"NAS here I come\"\n"
                  + "  os.makedirs(dir1)\n"
                  + "  fw = open(dir1+file_name, \"w+\")\n"
                  + "  fw.write(content)\n"
                  + "  fw.close()\n"
                  + "  fr = open(dir1+file_name)\n"
                  + "  line = fr.readline()\n"
                  + "  if line != content:\n"
                  + "      return False\n"
                  + "  fr.close()\n"
                  + "  os.remove(dir1+file_name)\n"
                  + "  os.rmdir(dir1)\n"
                  + "  return True\n"
                  + "  \n"
                  + "def randomString(n):\n"
                  + "  return ''.join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(n))\n";
      }
  
      @Test
      public void testCreateNASService() throws IOException {
          String service_name = SERVICE_NAME + "-nas";
          createVPCService(service_name);
          // Create a function that uses NAS
          String source = generateNASPythonCode();
  
          byte[] data = Util.createZipByteData("main.py", source);
          String funcName = FUNCTION_NAME + "-nas";
          // create function
          createFunction(service_name, funcName, "main.handler", "python2.7", data);
  
          // Invoke the function
          InvokeFunctionRequest request = new InvokeFunctionRequest(service_name, funcName);
          request.setPayload("".getBytes());
  
          InvokeFunctionResponse response = client.invokeFunction(request);
          assertEquals("true", new String(response.getPayload()));
  
          // Cleanups
          client.deleteFunction(new DeleteFunctionRequest(service_name, funcName));
          client.deleteService(new DeleteServiceRequest(service_name));
      }
  
      private void preTestProvisionConfig(String serviceName, String functionName, String aliasName) throws Exception {
          // create service
          createService(serviceName, false);
  
          // create function
          createFunction(serviceName, functionName);
  
          // publish a version
          String lastVersion = cleanUpVersions(serviceName);
          ;
          PublishVersionRequest publishVersionRequest = new PublishVersionRequest(serviceName);
          PublishVersionResponse publishVersionResponse = client.publishVersion(publishVersionRequest);
          assertEquals(String.format("%d", Integer.parseInt(lastVersion) + 1), publishVersionResponse.getVersionId());
  
          //Create a Alias against it
          String versionId = publishVersionResponse.getVersionId();
          CreateAliasRequest createAliasRequest = new CreateAliasRequest(serviceName, aliasName, versionId);
          CreateAliasResponse createAliasResponse = client.createAlias(createAliasRequest);
          assertEquals(HttpURLConnection.HTTP_OK, createAliasResponse.getStatus());
          assertEquals(versionId, createAliasResponse.getVersionId());
          assertEquals(aliasName, createAliasResponse.getAliasName());
      }
  
      private void afterTestProvisionConfig(String serviceName) {
          cleanUpAliases(serviceName);
          cleanUpVersions(serviceName);
          cleanUpFunctions(serviceName);
          cleanupService(serviceName);
      }
  
      @Test
      public void testProvisionConfig() {
          String serviceName = SERVICE_NAME + UUID.randomUUID().toString().substring(0, 5);
          String functionName = "hello_world" + UUID.randomUUID().toString().substring(0, 5);
          String aliasName = "myAlias";
  
          try {
              preTestProvisionConfig(serviceName, functionName, aliasName);
  
              try {
                  // create provision config
                  Integer target = 3;
                  PutProvisionConfigRequest provisionConfigRequest = new PutProvisionConfigRequest(serviceName, aliasName, functionName);
                  provisionConfigRequest.setTarget(target);
                  PutProvisionConfigResponse provisionConfigResponse = client.putProvisionConfig(provisionConfigRequest);
                  assertEquals(HttpURLConnection.HTTP_OK, provisionConfigResponse.getStatus());
                  assertEquals(target, provisionConfigResponse.getTarget());
  
                  // listProvisionConfig
                  ListProvisionConfigsRequest listProvisionConfigsRequest = new ListProvisionConfigsRequest();
                  listProvisionConfigsRequest.setServiceName(serviceName);
                  listProvisionConfigsRequest.setQualifier(aliasName);
                  listProvisionConfigsRequest.setLimit(100);
                  ListProvisionConfigsResponse listProvisionConfigsResponse = client.listProvisionConfigs(listProvisionConfigsRequest);
                  assertEquals(HttpURLConnection.HTTP_OK, listProvisionConfigsResponse.getStatus());
                  assertEquals(target, listProvisionConfigsResponse.getProvisionConfigs()[0].getTarget());
              } catch (Exception e0) {
                  assertNull(e0);
              } finally {
                  cleanupProvision(serviceName, aliasName, functionName);
              }
          } catch (Exception e) {
              assertNull(e);
          } finally {
              afterTestProvisionConfig(serviceName);
          }
      }
  
      @Test
      public void testProvisionConfigWithScheduledAction() {
          String serviceName = SERVICE_NAME + UUID.randomUUID().toString().substring(0, 5);
          String functionName = "hello_world" + UUID.randomUUID().toString().substring(0, 5);
          String aliasName = "myAlias";
  
          try {
              preTestProvisionConfig(serviceName, functionName, aliasName);
  
              try {
                  // create provision config
                  Integer target = 3;
                  Integer scheduledActionTarget1 = 5;
                  Integer scheduledActionTarget2 = 5;
                  PutProvisionConfigRequest provisionConfigRequest = new PutProvisionConfigRequest(serviceName, aliasName, functionName);
                  ScheduledAction[] scheduledActions = new ScheduledAction[2];
                  scheduledActions[0] = new ScheduledAction("a1", "2020-10-10T10:10:10Z",
                          "2030-10-10T10:10:10Z", scheduledActionTarget1, "at(2020-10-20T10:10:10Z)");
                  scheduledActions[1] = new ScheduledAction("a2", "2020-10-10T10:10:10Z",
                          "2030-10-10T10:10:10Z", scheduledActionTarget2, "cron(0 */30 * * * *)");
                  provisionConfigRequest.setTarget(target);
                  provisionConfigRequest.setScheduledActions(scheduledActions);
  
                  PutProvisionConfigResponse provisionConfigResponse = client.putProvisionConfig(provisionConfigRequest);
                  assertEquals(HttpURLConnection.HTTP_OK, provisionConfigResponse.getStatus());
                  assertEquals(target, provisionConfigResponse.getTarget());
                  assertEquals(scheduledActions.length, provisionConfigResponse.getScheduledActions().length);
                  assertEquals(scheduledActionTarget1, provisionConfigResponse.getScheduledActions()[0].getTarget());
                  assertEquals(scheduledActionTarget2, provisionConfigResponse.getScheduledActions()[1].getTarget());
  
                  // retry 120s for autoScalingLoop
                  int retryTimes = 0;
                  while (retryTimes < 120) {
                      // get provisionConfig
                      GetProvisionConfigRequest getProvisionConfigRequest = new GetProvisionConfigRequest(serviceName, aliasName, functionName);
                      GetProvisionConfigResponse getProvisionConfigResponse = client.getProvisionConfig(getProvisionConfigRequest);
                      if (getProvisionConfigResponse.getCurrent() != scheduledActionTarget2) {
                          Thread.sleep(1000); // sleep 1s
                          retryTimes++;
                          continue;
                      }
                      assertEquals(scheduledActionTarget2, getProvisionConfigResponse.getTarget());
                      assertEquals(scheduledActionTarget2, getProvisionConfigResponse.getCurrent());
                      assertEquals(scheduledActions.length, provisionConfigResponse.getScheduledActions().length);
                      assertEquals(scheduledActionTarget1, provisionConfigResponse.getScheduledActions()[0].getTarget());
                      assertEquals(scheduledActionTarget2, provisionConfigResponse.getScheduledActions()[1].getTarget());
                      break;
                  }
                  assertEquals(true, retryTimes < 120);
  
                  // set scheduledActions to null, assert scheduledActions will not be modified
                  provisionConfigRequest.setScheduledActions(null);
                  PutProvisionConfigResponse provisionConfigResponse2 = client.putProvisionConfig(provisionConfigRequest);
                  assertEquals(HttpURLConnection.HTTP_OK, provisionConfigResponse2.getStatus());
                  assertEquals(scheduledActions.length, provisionConfigResponse2.getScheduledActions().length);
  
                  // set scheduledActions to [], assert scheduledActions will be modified to empty
                  provisionConfigRequest.setScheduledActions(new ScheduledAction[0]);
                  PutProvisionConfigResponse provisionConfigResponse3 = client.putProvisionConfig(provisionConfigRequest);
                  assertEquals(HttpURLConnection.HTTP_OK, provisionConfigResponse3.getStatus());
                  assertEquals(0, provisionConfigResponse3.getScheduledActions().length);
              } catch (Exception e0) {
                  e0.printStackTrace();
                  assertNull(e0);
              } finally {
                  cleanupProvision(serviceName, aliasName, functionName);
              }
          } catch (Exception e) {
              e.printStackTrace();
              assertNull(e);
          } finally {
              afterTestProvisionConfig(serviceName);
          }
      }
  
      @Test
      public void testProvisionConfigWithScheduledActionValidate() {
          String serviceName = SERVICE_NAME + UUID.randomUUID().toString().substring(0, 5);
          String functionName = "hello_world" + UUID.randomUUID().toString().substring(0, 5);
          String aliasName = "myAlias";
  
          try {
              preTestProvisionConfig(serviceName, functionName, aliasName);
  
              try {
                  // actionName repeated
                  scheduledActionValidate1(serviceName, functionName, aliasName);
  
                  // utc time format error
                  scheduledActionValidate2(serviceName, functionName, aliasName);
  
                  // scheduleExpression error
                  scheduledActionValidate3(serviceName, functionName, aliasName);
  
                  // actions out of size
                  scheduledActionValidate4(serviceName, functionName, aliasName);
              } catch (Exception e0) {
                  assertNull(e0);
              }
          } catch (Exception e) {
              assertNull(e);
          } finally {
              afterTestProvisionConfig(serviceName);
          }
      }
  
      @Test
      public void testProvisionConfigWithTargetTrackingPolicies() {
          String serviceName = SERVICE_NAME + UUID.randomUUID().toString().substring(0, 5);
          String functionName = "hello_world" + UUID.randomUUID().toString().substring(0, 5);
          String aliasName = "myAlias";
  
          try {
              preTestProvisionConfig(serviceName, functionName, aliasName);
  
              try {
                  // create provision config
                  Integer target = 3;
                  PutProvisionConfigRequest provisionConfigRequest = new PutProvisionConfigRequest(serviceName, aliasName, functionName);
                  TargetTrackingPolicy[] policies = new TargetTrackingPolicy[1];
                  policies[0] = new TargetTrackingPolicy("p1", "2020-10-10T10:10:10Z",
                          "2030-10-10T10:10:10Z", "ProvisionedConcurrencyUtilization", new Double(0.6f), 5, 200);
                  provisionConfigRequest.setTarget(target);
                  provisionConfigRequest.setTargetTrackingPolicies(policies);
  
                  PutProvisionConfigResponse provisionConfigResponse = client.putProvisionConfig(provisionConfigRequest);
                  assertEquals(HttpURLConnection.HTTP_OK, provisionConfigResponse.getStatus());
                  assertEquals(target, provisionConfigResponse.getTarget());
                  assertEquals(1, provisionConfigResponse.getTargetTrackingPolicies().length);
                  assertEquals("p1", provisionConfigResponse.getTargetTrackingPolicies()[0].getName());
                  assertEquals("2020-10-10T10:10:10Z", provisionConfigResponse.getTargetTrackingPolicies()[0].getStartTime());
                  assertEquals("2030-10-10T10:10:10Z", provisionConfigResponse.getTargetTrackingPolicies()[0].getEndTime());
                  assertEquals("ProvisionedConcurrencyUtilization", provisionConfigResponse.getTargetTrackingPolicies()[0].getMetricType());
                  assertEquals(new Double(0.6f), provisionConfigResponse.getTargetTrackingPolicies()[0].getMetricTarget());
                  assertEquals(5, provisionConfigResponse.getTargetTrackingPolicies()[0].getMinCapacity().intValue());
                  assertEquals(200, provisionConfigResponse.getTargetTrackingPolicies()[0].getMaxCapacity().intValue());
  
                  // set targetTrackingPolicies to null, assert targetTrackingPolicies will not be modified
                  provisionConfigRequest.setTargetTrackingPolicies(null);
                  PutProvisionConfigResponse provisionConfigResponse2 = client.putProvisionConfig(provisionConfigRequest);
                  assertEquals(HttpURLConnection.HTTP_OK, provisionConfigResponse2.getStatus());
                  assertEquals(1, provisionConfigResponse2.getTargetTrackingPolicies().length);
  
                  // set targetTrackingPolicies to [], assert targetTrackingPolicies will be modified to empty
                  provisionConfigRequest.setTargetTrackingPolicies(new TargetTrackingPolicy[0]);
                  PutProvisionConfigResponse provisionConfigResponse3 = client.putProvisionConfig(provisionConfigRequest);
                  assertEquals(HttpURLConnection.HTTP_OK, provisionConfigResponse3.getStatus());
                  assertEquals(0, provisionConfigResponse3.getTargetTrackingPolicies().length);
              } catch (Exception e0) {
                  e0.printStackTrace();
                  assertNull(e0);
              } finally {
                  cleanupProvision(serviceName, aliasName, functionName);
              }
          } catch (Exception e) {
              e.printStackTrace();
              assertNull(e);
          } finally {
              afterTestProvisionConfig(serviceName);
          }
      }
  
      // actionName repeated
      private void scheduledActionValidate1(String serviceName, String functionName, String aliasName) {
          String actionName = "action1";
          try {
              // create provision config
              Integer target = 3;
              Integer scheduledActionTarget1 = 5;
              Integer scheduledActionTarget2 = 5;
              PutProvisionConfigRequest provisionConfigRequest = new PutProvisionConfigRequest(serviceName, aliasName, functionName);
              ScheduledAction[] scheduledActions = new ScheduledAction[2];
              scheduledActions[0] = new ScheduledAction(actionName, "2020-10-10T10:10:10Z",
                      "2030-10-10T10:10:10Z", scheduledActionTarget1, "at(2020-10-20T10:10:10Z)");
              scheduledActions[1] = new ScheduledAction(actionName, "2020-10-10T10:10:10Z",
                      "2030-10-10T10:10:10Z", scheduledActionTarget2, "cron(0 */30 * * * *)");
              provisionConfigRequest.setTarget(target);
              provisionConfigRequest.setScheduledActions(scheduledActions);
  
              client.putProvisionConfig(provisionConfigRequest);
          } catch (ClientException clientException) {
              assertEquals("Duplicate action name '" + actionName + "' in ScheduledActions is not allowed",
                      clientException.getErrorMessage());
          }
      }
  
      // utc time format error
      private void scheduledActionValidate2(String serviceName, String functionName, String aliasName) {
          try {
              // create provision config
              Integer target = 3;
              Integer scheduledActionTarget1 = 5;
              PutProvisionConfigRequest provisionConfigRequest = new PutProvisionConfigRequest(serviceName, aliasName, functionName);
              ScheduledAction[] scheduledActions = new ScheduledAction[1];
              scheduledActions[0] = new ScheduledAction("a1", "2020-10-10T10:10:10",
                      "2030-10-10T10:10:10Z", scheduledActionTarget1, "at(2020-10-20T10:10:10Z)");
              provisionConfigRequest.setTarget(target);
              provisionConfigRequest.setScheduledActions(scheduledActions);
  
              client.putProvisionConfig(provisionConfigRequest);
          } catch (ClientException clientException) {
              assertEquals("The StartTime is not in UTC time format (example: '2020-10-10T10:10:10Z', " +
                      "actual: '2020-10-10T10:10:10')", clientException.getErrorMessage());
          }
      }
  
      // scheduleExpression error
      private void scheduledActionValidate3(String serviceName, String functionName, String aliasName) {
          try {
              // create provision config
              Integer target = 3;
              Integer scheduledActionTarget1 = 5;
              PutProvisionConfigRequest provisionConfigRequest = new PutProvisionConfigRequest(serviceName, aliasName, functionName);
              ScheduledAction[] scheduledActions = new ScheduledAction[1];
              scheduledActions[0] = new ScheduledAction("a1", "2020-10-10T10:10:10Z",
                      "2030-10-10T10:10:10Z", scheduledActionTarget1, "cron(0s */30 * * * *)");
              provisionConfigRequest.setTarget(target);
              provisionConfigRequest.setScheduledActions(scheduledActions);
  
              client.putProvisionConfig(provisionConfigRequest);
          } catch (ClientException clientException) {
              assertEquals("The ScheduleExpression should be atTime or cron expression " +
                              "(example: ['at(2020-10-10T10:10:10Z)', 'cron(0 */30 * * * *)'], actual: 'cron(0s */30 * * * *)')",
                      clientException.getErrorMessage());
          }
      }
  
      // actions out of size
      private void scheduledActionValidate4(String serviceName, String functionName, String aliasName) {
          try {
              // create provision config
              Integer target = 3;
              Integer scheduledActionTarget = 5;
              PutProvisionConfigRequest provisionConfigRequest = new PutProvisionConfigRequest(serviceName, aliasName, functionName);
              ScheduledAction[] scheduledActions = new ScheduledAction[110];
              for (int index = 0; index < 110; index++) {
                  scheduledActions[index] = new ScheduledAction("action_" + index, "2020-10-10T10:10:10Z",
                          "2030-10-10T10:10:10Z", scheduledActionTarget, "cron(0 */30 * * * *)");
              }
              provisionConfigRequest.setTarget(target);
              provisionConfigRequest.setScheduledActions(scheduledActions);
  
              client.putProvisionConfig(provisionConfigRequest);
          } catch (ClientException clientException) {
              assertEquals("ScheduledActions contains too many values (max: 100, actual: 110)",
                      clientException.getErrorMessage());
          }
      }
  
      @Test
      public void testServiceWithTracingConfig() {
          String serviceName = SERVICE_NAME + "-tracing";
          String functionName = "hello_world";
          JaegerConfig jaegerConfig = new JaegerConfig();
          jaegerConfig.setEndpoint(JAEGER_ENDPOINT);
          TracingConfig tracingConfig = new TracingConfig();
          tracingConfig.setJaegerConfig(jaegerConfig);
  
          try {
              // create service with tracingConfig
              CreateServiceRequest req = new CreateServiceRequest();
              req.setServiceName(serviceName);
              req.setTracingConfig(tracingConfig);
              CreateServiceResponse resp = client.createService(req);
              assertNotNull(resp.getTracingConfig());
              assertNotNull(resp.getTracingConfig().getJaegerConfig());
              assertEquals(JAEGER_ENDPOINT, resp.getTracingConfig().getJaegerConfig().getEndpoint());
  
              // get service with tracingConfig
              GetServiceRequest getServiceRequest = new GetServiceRequest(serviceName);
              GetServiceResponse getServiceResponse = client.getService(getServiceRequest);
              assertNotNull(getServiceResponse.getTracingConfig());
              assertNotNull(getServiceResponse.getTracingConfig().getJaegerConfig());
              assertEquals(JAEGER_ENDPOINT, getServiceResponse.getTracingConfig().getJaegerConfig().getEndpoint());
  
              // create function
              String source = "exports.handler = function(event, context, callback) {\n" +
                      "  callback(null, context.tracing.openTracingSpanContext + '|' + context.tracing.openTracingSpanBaggages['key']);\n" +
                      "};";
  
              byte[] code = Util.createZipByteData("hello_world.js", source);
  
              CreateFunctionRequest createFuncReq = new CreateFunctionRequest(serviceName);
              createFuncReq.setFunctionName(functionName);
              createFuncReq.setDescription(FUNCTION_DESC_OLD);
              createFuncReq.setMemorySize(128);
              createFuncReq.setHandler("hello_world.handler");
              createFuncReq.setRuntime("nodejs4.4");
              createFuncReq.setCode(new Code().setZipFile(code));
              createFuncReq.setTimeout(10);
              CreateFunctionResponse response = client.createFunction(createFuncReq);
              assertEquals(functionName, response.getFunctionName());
  
              // invokeFunction with injected span context
              InvokeFunctionRequest invokeFunctionRequest = new InvokeFunctionRequest(serviceName, functionName);
              invokeFunctionRequest.setHeader(OPENTRACING_SPANCONTEXT, "124ed43254b54966:124ed43254b54966:0:1");
              invokeFunctionRequest.setHeader(OPENTRACING_SPANCONTEXT_BAGGAGE_PREFIX + "key", "val");
              InvokeFunctionResponse invokeFunctionResponse = client.invokeFunction(invokeFunctionRequest);
  
              String payload = new String(invokeFunctionResponse.getPayload());
              assertTrue(payload.contains("124ed43254b54966"));
              assertTrue(payload.contains("val"));
  
              // update service and disable tracingConfig
              UpdateServiceRequest updateServiceRequest = new UpdateServiceRequest(serviceName);
              updateServiceRequest.setTracingConfig(new TracingConfig());
              UpdateServiceResponse updateServiceResponse = client.updateService(updateServiceRequest);
              assertNotNull(updateServiceResponse.getTracingConfig());
              assertNull(updateServiceResponse.getTracingConfig().getType());
              assertNull(updateServiceResponse.getTracingConfig().getParams());
          } catch (Exception e) {
              e.printStackTrace();
              // assert case fail
              assertNull(e);
          } finally {
              cleanUpFunctions(serviceName);
              cleanupService(serviceName);
          }
      }
  
      @Test
      public void testServiceWithRequestMetrics() {
          String serviceName = SERVICE_NAME + "-requestMetrics";
          LogConfig logConfig = new LogConfig(LOG_PROJECT, LOG_STORE, true);
  
          try {
              // create service with enableRequestMetrics
              CreateServiceRequest req = new CreateServiceRequest();
              req.setServiceName(serviceName);
              req.setRole(ROLE);
              req.setLogConfig(logConfig);
              CreateServiceResponse resp = client.createService(req);
              assertNotNull(resp.getLogConfig());
              assertTrue(resp.getLogConfig().getEnableRequestMetrics());
              assertFalse(resp.getLogConfig().getEnableInstanceMetrics());
  
              // get service
              GetServiceRequest getServiceRequest = new GetServiceRequest(serviceName);
              GetServiceResponse getServiceResponse = client.getService(getServiceRequest);
              assertNotNull(getServiceResponse.getLogConfig());
              assertTrue(getServiceResponse.getLogConfig().getEnableRequestMetrics());
              assertFalse(getServiceResponse.getLogConfig().getEnableInstanceMetrics());
  
              // update service and disable requestMetrics
              logConfig.setEnableRequestMetrics(false);
              UpdateServiceRequest updateServiceRequest = new UpdateServiceRequest(serviceName);
              updateServiceRequest.setLogConfig(logConfig);
              UpdateServiceResponse updateServiceResponse = client.updateService(updateServiceRequest);
              assertNotNull(updateServiceResponse.getLogConfig());
              assertEquals(LOG_PROJECT, updateServiceResponse.getLogConfig().getProject());
              assertEquals(LOG_STORE, updateServiceResponse.getLogConfig().getLogStore());
              assertFalse(updateServiceResponse.getLogConfig().getEnableRequestMetrics());
              assertFalse(updateServiceResponse.getLogConfig().getEnableInstanceMetrics());
  
              // update service and disable logs
              logConfig = new LogConfig("", "", false);
              updateServiceRequest = new UpdateServiceRequest(serviceName);
              updateServiceRequest.setLogConfig(logConfig);
              updateServiceResponse = client.updateService(updateServiceRequest);
              assertNotNull(updateServiceResponse.getLogConfig());
              assertEquals("", updateServiceResponse.getLogConfig().getProject());
              assertEquals("", updateServiceResponse.getLogConfig().getLogStore());
              assertFalse(updateServiceResponse.getLogConfig().getEnableRequestMetrics());
              assertFalse(updateServiceResponse.getLogConfig().getEnableInstanceMetrics());
          } catch (Exception e) {
              e.printStackTrace();
              // assert case fail
              assertNull(e);
          } finally {
              cleanUpFunctions(serviceName);
              cleanupService(serviceName);
          }
      }
  
      @Test
      public void testServiceWithRequestMetricsAndInstanceMetrics() {
          String serviceName = SERVICE_NAME + "-instanceMetrics";
          LogConfig logConfig = new LogConfig(LOG_PROJECT, LOG_STORE, true, true, NONE);
  
          try {
              // create service with enableRequestMetrics and enableInstanceMetrics
              CreateServiceRequest req = new CreateServiceRequest();
              req.setServiceName(serviceName);
              req.setRole(ROLE);
              req.setLogConfig(logConfig);
              CreateServiceResponse resp = client.createService(req);
              assertNotNull(resp.getLogConfig());
              assertTrue(resp.getLogConfig().getEnableRequestMetrics());
              assertTrue(resp.getLogConfig().getEnableInstanceMetrics());
              assertEquals(NONE, resp.getLogConfig().getLogBeginRule());
  
              // get service
              GetServiceRequest getServiceRequest = new GetServiceRequest(serviceName);
              GetServiceResponse getServiceResponse = client.getService(getServiceRequest);
              assertNotNull(getServiceResponse.getLogConfig());
              assertTrue(getServiceResponse.getLogConfig().getEnableRequestMetrics());
              assertTrue(getServiceResponse.getLogConfig().getEnableInstanceMetrics());
              assertEquals(NONE, getServiceResponse.getLogConfig().getLogBeginRule());
  
  
              // update service and disable requestMetrics and instance metrics
              logConfig.setEnableRequestMetrics(false);
              logConfig.setEnableInstanceMetrics(false);
              logConfig.setLogBeginRule(DEFAULT_REGEX);
              UpdateServiceRequest updateServiceRequest = new UpdateServiceRequest(serviceName);
              updateServiceRequest.setLogConfig(logConfig);
              UpdateServiceResponse updateServiceResponse = client.updateService(updateServiceRequest);
              assertNotNull(updateServiceResponse.getLogConfig());
              assertEquals(LOG_PROJECT, updateServiceResponse.getLogConfig().getProject());
              assertEquals(LOG_STORE, updateServiceResponse.getLogConfig().getLogStore());
              assertFalse(updateServiceResponse.getLogConfig().getEnableRequestMetrics());
              assertFalse(updateServiceResponse.getLogConfig().getEnableInstanceMetrics());
              assertEquals(DEFAULT_REGEX, updateServiceResponse.getLogConfig().getLogBeginRule());
  
              // update service and disable logs
              logConfig = new LogConfig("", "",
                      false, false, NONE);
              updateServiceRequest = new UpdateServiceRequest(serviceName);
              updateServiceRequest.setLogConfig(logConfig);
              updateServiceResponse = client.updateService(updateServiceRequest);
              assertNotNull(updateServiceResponse.getLogConfig());
              assertEquals("", updateServiceResponse.getLogConfig().getProject());
              assertEquals("", updateServiceResponse.getLogConfig().getLogStore());
              assertFalse(updateServiceResponse.getLogConfig().getEnableRequestMetrics());
              assertFalse(updateServiceResponse.getLogConfig().getEnableInstanceMetrics());
              assertEquals(NONE, updateServiceResponse.getLogConfig().getLogBeginRule());
          } catch (Exception e) {
              e.printStackTrace();
              // assert case fail
              assertNull(e);
          } finally {
              cleanUpFunctions(serviceName);
              cleanupService(serviceName);
          }
      }
  
      @Test
      public void testCreateServiceStsTokenNoPassRole()
              throws com.aliyuncs.exceptions.ClientException {
          // Use a policy that does not have ram:PassRole, this policy will intersect with the role policy
          // Access denied is expected if using STS without PassRole allowed
          // Policy intersection doc: https://help.aliyun.com/document_detail/31935.html
          String policy = "{\"Version\": \"1\",\"Statement\": [{\"Effect\": \"Allow\",\"Action\": [\"fc:*\"],\"Resource\": [\"*\"]}]}";
          Credentials creds = getAssumeRoleCredentials(policy);
          client = new FunctionComputeClient(new Config(REGION, ACCOUNT_ID,
                  creds.getAccessKeyId(), creds.getAccessKeySecret(), creds.getSecurityToken(),
                  false));
  
          try {
              createService(SERVICE_NAME);
              fail("ClientException is expected");
          } catch (ClientException e) {
              assertTrue(e.getErrorMessage(), e.getErrorMessage()
                      .contains("the caller is not authorized to perform 'ram:PassRole'"));
          }
      }
  
      @Test
      public void testCRUDHttpTrigger() throws ParseException, InterruptedException, IOException {
  
          // create service
          createService(SERVICE_NAME);
  
          // Create Function
          createFunction(FUNCTION_NAME);
  
          // create http trigger
          createHttpTrigger(TRIGGER_NAME, ANONYMOUS, new HttpMethod[]{GET, POST});
  
          // List Triggers
          TriggerMetadata[] triggers = listTriggers(SERVICE_NAME, FUNCTION_NAME);
  
          assertEquals(1, triggers.length);
  
          TriggerMetadata trigger = triggers[0];
  
          assertEquals(TRIGGER_NAME, trigger.getTriggerName());
          assertEquals("http", trigger.getTriggerType());
  
          // retrieve http trigger
          GetTriggerRequest getTReq = new GetTriggerRequest(SERVICE_NAME, FUNCTION_NAME,
                  TRIGGER_NAME);
  
          GetTriggerResponse getTResp = client.getTrigger(getTReq);
          HttpTriggerConfig triggerConfig = gson
                  .fromJson(gson.toJson(getTResp.getTriggerConfig()), HttpTriggerConfig.class);
  
          assertFalse(Strings.isNullOrEmpty(getTResp.getRequestId()));
          assertEquals(TRIGGER_NAME, getTResp.getTriggerName());
          assertEquals(TRIGGER_TYPE_HTTP, getTResp.getTriggerType());
          assertTrue(deepEquals(new HttpMethod[]{GET, POST}, triggerConfig.getMethods()));
  
          // update http trigger
          GetTriggerResponse triggerOld = getTResp;
          HttpTriggerConfig updateTriggerConfig = new HttpTriggerConfig(
                  FUNCTION, new HttpMethod[]{POST});
  
          UpdateTriggerRequest updateTReq = new UpdateTriggerRequest(SERVICE_NAME, FUNCTION_NAME,
                  TRIGGER_NAME);
          updateTReq.setTriggerConfig(updateTriggerConfig);
  
          Thread.sleep(1000);
  
          UpdateTriggerResponse updateTResp = updateTrigger(updateTReq);
          assertEquals(triggerOld.getTriggerName(), updateTResp.getTriggerName());
  
          Gson gson = new Gson();
          HttpTriggerConfig tcOld = gson
                  .fromJson(gson.toJson(triggerOld.getTriggerConfig()), HttpTriggerConfig.class);
          HttpTriggerConfig tcNew = gson
                  .fromJson(gson.toJson(updateTResp.getTriggerConfig()), HttpTriggerConfig.class);
          assertFalse(deepEquals(tcOld.getMethods(), tcNew.getMethods()));
          assertNotEquals(tcOld.getAuthType(), tcNew.getAuthType());
  
          assertEquals(triggerOld.getCreatedTime(), updateTResp.getCreatedTime());
          assertEquals(triggerOld.getTriggerType(), updateTResp.getTriggerType());
  
          Date dateOld = DATE_FORMAT.parse(triggerOld.getLastModifiedTime());
          Date dateNew = DATE_FORMAT.parse(updateTResp.getLastModifiedTime());
  
          assertTrue(dateOld.before(dateNew));
  
          // delete http trigger
          deleteTrigger(SERVICE_NAME, FUNCTION_NAME, TRIGGER_NAME);
  
          getTReq = new GetTriggerRequest(SERVICE_NAME, FUNCTION_NAME,
                  TRIGGER_NAME);
  
          try {
              client.getTrigger(getTReq);
          } catch (ClientException e) {
              assertEquals(404, e.getStatusCode());
          }
  
          cleanUpFunctions(SERVICE_NAME);
          cleanupService(SERVICE_NAME);
      }
  
      @Test
      public void testListServices() {
          final int numServices = 10;
          final int limit = 3;
          final String serviceNamePrefix = SERVICE_NAME + "_listService_test_";
          // Create multiple services
          for (int i = 0; i < numServices; i++) {
              try {
                  client.getService(new GetServiceRequest(serviceNamePrefix + i));
                  cleanupService(serviceNamePrefix + i);
              } catch (ClientException e) {
                  if (!ErrorCodes.SERVICE_NOT_FOUND.equals(e.getErrorCode())) {
                      throw new RuntimeException("Cleanup failed");
                  }
              }
              CreateServiceRequest request = new CreateServiceRequest();
              request.setServiceName(serviceNamePrefix + i);
              request.setDescription(SERVICE_DESC_OLD);
              request.setRole(ROLE);
              CreateServiceResponse response = client.createService(request);
              assertFalse(Strings.isNullOrEmpty(response.getRequestId()));
  
              TagResourceRequest req = new TagResourceRequest();
              req.setResourceArn(String.format("acs:fc:%s:%s:services/%s", REGION, ACCOUNT_ID, serviceNamePrefix + i));
              Map<String, String> tags = new HashMap<String, String>();
              if (i % 2 == 0) {
                  tags.put("k1", "v1");
              } else {
                  tags.put("k2", "v2");
              }
              tags.put("k3", "v3");
              req.setTags(tags);
              TagResourceResponse resp = client.tagResource(req);
              assertFalse(Strings.isNullOrEmpty(resp.getRequestId()));
          }
          ListServicesRequest listRequest = new ListServicesRequest();
          listRequest.setLimit(limit);
          listRequest.setPrefix(serviceNamePrefix);
          ListServicesResponse listResponse = client.listServices(listRequest);
          int numCalled = 1;
          String nextToken = listResponse.getNextToken();
          while (nextToken != null) {
              listRequest.setNextToken(nextToken);
              listResponse = client.listServices(listRequest);
              nextToken = listResponse.getNextToken();
              numCalled++;
          }
          assertEquals(numServices / limit + 1, numCalled);
  
          listRequest = new ListServicesRequest();
          listRequest.setPrefix(serviceNamePrefix);
          Map<String, String> tags = new HashMap<String, String>();
          tags.put("k3", "v3");
          listRequest.setTags(tags);
          listResponse = client.listServices(listRequest);
          assertEquals(numServices, listResponse.getServices().length);
  
          tags.put("k1", "v1");
          listRequest.setTags(tags);
          listResponse = client.listServices(listRequest);
          assertEquals(numServices / 2, listResponse.getServices().length);
  
          tags.put("k2", "v2");
          listRequest.setTags(tags);
          listResponse = client.listServices(listRequest);
          assertEquals(0, listResponse.getServices().length);
  
          // Delete services
          for (int i = 0; i < numServices; i++) {
              String resourceArn = String.format("acs:fc:%s:%s:services/%s", REGION, ACCOUNT_ID, serviceNamePrefix + i);
              UntagResourceRequest req = new UntagResourceRequest();
              req.setResourceArn(resourceArn);
              String[] tagKeys = new String[]{};
              req.setTagKeys(tagKeys);
              req.setAll(true);
              UntagResourceResponse resp = client.untagResource(req);
              assertFalse(Strings.isNullOrEmpty(resp.getRequestId()));
              cleanupService(serviceNamePrefix + i);
          }
      }
  
      @Test
      public void testListFunctions() throws IOException {
          final int numServices = 10;
          final int limit = 3;
  
          // Create service
          createService(SERVICE_NAME);
  
          // Create multiple functions under the test service
          for (int i = 0; i < numServices; i++) {
              CreateFunctionResponse createFResp = createFunction(FUNCTION_NAME + i);
              assertFalse(Strings.isNullOrEmpty(createFResp.getRequestId()));
          }
          ListFunctionsRequest listRequest = new ListFunctionsRequest(SERVICE_NAME);
          listRequest.setLimit(limit);
          listRequest.setPrefix(FUNCTION_NAME);
          ListFunctionsResponse listResponse = client.listFunctions(listRequest);
          int numCalled = 1;
          String nextToken = listResponse.getNextToken();
          while (nextToken != null) {
              listRequest.setNextToken(nextToken);
              listResponse = client.listFunctions(listRequest);
              nextToken = listResponse.getNextToken();
              numCalled++;
          }
          assertEquals(numServices / limit + 1, numCalled);
      }
  
      public void ignoreTestListTriggers() throws IOException {
          final int numTriggers = 5;
          final int limit = 2;
  
          // Create service
          createService(SERVICE_NAME);
          createFunction(FUNCTION_NAME);
  
          // Create multiple trigger under the test function
          for (int i = 0; i < numTriggers; i++) {
              String prefix = "prefix";
              String suffix = "suffix";
              CreateTriggerResponse createTResp = createOssTrigger(TRIGGER_NAME + i,
                      prefix + i, suffix + i);
              assertFalse(Strings.isNullOrEmpty(createTResp.getRequestId()));
          }
  
          ListTriggersRequest listTReq = new ListTriggersRequest(SERVICE_NAME, FUNCTION_NAME);
          listTReq.setLimit(limit);
          ListTriggersResponse listTResp = client.listTriggers(listTReq);
          int numCalled = 1;
          String nextToken = listTResp.getNextToken();
          while (nextToken != null) {
              listTReq.setNextToken(nextToken);
              listTResp = client.listTriggers(listTReq);
              nextToken = listTResp.getNextToken();
              numCalled++;
          }
  
          assertEquals(numTriggers / limit + 1, numCalled);
  
          for (int i = 0; i < numTriggers; i++) {
              DeleteTriggerResponse deleteTResp = deleteTrigger(
                      SERVICE_NAME, FUNCTION_NAME, TRIGGER_NAME + i);
              assertFalse(Strings.isNullOrEmpty(deleteTResp.getRequestId()));
          }
      }
  
      @Test
      public void testOnDemandConfig() throws Exception {
          final int numOnDemandConfigs = 5;
          for (int i = 0; i < numOnDemandConfigs; i++) {
              // put first
              String qualifier = String.format("test-qualifier-%d", i);
              PutOnDemandConfigRequest putOnDemandConfigRequest = new PutOnDemandConfigRequest(SERVICE_NAME, qualifier, FUNCTION_NAME, i);
              PutOnDemandConfigResponse putOnDemandConfigResponse = client.putOnDemandConfig(putOnDemandConfigRequest);
  
              // validate put response
              assertNotEquals("", putOnDemandConfigResponse.getRequestId());
              String resource = putOnDemandConfigResponse.getResource();
              assertEquals(String.format("services/%s.%s/functions/%s", SERVICE_NAME, qualifier, FUNCTION_NAME), resource);
              assertEquals(i, putOnDemandConfigResponse.getMaximumInstanceCount());
  
              // validate get response
              GetOnDemandConfigRequest getOnDemandConfigRequest = new GetOnDemandConfigRequest(SERVICE_NAME, qualifier, FUNCTION_NAME);
              GetOnDemandConfigResponse getOnDemandConfigResponse = client.getOnDemandConfig(getOnDemandConfigRequest);
              assertNotEquals("", getOnDemandConfigResponse.getRequestId());
              resource = getOnDemandConfigResponse.getResource();
              assertEquals(String.format("services/%s.%s/functions/%s", SERVICE_NAME, qualifier, FUNCTION_NAME), resource);
              assertEquals(i, putOnDemandConfigResponse.getMaximumInstanceCount());
          }
  
  
          // validate list response
          ListOnDemandConfigsRequest listOnDemandConfigsRequest = new ListOnDemandConfigsRequest();
          ListOnDemandConfigsResponse listOnDemandConfigsResponse = client.listOnDemandConfigs(listOnDemandConfigsRequest);
          assertNotEquals("", listOnDemandConfigsResponse.getRequestId());
          assertEquals(numOnDemandConfigs, listOnDemandConfigsResponse.getOnDemandConfigs().length);
  
          // with prefix
          listOnDemandConfigsRequest = new ListOnDemandConfigsRequest();
          listOnDemandConfigsRequest.setPrefix(String.format("services/%s.%s", SERVICE_NAME, "test-qualifier-1"));
          listOnDemandConfigsResponse = client.listOnDemandConfigs(listOnDemandConfigsRequest);
          assertNotEquals("", listOnDemandConfigsResponse.getRequestId());
          assertEquals(1, listOnDemandConfigsResponse.getOnDemandConfigs().length);
  
          listOnDemandConfigsRequest = new ListOnDemandConfigsRequest();
          listOnDemandConfigsRequest.setStartKey(String.format("services/%s.%s", SERVICE_NAME, "test-qualifier-3"));
          listOnDemandConfigsResponse = client.listOnDemandConfigs(listOnDemandConfigsRequest);
          assertNotEquals("", listOnDemandConfigsResponse.getRequestId());
          assertEquals(2, listOnDemandConfigsResponse.getOnDemandConfigs().length);
  
          // with limit
          listOnDemandConfigsRequest = new ListOnDemandConfigsRequest();
          listOnDemandConfigsRequest.setLimit(3);
          listOnDemandConfigsResponse = client.listOnDemandConfigs(listOnDemandConfigsRequest);
          assertEquals(3, listOnDemandConfigsResponse.getOnDemandConfigs().length);
  
          HashMap<String, OnDemandConfigMetadata> map = new HashMap<String, OnDemandConfigMetadata>();
          for (OnDemandConfigMetadata data : listOnDemandConfigsResponse.getOnDemandConfigs()) {
              map.put(data.getResource(), data);
          }
  
          // list again
          listOnDemandConfigsRequest.setNextToken(listOnDemandConfigsResponse.getNextToken());
          listOnDemandConfigsResponse = client.listOnDemandConfigs(listOnDemandConfigsRequest);
          assertEquals(2, listOnDemandConfigsResponse.getOnDemandConfigs().length);
          for (OnDemandConfigMetadata data : listOnDemandConfigsResponse.getOnDemandConfigs()) {
              map.put(data.getResource(), data);
          }
  
          // validate there's no dup resource
          assertEquals(numOnDemandConfigs, map.keySet().size());
  
          for (int i = 0; i < numOnDemandConfigs; i++) {
              // delete configs
              String qualifier = String.format("test-qualifier-%d", i);
              DeleteOnDemandConfigRequest deleteOnDemandConfigRequest = new DeleteOnDemandConfigRequest(SERVICE_NAME, qualifier, FUNCTION_NAME);
              DeleteOnDemandConfigResponse deleteOnDemandConfigResponse = client.deleteOnDemandConfig(deleteOnDemandConfigRequest);
              assertNotEquals("", deleteOnDemandConfigResponse.getRequestId());
  
              try {
                  // validate config not found
                  GetOnDemandConfigRequest getOnDemandConfigRequest = new GetOnDemandConfigRequest(SERVICE_NAME, qualifier, FUNCTION_NAME);
                  GetOnDemandConfigResponse getOnDemandConfigResponse = client.getOnDemandConfig(getOnDemandConfigRequest);
              } catch (ClientException e) {
                  assertEquals("OnDemandConfigNotFound", e.getErrorCode());
              }
          }
  
          // validate no configs can be listed
          listOnDemandConfigsRequest = new ListOnDemandConfigsRequest();
          listOnDemandConfigsResponse = client.listOnDemandConfigs(listOnDemandConfigsRequest);
          assertNotEquals("", listOnDemandConfigsResponse.getRequestId());
          assertEquals(null, listOnDemandConfigsResponse.getOnDemandConfigs());
      }
  
      @Test
      public void testFunctionAsyncConfig() throws Exception {
          createService(SERVICE_NAME);
          createFunction(FUNCTION_NAME);
          String lastVersion = cleanUpVersions(SERVICE_NAME);
          // publish a version
          PublishVersionRequest publishVersionRequest = new PublishVersionRequest(SERVICE_NAME);
          PublishVersionResponse publishVersionResponse = client
                  .publishVersion(publishVersionRequest);
          assertEquals(String.format("%d", Integer.parseInt(lastVersion) + 1),
                  publishVersionResponse.getVersionId());
          lastVersion = publishVersionResponse.getVersionId();
  
          String destFmt = "acs:mns:%s:%s:/queues/qx/messages";
  
          Destination dest = new Destination();
          dest.setDestination(String
                  .format(destFmt, REGION, ACCOUNT_ID));
          AsyncConfig config = new AsyncConfig();
          config.setDestinationConfig(new DestinationConfig());
          config.destinationConfig.setonFailure(dest);
          config.setMaxAsyncEventAgeInSeconds(null);
          config.setMaxAsyncRetryAttempts(1);
  
          final int numConfigs = 3;
          for (int i = 0; i < numConfigs; i++) {
              String qualifier = String.format("test-qualifier-%d", i);
              //Create a Alias against it
              String aliasName = qualifier;
              CreateAliasRequest createAliasRequest = new CreateAliasRequest(SERVICE_NAME, aliasName,
                      lastVersion);
              CreateAliasResponse createAliasResponse = client.createAlias(createAliasRequest);
              // put first
  
              PutFunctionAsyncConfigRequest putFunctionAsyncConfigRequest = new PutFunctionAsyncConfigRequest(SERVICE_NAME, qualifier, FUNCTION_NAME);
              putFunctionAsyncConfigRequest.setAsyncConfig(config);
              PutFunctionAsyncConfigResponse pResp = client.putFunctionAsyncConfig(putFunctionAsyncConfigRequest);
  
              // validate put response
              assertNotEquals("", pResp.getRequestId());
              assertNull(pResp.getAsyncConfig().getMaxAsyncEventAgeInSeconds());
              assertEquals(config.getMaxAsyncRetryAttempts(), pResp.getAsyncConfig().getMaxAsyncRetryAttempts());
              assertEquals(String.format(destFmt, REGION, ACCOUNT_ID),
                      pResp.getAsyncConfig().getDestinationConfig().getOnFailure().getDestination());
  
  
              // validate get response
              GetFunctionAsyncConfigRequest getFunctionAsyncConfigRequest = new GetFunctionAsyncConfigRequest(SERVICE_NAME, qualifier, FUNCTION_NAME);
              GetFunctionAsyncConfigResponse gResp = client.getFunctionAsyncConfig(getFunctionAsyncConfigRequest);
              assertNotEquals("", gResp.getRequestId());
              assertEquals(String.format(destFmt, REGION, ACCOUNT_ID),
                      gResp.getAsyncConfig().getDestinationConfig().getOnFailure().getDestination());
          }
  
  
          // validate list response
          ListFunctionAsyncConfigsRequest listFunctionAsyncConfigsRequest = new ListFunctionAsyncConfigsRequest(SERVICE_NAME, FUNCTION_NAME);
          ListFunctionAsyncConfigsResponse lResp = client.listFunctionAsyncConfigs(listFunctionAsyncConfigsRequest);
          assertNotEquals("", lResp.getRequestId());
          assertEquals(numConfigs, lResp.getAsyncConfigs().length);
          assertEquals("", lResp.getNextToken());
  
  
          // with limit
          listFunctionAsyncConfigsRequest = new ListFunctionAsyncConfigsRequest(SERVICE_NAME, FUNCTION_NAME);
          listFunctionAsyncConfigsRequest.setLimit(1);
          lResp = client.listFunctionAsyncConfigs(listFunctionAsyncConfigsRequest);
          assertEquals(1, lResp.getAsyncConfigs().length);
          assertEquals("test-qualifier-1", lResp.getNextToken());
  
  
          // list again
          ListFunctionAsyncConfigsRequest listFunctionAsyncConfigsRequest2 = new ListFunctionAsyncConfigsRequest(SERVICE_NAME, FUNCTION_NAME);
          listFunctionAsyncConfigsRequest2.setNextToken(lResp.getNextToken());
          lResp = client.listFunctionAsyncConfigs(listFunctionAsyncConfigsRequest2);
          assertEquals(2, lResp.getAsyncConfigs().length);
  
  
          for (int i = 0; i < numConfigs; i++) {
              // delete configs
              String qualifier = String.format("test-qualifier-%d", i);
              DeleteFunctionAsyncConfigRequest deleteFunctionAsyncConfigRequest = new DeleteFunctionAsyncConfigRequest(SERVICE_NAME, qualifier, FUNCTION_NAME);
              DeleteFunctionAsyncConfigResponse deleteFunctionAsyncConfigResponse = client.deleteFunctionAsyncConfig(deleteFunctionAsyncConfigRequest);
              assertNotEquals("", deleteFunctionAsyncConfigResponse.getRequestId());
  
          }
  
          // validate no configs can be listed
          ListFunctionAsyncConfigsRequest listFunctionAsyncConfigsRequest3 = new ListFunctionAsyncConfigsRequest(SERVICE_NAME, FUNCTION_NAME);
          lResp = client.listFunctionAsyncConfigs(listFunctionAsyncConfigsRequest3);
          assertNotEquals("", lResp.getRequestId());
          assertEquals(null, lResp.getAsyncConfigs());
      }
  
      @Test
      @Ignore
      public void testStatefulAsyncInvocation() throws Exception {
  
          long timeNow = System.currentTimeMillis();
          String invocationID = String.format("stateful-invocationId-%d-1", timeNow);
          String invocationID2 = String.format("stateful-invocationId-%d-2", timeNow);
          createService(SERVICE_NAME);
          createFunction(FUNCTION_NAME);
  
          AsyncConfig config = new AsyncConfig();
          config.setStatefulInvocation(true);
  
          PutFunctionAsyncConfigRequest putFunctionAsyncConfigRequest = new PutFunctionAsyncConfigRequest(SERVICE_NAME, "", FUNCTION_NAME);
          putFunctionAsyncConfigRequest.setAsyncConfig(config);
          PutFunctionAsyncConfigResponse pResp = client.putFunctionAsyncConfig(putFunctionAsyncConfigRequest);
  
          // validate put response
          assertTrue(pResp.getAsyncConfig().getStatefulInvocation());
  
          // validate get response
          GetFunctionAsyncConfigRequest getFunctionAsyncConfigRequest = new GetFunctionAsyncConfigRequest(SERVICE_NAME, "", FUNCTION_NAME);
          GetFunctionAsyncConfigResponse gResp = client.getFunctionAsyncConfig(getFunctionAsyncConfigRequest);
          assertTrue(gResp.getAsyncConfig().getStatefulInvocation());
  
          // async invocation
          // Headers passed in through setHeader should be respected
          InvokeFunctionRequest request = new InvokeFunctionRequest(SERVICE_NAME, FUNCTION_NAME);
          request.setHeader("x-fc-invocation-type", Const.INVOCATION_TYPE_ASYNC);
          request.setStatefulAsyncInvocationId(invocationID);
          InvokeFunctionResponse ivkResp = client.invokeFunction(request);
          request.setStatefulAsyncInvocationId(invocationID2);
          client.invokeFunction(request);
  
          // get stateful invocation
          GetStatefulAsyncInvocationRequest req = new GetStatefulAsyncInvocationRequest(SERVICE_NAME, "", FUNCTION_NAME, invocationID);
          GetStatefulAsyncInvocationResponse resp = client.getStatefulAsyncInvocation(req);
          while (!resp.getStatefulAsyncInvocation().status.equals(StatefulInvocationStatus.Failed.toString()) &&
                  !resp.getStatefulAsyncInvocation().status.equals(StatefulInvocationStatus.Succeeded.toString()) &&
                  !resp.getStatefulAsyncInvocation().status.equals(StatefulInvocationStatus.Stopped.toString())) {
              resp = client.getStatefulAsyncInvocation(req);
              assertEquals(invocationID, resp.getStatefulAsyncInvocation().invocationId);
          }
          assertNotNull(resp.getStatefulAsyncInvocation());
          assertEquals(StatefulInvocationStatus.Succeeded.toString(), resp.getStatefulAsyncInvocation().status);
          assertEquals(ivkResp.getRequestId(), resp.getStatefulAsyncInvocation().getRequestId());
  
          GetStatefulAsyncInvocationResponse resp2 = client.getStatefulAsyncInvocation(req);
  
          // get stateful invocation 2
          while (!resp2.getStatefulAsyncInvocation().status.equals(StatefulInvocationStatus.Failed.toString()) &&
                  !resp2.getStatefulAsyncInvocation().status.equals(StatefulInvocationStatus.Succeeded.toString()) &&
                  !resp2.getStatefulAsyncInvocation().status.equals(StatefulInvocationStatus.Stopped.toString())) {
              GetStatefulAsyncInvocationRequest req2 = new GetStatefulAsyncInvocationRequest(SERVICE_NAME, "", FUNCTION_NAME, invocationID2);
              resp2 = client.getStatefulAsyncInvocation(req2);
              assertEquals(invocationID2, resp2.getStatefulAsyncInvocation().invocationId);
          }
          assertNotNull(resp2.getStatefulAsyncInvocation());
          assertEquals(StatefulInvocationStatus.Succeeded.toString(), resp2.getStatefulAsyncInvocation().status);
  
  
          Thread.sleep(5000);
          // list stateful invocation
          ListStatefulAsyncInvocationsRequest lReq = new ListStatefulAsyncInvocationsRequest(SERVICE_NAME, FUNCTION_NAME);
          lReq.setInvocationIdPrefix(String.format("stateful-invocationId-%d", timeNow));
          lReq.setIncludePayload(true);
          lReq.setLimit(100);
          lReq.setSortOrderByTime(SortOrder.desc);
          lReq.setStartedTimeBegin(String.format("%d", timeNow - 5 * 1000));
          ListStatefulAsyncInvocationsResponse lResp = client.listStatefulAsyncInvocations(lReq);
          assertNotNull(lResp.getStatefulAsyncInvocations());
          assertEquals(2, lResp.getStatefulAsyncInvocations().length);
  
          long timeNowx = System.currentTimeMillis();
          lReq.setStartedTimeBegin(String.format("%d", timeNowx));
          ListStatefulAsyncInvocationsResponse lResp2 = client.listStatefulAsyncInvocations(lReq);
          assertNull(lResp2.getStatefulAsyncInvocations());
  
          // stop an unsupported status invocation
          StopStatefulAsyncInvocationRequest sReq = new StopStatefulAsyncInvocationRequest(SERVICE_NAME, "", FUNCTION_NAME, invocationID);
          StopStatefulAsyncInvocationResponse sResp = new StopStatefulAsyncInvocationResponse();
          try {
              sResp = client.stopStatefulAsyncInvocation(sReq);
              fail("should get InvalidArgument");
          } catch (ClientException ex) {
              assertEquals("InvalidArgument", ex.getErrorCode());
          }
  
          // delete async config for stateful async invocation
          DeleteFunctionAsyncConfigRequest deleteFunctionAsyncConfigRequest = new DeleteFunctionAsyncConfigRequest(SERVICE_NAME, "", FUNCTION_NAME);
          DeleteFunctionAsyncConfigResponse deleteFunctionAsyncConfigResponse = client.deleteFunctionAsyncConfig(deleteFunctionAsyncConfigRequest);
          assertNotEquals("", deleteFunctionAsyncConfigResponse.getRequestId());
      }
  
      @Test
      public void testOpenFunctionCompute() {
          OpenFunctionComputeRequest request = new OpenFunctionComputeRequest();
          request.setAction("OpenFCService");
          OpenFunctionComputeResponse response = client.openFunctionComputeService(request);
          if (response.isSuccess()) {
              assertEquals(true, StringUtils.isNotEmpty(response.getOrderId()));
              assertEquals(true, StringUtils.isNotEmpty(response.getRequestId()));
          } else {
              assertEquals(PopClient.ERROR_CODE_ORDER_OPENED, response.getCode());
          }
      }
  
      @Test
      public void testOpenFunctionComputeValidate() {
          OpenFunctionComputeRequest request = new OpenFunctionComputeRequest();
          request.setAction("OpenFCService222");
          try {
              client.openFunctionComputeService(request);
          } catch (ClientException e) {
              assertEquals("action value must be OpenFCService", e.getMessage());
          }
  
          request = new OpenFunctionComputeRequest();
          request.setAction(null);
          try {
              client.openFunctionComputeService(request);
          } catch (ClientException e) {
              assertEquals("action cannot be blank", e.getMessage());
          }
      }
  
      @Test
      public void testCreateServiceValidate() {
          try {
              CreateServiceRequest request = new CreateServiceRequest();
              client.createService(request);
              fail("ClientException is expected");
          } catch (ClientException e) {
              assertEquals(ErrorCodes.INVALID_ARGUMENT, e.getErrorCode());
          }
  
          try {
              CreateServiceRequest request = new CreateServiceRequest();
              client.createService(request);
              fail("ClientException is expected");
          } catch (ClientException e) {
              assertEquals(ErrorCodes.INVALID_ARGUMENT, e.getErrorCode());
          }
      }
  
      @Test
      public void testCreateFunctionValidate() {
          try {
              CreateFunctionRequest request = new CreateFunctionRequest(null);
              client.createFunction(request);
              fail("ClientException is expected");
          } catch (ClientException e) {
              assertTrue(e.getMessage().contains(VALIDATE_MSG));
          }
  
          try {
              CreateFunctionRequest request = new CreateFunctionRequest("");
              client.createFunction(request);
              fail("ClientException is expected");
          } catch (ClientException e) {
              assertTrue(e.getMessage().contains(VALIDATE_MSG));
          }
      }
  
      @Test
      public void testCreateTriggerValidate() {
          try {
              CreateTriggerRequest request = new CreateTriggerRequest(SERVICE_NAME, null);
              client.createTrigger(request);
              fail("ClientException is expected");
          } catch (ClientException e) {
              assertTrue(e.getMessage().contains(VALIDATE_MSG));
          }
  
          try {
              CreateTriggerRequest request = new CreateTriggerRequest(SERVICE_NAME, "");
              client.createTrigger(request);
              fail("ClientException is expected");
          } catch (ClientException e) {
              assertTrue(e.getMessage().contains(VALIDATE_MSG));
          }
  
          try {
              CreateTriggerRequest request = new CreateTriggerRequest(null, FUNCTION_NAME);
              client.createTrigger(request);
              fail("ClientException is expected");
          } catch (ClientException e) {
              assertTrue(e.getMessage().contains(VALIDATE_MSG));
          }
  
          try {
              CreateTriggerRequest request = new CreateTriggerRequest("", FUNCTION_NAME);
              client.createTrigger(request);
              fail("ClientException is expected");
          } catch (ClientException e) {
              assertTrue(e.getMessage().contains(VALIDATE_MSG));
          }
      }
  
      @Test
      public void testGetServiceValidate() {
          try {
              GetServiceRequest request = new GetServiceRequest(null);
              client.getService(request);
              fail("ClientException is expected");
          } catch (ClientException e) {
              assertTrue(e.getMessage().contains(VALIDATE_MSG));
          }
  
          try {
              GetServiceRequest request = new GetServiceRequest("");
              client.getService(request);
              fail("ClientException is expected");
          } catch (ClientException e) {
              assertTrue(e.getMessage().contains(VALIDATE_MSG));
          }
      }
  
      @Test
      public void testGetFunctionValidate() {
          try {
              GetFunctionRequest request = new GetFunctionRequest(SERVICE_NAME, null);
              client.getFunction(request);
              fail("ClientException is expected");
          } catch (ClientException e) {
              assertTrue(e.getMessage().contains(VALIDATE_MSG));
          }
  
          try {
              GetFunctionRequest request = new GetFunctionRequest(SERVICE_NAME, "");
              client.getFunction(request);
              fail("ClientException is expected");
          } catch (ClientException e) {
              assertTrue(e.getMessage().contains(VALIDATE_MSG));
          }
  
          try {
              GetFunctionRequest request = new GetFunctionRequest(null, FUNCTION_NAME);
              client.getFunction(request);
              fail("ClientException is expected");
          } catch (ClientException e) {
              assertTrue(e.getMessage().contains(VALIDATE_MSG));
          }
  
          try {
              GetFunctionRequest request = new GetFunctionRequest("", FUNCTION_NAME);
              client.getFunction(request);
              fail("ClientException is expected");
          } catch (ClientException e) {
              assertTrue(e.getMessage().contains(VALIDATE_MSG));
          }
      }
  
      @Test
      public void testGetTriggerValidate() {
          try {
              GetTriggerRequest request = new GetTriggerRequest(SERVICE_NAME, FUNCTION_NAME, null);
              client.getTrigger(request);
              fail("ClientException is expected");
          } catch (ClientException e) {
              assertTrue(e.getMessage().contains(VALIDATE_MSG));
          }
  
          try {
              GetTriggerRequest request = new GetTriggerRequest(SERVICE_NAME, FUNCTION_NAME, "");
              client.getTrigger(request);
              fail("ClientException is expected");
          } catch (ClientException e) {
              assertTrue(e.getMessage().contains(VALIDATE_MSG));
          }
  
          try {
              GetTriggerRequest request = new GetTriggerRequest(SERVICE_NAME, null, TRIGGER_NAME);
              client.getTrigger(request);
              fail("ClientException is expected");
          } catch (ClientException e) {
              assertTrue(e.getMessage().contains(VALIDATE_MSG));
          }
  
          try {
              GetTriggerRequest request = new GetTriggerRequest(SERVICE_NAME, "", TRIGGER_NAME);
              client.getTrigger(request);
              fail("ClientException is expected");
          } catch (ClientException e) {
              assertTrue(e.getMessage().contains(VALIDATE_MSG));
          }
  
          try {
              GetTriggerRequest request = new GetTriggerRequest(null, FUNCTION_NAME, TRIGGER_NAME);
              client.getTrigger(request);
              fail("ClientException is expected");
          } catch (ClientException e) {
              assertTrue(e.getMessage().contains(VALIDATE_MSG));
          }
  
          try {
              GetTriggerRequest request = new GetTriggerRequest("", FUNCTION_NAME, TRIGGER_NAME);
              client.getTrigger(request);
              fail("ClientException is expected");
          } catch (ClientException e) {
              assertTrue(e.getMessage().contains(VALIDATE_MSG));
          }
      }
  
      @Test
      public void testInvokeFunctionValidate() {
          try {
              InvokeFunctionRequest request = new InvokeFunctionRequest(SERVICE_NAME, null);
              client.invokeFunction(request);
              fail("ClientException is expected");
          } catch (ClientException e) {
              assertTrue(e.getMessage().contains(VALIDATE_MSG));
          }
  
          try {
              InvokeFunctionRequest request = new InvokeFunctionRequest(SERVICE_NAME, "");
              client.invokeFunction(request);
              fail("ClientException is expected");
          } catch (ClientException e) {
              assertTrue(e.getMessage().contains(VALIDATE_MSG));
          }
  
          try {
              InvokeFunctionRequest request = new InvokeFunctionRequest("", FUNCTION_NAME);
              client.invokeFunction(request);
              fail("ClientException is expected");
          } catch (ClientException e) {
              assertTrue(e.getMessage().contains(VALIDATE_MSG));
          }
  
          try {
              InvokeFunctionRequest request = new InvokeFunctionRequest(null, FUNCTION_NAME);
              client.invokeFunction(request);
              fail("ClientException is expected");
          } catch (ClientException e) {
              assertTrue(e.getMessage().contains(VALIDATE_MSG));
          }
      }
  
      @Test
      public void testListFunctionsValidate() {
          try {
              ListFunctionsRequest request = new ListFunctionsRequest(null);
              client.listFunctions(request);
              fail("ClientException is expected");
          } catch (ClientException e) {
              assertTrue(e.getMessage().contains(VALIDATE_MSG));
          }
  
          try {
              ListFunctionsRequest request = new ListFunctionsRequest("");
              client.listFunctions(request);
              fail("ClientException is expected");
          } catch (ClientException e) {
              assertTrue(e.getMessage().contains(VALIDATE_MSG));
          }
      }
  
      @Test
      public void testListTriggersValidate() {
          try {
              listTriggers(null, FUNCTION_NAME);
              fail("ClientException is expected");
          } catch (ClientException e) {
              assertTrue(e.getMessage().contains(VALIDATE_MSG));
          }
  
          try {
              listTriggers("", FUNCTION_NAME);
              fail("ClientException is expected");
          } catch (ClientException e) {
              assertTrue(e.getMessage().contains(VALIDATE_MSG));
          }
  
          try {
              listTriggers(SERVICE_NAME, null);
              fail("ClientException is expected");
          } catch (ClientException e) {
              assertTrue(e.getMessage().contains(VALIDATE_MSG));
          }
  
          try {
              listTriggers(SERVICE_NAME, "");
              fail("ClientException is expected");
          } catch (ClientException e) {
              assertTrue(e.getMessage().contains(VALIDATE_MSG));
          }
      }
  
      @Test
      public void testUpdateServiceValidate() {
          try {
              UpdateServiceRequest request = new UpdateServiceRequest(null);
              client.updateService(request);
              fail("ClientException is expected");
          } catch (ClientException e) {
              assertTrue(e.getMessage().contains(VALIDATE_MSG));
          }
  
          try {
              UpdateServiceRequest request = new UpdateServiceRequest("");
              client.updateService(request);
              fail("ClientException is expected");
          } catch (ClientException e) {
              assertTrue(e.getMessage().contains(VALIDATE_MSG));
          }
      }
  
      @Test
      public void testUpdateFunctionValidate() {
          try {
              UpdateFunctionRequest request = new UpdateFunctionRequest(SERVICE_NAME, null);
              client.updateFunction(request);
              fail("ClientException is expected");
          } catch (ClientException e) {
              assertTrue(e.getMessage().contains(VALIDATE_MSG));
          }
  
          try {
              UpdateFunctionRequest request = new UpdateFunctionRequest(SERVICE_NAME, "");
              client.updateFunction(request);
              fail("ClientException is expected");
          } catch (ClientException e) {
              assertTrue(e.getMessage().contains(VALIDATE_MSG));
          }
  
          try {
              UpdateFunctionRequest request = new UpdateFunctionRequest(null, FUNCTION_NAME);
              client.updateFunction(request);
              fail("ClientException is expected");
          } catch (ClientException e) {
              assertTrue(e.getMessage().contains(VALIDATE_MSG));
          }
  
          try {
              UpdateFunctionRequest request = new UpdateFunctionRequest("", FUNCTION_NAME);
              client.updateFunction(request);
              fail("ClientException is expected");
          } catch (ClientException e) {
              assertTrue(e.getMessage().contains(VALIDATE_MSG));
          }
      }
  
      @Test
      public void testUpdateTriggerValidate() {
          try {
              UpdateTriggerRequest request = new UpdateTriggerRequest(SERVICE_NAME, FUNCTION_NAME,
                      null);
              client.updateTrigger(request);
              fail("ClientException is expected");
          } catch (ClientException e) {
              assertTrue(e.getMessage().contains(VALIDATE_MSG));
          }
  
          try {
              UpdateTriggerRequest request = new UpdateTriggerRequest(SERVICE_NAME, FUNCTION_NAME,
                      "");
              client.updateTrigger(request);
              fail("ClientException is expected");
          } catch (ClientException e) {
              assertTrue(e.getMessage().contains(VALIDATE_MSG));
          }
  
          try {
              UpdateTriggerRequest request = new UpdateTriggerRequest(SERVICE_NAME, null,
                      TRIGGER_NAME);
              client.updateTrigger(request);
              fail("ClientException is expected");
          } catch (ClientException e) {
              assertTrue(e.getMessage().contains(VALIDATE_MSG));
          }
  
          try {
              UpdateTriggerRequest request = new UpdateTriggerRequest(SERVICE_NAME, "", TRIGGER_NAME);
              client.updateTrigger(request);
              fail("ClientException is expected");
          } catch (ClientException e) {
              assertTrue(e.getMessage().contains(VALIDATE_MSG));
          }
  
          try {
              UpdateTriggerRequest request = new UpdateTriggerRequest(null, FUNCTION_NAME,
                      TRIGGER_NAME);
              client.updateTrigger(request);
              fail("ClientException is expected");
          } catch (ClientException e) {
              assertTrue(e.getMessage().contains(VALIDATE_MSG));
          }
  
          try {
              UpdateTriggerRequest request = new UpdateTriggerRequest("", FUNCTION_NAME,
                      TRIGGER_NAME);
              client.updateTrigger(request);
              fail("ClientException is expected");
          } catch (ClientException e) {
              assertTrue(e.getMessage().contains(VALIDATE_MSG));
          }
      }
  
      @Test
      public void testCreateFunctionSetDir() throws IOException {
          createService(SERVICE_NAME);
  
          // Create a function
          CreateFunctionRequest createFuncReq = new CreateFunctionRequest(SERVICE_NAME);
          createFuncReq.setFunctionName(FUNCTION_NAME);
          createFuncReq.setDescription("Function for test");
          createFuncReq.setMemorySize(128);
          createFuncReq.setHandler("hello_world.handler");
          createFuncReq.setRuntime("nodejs4.4");
  
          // Setup code directory
          String tmpDir = "/tmp/fc_test_" + UUID.randomUUID();
          String funcFilePath = tmpDir + "/" + "hello_world.js";
          new File(tmpDir).mkdir();
  
          PrintWriter out = new PrintWriter(funcFilePath);
          out.println(
                  "'use strict'; module.exports.handler = function(event, context, callback) {console.log('hello world'); callback(null, 'hello world');};");
          out.close();
  
          Code code = new Code().setDir(tmpDir);
          createFuncReq.setCode(code);
          createFuncReq.setTimeout(10);
          client.createFunction(createFuncReq);
  
          // Invoke the function
          InvokeFunctionRequest request = new InvokeFunctionRequest(SERVICE_NAME, FUNCTION_NAME);
          InvokeFunctionResponse response = client.invokeFunction(request);
  
          assertEquals("hello world", new String(response.getPayload()));
  
          // Cleanups
          client.deleteFunction(new DeleteFunctionRequest(SERVICE_NAME, FUNCTION_NAME));
          client.deleteService(new DeleteServiceRequest(SERVICE_NAME));
  
          new File(funcFilePath).delete();
          new File(tmpDir).delete();
      }
  
      @Test
      public void testInvokeFunctionLogTypeSyncTail() throws IOException {
          createService(SERVICE_NAME);
          createFunction(FUNCTION_NAME);
  
          InvokeFunctionRequest request = new InvokeFunctionRequest(SERVICE_NAME, FUNCTION_NAME);
          request.setLogType("Tail");
          InvokeFunctionResponse response = client.invokeFunction(request);
          assertNotNull(response.getLogResult());
          assertTrue(response.getLogResult().contains("Duration"));
          assertTrue(response.getLogResult().contains("Billed Duration"));
          assertTrue(response.getLogResult().contains("Memory Size: 128 MB"));
          assertEquals("hello world", new String(response.getPayload()));
      }
  
      @Test(expected = ClientException.class)
      public void testInvokeFunctionTimeout() throws IOException {
          createService(SERVICE_NAME);
          createFunction(FUNCTION_NAME);
  
          InvokeFunctionRequest request = new InvokeFunctionRequest(SERVICE_NAME, FUNCTION_NAME);
  
          // 1 milliseconds timeout
          client.getConfig().setReadTimeoutMillis(1);
  
          client.invokeFunction(request);
      }
  
      @Test
      public void testInvokeFunctionLogTypeSyncNone() throws IOException {
          createService(SERVICE_NAME);
          String funcName = FUNCTION_NAME + "-logtype";
          createFunction(funcName);
  
          InvokeFunctionRequest request = new InvokeFunctionRequest(SERVICE_NAME, funcName);
          request.setLogType("None");
          InvokeFunctionResponse response = client.invokeFunction(request);
          assertNull(response.getLogResult());
          assertEquals("hello world", new String(response.getPayload()));
      }
  
      @Test
      public void testInvokeFunctionLogTypeSyncInvalid() throws IOException {
          createService(SERVICE_NAME);
          createFunction(FUNCTION_NAME);
  
          InvokeFunctionRequest request = new InvokeFunctionRequest(SERVICE_NAME, FUNCTION_NAME);
          request.setLogType("Invalid");
          try {
              client.invokeFunction(request);
          } catch (ClientException e) {
              assertEquals("InvalidArgument", e.getErrorCode());
              assertEquals(
                      "LogType is set to an invalid value (allowed: Tail | None, actual: 'Invalid')",
                      e.getErrorMessage());
              return;
          }
  
          fail("ClientException is expected");
      }
  
      @Test
      public void testInvokeFunctionLogTypeAsyncNone() throws IOException, InterruptedException {
          createService(SERVICE_NAME);
          createFunction(FUNCTION_NAME);
  
          InvokeFunctionRequest request = new InvokeFunctionRequest(SERVICE_NAME, FUNCTION_NAME);
          request.setInvocationType(Const.INVOCATION_TYPE_ASYNC);
          request.setLogType("None");
          InvokeFunctionResponse response = client.invokeFunction(request);
          assertEquals(HttpURLConnection.HTTP_ACCEPTED, response.getStatus());
  
          Thread.sleep(1000);
      }
  
      @Test
      public void testInvokeFunctionLogTypeAsyncInvalid() throws IOException {
          createService(SERVICE_NAME);
          createFunction(FUNCTION_NAME);
  
          InvokeFunctionRequest request = new InvokeFunctionRequest(SERVICE_NAME, FUNCTION_NAME);
          request.setInvocationType(Const.INVOCATION_TYPE_ASYNC);
          // Anything other than None for async invoke is invalid
          request.setLogType("Tail");
          try {
              client.invokeFunction(request);
          } catch (ClientException e) {
              assertEquals("InvalidArgument", e.getErrorCode());
              assertEquals("LogType is set to an invalid value (allowed: None, actual: 'Tail')",
                      e.getErrorMessage());
              return;
          }
  
          fail("ClientException is expected");
      }
  
      private void createFunction(String serviceName, String functionName, String handler,
                                  String runtime, byte[] data) {
          CreateFunctionRequest createFuncReq = new CreateFunctionRequest(serviceName);
  
          Code code = new Code().setZipFile(data);
          createFuncReq.setFunctionName(functionName);
          createFuncReq.setDescription("test");
          createFuncReq.setHandler(handler);
          createFuncReq.setMemorySize(128);
          createFuncReq.setRuntime(runtime);
          createFuncReq.setCode(code);
          createFuncReq.setTimeout(10);
  
          client.createFunction(createFuncReq);
      }
  
      @Test
      public void testHttpInvokeFunction() throws IOException, InterruptedException, Exception {
          createService(SERVICE_NAME);
  
          // Create a function
          String source = generatePythonHttpCode();
  
          byte[] data = Util.createZipByteData("main.py", source);
  
          // create function
          createFunction(SERVICE_NAME, FUNCTION_NAME, "main.echo_handler", "python2.7", data);
  
          for (HttpAuthType auth : new HttpAuthType[]{ANONYMOUS, FUNCTION}) {
              // create http trigger
              createHttpTrigger(TRIGGER_NAME.concat(auth.toString()), auth,
                      new HttpMethod[]{GET, POST});
              // sleep sometime so that the function cache in the API server will
              // be updated (default is 10 seconds)
              Thread.sleep(15000);
  
              {
                  // Invoke the function
                  HttpInvokeFunctionRequest request = new HttpInvokeFunctionRequest(SERVICE_NAME,
                          FUNCTION_NAME, auth, POST, "/test/path/中文");
  
                  request.addQuery("a", "1");
                  request.addQuery("aaa", null);
  
                  request.setHeader("Test-Header-Key", "testHeaderValue");
                  request.setHeader("Content-Type", "application/json");
  
                  request.setPayload(new String("data").getBytes());
  
                  InvokeFunctionResponse response = client.invokeFunction(request);
  
                  assertEquals(200, response.getStatus());
                  assertTrue(response.getHeader("Content-Type").startsWith("application/json"));
                  assertEquals("testHeaderValue", response.getHeader("Test-Header-Key"));
  
                  JsonObject jsonObject = gson
                          .fromJson(new String(response.getPayload()), JsonObject.class);
  
                  assertEquals("/test/path/中文", jsonObject.get("path").getAsString());
                  assertEquals("aaa=&a=1", jsonObject.get("queries").getAsString());
                  assertEquals("data", jsonObject.get("body").getAsString());
              }
              if (auth == FUNCTION) {
                  Date expires = new Date();
                  expires.setTime(expires.getTime() + 5000);
                  SignURLConfig input = new SignURLConfig(POST, SERVICE_NAME, FUNCTION_NAME, expires);
                  input.setQualifier("LATEST");
                  input.setEscapedPath("/test/path/" + AcsURLEncoder.percentEncode("中文"));
  
                  // with header
                  HashMap<String, String> header = new HashMap<String, String>();
                  header.put("Test-Header-Key", "testHeaderValue");
                  header.put("Content-Type", "application/json");
                  input.setHeader(header);
  
                  HashMap<String, String[]> queries = new HashMap<String, String[]>();
                  queries.put("a", new String[]{"2", "3", "1"});
                  queries.put("aaa", null);
                  input.setQueries(queries);
  
                  String urlLink = client.SignURL(input);
                  URL url = new URL(urlLink);
                  HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
                  httpConn.setRequestMethod("POST");
                  for (String k : header.keySet()) {
                      httpConn.setRequestProperty(k, header.get(k));
                  }
                  httpConn.setConnectTimeout(60 * 1000);
                  httpConn.setReadTimeout(120 * 1000);
                  httpConn.connect();
  
                  assertEquals(200, httpConn.getResponseCode());
                  Map<String, List<String>> respHeaders = httpConn.getHeaderFields();
  
                  assertTrue(respHeaders.get("Content-Type").contains("application/json"));
                  assertTrue(respHeaders.get("Test-Header-Key").contains("testHeaderValue"));
  
                  // expires
                  {
                      Thread.sleep(7000);
                      URL urlExpires = new URL(urlLink);
                      HttpURLConnection httpConnExpires = (HttpURLConnection) urlExpires.openConnection();
                      httpConnExpires.setRequestMethod("POST");
                      for (String k : header.keySet()) {
                          httpConnExpires.setRequestProperty(k, header.get(k));
                      }
                      httpConnExpires.setConnectTimeout(60 * 1000);
                      httpConnExpires.setReadTimeout(120 * 1000);
                      httpConnExpires.connect();
  
                      assertEquals(403, httpConnExpires.getResponseCode());
                  }
              }
  
              // delete trigger
              deleteTrigger(SERVICE_NAME, FUNCTION_NAME, TRIGGER_NAME.concat(auth.toString()));
          }
  
          // Cleanups
          client.deleteFunction(new DeleteFunctionRequest(SERVICE_NAME, FUNCTION_NAME));
          client.deleteService(new DeleteServiceRequest(SERVICE_NAME));
      }
  
      private String generatePythonHttpCode() {
          return "import json\n" +
                  "from cgi import parse_qs, escape\n" +
                  "\n" +
                  "def echo_handler(environ, start_response):\n" +
                  "  \n" +
                  "    resp_body_map = {\n" +
                  "      \"headers\": {},\n" +
                  "      \"queries\": environ.get('QUERY_STRING',''),\n" +
                  "      \"body\": environ[\"wsgi.input\"].read(int(environ.get('CONTENT_LENGTH', 0))),\n"
                  +
                  "      \"path\": environ[\"PATH_INFO\"],\n" +
                  "      \"request_uri\": environ['fc.request_uri']\n" +
                  "    }\n" +
                  "    \n" +
                  "    for k, v in environ.items():\n" +
                  "      if k.startswith(\"HTTP_\"):\n" +
                  "        resp_body_map[\"headers\"][k[5:]] = v\n" +
                  "      \n" +
                  "    body = json.dumps(resp_body_map)\n" +
                  "    \n" +
                  "    # do something here\n" +
                  "    status = '200 OK'\n" +
                  "    response_headers = [('Content-type', 'application/json'),('Test-Header-Key', environ['HTTP_TEST_HEADER_KEY'])]\n"
                  +
                  "    start_response(status, response_headers)\n" +
                  "    return [body]";
      }
  
      @Test
      public void testHttpInvokeFunctionWithoutQueriesAndBody() throws IOException {
          createService(SERVICE_NAME);
  
          // Create a function
          String source = generatePythonHttpCode();
  
          byte[] data = Util.createZipByteData("main.py", source);
  
          // create function
          createFunction(SERVICE_NAME, FUNCTION_NAME, "main.echo_handler", "python2.7", data);
  
          for (HttpAuthType auth : new HttpAuthType[]{ANONYMOUS, FUNCTION}) {
              // create http trigger
              createHttpTrigger(TRIGGER_NAME, auth, new HttpMethod[]{GET, POST, PUT, HEAD, DELETE, PATCH});
  
              // Invoke the function
              HttpInvokeFunctionRequest request = new HttpInvokeFunctionRequest(SERVICE_NAME,
                      FUNCTION_NAME, auth, POST, "/test/path");
  
              request.setHeader("Test-Header-Key", "testHeaderValue");
              request.setHeader("Content-Type", "application/json");
  
              InvokeFunctionResponse response = client.invokeFunction(request);
  
              assertEquals(200, response.getStatus());
              assertTrue(response.getHeader("Content-Type").startsWith("application/json"));
              assertEquals("testHeaderValue", response.getHeader("Test-Header-Key"));
  
              JsonObject jsonObject = gson
                      .fromJson(new String(response.getPayload()), JsonObject.class);
  
              assertEquals("/test/path", jsonObject.get("path").getAsString());
              assertEquals("", jsonObject.get("body").getAsString());
  
              // delete trigger
              deleteTrigger(SERVICE_NAME, FUNCTION_NAME, TRIGGER_NAME);
          }
  
          // Cleanups
          client.deleteFunction(new DeleteFunctionRequest(SERVICE_NAME, FUNCTION_NAME));
          client.deleteService(new DeleteServiceRequest(SERVICE_NAME));
      }
  
      @Test
      public void testCreateFunctionSetZipFile() throws IOException {
          createService(SERVICE_NAME);
  
          String source = "'use strict'; module.exports.handler = function(event, context, callback) {console.log('hello world'); callback(null, 'hello world');};";
  
          byte[] data = Util.createZipByteData("hello_world.js", source);
  
          // Create a function
          CreateFunctionRequest createFuncReq = new CreateFunctionRequest(SERVICE_NAME);
          createFuncReq.setFunctionName(FUNCTION_NAME);
          createFuncReq.setDescription("Function for test");
          createFuncReq.setMemorySize(128);
          createFuncReq.setHandler("hello_world.handler");
          createFuncReq.setRuntime("nodejs4.4");
  
          Code code = new Code().setZipFile(data);
          createFuncReq.setCode(code);
          createFuncReq.setTimeout(10);
          client.createFunction(createFuncReq);
  
          // Invoke the function
          InvokeFunctionRequest request = new InvokeFunctionRequest(SERVICE_NAME, FUNCTION_NAME);
          InvokeFunctionResponse response = client.invokeFunction(request);
  
          assertEquals("hello world", new String(response.getPayload()));
  
          // Cleanups
          client.deleteFunction(new DeleteFunctionRequest(SERVICE_NAME, FUNCTION_NAME));
          client.deleteService(new DeleteServiceRequest(SERVICE_NAME));
      }
  
      @Test
      public void testFunctionInstanceConcurrency() throws IOException {
          createService(SERVICE_NAME);
  
          String functionName = "testInstanceConcurrency";
          String source = "exports.handler = function(event, context, callback) {\n" +
                  "  callback(null, 'hello world');\n" +
                  "};";
  
          byte[] code = Util.createZipByteData("hello_world.js", source);
  
          CreateFunctionRequest createFuncReq = new CreateFunctionRequest(SERVICE_NAME);
          createFuncReq.setFunctionName(functionName);
          createFuncReq.setDescription(FUNCTION_DESC_OLD);
          createFuncReq.setMemorySize(128);
          createFuncReq.setHandler("hello_world.handler");
          createFuncReq.setRuntime("nodejs4.4");
          Map<String, String> environmentVariables = new HashMap<String, String>();
          environmentVariables.put("testKey", "testValue");
          createFuncReq.setEnvironmentVariables(environmentVariables);
          createFuncReq.setCode(new Code().setZipFile(code));
          createFuncReq.setTimeout(10);
          createFuncReq.setInstanceConcurrency(10);
  
          CreateFunctionResponse response = client.createFunction(createFuncReq);
  
          assertFalse(Strings.isNullOrEmpty(response.getRequestId()));
          assertFalse(Strings.isNullOrEmpty(response.getFunctionId()));
          assertEquals(functionName, response.getFunctionName());
          assertEquals(FUNCTION_DESC_OLD, response.getDescription());
          assertEquals(10, response.getInstanceConcurrency().intValue());
  
          GetFunctionRequest getReq = new GetFunctionRequest(SERVICE_NAME, functionName);
          GetFunctionResponse getResp = client.getFunction(getReq);
          assertEquals(10, getResp.getInstanceConcurrency().intValue());
  
          UpdateFunctionRequest updateReq = new UpdateFunctionRequest(SERVICE_NAME, functionName);
          updateReq.setInstanceConcurrency(20);
          UpdateFunctionResponse updateResp = client.updateFunction(updateReq);
          assertEquals(20, updateResp.getInstanceConcurrency().intValue());
  
          getResp = client.getFunction(getReq);
          assertEquals(20, getResp.getInstanceConcurrency().intValue());
      }
  
      @Test
      public void testInvokeFunctionSetHeader() throws IOException, InterruptedException {
          createService(SERVICE_NAME);
          createFunction(FUNCTION_NAME);
  
          // Headers passed in through setHeader should be respected
          InvokeFunctionRequest request = new InvokeFunctionRequest(SERVICE_NAME, FUNCTION_NAME);
          request.setHeader("x-fc-invocation-type", Const.INVOCATION_TYPE_ASYNC);
  
          InvokeFunctionResponse response = client.invokeFunction(request);
          assertEquals(HttpURLConnection.HTTP_ACCEPTED, response.getStatus());
  
          Thread.sleep(5000);
  
      }
  
      private Credentials getAssumeRoleCredentials(String policy)
              throws com.aliyuncs.exceptions.ClientException {
          IClientProfile profile = DefaultProfile
                  .getProfile(REGION, ACCESS_KEY, SECRET_KEY);
          //DefaultProfile.addEndpoint("sts.us-west-1.aliyuncs.com", "us-west-1", "Sts", "sts.us-west-1.aliyuncs.com");
          DefaultAcsClient client = new DefaultAcsClient(profile);
  
          AssumeRoleRequest request = new AssumeRoleRequest();
          request.setVersion(STS_API_VERSION);
          request.setMethod(MethodType.POST);
          request.setProtocol(ProtocolType.HTTPS);
          request.setRoleArn(STS_ROLE);
          request.setRoleSessionName("test-session");
          if (policy != null) {
              request.setPolicy(policy);
          }
  
          AssumeRoleResponse stsResponse;
          try {
              stsResponse = client.getAcsResponse(request);
          } catch (com.aliyuncs.exceptions.ClientException e) {
              throw new RuntimeException(e);
          }
  
          String accessKey = stsResponse.getCredentials().getAccessKeyId();
          String secretKey = stsResponse.getCredentials().getAccessKeySecret();
          String stsToken = stsResponse.getCredentials().getSecurityToken();
  
          assertNotNull(accessKey);
          assertNotNull(secretKey);
          assertNotNull(stsToken);
  
          return stsResponse.getCredentials();
      }
  
      private void testCRUDHelper(boolean testTrigger)
              throws ParseException, InterruptedException, IOException {
          // Create Service
          createService(SERVICE_NAME);
  
          GetServiceResponse svcOldResp = client.getService(new GetServiceRequest(SERVICE_NAME));
  
          // Update Service
          UpdateServiceRequest updateSReq = new UpdateServiceRequest(SERVICE_NAME);
          updateSReq.setDescription(SERVICE_DESC_NEW);
          Thread.sleep(1000L);
          UpdateServiceResponse updateSResp = client.updateService(updateSReq);
          verifyUpdate(svcOldResp.getServiceName(), updateSResp.getServiceName(),
                  svcOldResp.getServiceId(), updateSResp.getServiceId(),
                  svcOldResp.getLastModifiedTime(), updateSResp.getLastModifiedTime(),
                  svcOldResp.getCreatedTime(), updateSResp.getCreatedTime(),
                  svcOldResp.getDescription(), updateSResp.getDescription());
  
          // Get Service
          GetServiceRequest getSReq = new GetServiceRequest(SERVICE_NAME);
          GetServiceResponse getSResp = client.getService(getSReq);
          assertEquals(SERVICE_NAME, getSResp.getServiceName());
          assertEquals(svcOldResp.getServiceId(), getSResp.getServiceId());
          assertEquals(ROLE, getSResp.getRole());
  
          // Create Function
          CreateFunctionResponse createFResp = createFunction(FUNCTION_NAME);
          assertFalse(Strings.isNullOrEmpty(createFResp.getRequestId()));
          assertFalse(Strings.isNullOrEmpty(createFResp.getFunctionId()));
          Map<String, String> environmentVariables = createFResp.getEnvironmentVariables();
          assertEquals(1, environmentVariables.size());
          assertEquals("testValue", environmentVariables.get("testKey"));
          assertEquals(FUNCTION_NAME, createFResp.getFunctionName());
          assertEquals(FUNCTION_DESC_OLD, createFResp.getDescription());
  
          // List Functions
          ListFunctionsRequest listFReq = new ListFunctionsRequest(SERVICE_NAME);
          ListFunctionsResponse listFResp = client.listFunctions(listFReq);
          assertFalse(Strings.isNullOrEmpty(listFResp.getRequestId()));
          assertEquals(1, listFResp.getFunctions().length);
          FunctionMetadata funcOld = listFResp.getFunctions()[0];
          assertEquals(FUNCTION_NAME, funcOld.getFunctionName());
  
          // Update Function
          UpdateFunctionRequest updateFReq = new UpdateFunctionRequest(SERVICE_NAME, FUNCTION_NAME);
          updateFReq.setDescription(FUNCTION_DESC_NEW);
          GetFunctionRequest getFReq = new GetFunctionRequest(SERVICE_NAME, FUNCTION_NAME);
          GetFunctionResponse getFResp = client.getFunction(getFReq);
          Map<String, String> envOriginal = getFResp.getEnvironmentVariables();
          envOriginal.put("testKey", "testValueNew");
          updateFReq.setEnvironmentVariables(envOriginal);
          Thread.sleep(1000L);
          UpdateFunctionResponse updateFResp = client.updateFunction(updateFReq);
          listFResp = client.listFunctions(listFReq);
          Assert.assertEquals("testValueNew", updateFResp.getEnvironmentVariables().get("testKey"));
          assertFalse(Strings.isNullOrEmpty(listFResp.getRequestId()));
          assertEquals(1, listFResp.getFunctions().length);
          FunctionMetadata funcNew = listFResp.getFunctions()[0];
          verifyUpdate(funcOld.getFunctionName(), funcNew.getFunctionName(),
                  funcOld.getFunctionId(), funcNew.getFunctionId(),
                  funcOld.getLastModifiedTime(), funcNew.getLastModifiedTime(),
                  funcOld.getCreatedTime(), funcNew.getCreatedTime(),
                  funcOld.getDescription(), funcNew.getDescription());
  
          // Get Function
          getFReq = new GetFunctionRequest(SERVICE_NAME, FUNCTION_NAME);
          getFResp = client.getFunction(getFReq);
          Map<String, String> envGet = getFResp.getEnvironmentVariables();
          assertEquals(1, envGet.size());
          assertEquals("testValueNew", envGet.get("testKey"));
          assertFalse(Strings.isNullOrEmpty(getFResp.getRequestId()));
          assertEquals(FUNCTION_NAME, getFResp.getFunctionName());
  
          // Get Function Code
          GetFunctionCodeRequest getFCReq = new GetFunctionCodeRequest(SERVICE_NAME, FUNCTION_NAME);
          GetFunctionCodeResponse getFCResp = client.getFunctionCode(getFCReq);
          assertFalse(Strings.isNullOrEmpty(getFResp.getRequestId()));
          String crc64 = fetchFromURL(getFCResp.getCodeUrl());
          assertEquals(crc64, getFCResp.getCodeChecksum());
  
          // Invoke Function
          InvokeFunctionRequest invkReq = new InvokeFunctionRequest(SERVICE_NAME, FUNCTION_NAME);
          InvokeFunctionResponse invkResp = client.invokeFunction(invkReq);
          assertTrue(!Strings.isNullOrEmpty(invkResp.getRequestId()));
          assertEquals("hello world", new String(invkResp.getContent()));
  
          // Invoke Function Async
          invkReq.setInvocationType(Const.INVOCATION_TYPE_ASYNC);
          invkResp = client.invokeFunction(invkReq);
          assertEquals(HttpURLConnection.HTTP_ACCEPTED, invkResp.getStatus());
  
          if (testTrigger) {
              // Create Trigger
              String tfPrefix = "prefix";
              String tfSuffix = "suffix";
  
              createOssTrigger(TRIGGER_NAME, tfPrefix, tfSuffix);
  
              // List Triggers
              TriggerMetadata[] triggers = listTriggers(SERVICE_NAME, FUNCTION_NAME);
  
              assertEquals(1, triggers.length);
              TriggerMetadata triggerOld = triggers[0];
              assertEquals(TRIGGER_NAME, triggerOld.getTriggerName());
  
              // Update Trigger
              String newInvocationRole = INVOCATION_ROLE + "_new";
              String tfPrefixNew = "prefix_new";
              String tfSuffixNew = "suffix_new";
              String[] eventsNew = new String[]{"oss:ObjectCreated:PutObject"};
              OSSTriggerConfig updateTriggerConfig = new OSSTriggerConfig(
                      new String[]{"oss:ObjectCreated:PutObject"}, tfPrefixNew, tfSuffixNew);
  
              UpdateTriggerRequest updateTReq = new UpdateTriggerRequest(SERVICE_NAME, FUNCTION_NAME,
                      TRIGGER_NAME);
              updateTReq.setInvocationRole(newInvocationRole);
              updateTReq.setTriggerConfig(updateTriggerConfig);
              UpdateTriggerResponse updateTResp = updateTrigger(updateTReq);
              assertEquals(triggerOld.getTriggerName(), updateTResp.getTriggerName());
              assertNotEquals(triggerOld.getInvocationRole(), updateTResp.getInvocationRole());
              assertEquals(triggerOld.getSourceArn(), updateTResp.getSourceArn());
              Gson gson = new Gson();
              OSSTriggerConfig tcOld = gson
                      .fromJson(gson.toJson(triggerOld.getTriggerConfig()), OSSTriggerConfig.class);
              OSSTriggerConfig tcNew = gson
                      .fromJson(gson.toJson(updateTResp.getTriggerConfig()), OSSTriggerConfig.class);
              assertFalse(deepEquals(tcOld.getEvents(), tcNew.getEvents()));
              assertNotEquals(tcOld.getFilter().getKey().getPrefix(),
                      tcNew.getFilter().getKey().getPrefix());
              assertNotEquals(tcOld.getFilter().getKey().getSuffix(),
                      tcNew.getFilter().getKey().getSuffix());
              assertEquals(triggerOld.getCreatedTime(), updateTResp.getCreatedTime());
              assertEquals(triggerOld.getTriggerType(), updateTResp.getTriggerType());
              assertNotEquals(triggerOld.getInvocationRole(), updateTResp.getInvocationRole());
  
              Date dateOld = DATE_FORMAT.parse(triggerOld.getLastModifiedTime());
              Date dateNew = DATE_FORMAT.parse(updateTResp.getLastModifiedTime());
              assertTrue(dateOld.before(dateNew));
  
              // Get Trigger
              GetTriggerRequest getTReq = new GetTriggerRequest(SERVICE_NAME, FUNCTION_NAME,
                      TRIGGER_NAME);
              GetTriggerResponse getTResp = client.getTrigger(getTReq);
              OSSTriggerConfig getTConfig = gson
                      .fromJson(gson.toJson(getTResp.getTriggerConfig()), OSSTriggerConfig.class);
              assertFalse(Strings.isNullOrEmpty(getTResp.getRequestId()));
              assertEquals(TRIGGER_NAME, getTResp.getTriggerName());
              assertEquals(OSS_SOURCE_ARN, getTResp.getSourceARN());
              assertEquals(TRIGGER_TYPE_OSS, getTResp.getTriggerType());
              assertEquals(newInvocationRole, getTResp.getInvocationRole());
              assertEquals(tfPrefixNew, getTConfig.getFilter().getKey().getPrefix());
              assertEquals(tfSuffixNew, getTConfig.getFilter().getKey().getSuffix());
              assertTrue(deepEquals(eventsNew, getTConfig.getEvents()));
  
              // Delete Trigger
              deleteTrigger(SERVICE_NAME, FUNCTION_NAME, TRIGGER_NAME);
          }
  
          testLogTrigger();
          testTimeTrigger();
          testCdnEventsTrigger();
  
          // Delete Function
          DeleteFunctionRequest deleteFReq = new DeleteFunctionRequest(SERVICE_NAME, FUNCTION_NAME);
          int numFunctionsOld = listFResp.getFunctions().length;
          DeleteFunctionResponse deleteFResp = client.deleteFunction(deleteFReq);
          assertFalse(Strings.isNullOrEmpty(deleteFResp.getRequestId()));
          listFResp = client.listFunctions(listFReq);
  
          try {
              getFunction(FUNCTION_NAME, listFResp.getFunctions());
              fail("Function " + FUNCTION_NAME + " failed to be deleted");
          } catch (RuntimeException e) {
              int numFunctionsNew = (listFResp.getFunctions() == null) ? 0 :
                      listFResp.getFunctions().length;
              assertEquals(numFunctionsOld, numFunctionsNew + 1);
          }
          GetFunctionResponse getFResp2 = null;
          try {
              getFResp2 = client.getFunction(getFReq);
              fail(
                      "Get Function " + FUNCTION_NAME + " should have no function returned after delete");
          } catch (ClientException e) {
              assertNull(getFResp2);
          }
  
          // Delete Service
          DeleteServiceRequest deleteSReq = new DeleteServiceRequest(SERVICE_NAME);
          DeleteServiceResponse deleteSResp = client.deleteService(deleteSReq);
          assertFalse(Strings.isNullOrEmpty(deleteSResp.getRequestId()));
  
          GetServiceResponse getSResp2 = null;
          try {
              getSResp2 = client.getService(getSReq);
              fail("Get service " + FUNCTION_NAME + " should have no service returned after delete");
          } catch (ClientException e) {
              assertNull(getSResp2);
          }
      }
  
      private void testCdnEventsTrigger() throws ParseException, InterruptedException {
          String triggerName = TRIGGER_TYPE_CDN + "_" + TRIGGER_NAME;
  
          String EVENT_NAME = "LogFileCreated";
  
          String EVENT_VERSION = "1.0.0";
  
          String NOTES = "notes";
          String NEW_NOTES = "updateNotes";
  
          CdnEventsTriggerConfig config = new CdnEventsTriggerConfig();
          config.setEventName(EVENT_NAME);
          config.setEventVersion(EVENT_VERSION);
          config.setNotes(NOTES);
  
          Map<String, List<String>> filters = new HashMap<String, List<String>>();
          filters.put("domain", asList("www.taobao.com"));
          filters.put("stream", asList("def"));
  
          config.setFilter(filters);
  
          CreateTriggerRequest createTReq = new CreateTriggerRequest(SERVICE_NAME, FUNCTION_NAME);
          createTReq.setTriggerName(triggerName);
          createTReq.setTriggerType(TRIGGER_TYPE_CDN);
          createTReq.setInvocationRole(INVOCATION_ROLE);
          createTReq.setSourceArn(CDN_SOURCE_ARN);
          createTReq.setTriggerConfig(config);
          client.createTrigger(createTReq);
  
          // List Triggers
          TriggerMetadata[] triggers = listTriggers(SERVICE_NAME, FUNCTION_NAME);
  
          assertEquals(1, triggers.length);
          TriggerMetadata triggerOld = triggers[0];
          assertEquals(triggerName, triggerOld.getTriggerName());
  
          Thread.sleep(300);
  
          Map<String, List<String>> newFilters = new HashMap<String, List<String>>();
          newFilters.put("a", asList("b"));
  
          CdnEventsTriggerConfig updateConfig = new CdnEventsTriggerConfig();
          updateConfig.setNotes(NEW_NOTES);
          updateConfig.setFilter(newFilters);
  
          UpdateTriggerRequest req = new UpdateTriggerRequest(SERVICE_NAME, FUNCTION_NAME,
                  triggerName);
          req.setInvocationRole(INVOCATION_ROLE);
          req.setTriggerConfig(updateConfig);
  
          UpdateTriggerResponse updateTResp = client.updateTrigger(req);
          assertEquals(triggerOld.getTriggerName(), updateTResp.getTriggerName());
          assertEquals(triggerOld.getInvocationRole(), updateTResp.getInvocationRole());
          assertEquals(triggerOld.getSourceArn(), updateTResp.getSourceArn());
          Gson gson = new Gson();
          CdnEventsTriggerConfig tcOld = gson
                  .fromJson(gson.toJson(triggerOld.getTriggerConfig()), CdnEventsTriggerConfig.class);
          CdnEventsTriggerConfig tcNew = gson
                  .fromJson(gson.toJson(updateTResp.getTriggerConfig()), CdnEventsTriggerConfig.class);
          assertEquals(triggerOld.getCreatedTime(), updateTResp.getCreatedTime());
          assertEquals(triggerOld.getTriggerType(), updateTResp.getTriggerType());
          assertEquals(triggerOld.getInvocationRole(), updateTResp.getInvocationRole());
          assertEquals(EVENT_NAME, tcNew.getEventName());
          assertEquals(tcOld.getEventVersion(), tcNew.getEventVersion());
          assertEquals(NEW_NOTES, tcNew.getNotes());
          assertNotEquals(tcOld.getFilter(), tcNew.getFilter());
  
          Date dateOld = DATE_FORMAT.parse(triggerOld.getLastModifiedTime());
          Date dateNew = DATE_FORMAT.parse(updateTResp.getLastModifiedTime());
          assertTrue(dateOld.before(dateNew));
  
          // Get Trigger
          GetTriggerRequest getTReq = new GetTriggerRequest(SERVICE_NAME, FUNCTION_NAME,
                  triggerName);
          GetTriggerResponse getTResp = client.getTrigger(getTReq);
          config = gson
                  .fromJson(gson.toJson(getTResp.getTriggerConfig()), CdnEventsTriggerConfig.class);
          assertFalse(Strings.isNullOrEmpty(getTResp.getRequestId()));
          assertEquals(triggerName, getTResp.getTriggerName());
          assertEquals(CDN_SOURCE_ARN, getTResp.getSourceARN());
          assertEquals(TRIGGER_TYPE_CDN, getTResp.getTriggerType());
          assertEquals(EVENT_NAME, config.getEventName());
          assertEquals(EVENT_VERSION, config.getEventVersion());
  
          // Delete Trigger
          deleteTrigger(SERVICE_NAME, FUNCTION_NAME, triggerName);
      }
  
      private CreateTriggerResponse createLogTrigger(String triggerName,
                                                     LogTriggerConfig triggerConfig) {
  
          CreateTriggerRequest createTReq = new CreateTriggerRequest(SERVICE_NAME, FUNCTION_NAME);
          createTReq.setTriggerName(triggerName);
          createTReq.setTriggerType(TRIGGER_TYPE_LOG);
          createTReq.setInvocationRole(INVOCATION_ROLE);
          createTReq.setSourceArn(LOG_SOURCE_ARN);
          createTReq.setTriggerConfig(triggerConfig);
  
          return client.createTrigger(createTReq);
      }
  
      private void testLogTrigger() throws ParseException {
  
          String triggerName = TRIGGER_TYPE_LOG + "_" + TRIGGER_NAME;
          LogTriggerConfig triggerConfig = new LogTriggerConfig()
                  .setSourceConfig(new LogTriggerConfig.SourceConfig(LOG_STORE)).
                          setJobConfig(
                                  new LogTriggerConfig.JobConfig().setMaxRetryTime(3).setTriggerInterval(60)).
                          setLogConfig(new LogTriggerConfig.LogConfig("", "")).
                          setFunctionParameter(new HashMap<String, Object>()).setEnable(true);
  
          createLogTrigger(triggerName, triggerConfig);
  
          // List Triggers
          TriggerMetadata[] triggers = listTriggers(SERVICE_NAME, FUNCTION_NAME);
  
          assertEquals(1, triggers.length);
          TriggerMetadata triggerOld = triggers[0];
  
          assertEquals(triggerName, triggerOld.getTriggerName());
  
          try {
              Thread.sleep(1000);
          } catch (InterruptedException e) {
          }
  
          UpdateTriggerRequest req = new UpdateTriggerRequest(SERVICE_NAME, FUNCTION_NAME,
                  triggerName);
          req.setInvocationRole(INVOCATION_ROLE);
          req.setTriggerConfig(
                  new LogTriggerConfig().
                          setJobConfig(
                                  new LogTriggerConfig.JobConfig().setMaxRetryTime(5).setTriggerInterval(120)));
          UpdateTriggerResponse updateTResp = client.updateTrigger(req);
          assertEquals(triggerOld.getTriggerName(), updateTResp.getTriggerName());
          assertEquals(triggerOld.getInvocationRole(), updateTResp.getInvocationRole());
          assertEquals(triggerOld.getSourceArn(), updateTResp.getSourceArn());
          Gson gson = new Gson();
          LogTriggerConfig tcOld = gson
                  .fromJson(gson.toJson(triggerOld.getTriggerConfig()), LogTriggerConfig.class);
          LogTriggerConfig tcNew = gson
                  .fromJson(gson.toJson(updateTResp.getTriggerConfig()), LogTriggerConfig.class);
          assertEquals(triggerOld.getCreatedTime(), updateTResp.getCreatedTime());
          assertEquals(triggerOld.getTriggerType(), updateTResp.getTriggerType());
          assertEquals(triggerOld.getInvocationRole(), updateTResp.getInvocationRole());
          assertEquals(tcOld.getSourceConfig(), tcNew.getSourceConfig());
          assertEquals(tcOld.getLogConfig(), tcNew.getLogConfig());
          assertEquals(tcOld.isEnable(), tcNew.isEnable());
          assertNotEquals(tcOld.getJobConfig(), tcNew.getJobConfig());
  
          Date dateOld = DATE_FORMAT.parse(triggerOld.getLastModifiedTime());
          Date dateNew = DATE_FORMAT.parse(updateTResp.getLastModifiedTime());
          assertTrue(dateOld.before(dateNew));
  
          // Get Trigger
          GetTriggerRequest getTReq = new GetTriggerRequest(SERVICE_NAME, FUNCTION_NAME,
                  triggerName);
          GetTriggerResponse getTResp = client.getTrigger(getTReq);
          LogTriggerConfig getTConfig = gson
                  .fromJson(gson.toJson(getTResp.getTriggerConfig()), LogTriggerConfig.class);
          assertFalse(Strings.isNullOrEmpty(getTResp.getRequestId()));
          assertEquals(triggerName, getTResp.getTriggerName());
          assertEquals(LOG_SOURCE_ARN, getTResp.getSourceARN());
          assertEquals(TRIGGER_TYPE_LOG, getTResp.getTriggerType());
          assertEquals(5, getTConfig.getJobConfig().getMaxRetryTime().intValue());
          assertEquals(120, getTConfig.getJobConfig().getTriggerInterval().intValue());
  
          // Delete Trigger
          deleteTrigger(SERVICE_NAME, FUNCTION_NAME, triggerName);
      }
  
      private CreateTriggerResponse createTimeTrigger(String triggerName,
                                                      TimeTriggerConfig timeTriggerConfig) {
          CreateTriggerRequest createTReq = new CreateTriggerRequest(SERVICE_NAME, FUNCTION_NAME);
          createTReq.setTriggerName(triggerName);
          createTReq.setTriggerType(TRIGGER_TYPE_TIMER);
          createTReq.setTriggerConfig(timeTriggerConfig);
  
          return client.createTrigger(createTReq);
      }
  
      @Test
      public void testInvokeFunctionWithInitializer()
              throws ParseException, InterruptedException, IOException {
          createService(SERVICE_NAME);
          String functionName = "testInitializer";
          String source = "'use strict';\n" +
                  "var counter = 0;\n" +
                  "exports.initializer = function(ctx, callback) {\n" +
                  "++counter;\n" +
                  "callback(null, '');};\n" +
                  "exports.handler = function(event, context, callback) {\n" +
                  "console.log('hello world, counter is %d', counter);\n" +
                  "callback(null, String(counter));};\n";
  
          byte[] data = Util.createZipByteData("counter.js", source);
  
          // Create a function
          CreateFunctionRequest createFuncReq = new CreateFunctionRequest(SERVICE_NAME);
          createFuncReq.setFunctionName(functionName);
          createFuncReq.setDescription("Function for initializer test");
          createFuncReq.setMemorySize(128);
          createFuncReq.setHandler("counter.handler");
          createFuncReq.setInitializer("counter.initializer");
          createFuncReq.setRuntime("nodejs4.4");
  
          Code code = new Code().setZipFile(data);
          createFuncReq.setCode(code);
          createFuncReq.setTimeout(10);
          client.createFunction(createFuncReq);
  
          // Update Function
          ListFunctionsRequest listFReq = new ListFunctionsRequest(SERVICE_NAME);
          ListFunctionsResponse listFResp = client.listFunctions(listFReq);
          FunctionMetadata funcOld = listFResp.getFunctions()[0];
          UpdateFunctionRequest updateFReq = new UpdateFunctionRequest(SERVICE_NAME, functionName);
          updateFReq.setDescription(FUNCTION_DESC_NEW);
          GetFunctionRequest getFReq = new GetFunctionRequest(SERVICE_NAME, functionName);
          GetFunctionResponse getFResp = client.getFunction(getFReq);
          Map<String, String> envOriginal = getFResp.getEnvironmentVariables();
          envOriginal.put("testKey", "testValueNew");
          updateFReq.setEnvironmentVariables(envOriginal);
          Thread.sleep(1000L);
          UpdateFunctionResponse updateFResp = client.updateFunction(updateFReq);
          listFResp = client.listFunctions(listFReq);
          Assert.assertEquals("testValueNew", updateFResp.getEnvironmentVariables().get("testKey"));
          assertFalse(Strings.isNullOrEmpty(listFResp.getRequestId()));
          assertEquals(1, listFResp.getFunctions().length);
          FunctionMetadata funcNew = listFResp.getFunctions()[0];
          verifyUpdate(funcOld.getFunctionName(), funcNew.getFunctionName(),
                  funcOld.getFunctionId(), funcNew.getFunctionId(),
                  funcOld.getLastModifiedTime(), funcNew.getLastModifiedTime(),
                  funcOld.getCreatedTime(), funcNew.getCreatedTime(),
                  funcOld.getDescription(), funcNew.getDescription());
  
          // Invoke the function
          InvokeFunctionRequest request = new InvokeFunctionRequest(SERVICE_NAME, functionName);
          InvokeFunctionResponse response = client.invokeFunction(request);
  
          assertEquals("1", new String(response.getPayload()));
  
          // Cleanups
          client.deleteFunction(new DeleteFunctionRequest(SERVICE_NAME, functionName));
          client.deleteService(new DeleteServiceRequest(SERVICE_NAME));
      }
  
      private void testTimeTrigger() throws ParseException {
          String cronEvery = "@every 5m";
          String cronExpression = "0 2 * * * *";
          String payload = "awesome-fc";
          String triggerName = TRIGGER_TYPE_TIMER + "_" + TRIGGER_NAME;
          Gson gson = new Gson();
  
          // Create Trigger
          TimeTriggerConfig timeTriggerConfig = new TimeTriggerConfig(cronEvery, payload, true);
  
          CreateTriggerResponse createTriggerResponse = createTimeTrigger(triggerName,
                  timeTriggerConfig);
  
          assertEquals(triggerName, createTriggerResponse.getTriggerName());
          assertEquals(TRIGGER_TYPE_TIMER, createTriggerResponse.getTriggerType());
  
          String createTime = createTriggerResponse.getCreatedTime();
          String lastModifiedTime = createTriggerResponse.getLastModifiedTime();
          TimeTriggerConfig tRConfig = gson
                  .fromJson(gson.toJson(createTriggerResponse.getTriggerConfig()),
                          TimeTriggerConfig.class);
  
          assertEquals(timeTriggerConfig.getCronExpression(), tRConfig.getCronExpression());
          assertEquals(timeTriggerConfig.getPayload(), tRConfig.getPayload());
          assertEquals(timeTriggerConfig.isEnable(), tRConfig.isEnable());
  
          // Get Trigger
          GetTriggerRequest getTReq = new GetTriggerRequest(SERVICE_NAME, FUNCTION_NAME, triggerName);
          GetTriggerResponse getTResp = client.getTrigger(getTReq);
          TimeTriggerConfig getTConfig = gson
                  .fromJson(gson.toJson(getTResp.getTriggerConfig()), TimeTriggerConfig.class);
          assertFalse(Strings.isNullOrEmpty(getTResp.getRequestId()));
          assertEquals(triggerName, getTResp.getTriggerName());
          assertEquals(TRIGGER_TYPE_TIMER, getTResp.getTriggerType());
          assertEquals(timeTriggerConfig.getCronExpression(), getTConfig.getCronExpression());
          assertEquals(timeTriggerConfig.getPayload(), getTConfig.getPayload());
          assertEquals(timeTriggerConfig.isEnable(), getTConfig.isEnable());
          assertEquals(createTime, getTResp.getCreatedTime());
          assertEquals(lastModifiedTime, getTResp.getLastModifiedTime());
          // List Triggers
          TriggerMetadata[] triggers = listTriggers(SERVICE_NAME, FUNCTION_NAME);
  
          assertEquals(1, triggers.length);
          TriggerMetadata triggerOld = triggers[0];
          assertEquals(triggerName, triggerOld.getTriggerName());
  
          try {
              Thread.sleep(1000);
          } catch (InterruptedException e) {
          }
  
          // Update Triggers
          UpdateTriggerRequest req = new UpdateTriggerRequest(SERVICE_NAME, FUNCTION_NAME,
                  triggerName);
          req.setTriggerConfig(
                  new TimeTriggerConfig().setCronExpression(cronExpression).setPayload(payload)
                          .setEnable(true));
          UpdateTriggerResponse updateTResp = client.updateTrigger(req);
          assertEquals(triggerOld.getTriggerName(), updateTResp.getTriggerName());
          TimeTriggerConfig tcOld = gson
                  .fromJson(gson.toJson(triggerOld.getTriggerConfig()), TimeTriggerConfig.class);
          TimeTriggerConfig tcNew = gson
                  .fromJson(gson.toJson(updateTResp.getTriggerConfig()), TimeTriggerConfig.class);
          Date dateOld = DATE_FORMAT.parse(triggerOld.getLastModifiedTime());
          Date dateNew = DATE_FORMAT.parse(updateTResp.getLastModifiedTime());
          assertTrue(dateOld.before(dateNew));
          assertNotEquals(tcOld.getCronExpression(), tcNew.getCronExpression());
          assertEquals(tcOld.getPayload(), tcNew.getPayload());
          assertEquals(tcOld.isEnable(), tcNew.isEnable());
          // Delete Trigger
          deleteTrigger(SERVICE_NAME, FUNCTION_NAME, triggerName);
      }
  
      private void verifyUpdate(String nameOld, String nameNew, String idOld,
                                String idNew, String lastModifiedTimeOld, String lastModifiedTimeNew,
                                String createdTimeOld, String createdTimeNew, String descOld, String descNew)
              throws ParseException {
          assertEquals(nameNew, nameOld);
          assertEquals(idNew, idOld);
          Date dateOld = DATE_FORMAT.parse(lastModifiedTimeOld);
          Date dateNew = DATE_FORMAT.parse(lastModifiedTimeNew);
          assertTrue(dateOld.before(dateNew));
          Date cDateOld = DATE_FORMAT.parse(createdTimeOld);
          Date cDateNew = DATE_FORMAT.parse(createdTimeNew);
          assertEquals(cDateNew, cDateOld);
  
          assertNotEquals(descNew, descOld);
      }
  
      private FunctionMetadata getFunction(String functionName, FunctionMetadata[] functions) {
          for (FunctionMetadata function : functions) {
              if (functionName.equals(function.getFunctionName())) {
                  return function;
              }
          }
          throw new RuntimeException("Function " + functionName + " does not exist");
      }
  
      @Test
      public void testVpcBinding() throws ClientException {
          String serviceName = "testvpcbindforgithubtest";
          cleanupService(serviceName);
  
          // create service with role
          CreateServiceRequest createSReq = new CreateServiceRequest();
          createSReq.setServiceName(serviceName);
          createSReq.setRole(ROLE);
          CreateServiceResponse response = client.createService(createSReq);
          assertEquals(serviceName, response.getServiceName());
          assertFalse(Strings.isNullOrEmpty(response.getRequestId()));
          assertFalse(Strings.isNullOrEmpty(response.getServiceId()));
          assertEquals(ROLE, response.getRole());
  
          // create vpc binding
          CreateVpcBindingRequest request = new CreateVpcBindingRequest();
          request.setServiceName(serviceName).setVpcId(VPC_ID);
          CreateVpcBindingResponse createVpcBindingResponse = client.createVpcBinding(request);
          assertEquals(200, createVpcBindingResponse.getStatus());
  
          // list vpc binding
          ListVpcBindingsRequest listVpcBindingsRequest = new ListVpcBindingsRequest();
          listVpcBindingsRequest.setServiceName(serviceName);
          ListVpcBindingsResponse listVpcBindingsResponse = client.listVpcBindings(listVpcBindingsRequest);
          assertEquals(1, listVpcBindingsResponse.getVpcIDs().length);
          assertEquals(VPC_ID, listVpcBindingsResponse.getVpcIDs()[0]);
  
          // delete vpc binding
          DeleteVpcBindingRequest deleteVpcBindingRequest = new DeleteVpcBindingRequest();
          deleteVpcBindingRequest.setServiceName(serviceName).setVpcId(VPC_ID);
          DeleteVpcBindingResponse deleteVpcBindingResponse = client.deleteVpcBinding(deleteVpcBindingRequest);
          assertEquals(200, deleteVpcBindingResponse.getStatus());
  
          cleanupService(serviceName);
      }
  
      @Test
      public void testCustomDomain()
              throws ClientException, JSONException, InterruptedException, ParseException, IOException {
          cleanupCustomDomain(CUSTOMDOMAIN_NAME);
          // Create custom domain
          CreateCustomDomainRequest createCustomDomainRequest = new CreateCustomDomainRequest();
          PathConfig pathConfig = new PathConfig("/", "serviceName", "functionName", null);
          PathConfig[] routes = new PathConfig[1];
          routes[0] = pathConfig;
          RouteConfig routeConfig = new RouteConfig(routes);
          createCustomDomainRequest.setDomainName(CUSTOMDOMAIN_NAME);
          createCustomDomainRequest.setProtocol("HTTP");
          createCustomDomainRequest.setRouteConfig(routeConfig);
          CreateCustomDomainResponse createCustomDomainResponse = client
                  .createCustomDomain(createCustomDomainRequest);
          assertEquals(CUSTOMDOMAIN_NAME, createCustomDomainResponse.getDomainName());
          assertEquals("HTTP", createCustomDomainResponse.getProtocol());
          assertNotNull(createCustomDomainResponse.getRouteConfig().getRoutes());
          assertEqualsRouteConfig(routeConfig, createCustomDomainResponse.getRouteConfig());
  
          // Update custom domain
          UpdateCustomDomainRequest updateCustomDomainRequest = new UpdateCustomDomainRequest(
                  CUSTOMDOMAIN_NAME);
          PathConfig pathConfig1 = new PathConfig("/login", "serviceName1", "functionName1", null);
          PathConfig[] routes1 = new PathConfig[2];
          routes1[0] = pathConfig;
          routes1[1] = pathConfig1;
          RouteConfig routeConfig1 = new RouteConfig(routes1);
          updateCustomDomainRequest.setRouteConfig(routeConfig1);
          UpdateCustomDomainResponse updateCustomDomainResponse = client
                  .updateCustomDomain(updateCustomDomainRequest);
          assertEqualsRouteConfig(routeConfig1, updateCustomDomainResponse.getRouteConfig());
  
          // Get custom domain
          GetCustomDomainRequest getCustomDomainRequest = new GetCustomDomainRequest(
                  CUSTOMDOMAIN_NAME);
          GetCustomDomainResponse getCustomDomainResponse = client
                  .getCustomDomain(getCustomDomainRequest);
          assertEquals(CUSTOMDOMAIN_NAME, getCustomDomainResponse.getDomainName());
          assertEquals("HTTP", getCustomDomainResponse.getProtocol());
          assertEqualsRouteConfig(routeConfig1, getCustomDomainResponse.getRouteConfig());
  
          // List custom domain
          ListCustomDomainsRequest listCustomDomainsRequest = new ListCustomDomainsRequest();
          ListCustomDomainsResponse listCustomDomainsResponse = client
                  .listCustomDomains(listCustomDomainsRequest);
          assertTrue(listCustomDomainsResponse.getStatus() == HttpURLConnection.HTTP_OK);
  
          // Delete custom domain
          DeleteCustomDomainRequest deleteCustomDomainRequest = new DeleteCustomDomainRequest(
                  CUSTOMDOMAIN_NAME);
          client.deleteCustomDomain(deleteCustomDomainRequest);
      }
  
      @Test
      public void testCustomDomainWithHTTPS()
              throws ClientException, JSONException, InterruptedException, ParseException, IOException {
          cleanupCustomDomain(CUSTOMDOMAIN_NAME);
          String certificate1 = PUBLIC_KEY_CERTIFICATE_01.replace("\\n", "\n").replace("\"", "");
          String certificate2 = PUBLIC_KEY_CERTIFICATE_02.replace("\\n", "\n").replace("\"", "");
          String privateKey1 = PRIVATE_KEY_01.replace("\\n", "\n").replace("\"", "");
          String privateKey2 = PRIVATE_KEY_02.replace("\\n", "\n").replace("\"", "");
          // Create custom domain
          CreateCustomDomainRequest createCustomDomainRequest = new CreateCustomDomainRequest();
          PathConfig pathConfig = new PathConfig("/", "serviceName", "functionName", null);
          PathConfig[] routes = new PathConfig[1];
          routes[0] = pathConfig;
          RouteConfig routeConfig = new RouteConfig(routes);
          CertConfig certConfig = new CertConfig(CERT_NAME, certificate1, privateKey1);
          createCustomDomainRequest.setDomainName(CUSTOMDOMAIN_NAME);
          createCustomDomainRequest.setProtocol("HTTP,HTTPS");
          createCustomDomainRequest.setRouteConfig(routeConfig);
          createCustomDomainRequest.setCertConfig(certConfig);
          CreateCustomDomainResponse createCustomDomainResponse = client
                  .createCustomDomain(createCustomDomainRequest);
          assertEquals(CUSTOMDOMAIN_NAME, createCustomDomainResponse.getDomainName());
          assertEquals("HTTP,HTTPS", createCustomDomainResponse.getProtocol());
          assertNotNull(createCustomDomainResponse.getRouteConfig().getRoutes());
          assertEqualsRouteConfig(routeConfig, createCustomDomainResponse.getRouteConfig());
          assertNotNull(createCustomDomainResponse.getCertConfig());
          assertEquals(certificate1, createCustomDomainResponse.getCertConfig().getCertificate());
  
          // Update custom domain
          UpdateCustomDomainRequest updateCustomDomainRequest = new UpdateCustomDomainRequest(
                  CUSTOMDOMAIN_NAME);
          PathConfig pathConfig1 = new PathConfig("/login", "serviceName1", "functionName1", null);
          PathConfig[] routes1 = new PathConfig[2];
          routes1[0] = pathConfig;
          routes1[1] = pathConfig1;
          RouteConfig routeConfig1 = new RouteConfig(routes1);
          certConfig = new CertConfig(CERT_NAME, certificate2, privateKey2);
          updateCustomDomainRequest.setRouteConfig(routeConfig1);
          updateCustomDomainRequest.setCertConfig(certConfig);
          UpdateCustomDomainResponse updateCustomDomainResponse = client
                  .updateCustomDomain(updateCustomDomainRequest);
          assertEqualsRouteConfig(routeConfig1, updateCustomDomainResponse.getRouteConfig());
          assertNotNull(updateCustomDomainResponse.getCertConfig());
          assertEquals(certificate2, updateCustomDomainResponse.getCertConfig().getCertificate());
  
          // Get custom domain
          GetCustomDomainRequest getCustomDomainRequest = new GetCustomDomainRequest(
                  CUSTOMDOMAIN_NAME);
          GetCustomDomainResponse getCustomDomainResponse = client
                  .getCustomDomain(getCustomDomainRequest);
          assertEquals(CUSTOMDOMAIN_NAME, getCustomDomainResponse.getDomainName());
          assertEquals("HTTP,HTTPS", getCustomDomainResponse.getProtocol());
          assertEqualsRouteConfig(routeConfig1, getCustomDomainResponse.getRouteConfig());
          assertNotNull(getCustomDomainResponse.getCertConfig());
          assertEquals(certificate2, getCustomDomainResponse.getCertConfig().getCertificate());
  
          // List custom domain
          ListCustomDomainsRequest listCustomDomainsRequest = new ListCustomDomainsRequest();
          ListCustomDomainsResponse listCustomDomainsResponse = client
                  .listCustomDomains(listCustomDomainsRequest);
          assertTrue(listCustomDomainsResponse.getStatus() == HttpURLConnection.HTTP_OK);
  
          // Delete custom domain
          DeleteCustomDomainRequest deleteCustomDomainRequest = new DeleteCustomDomainRequest(
                  CUSTOMDOMAIN_NAME);
          client.deleteCustomDomain(deleteCustomDomainRequest);
      }
  
      @Test
      public void testTag()
              throws ClientException, JSONException, InterruptedException, ParseException, IOException {
  
          final int numServices = 3;
          for (int i = 0; i < numServices; i++) {
              try {
                  client.getService(new GetServiceRequest(SERVICE_NAME + i));
                  cleanupService(SERVICE_NAME + i);
              } catch (ClientException e) {
                  if (!ErrorCodes.SERVICE_NOT_FOUND.equals(e.getErrorCode())) {
                      throw new RuntimeException("Cleanup failed");
                  }
              }
              CreateServiceRequest request = new CreateServiceRequest();
              request.setServiceName(SERVICE_NAME + i);
              request.setDescription(SERVICE_DESC_OLD);
              request.setRole(ROLE);
              CreateServiceResponse response = client.createService(request);
              assertFalse(Strings.isNullOrEmpty(response.getRequestId()));
  
              TagResourceRequest req = new TagResourceRequest();
              req.setResourceArn(String.format("acs:fc:%s:%s:services/%s", REGION, ACCOUNT_ID, SERVICE_NAME + i));
              Map<String, String> tags = new HashMap<String, String>();
              if (i % 2 == 0) {
                  tags.put("k1", "v1");
              } else {
                  tags.put("k2", "v2");
              }
              tags.put("k3", "v3");
              req.setTags(tags);
              TagResourceResponse resp = client.tagResource(req);
              assertFalse(Strings.isNullOrEmpty(resp.getRequestId()));
          }
  
          for (int i = 0; i < numServices; i++) {
              String resourceArn = String.format("acs:fc:%s:%s:services/%s", REGION, ACCOUNT_ID, SERVICE_NAME + i);
              GetResourceTagsRequest getReq = new GetResourceTagsRequest(resourceArn);
              GetResourceTagsResponse getResp = client.getResourceTags(getReq);
              Assert.assertEquals(resourceArn, getResp.getResourceArn());
              Assert.assertEquals("v3", getResp.getTags().get("k3"));
              if (i % 2 == 0) {
                  Assert.assertFalse(getResp.getTags().containsKey("k2"));
                  Assert.assertEquals("v1", getResp.getTags().get("k1"));
              } else {
                  Assert.assertFalse(getResp.getTags().containsKey("k1"));
                  Assert.assertEquals("v2", getResp.getTags().get("k2"));
              }
  
              // unTag k3
              UntagResourceRequest req = new UntagResourceRequest();
              req.setResourceArn(resourceArn);
              String[] tagKeys = new String[]{"k3"};
              req.setTagKeys(tagKeys);
              UntagResourceResponse resp = client.untagResource(req);
              assertFalse(Strings.isNullOrEmpty(resp.getRequestId()));
  
              getReq = new GetResourceTagsRequest(resourceArn);
              getResp = client.getResourceTags(getReq);
              Assert.assertEquals(resourceArn, getResp.getResourceArn());
              Assert.assertFalse(getResp.getTags().containsKey("k3"));
              if (i % 2 == 0) {
                  Assert.assertEquals("v1", getResp.getTags().get("k1"));
              } else {
                  Assert.assertEquals("v2", getResp.getTags().get("k2"));
              }
  
              // unTag all
              req = new UntagResourceRequest();
              req.setResourceArn(resourceArn);
              tagKeys = new String[]{};
              req.setTagKeys(tagKeys);
              req.setAll(true);
              resp = client.untagResource(req);
              assertFalse(Strings.isNullOrEmpty(resp.getRequestId()));
              getReq = new GetResourceTagsRequest(resourceArn);
              getResp = client.getResourceTags(getReq);
              Assert.assertFalse(getResp.getTags().containsKey("k1"));
              Assert.assertFalse(getResp.getTags().containsKey("k2"));
              Assert.assertFalse(getResp.getTags().containsKey("k3"));
          }
  
          // Delete services
          for (int i = 0; i < numServices; i++) {
              cleanupService(SERVICE_NAME + i);
          }
      }
  
      @Test
      public void testVersions() throws ClientException {
          createService(SERVICE_NAME);
          String lastVersion = "0";
          // publish a version
          PublishVersionRequest publishVersionRequest = new PublishVersionRequest(SERVICE_NAME);
          PublishVersionResponse publishVersionResponse = client
                  .publishVersion(publishVersionRequest);
          assertEquals(String.format("%d", Integer.parseInt(lastVersion) + 1),
                  publishVersionResponse.getVersionId());
  
          // List versions
          ListVersionsRequest listVersionsRequest = new ListVersionsRequest(SERVICE_NAME);
          ListVersionsResponse listVersionsResponse = client.listVersions(listVersionsRequest);
          assertTrue(listVersionsResponse.getStatus() == HttpURLConnection.HTTP_OK);
          assertEquals(1, listVersionsResponse.getVersions().length);
          assertEquals(publishVersionResponse.getVersionId(),
                  listVersionsResponse.getVersions()[0].getVersionId());
  
          // Delete version
          DeleteVersionRequest deleteVersionRequest = new DeleteVersionRequest(SERVICE_NAME,
                  publishVersionResponse.getVersionId());
          DeleteVersionResponse deleteVersionResponse = client.deleteVersion(deleteVersionRequest);
          assertEquals(HttpURLConnection.HTTP_NO_CONTENT, deleteVersionResponse.getStatus());
      }
  
      @Test
      public void testAlias() throws ClientException {
          createService(SERVICE_NAME);
          String lastVersion = cleanUpVersions(SERVICE_NAME);
          // publish a version
          PublishVersionRequest publishVersionRequest = new PublishVersionRequest(SERVICE_NAME);
          PublishVersionResponse publishVersionResponse = client
                  .publishVersion(publishVersionRequest);
          assertEquals(String.format("%d", Integer.parseInt(lastVersion) + 1),
                  publishVersionResponse.getVersionId());
          lastVersion = publishVersionResponse.getVersionId();
  
          //Create a Alias against it
          String aliasName = "myAlias";
          CreateAliasRequest createAliasRequest = new CreateAliasRequest(SERVICE_NAME, aliasName,
                  lastVersion);
          CreateAliasResponse createAliasResponse = client.createAlias(createAliasRequest);
          assertEquals(HttpURLConnection.HTTP_OK, createAliasResponse.getStatus());
          assertEquals(lastVersion, createAliasResponse.getVersionId());
          assertEquals(aliasName, createAliasResponse.getAliasName());
  
          //Get Alias
          GetAliasRequest getAliasRequest = new GetAliasRequest(SERVICE_NAME, aliasName);
          GetAliasResponse getAliasResponse = client.getAlias(getAliasRequest);
          assertEquals(HttpURLConnection.HTTP_OK, getAliasResponse.getStatus());
          assertEquals(lastVersion, getAliasResponse.getVersionId());
          assertEquals(aliasName, getAliasResponse.getAliasName());
          assertEquals(0, getAliasResponse.getDescription().length());
          assertNull(getAliasResponse.getAdditionalVersionWeight());
  
          //Update the Alias
          String description = "my test Alias";
          UpdateAliasRequest updateAliasRequest = new UpdateAliasRequest(SERVICE_NAME, aliasName);
          updateAliasRequest.setDescription(description);
          UpdateAliasResponse updateAliasResponse = client.updateAlias(updateAliasRequest);
          assertEquals(HttpURLConnection.HTTP_OK, updateAliasResponse.getStatus());
          assertEquals(lastVersion, updateAliasResponse.getVersionId());
          assertEquals(aliasName, updateAliasResponse.getAliasName());
          assertEquals(description, updateAliasResponse.getDescription());
  
          //Get Alias
          getAliasResponse = client.getAlias(getAliasRequest);
          assertEquals(HttpURLConnection.HTTP_OK, getAliasResponse.getStatus());
          assertEquals(lastVersion, getAliasResponse.getVersionId());
          assertEquals(aliasName, getAliasResponse.getAliasName());
          assertEquals(description, getAliasResponse.getDescription());
  
          // List Alias
          ListAliasesRequest listAliasesRequest = new ListAliasesRequest(SERVICE_NAME);
          ListAliasesResponse listAliasesResponse = client.listAliases(listAliasesRequest);
          assertEquals(HttpURLConnection.HTTP_OK, listAliasesResponse.getStatus());
          assertEquals(1, listAliasesResponse.getAliases().length);
          assertEquals(aliasName, listAliasesResponse.getAliases()[0].getAliasName());
          assertEquals(lastVersion, listAliasesResponse.getAliases()[0].getVersionId());
          assertEquals(description, listAliasesResponse.getAliases()[0].getDescription());
  
          // Delete Alias
          DeleteAliasRequest deleteAliasRequest = new DeleteAliasRequest(SERVICE_NAME, aliasName);
          DeleteAliasResponse deleteAliasResponse = client
                  .deleteAlias(deleteAliasRequest);
          assertEquals(HttpURLConnection.HTTP_NO_CONTENT, deleteAliasResponse.getStatus());
      }
  
      public void testHTTPTriggerWithVersion() throws ClientException, IOException {
          createService(SERVICE_NAME);
          String lastVersion = "0";
  
          // Create a function
          String source = generatePythonHttpCode();
  
          byte[] data = Util.createZipByteData("main.py", source);
  
          // create function
          createFunction(SERVICE_NAME, FUNCTION_NAME, "main.echo_handler", "python2.7", data);
  
          // publish a version
          PublishVersionRequest publishVersionRequest = new PublishVersionRequest(SERVICE_NAME);
          PublishVersionResponse publishVersionResponse = client
                  .publishVersion(publishVersionRequest);
          assertEquals(String.format("%d", Integer.parseInt(lastVersion) + 1),
                  publishVersionResponse.getVersionId());
  
          for (HttpAuthType auth : new HttpAuthType[]{ANONYMOUS, FUNCTION}) {
              // create http trigger
              createHttpTriggerWithQualifier(TRIGGER_NAME, publishVersionResponse.getVersionId(),
                      auth, new HttpMethod[]{GET, POST});
  
              // Invoke the function
              HttpInvokeFunctionRequest request = new HttpInvokeFunctionRequest(SERVICE_NAME,
                      FUNCTION_NAME, auth, POST, "/test/path/中文");
              request.setQualifier(publishVersionResponse.getVersionId());
              request.addQuery("a", "1");
              request.addQuery("aaa", null);
  
              request.setHeader("Test-Header-Key", "testHeaderValue");
              request.setHeader("Content-Type", "application/json");
  
              request.setPayload(new String("data").getBytes());
  
              InvokeFunctionResponse response = client.invokeFunction(request);
  
              assertEquals(200, response.getStatus());
              assertTrue(response.getHeader("Content-Type").startsWith("application/json"));
              assertEquals("testHeaderValue", response.getHeader("Test-Header-Key"));
  
              JsonObject jsonObject = gson
                      .fromJson(new String(response.getPayload()), JsonObject.class);
  
              assertEquals("/test/path/中文", jsonObject.get("path").getAsString());
              assertEquals("aaa=&a=1", jsonObject.get("queries").getAsString());
              assertEquals("data", jsonObject.get("body").getAsString());
  
              // delete trigger
              deleteTrigger(SERVICE_NAME, FUNCTION_NAME, TRIGGER_NAME);
          }
  
          // Delete version
          DeleteVersionRequest deleteVersionRequest = new DeleteVersionRequest(SERVICE_NAME,
                  publishVersionResponse.getVersionId());
          DeleteVersionResponse deleteVersionResponse = client.deleteVersion(deleteVersionRequest);
          assertEquals(HttpURLConnection.HTTP_NO_CONTENT, deleteVersionResponse.getStatus());
  
          // Cleanups
          client.deleteFunction(new DeleteFunctionRequest(SERVICE_NAME, FUNCTION_NAME));
          client.deleteService(new DeleteServiceRequest(SERVICE_NAME));
      }
  
  
      private void assertEqualsRouteConfig(RouteConfig routeConfig, RouteConfig routeConfigResp) {
          int len = routeConfigResp.getRoutes().length;
          for (int i = 0; i < len; i++) {
              assertEquals(routeConfig.getRoutes()[i].getPath(),
                      routeConfigResp.getRoutes()[i].getPath());
              assertEquals(routeConfig.getRoutes()[i].getServiceName(),
                      routeConfigResp.getRoutes()[i].getServiceName());
              assertEquals(routeConfig.getRoutes()[i].getFunctionName(),
                      routeConfigResp.getRoutes()[i].getFunctionName());
              assertEquals(routeConfig.getRoutes()[i].getQualifier(),
                      routeConfigResp.getRoutes()[i].getQualifier());
          }
      }
  
      @Test
      public void testClientCredentialProviderMock() {
          // init CredentialProvider
          String ak = "ak";
          String sk = "sk";
          String stsToken = "sts_token";
          BasicSessionCredentials creds = new BasicSessionCredentials(ak, sk, stsToken);
  
          // mock
          InstanceProfileCredentialsProvider credsProvider = mock(InstanceProfileCredentialsProvider.class);
  
          try {
              when(credsProvider.getCredentials()).thenReturn(creds);
          } catch (com.aliyuncs.exceptions.ClientException e) {
              e.printStackTrace();
          }
  
          // init fc client
          Config config = new Config(REGION, ACCOUNT_ID, credsProvider, false);
          FunctionComputeClient fcClient = new FunctionComputeClient(config);
          client = fcClient;
  
          // Create a service
          try {
              createService(SERVICE_NAME);
          } catch (Exception e) {
          }
  
          assertEquals(creds.getAccessKeyId(), config.getAccessKeyID());
          assertEquals(creds.getAccessKeySecret(), config.getAccessKeySecret());
          assertEquals(creds.getSessionToken(), config.getSecurityToken());
      }
  
      /**
       * run only on aliyun ecs, and that ecs need bind a RAM Role
       */
      public void testClientCredentialProvider() {
          // init CredentialProvider
          String roleName = "ECSAccessingFCTestRole";
          InstanceProfileCredentialsProvider credsProvider = new InstanceProfileCredentialsProvider(roleName);
  
          // init fc client
          Config config = new Config(REGION, ACCOUNT_ID, credsProvider, false);
          FunctionComputeClient fcClient = new FunctionComputeClient(config);
          client = fcClient;
  
          // Create a service
          try {
              createService(SERVICE_NAME, false);
          } catch (Exception e) {
              e.printStackTrace();
          }
  
          // create a function
          try {
              createFunction(FUNCTION_NAME);
          } catch (Exception e) {
              e.printStackTrace();
          }
  
          // Invoke the function with a string as function event parameter, Sync mode
          InvokeFunctionRequest invkReq = new InvokeFunctionRequest(SERVICE_NAME, FUNCTION_NAME);
          String payload = "Hello FunctionCompute!";
          invkReq.setPayload(payload.getBytes());
          InvokeFunctionResponse invkResp = client.invokeFunction(invkReq);
          System.out.println(new String(invkResp.getContent()));
  
          cleanUpFunctions(SERVICE_NAME);
          cleanupService(SERVICE_NAME);
      }
  
  }
  
  