diff --git a/.github/workflows/java-test-oceanbase-ce.yml b/.github/workflows/java-test-oceanbase-ce.yml
new file mode 100644
index 0000000..ebcc67b
--- /dev/null
+++ b/.github/workflows/java-test-oceanbase-ce.yml
@@ -0,0 +1,102 @@
+name: java test oceanbase-ce
+
+on:
+ workflow_call:
+ inputs:
+ cache_key:
+ required: true
+ type: string
+ image_file:
+ required: true
+ type: string
+ mode:
+ required: true
+ type: string
+ cluster_name:
+ required: false
+ type: string
+ default: 'obcluster'
+ port:
+ required: true
+ type: string
+ sys_password:
+ required: false
+ type: string
+ default: ''
+ test_tenant:
+ required: false
+ type: string
+ default: 'test'
+ test_password:
+ required: false
+ type: string
+ default: ''
+ init_sql:
+ required: false
+ type: string
+ default: ''
+
+jobs:
+ test-oceanbase-ce:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Download artifact
+ uses: actions/download-artifact@v4
+ with:
+ name: ${{ inputs.cache_key }}
+ path: /tmp
+
+ - name: Load Docker image
+ run: docker load -i /tmp/${{ inputs.image_file }}
+
+ - name: Start Docker container
+ uses: oceanbase/setup-oceanbase-ce@v1
+ with:
+ image_name: oceanbase-ce
+ container_name: oceanbase-ce
+ mode: ${{ inputs.mode }}
+ cluster_name: ${{ inputs.cluster_name }}
+ sql_port: ${{ inputs.port }}
+ sys_root_password: ${{ inputs.sys_password }}
+ tenant_name: ${{ inputs.test_tenant }}
+ tenant_root_password: ${{ inputs.test_password }}
+ init_sql: ${{ inputs.init_sql }}
+
+ - name: Set server IP
+ id: set_server_ip
+ run: |
+ if [ "${{ inputs.mode }}" == "slim" ]; then
+ echo "Use '127.0.0.1' as server_ip on slim mode."
+ echo "server_ip=127.0.0.1" >> $GITHUB_OUTPUT
+ else
+ echo "Getting IP from container..."
+ container_ip=$(docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' oceanbase-ce)
+ echo "Container IP is $container_ip. Setting server_ip to container IP."
+ echo "server_ip=$container_ip" >> $GITHUB_OUTPUT
+ fi
+
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Set up Java
+ uses: actions/setup-java@v4
+ with:
+ java-version: '8'
+ distribution: 'zulu'
+
+ - name: Build test project
+ run: |
+ cd test
+ mvn install -DskipTests=true
+
+ - name: Test Docker container
+ env:
+ server_ip: ${{ steps.set_server_ip.outputs.server_ip }}
+ cluster_name: ${{ inputs.cluster_name }}
+ port: ${{ inputs.port }}
+ sys_password: ${{ inputs.sys_password }}
+ test_tenant: ${{ inputs.test_tenant }}
+ test_password: ${{ inputs.test_password }}
+ run: |
+ cd test
+ mvn verify -Dtest=OceanBaseCETest -DfailIfNoTests=false
diff --git a/.github/workflows/test-oceanbase-ce.yml b/.github/workflows/test-oceanbase-ce.yml
index b6186f9..178235c 100644
--- a/.github/workflows/test-oceanbase-ce.yml
+++ b/.github/workflows/test-oceanbase-ce.yml
@@ -2,13 +2,16 @@ name: test oceanbase-ce
on:
push:
- paths:
- - '.github/workflows/test-oceanbase-ce.yml'
- - 'oceanbase-ce/**'
+ branches: [ main ]
pull_request:
paths:
- - '.github/workflows/test-oceanbase-ce.yml'
+ - '.github/workflows/**-oceanbase-ce.yml'
- 'oceanbase-ce/**'
+ - 'test/**'
+
+concurrency:
+ group: test-oceanbase-ce-${{ github.event.pull_request.number || github.ref }}
+ cancel-in-progress: true
jobs:
build:
@@ -48,26 +51,30 @@ jobs:
path: oceanbase-ce.tar
test-slim:
- runs-on: ubuntu-latest
needs: build
- steps:
- - name: Download artifact
- uses: actions/download-artifact@v4
- with:
- name: oceanbase-ce
- path: /tmp
-
- - name: Load Docker image
- run: docker load -i /tmp/oceanbase-ce.tar
+ uses: ./.github/workflows/java-test-oceanbase-ce.yml
+ with:
+ cache_key: oceanbase-ce
+ image_file: oceanbase-ce.tar
+ mode: slim
+ port: 1234
+ test_password: 123456
+ init_sql: "USE test;
+ CREATE TABLE user(id INT(10) PRIMARY KEY, name VARCHAR(20));
+ INSERT INTO user VALUES (1, 'tom'), (2, 'jerry');"
- - name: Start Docker container
- uses: oceanbase/setup-oceanbase-ce@v1
- with:
- image_name: oceanbase-ce
- container_name: ob-slim
- fastboot: false
-
- - name: Test Docker container
- run: |
- docker exec ob-slim obclient -h127.0.0.1 -P2881 -uroot -e 'select version()'
- docker exec ob-slim obclient -h127.0.0.1 -P2881 -uroot@test -e 'show databases'
+ test-mini:
+ needs: build
+ uses: ./.github/workflows/java-test-oceanbase-ce.yml
+ with:
+ cache_key: oceanbase-ce
+ image_file: oceanbase-ce.tar
+ cluster_name: github-action
+ mode: mini
+ port: 1234
+ sys_password: 1234567
+ test_tenant: mini
+ test_password: 7654321
+ init_sql: "USE test;
+ CREATE TABLE user(id INT(10) PRIMARY KEY, name VARCHAR(20));
+ INSERT INTO user VALUES (1, 'tom'), (2, 'jerry');"
diff --git a/.gitignore b/.gitignore
index 7c6ddf0..72cc5b1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,4 +2,5 @@
.idea
.vscode
*.log
+target
oceanbase-ce/Dockerfile.inner
diff --git a/test/pom.xml b/test/pom.xml
new file mode 100644
index 0000000..c82fb8b
--- /dev/null
+++ b/test/pom.xml
@@ -0,0 +1,110 @@
+
+
+ 4.0.0
+
+ com.oceanbase
+ docker-images-test
+ 1.0-SNAPSHOT
+
+
+ UTF-8
+ ${encoding}
+ ${encoding}
+
+ 1.8
+ ${java.version}
+ ${java.version}
+
+
+
+
+ mysql
+ mysql-connector-java
+ 5.1.47
+ test
+
+
+ com.mysql
+ mysql-connector-j
+ 8.4.0
+ test
+
+
+ com.google.protobuf
+ protobuf-java
+
+
+
+
+ org.junit.jupiter
+ junit-jupiter
+ 5.10.2
+ test
+
+
+ org.apache.logging.log4j
+ log4j-slf4j-impl
+ 2.23.1
+ test
+
+
+
+
+
+
+ maven-surefire-plugin
+ 3.1.2
+
+
+ maven-failsafe-plugin
+ 3.1.2
+
+
+ com.diffplug.spotless
+ spotless-maven-plugin
+ 2.27.1
+
+
+
+ 1.7
+
+
+
+
+
+
+ UTF-8
+ 4
+ true
+ false
+ false
+ true
+ false
+ false
+ false
+ false
+
+
+ Leading blank line
+ project
+ project
+
+
+
+ true
+
+
+
+
+ spotless-check
+
+ check
+
+ validate
+
+
+
+
+
+
+
diff --git a/test/src/test/java/com/oceanbase/test/OceanBaseCETest.java b/test/src/test/java/com/oceanbase/test/OceanBaseCETest.java
new file mode 100644
index 0000000..1f5481a
--- /dev/null
+++ b/test/src/test/java/com/oceanbase/test/OceanBaseCETest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2024 OceanBase.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.oceanbase.test;
+
+import java.sql.Connection;
+import java.sql.Driver;
+import java.sql.SQLException;
+import java.util.Properties;
+import java.util.stream.Stream;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class OceanBaseCETest {
+
+ private static final Logger LOG = LoggerFactory.getLogger(OceanBaseCETest.class);
+
+ static Stream testOceanBaseCEArgs() {
+ // non-null env vars
+ String serverIP = Utils.getNonEmptyEnv("server_ip");
+ String clusterName = Utils.getNonEmptyEnv("cluster_name");
+ String port = Utils.getNonEmptyEnv("port");
+ String testTenant = Utils.getNonEmptyEnv("test_tenant");
+
+ // nullable env vars
+ String sysPassword = System.getenv("sys_password");
+ String testPassword = System.getenv("test_password");
+
+ return Stream.of(
+ Arguments.of(true, serverIP, clusterName, port, "sys", "root", sysPassword),
+ Arguments.of(false, serverIP, clusterName, port, "sys", "root", sysPassword),
+ Arguments.of(
+ true,
+ serverIP,
+ clusterName,
+ port,
+ testTenant,
+ "root@" + testTenant,
+ testPassword),
+ Arguments.of(
+ false,
+ serverIP,
+ clusterName,
+ port,
+ testTenant,
+ "root@" + testTenant,
+ testPassword));
+ }
+
+ @ParameterizedTest
+ @MethodSource("testOceanBaseCEArgs")
+ public void testOceanBaseCE(
+ boolean useLegacyDriver,
+ String serverIP,
+ String clusterName,
+ String port,
+ String tenantName,
+ String username,
+ String password) {
+
+ boolean slimMode = "127.0.0.1".equals(serverIP);
+
+ LOG.info(
+ "Testing with args: [useLegacyDriver: {}, server_ip: {}, cluster_name: {}, port: {}, username: {}, password: {}]",
+ useLegacyDriver,
+ serverIP,
+ clusterName,
+ port,
+ username,
+ password);
+
+ String jdbcUrl = String.format("jdbc:mysql://127.0.0.1:%s/test?useSSL=false", port);
+
+ Properties props = new Properties();
+ props.setProperty("user", username);
+ if (password != null) {
+ props.put("password", password);
+ }
+
+ Driver driver = Utils.getDriver(useLegacyDriver);
+ try (Connection conn = driver.connect(jdbcUrl, props)) {
+ LOG.info("Connected to OceanBase CE successfully");
+
+ Assertions.assertNotNull(Utils.getVersionComment(conn));
+
+ Assertions.assertEquals(serverIP + ":2882:2881", Utils.getRSList(conn));
+ Assertions.assertEquals(clusterName, Utils.getClusterName(conn));
+ Assertions.assertEquals(tenantName, Utils.getTenantName(conn));
+
+ if (!slimMode) {
+ Assertions.assertNotNull(Utils.getConfigUrl(conn));
+ }
+
+ if ("sys".equals(tenantName)) {
+ Assertions.assertEquals(serverIP, Utils.getServerIP(conn));
+ } else {
+ Assertions.assertEquals(2, Utils.getTableRowsCount(conn, "user"));
+ }
+ } catch (SQLException e) {
+ Assertions.fail(e);
+ }
+ }
+}
diff --git a/test/src/test/java/com/oceanbase/test/Utils.java b/test/src/test/java/com/oceanbase/test/Utils.java
new file mode 100644
index 0000000..9b767d2
--- /dev/null
+++ b/test/src/test/java/com/oceanbase/test/Utils.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2024 OceanBase.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.oceanbase.test;
+
+import java.sql.Connection;
+import java.sql.Driver;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+public class Utils {
+
+ private static final org.slf4j.Logger LOG = org.slf4j.LoggerFactory.getLogger(Utils.class);
+
+ public static Driver getDriver(boolean legacy) {
+ String className = legacy ? "com.mysql.jdbc.Driver" : "com.mysql.cj.jdbc.Driver";
+ try {
+ return (Driver) Class.forName(className).getDeclaredConstructor().newInstance();
+ } catch (Throwable throwable) {
+ throw new RuntimeException("Failed to load JDBC driver", throwable);
+ }
+ }
+
+ public static String getNonEmptyEnv(String key) {
+ String value = System.getenv(key);
+ if (value == null || value.trim().isEmpty()) {
+ throw new IllegalArgumentException("Environment variable '" + key + "' is required");
+ }
+ return value;
+ }
+
+ public static String getEnvOrDefault(String name, String defaultValue) {
+ String env = System.getenv(name);
+ return env == null ? defaultValue : env;
+ }
+
+ public static String getVersionComment(Connection connection) {
+ return (String)
+ query(
+ connection,
+ "version_comment",
+ "SHOW VARIABLES LIKE 'version_comment'",
+ rs -> rs.next() ? rs.getString("VALUE") : null);
+ }
+
+ public static String getClusterName(Connection connection) {
+ return (String)
+ query(
+ connection,
+ "cluster",
+ "SHOW PARAMETERS LIKE 'cluster'",
+ rs -> rs.next() ? rs.getString("VALUE") : null);
+ }
+
+ public static String getTenantName(Connection connection) {
+ return (String)
+ query(
+ connection,
+ "tenant",
+ "SHOW TENANT",
+ rs -> rs.next() ? rs.getString(1) : null);
+ }
+
+ public static String getServerIP(Connection connection) {
+ return (String)
+ query(
+ connection,
+ "svr_ip",
+ "SELECT svr_ip FROM oceanbase.__all_server",
+ rs -> rs.next() ? rs.getString(1) : null);
+ }
+
+ public static String getRSList(Connection connection) {
+ return (String)
+ query(
+ connection,
+ "rootservice_list",
+ "SHOW PARAMETERS LIKE 'rootservice_list'",
+ rs -> rs.next() ? rs.getString("VALUE") : null);
+ }
+
+ public static String getConfigUrl(Connection connection) {
+ return (String)
+ query(
+ connection,
+ "obconfig_url",
+ "SHOW PARAMETERS LIKE 'obconfig_url'",
+ rs -> rs.next() ? rs.getString("VALUE") : null);
+ }
+
+ public static int getTableRowsCount(Connection connection, String tableName) {
+ return (int)
+ query(
+ connection,
+ "table '" + tableName + "' rows count",
+ "SELECT COUNT(1) FROM " + tableName,
+ rs -> rs.next() ? rs.getInt(1) : 0);
+ }
+
+ @FunctionalInterface
+ interface ResultSetConsumer {
+ Object apply(ResultSet rs) throws SQLException;
+ }
+
+ static Object query(
+ Connection connection,
+ String queryName,
+ String sql,
+ ResultSetConsumer resultSetConsumer) {
+ try (Statement statement = connection.createStatement()) {
+ ResultSet rs = statement.executeQuery(sql);
+ Object result = resultSetConsumer.apply(rs);
+ LOG.info("Query: {}, got result: {}", queryName, result);
+ return result;
+ } catch (SQLException e) {
+ throw new RuntimeException("Failed to execute sql: " + sql, e);
+ }
+ }
+}
diff --git a/test/src/test/resources/log4j2-test.properties b/test/src/test/resources/log4j2-test.properties
new file mode 100644
index 0000000..3964819
--- /dev/null
+++ b/test/src/test/resources/log4j2-test.properties
@@ -0,0 +1,27 @@
+####################################################################
+# Copyright 2024 OceanBase.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+####################################################################
+
+# Set root logger level to OFF to not flood build logs
+# set manually to INFO for debugging purposes
+rootLogger.level=INFO
+rootLogger.appenderRef.test.ref = TestLogger
+
+appender.testlogger.name = TestLogger
+appender.testlogger.type = CONSOLE
+appender.testlogger.target = SYSTEM_ERR
+appender.testlogger.layout.type = PatternLayout
+appender.testlogger.layout.pattern = %-4r [%t] %-5p %c - %m%n