Repository: skiptests/astraea
Pull request #364 (2022-05-25 12:45:26)

Add embedded MySQL
新增embedded cluster，這可以讓我們之後測試kafka與database的互動


Patch:
diff --git a/app/build.gradle b/app/build.gradle
index b4ec5650..8f3b28bc 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -17,6 +17,7 @@ def versions = [
     slf4j: project.properties['slf4j.version'] ?: "1.7.32",
     zookeeper: project.properties['zookeeper.version'] ?: "3.6.3",
     gson: project.properties['gson.version'] ?: "2.9.0",
+    mysql: project.properties['mysql.version'] ?: "4.6.2",
 ]
 
 dependencies {
@@ -25,6 +26,7 @@ dependencies {
     testImplementation "org.mockito:mockito-inline:${versions["mockito"]}"
     testImplementation "org.apache.zookeeper:zookeeper:${versions["zookeeper"]}"
     testImplementation "org.apache.kafka:kafka_2.12:${versions["kafka"]}"
+    testImplementation "com.wix:wix-embedded-mysql:${versions["mysql"]}"
 
 
     implementation "org.apache.kafka:kafka-clients:${versions["kafka"]}"
diff --git a/app/src/main/java/org/astraea/Utils.java b/app/src/main/java/org/astraea/Utils.java
index 4fc5f55d..2bd2c0cd 100644
--- a/app/src/main/java/org/astraea/Utils.java
+++ b/app/src/main/java/org/astraea/Utils.java
@@ -4,6 +4,7 @@ import java.io.File;
 import java.io.IOException;
 import java.io.UncheckedIOException;
 import java.net.InetAddress;
+import java.net.ServerSocket;
 import java.net.UnknownHostException;
 import java.nio.file.Files;
 import java.time.Duration;
@@ -184,5 +185,21 @@ public final class Utils {
     return java.util.UUID.randomUUID().toString().replaceAll("-", "");
   }
 
+  public static int availablePort() {
+    try (ServerSocket socket = new ServerSocket(0)) {
+      socket.setReuseAddress(true);
+      var port = socket.getLocalPort();
+      // the port smaller than 1024 may be protected by OS.
+      return port > 1024 ? port : port + 1024;
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  public static int resolvePort(int port) {
+    if (port <= 0) return availablePort();
+    return port;
+  }
+
   private Utils() {}
 }


Test patch:
diff --git a/app/src/test/java/org/astraea/service/Database.java b/app/src/test/java/org/astraea/service/Database.java
new file mode 100644
index 00000000..bdc9e014
--- /dev/null
+++ b/app/src/test/java/org/astraea/service/Database.java
@@ -0,0 +1,138 @@
+package org.astraea.service;
+
+import static com.wix.mysql.EmbeddedMysql.anEmbeddedMysql;
+import static com.wix.mysql.config.Charset.UTF8;
+import static com.wix.mysql.config.MysqldConfig.aMysqldConfig;
+
+import com.wix.mysql.distribution.Version;
+import java.io.Closeable;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.util.Calendar;
+import java.util.concurrent.TimeUnit;
+import org.astraea.Utils;
+
+public interface Database extends Closeable {
+  static Database mysql() {
+    return Database.builder().build();
+  }
+
+  static Builder builder() {
+    return new Builder();
+  }
+
+  /** @return hostname to connect */
+  String hostname();
+
+  /** @return port to connect */
+  int port();
+
+  /** @return database name of this mysql */
+  String databaseName();
+
+  /** @return username to log in this mysql */
+  String user();
+
+  /** @return password to log in this mysql */
+  String password();
+
+  /** @return full JDBC url */
+  String url();
+
+  /** @return create a new JDBC connection. Please close it manually */
+  Connection createConnection();
+
+  @Override
+  void close();
+
+  class Builder {
+    private Builder() {}
+
+    private String databaseName = Utils.randomString(5);
+    private String user = "user";
+    private String password = "password";
+    private int port = 0;
+
+    public Builder databaseName(String databaseName) {
+      this.databaseName = databaseName;
+      return this;
+    }
+
+    public Builder user(String user) {
+      this.user = user;
+      return this;
+    }
+
+    public Builder password(String password) {
+      this.password = password;
+      return this;
+    }
+
+    public Builder port(int port) {
+      this.port = port;
+      return this;
+    }
+
+    public Database build() {
+      var config =
+          aMysqldConfig(Version.v5_7_latest)
+              .withCharset(UTF8)
+              .withUser(user, password)
+              .withTimeZone(Calendar.getInstance().getTimeZone().getID())
+              .withTimeout(2, TimeUnit.MINUTES)
+              .withServerVariable("max_connect_errors", 666)
+              .withTempDir(Utils.createTempDirectory("embedded_mysql").getAbsolutePath())
+              .withPort(Utils.resolvePort(port))
+              // make mysql use " replace '
+              // see https://stackoverflow.com/questions/13884854/mysql-double-quoted-table-names
+              .withServerVariable("sql-mode", "ANSI_QUOTES")
+              .build();
+      var database = anEmbeddedMysql(config).addSchema(databaseName).start();
+      return new Database() {
+        @Override
+        public void close() {
+          database.stop();
+        }
+
+        @Override
+        public String hostname() {
+          return Utils.hostname();
+        }
+
+        @Override
+        public int port() {
+          return config.getPort();
+        }
+
+        @Override
+        public String databaseName() {
+          return databaseName;
+        }
+
+        @Override
+        public String user() {
+          return config.getUsername();
+        }
+
+        @Override
+        public String password() {
+          return config.getPassword();
+        }
+
+        @Override
+        public String url() {
+          return "jdbc:mysql://" + hostname() + ":" + port() + "/" + databaseName();
+        }
+
+        @Override
+        public Connection createConnection() {
+          try {
+            return DriverManager.getConnection(url(), user(), password());
+          } catch (Exception e) {
+            throw new RuntimeException(e);
+          }
+        }
+      };
+    }
+  }
+}
diff --git a/app/src/test/java/org/astraea/service/DatabaseTest.java b/app/src/test/java/org/astraea/service/DatabaseTest.java
new file mode 100644
index 00000000..ef0a0ff3
--- /dev/null
+++ b/app/src/test/java/org/astraea/service/DatabaseTest.java
@@ -0,0 +1,29 @@
+package org.astraea.service;
+
+import org.astraea.Utils;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class DatabaseTest {
+
+  @Test
+  void testConnectionInformation() {
+    var user = Utils.randomString(10);
+    var password = Utils.randomString(10);
+    var databaseName = Utils.randomString(10);
+    var port = Utils.availablePort();
+    try (var database =
+        Database.builder()
+            .user(user)
+            .password(password)
+            .databaseName(databaseName)
+            .port(port)
+            .build()) {
+      Assertions.assertEquals(Utils.hostname(), database.hostname());
+      Assertions.assertEquals(port, database.port());
+      Assertions.assertEquals(user, database.user());
+      Assertions.assertEquals(password, database.password());
+      Assertions.assertEquals(databaseName, database.databaseName());
+    }
+  }
+}