diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1ec9e9e8..b0963335 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -26,7 +26,7 @@ jobs: cache: maven - name: Compile and test with Maven - run: mvn -B test --file pom.xml + run: mvn -B verify --file pom.xml - name: Set up QEMU uses: docker/setup-qemu-action@v2 diff --git a/Dockerfile b/Dockerfile index 1a18e81f..89f9b6d0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,14 @@ FROM eclipse-temurin:21-jre-jammy -COPY target/dependency /lib -COPY target/classes /app WORKDIR /app +ARG USERNAME=hazeuser +ARG USER_UID=1000 +ARG USER_GID=$USER_UID +RUN groupadd --gid $USER_GID $USERNAME && \ + useradd --uid $USER_UID --gid $USER_GID -m $USERNAME +ADD https://repo1.maven.org/maven2/org/apache/logging/log4j/log4j-core/2.20.0/log4j-core-2.20.0.jar ./lib/ +ADD https://repo1.maven.org/maven2/org/apache/logging/log4j/log4j-api/2.20.0/log4j-api-2.20.0.jar ./lib/ +COPY target/classes . +RUN chown --recursive $USERNAME:$USERNAME . +USER $USERNAME EXPOSE 6379 -ENTRYPOINT ["java","-cp" , "/app:/lib/*", "--enable-preview", "org.fungover.haze.Main"] +ENTRYPOINT ["java","-cp","/app:/app/lib/*","org.fungover.haze.Main"] diff --git a/pom.xml b/pom.xml index aea84a03..6ff3f4dd 100644 --- a/pom.xml +++ b/pom.xml @@ -48,6 +48,30 @@ log4j-core 2.20.0 + + redis.clients + jedis + 5.1.0 + test + + + org.testcontainers + testcontainers + 1.19.4 + test + + + org.testcontainers + junit-jupiter + 1.17.6 + test + + + org.slf4j + slf4j-nop + 1.7.36 + test + @@ -55,11 +79,6 @@ org.apache.maven.plugins maven-compiler-plugin 3.11.0 - - - --enable-preview - - org.apache.maven.plugins @@ -95,6 +114,16 @@ org.apache.maven.plugins maven-jar-plugin 3.3.0 + + + default-jar + none + + unwanted + unwanted + + + org.apache.maven.plugins @@ -114,7 +143,7 @@ org.apache.maven.plugins maven-failsafe-plugin - 3.0.0-M9 + 3.2.1 @@ -123,9 +152,6 @@ - - @{argLine} --enable-preview - org.jacoco @@ -154,32 +180,14 @@ org.apache.maven.plugins maven-surefire-plugin - 3.0.0-M9 + 3.2.5 6380 12345 - @{argLine} --enable-preview - - org.apache.maven.plugins - maven-dependency-plugin - 3.5.0 - - - copy-dependencies - test - - copy-dependencies - - - runtime - - - - diff --git a/src/test/java/org/fungover/haze/MainTest.java b/src/test/java/org/fungover/haze/MainTest.java index b7e5d380..4e9c2c29 100644 --- a/src/test/java/org/fungover/haze/MainTest.java +++ b/src/test/java/org/fungover/haze/MainTest.java @@ -7,12 +7,11 @@ import java.io.ByteArrayOutputStream; import java.io.PrintStream; -import java.util.ArrayList; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; import static org.fungover.haze.Main.printThreadDebug; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertFalse; class MainTest { diff --git a/src/test/java/org/fungover/haze/integration/HazeExtension.java b/src/test/java/org/fungover/haze/integration/HazeExtension.java new file mode 100644 index 00000000..88a45c30 --- /dev/null +++ b/src/test/java/org/fungover/haze/integration/HazeExtension.java @@ -0,0 +1,55 @@ +package org.fungover.haze.integration; + +import org.junit.jupiter.api.extension.AfterAllCallback; +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.images.builder.ImageFromDockerfile; +import redis.clients.jedis.JedisPooled; + +import java.lang.reflect.Field; +import java.nio.file.Path; +import java.util.List; + +public class HazeExtension implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback { + + private GenericContainer haze; + private JedisPooled pool; + + @Override + public void beforeAll(ExtensionContext extensionContext) throws Exception { + haze = new GenericContainer<>(new ImageFromDockerfile() + .withDockerfile(Path.of("./Dockerfile"))) + .withExposedPorts(6379); + haze.start(); + pool = new JedisPooled(haze.getHost(), haze.getFirstMappedPort()); + } + + @Override + public void afterAll(ExtensionContext context) { + // do nothing, Testcontainers handles container shutdown + if (pool != null) + pool.close(); + } + + @Override + public void beforeEach(ExtensionContext extensionContext) throws Exception { + + // Get the list of test instances (instances of test classes) + final List testInstances = + extensionContext.getRequiredTestInstances().getAllInstances(); + + testInstances.forEach((ti) -> { + for (Field field : ti.getClass().getDeclaredFields()) { + if (field.isAnnotationPresent(Pool.class)) { + try { + field.set(ti, pool); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } + } + }); + } +} diff --git a/src/test/java/org/fungover/haze/integration/HazeIT.java b/src/test/java/org/fungover/haze/integration/HazeIT.java new file mode 100644 index 00000000..c5bae8b0 --- /dev/null +++ b/src/test/java/org/fungover/haze/integration/HazeIT.java @@ -0,0 +1,90 @@ +package org.fungover.haze.integration; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import redis.clients.jedis.JedisPooled; +import redis.clients.jedis.Protocol; +import redis.clients.jedis.util.SafeEncoder; + +import static org.assertj.core.api.Assertions.assertThat; + +@ExtendWith(HazeExtension.class) +class HazeIT { + + @Pool + JedisPooled pool; + + @Test + void pingPong() { + //Simple PING command with no message should return PONG as simple string + var result = pool.sendCommand(Protocol.Command.PING); + assertThat(SafeEncoder.encode((byte[]) result)).isEqualTo("PONG"); + //PING with message argument should return bulk string with the argument + result = pool.sendCommand(Protocol.Command.PING, "HELLO"); + assertThat(SafeEncoder.encode((byte[]) result)).isEqualTo("HELLO"); + //PING with message argument containing space should return bulk string with the argument + // result = pool.sendCommand(Protocol.Command.PING, "HELLO\r\n There"); + // assertThat(SafeEncoder.encode((byte[]) result)).isEqualTo("HELLO\r\n There"); + } + + @Test + void setNx() { + pool.del("test"); + pool.del("test1"); + assertThat(pool.setnx("test", "test")).isEqualTo(1); + assertThat(pool.setnx("test1", "test")).isEqualTo(1); + //Key test already exists so should not be set + assertThat(pool.setnx("test", "test1")).isZero(); + pool.del("test"); + pool.del("test1"); + } + + @Test + void setGet() { + assertThat(pool.set("test", "test")).isEqualTo("OK"); + assertThat(pool.get("test")).isEqualTo("test"); + pool.del("test"); + } + + @Test + void exists() { + assertThat(pool.set("test", "test")).isEqualTo("OK"); + assertThat(pool.exists("test")).isTrue(); + pool.del("notused"); + assertThat(pool.exists("notused")).isFalse(); + } + + @Test + void listLPushLPop() { + assertThat(pool.lpush("left", "first")).isEqualTo(1); + assertThat(pool.llen("left")).isEqualTo(1); + assertThat(pool.lpop("left")).isEqualTo("first"); + assertThat(pool.llen("left")).isZero(); + pool.del("left"); + assertThat(pool.exists("left")).isFalse(); + } + + @Test + void listRPushRPop() { + assertThat(pool.rpush("right", "first")).isEqualTo(1); + assertThat(pool.llen("right")).isEqualTo(1); + assertThat(pool.rpop("right")).isEqualTo("first"); + assertThat(pool.llen("right")).isZero(); + pool.del("right"); + assertThat(pool.exists("right")).isFalse(); + } + + @Test + void listKeyWithMultipleValues() { + assertThat(pool.lpush("test", "first")).isEqualTo(1); + assertThat(pool.lpush("test", "second")).isEqualTo(2); + assertThat(pool.llen("test")).isEqualTo(2); + assertThat(pool.lpush("test", "third", "fourth")).isEqualTo(4); + assertThat(pool.llen("test")).isEqualTo(4); + assertThat(pool.rpush("test", "fifth", "sixth")).isEqualTo(6); + assertThat(pool.llen("test")).isEqualTo(6); + + pool.del("test"); + assertThat(pool.exists("right")).isFalse(); + } +} diff --git a/src/test/java/org/fungover/haze/integration/Pool.java b/src/test/java/org/fungover/haze/integration/Pool.java new file mode 100644 index 00000000..f264d017 --- /dev/null +++ b/src/test/java/org/fungover/haze/integration/Pool.java @@ -0,0 +1,9 @@ +package org.fungover.haze.integration; + +import java.lang.annotation.*; + +@Documented +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Pool { +}