From f725d55163c81a301447f89db8254ba175734d3f Mon Sep 17 00:00:00 2001 From: He Wang Date: Mon, 22 Apr 2024 17:32:42 +0800 Subject: [PATCH] feat: add testcontainers-java module (#35) * feat: add testcontainers-java module * docs: add README for testcontainers-java --- .github/workflows/ci.yml | 3 + java/testcontainers-java/README-CN.md | 60 ++++++++++++ java/testcontainers-java/README.md | 60 ++++++++++++ java/testcontainers-java/pom.xml | 39 ++++++++ java/testcontainers-java/run.sh | 2 + .../samples/OceanBaseCEContainerTest.java | 96 +++++++++++++++++++ .../src/test/resources/log4j2-test.properties | 8 ++ 7 files changed, 268 insertions(+) create mode 100644 java/testcontainers-java/README-CN.md create mode 100644 java/testcontainers-java/README.md create mode 100644 java/testcontainers-java/pom.xml create mode 100644 java/testcontainers-java/run.sh create mode 100644 java/testcontainers-java/src/test/java/com/oceanbase/samples/OceanBaseCEContainerTest.java create mode 100644 java/testcontainers-java/src/test/resources/log4j2-test.properties diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0a04612..7eb476d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,6 +22,9 @@ jobs: language: 'java' - name: 'oceanbase-client' language: 'java' + - name: 'testcontainers-java' + language: 'java' + with_oceanbase_container: false - name: 'pymysql' language: 'python' uses: ./.github/workflows/basic-ci.yml diff --git a/java/testcontainers-java/README-CN.md b/java/testcontainers-java/README-CN.md new file mode 100644 index 0000000..0f69381 --- /dev/null +++ b/java/testcontainers-java/README-CN.md @@ -0,0 +1,60 @@ +# TestContainers Java + +[English](README.md) | 简体中文 + +本文介绍了如何通过 testcontainers-java 启动和测试 OceanBase Docker 容器,更多详细信息可以参见 https://java.testcontainers.org/modules/databases/oceanbase 。 + +## 快速开始 + +将 TestContainers OceanBase 模块添加到 POM。 + +```xml + + org.testcontainers + oceanbase + 1.19.7 + test + +``` + +以 [OceanBaseCEContainerTest.java](src/test/java/com/oceanbase/samples/OceanBaseCEContainerTest.java) 代码为例。 + +以下代码实现了`OceanBaseCEContainer`的生命周期管理。 它将在执行任何测试用例之前启动容器实例,并在执行所有测试用例后停止容器。 + +```java +private static final Logger LOG = LoggerFactory.getLogger(OceanBaseCEContainerTest.class); + +public static final OceanBaseCEContainer CONTAINER = + new OceanBaseCEContainer("oceanbase/oceanbase-ce:latest") + .withEnv("MODE", "slim") + .withEnv("FASTBOOT", "true") + .withLogConsumer(new Slf4jLogConsumer(LOG)); + +@BeforeClass +public static void startContainers() { + Startables.deepStart(Stream.of(CONTAINER)).join(); + LOG.info( + "OceanBase docker container started, image: {}, host: {}, sql port: {}, rpc port:{}.", + CONTAINER.getDockerImageName(), + CONTAINER.getHost(), + CONTAINER.getMappedPort(2881), + CONTAINER.getMappedPort(2882)); +} + +@AfterClass +public static void closeContainers() { + CONTAINER.close(); + LOG.info("OceanBase docker container stopped."); +} +``` + +您可以在测试用例中使用容器实例,如下所示: + +```java +@Test +public void test() { + try (Connection connection = CONTAINER.createConnection("?useSSL=false")) { + ... + } +} +``` diff --git a/java/testcontainers-java/README.md b/java/testcontainers-java/README.md new file mode 100644 index 0000000..4c9ff6f --- /dev/null +++ b/java/testcontainers-java/README.md @@ -0,0 +1,60 @@ +# TestContainers Java + +English | [简体中文](README-CN.md) + +This article describes how to start and test OceanBase Docker container through `testcontainers-java`, you can see here for more details https://java.testcontainers.org/modules/databases/oceanbase/. + +## Quick Start + +Add TestContainers OceanBase module to POM. + +```xml + + org.testcontainers + oceanbase + 1.19.7 + test + +``` + +Take [OceanBaseCEContainerTest.java](src/test/java/com/oceanbase/samples/OceanBaseCEContainerTest.java) code as an example. + +The following code implements life cycle management of `OceanBaseCEContainer`. It will start the container instance before executing any test cases and stop the container after all test cases have been executed. + +```java +private static final Logger LOG = LoggerFactory.getLogger(OceanBaseCEContainerTest.class); + +public static final OceanBaseCEContainer CONTAINER = + new OceanBaseCEContainer("oceanbase/oceanbase-ce:latest") + .withEnv("MODE", "slim") + .withEnv("FASTBOOT", "true") + .withLogConsumer(new Slf4jLogConsumer(LOG)); + +@BeforeClass +public static void startContainers() { + Startables.deepStart(Stream.of(CONTAINER)).join(); + LOG.info( + "OceanBase docker container started, image: {}, host: {}, sql port: {}, rpc port:{}.", + CONTAINER.getDockerImageName(), + CONTAINER.getHost(), + CONTAINER.getMappedPort(2881), + CONTAINER.getMappedPort(2882)); +} + +@AfterClass +public static void closeContainers() { + CONTAINER.close(); + LOG.info("OceanBase docker container stopped."); +} +``` + +You can use the container instance in test cases like below: + +```java +@Test +public void test() { + try (Connection connection = CONTAINER.createConnection("?useSSL=false")) { + ... + } +} +``` diff --git a/java/testcontainers-java/pom.xml b/java/testcontainers-java/pom.xml new file mode 100644 index 0000000..82e92fc --- /dev/null +++ b/java/testcontainers-java/pom.xml @@ -0,0 +1,39 @@ + + + 4.0.0 + + com.oceanbase.samples + testcontainers-java + 1.0-SNAPSHOT + + ob-samples-testcontainers-java + + + UTF-8 + 1.8 + 1.8 + + + + + com.oceanbase + oceanbase-client + 2.4.9 + + + org.testcontainers + oceanbase + 1.19.7 + test + + + org.apache.logging.log4j + log4j-slf4j-impl + 2.17.1 + test + + + + diff --git a/java/testcontainers-java/run.sh b/java/testcontainers-java/run.sh new file mode 100644 index 0000000..7b5ea53 --- /dev/null +++ b/java/testcontainers-java/run.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +mvn verify diff --git a/java/testcontainers-java/src/test/java/com/oceanbase/samples/OceanBaseCEContainerTest.java b/java/testcontainers-java/src/test/java/com/oceanbase/samples/OceanBaseCEContainerTest.java new file mode 100644 index 0000000..0ee616c --- /dev/null +++ b/java/testcontainers-java/src/test/java/com/oceanbase/samples/OceanBaseCEContainerTest.java @@ -0,0 +1,96 @@ +package com.oceanbase.samples; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testcontainers.containers.output.Slf4jLogConsumer; +import org.testcontainers.lifecycle.Startables; +import org.testcontainers.oceanbase.OceanBaseCEContainer; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.stream.Stream; + +public class OceanBaseCEContainerTest { + + private static final Logger LOG = LoggerFactory.getLogger(OceanBaseCEContainerTest.class); + + public static final OceanBaseCEContainer CONTAINER = + new OceanBaseCEContainer("oceanbase/oceanbase-ce:latest") + .withEnv("MODE", "slim") + .withEnv("FASTBOOT", "true") + .withLogConsumer(new Slf4jLogConsumer(LOG)); + + @BeforeClass + public static void startContainers() { + Startables.deepStart(Stream.of(CONTAINER)).join(); + LOG.info( + "OceanBase docker container started, image: {}, host: {}, sql port: {}, rpc port:{}.", + CONTAINER.getDockerImageName(), + CONTAINER.getHost(), + CONTAINER.getMappedPort(2881), + CONTAINER.getMappedPort(2882)); + } + + @AfterClass + public static void closeContainers() { + CONTAINER.close(); + LOG.info("OceanBase docker container stopped."); + } + + @Test + public void test() { + String database = "testcontainers"; + String table = "person"; + String tableName = String.format("`%s`.`%s`", database, table); + + LOG.info( + "Try to connect to OceanBase docker container with url: {}.", + CONTAINER.getJdbcUrl()); + try (Connection connection = CONTAINER.createConnection("?useSSL=false")) { + LOG.info("Connect to OceanBase docker container successfully."); + + LOG.info("Prepare database and table."); + try (Statement statement = connection.createStatement()) { + statement.execute("CREATE DATABASE IF NOT EXISTS " + database); + statement.execute("USE " + database); + statement.execute( + "CREATE TABLE IF NOT EXISTS " + table + " (name VARCHAR(50), age INT)"); + } catch (SQLException e) { + throw new RuntimeException(e); + } + + LOG.info("Insert data to table {}.", tableName); + try (PreparedStatement ps = + connection.prepareStatement("INSERT INTO " + tableName + " values(?, ?)")) { + ps.setString(1, "Adam"); + ps.setInt(2, 28); + ps.executeUpdate(); + ps.setString(1, "Eve"); + ps.setInt(2, 26); + ps.executeUpdate(); + } + + LOG.info("Query rows from {}.", tableName); + try (PreparedStatement ps = + connection.prepareStatement( + "SELECT * from " + tableName, + ResultSet.TYPE_FORWARD_ONLY, + ResultSet.CONCUR_READ_ONLY)) { + ResultSet rs = ps.executeQuery(); + int count = 0; + while (rs.next()) { + LOG.info("Row {}: name {}, age {}.", count++, rs.getString(1), rs.getInt(2)); + } + assert count == 2; + } + } catch (SQLException e) { + throw new RuntimeException(e); + } + } +} diff --git a/java/testcontainers-java/src/test/resources/log4j2-test.properties b/java/testcontainers-java/src/test/resources/log4j2-test.properties new file mode 100644 index 0000000..f43aa2a --- /dev/null +++ b/java/testcontainers-java/src/test/resources/log4j2-test.properties @@ -0,0 +1,8 @@ +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