diff --git a/.gitignore b/.gitignore
index 549e00a..f2e78f2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,3 +31,5 @@ build/
### VS Code ###
.vscode/
+
+.envrc
\ No newline at end of file
diff --git a/.mvn/extensions.xml b/.mvn/extensions.xml
index 896770a..a58d883 100644
--- a/.mvn/extensions.xml
+++ b/.mvn/extensions.xml
@@ -8,6 +8,6 @@
kr.motd.maven
os-maven-plugin
- 1.7.0
+ 1.7.1
diff --git a/pom.xml b/pom.xml
index 8ae9ca5..8af79f4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -19,7 +19,7 @@
com.github.loki4j
loki-logback-appender
- 1.4.1
+ 1.5.0-m1
com.rometools
@@ -69,6 +69,12 @@
2.0.1
test
+
+ org.apache.maven.shared
+ maven-invoker
+ 3.2.0
+ test
+
org.springframework.boot
spring-boot-testcontainers
@@ -89,6 +95,12 @@
spring-boot-starter-test
test
+
+ fr.brouillard.oss
+ jgitver-maven-plugin
+ 1.9.0
+ test
+
@@ -159,6 +171,19 @@
+
+ org.apache.maven.plugins
+ maven-failsafe-plugin
+ 3.2.3
+
+
+
+ integration-test
+ verify
+
+
+
+
diff --git a/src/main/java/com/javagrunt/listener/youtube/Application.java b/src/main/java/com/javagrunt/listener/youtube/Application.java
index 2f25aee..8ae3550 100644
--- a/src/main/java/com/javagrunt/listener/youtube/Application.java
+++ b/src/main/java/com/javagrunt/listener/youtube/Application.java
@@ -16,11 +16,8 @@
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.annotation.Id;
-import org.springframework.data.redis.connection.RedisConnectionFactory;
-import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.observability.MicrometerTracingAdapter;
import org.springframework.data.redis.core.RedisHash;
-import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;
import org.springframework.data.repository.CrudRepository;
import org.springframework.web.bind.annotation.*;
@@ -88,25 +85,13 @@ record YouTubeEvent(@Id String id, String entryXml){}
@Configuration
@EnableRedisRepositories
class ApplicationConfig {
-
-// @Bean
-// public RedisConnectionFactory connectionFactory() {
-// return new LettuceConnectionFactory();
-// }
-//
-// @Bean
-// public RedisTemplate, ?> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
-// RedisTemplate template = new RedisTemplate();
-// template.setConnectionFactory(redisConnectionFactory);
-// return template;
-// }
-//
-// @Bean
-// public ClientResources clientResources(ObservationRegistry observationRegistry) {
-// return ClientResources.builder()
-// .tracing(new MicrometerTracingAdapter(observationRegistry, "youtube-listener"))
-// .build();
-// }
+
+ @Bean
+ public ClientResources clientResources(ObservationRegistry observationRegistry) {
+ return ClientResources.builder()
+ .tracing(new MicrometerTracingAdapter(observationRegistry, "youtube-listener"))
+ .build();
+ }
}
interface EventRepository extends CrudRepository {
}
\ No newline at end of file
diff --git a/src/test/java/com/javagrunt/listener/youtube/AbstractAppTests.java b/src/test/java/com/javagrunt/listener/youtube/AbstractAppTests.java
new file mode 100644
index 0000000..430c944
--- /dev/null
+++ b/src/test/java/com/javagrunt/listener/youtube/AbstractAppTests.java
@@ -0,0 +1,152 @@
+package com.javagrunt.listener.youtube;
+
+import com.redis.testcontainers.RedisContainer;
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.specification.RequestSpecification;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
+import org.springframework.restdocs.RestDocumentationContextProvider;
+import org.springframework.restdocs.RestDocumentationExtension;
+import org.testcontainers.containers.Network;
+import org.testcontainers.junit.jupiter.Container;
+import org.testcontainers.junit.jupiter.Testcontainers;
+
+import static io.restassured.RestAssured.given;
+import static org.hamcrest.core.Is.is;
+import static org.springframework.restdocs.operation.preprocess.Preprocessors.modifyUris;
+import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest;
+import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.document;
+import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.documentationConfiguration;
+
+@ExtendWith(RestDocumentationExtension.class)
+@Testcontainers
+public abstract class AbstractAppTests {
+
+ static Logger logger = LoggerFactory.getLogger(AbstractAppTests.class);
+
+ abstract int getPort();
+
+ private RequestSpecification spec;
+ private static final Network network = Network.newNetwork();
+
+ static Network getNetwork(){
+ return network;
+ }
+
+ @Container
+ @ServiceConnection(name = "redis")
+ static final RedisContainer redis = new RedisContainer(
+ RedisContainer.DEFAULT_IMAGE_NAME.withTag(RedisContainer.DEFAULT_TAG))
+ .withExposedPorts(6379)
+ .withNetworkAliases("redis")
+ .withNetwork(network);
+
+ @BeforeEach
+ void setUp(RestDocumentationContextProvider restDocumentation) {
+ this.spec = new RequestSpecBuilder()
+ .addFilter(documentationConfiguration(restDocumentation))
+ .build();
+ }
+ @Test
+ public void getShouldEchoHubChallenge() {
+ given(this.spec)
+ .filter(document("hello",
+ preprocessRequest(modifyUris()
+ .scheme("https")
+ .host("youtube-listener.javagrunt.com")
+ .removePort())))
+ .when()
+ .port(getPort())
+ .get("/api/?hub.mode=subscribe&hub.challenge=CHALLENGE_STRING&hub.topic=somthingcool")
+ .then()
+ .assertThat().statusCode(is(200))
+ .assertThat().body(is("CHALLENGE_STRING"));
+ }
+
+ @Test
+ void postShouldReturnSuccess() throws Exception {
+ String exampleEvent = """
+
+
+
+ YouTube video feed
+ 2015-04-01T19:05:24.552394234+00:00
+
+ yt:video:VIDEO_ID
+ VIDEO_ID
+ CHANNEL_ID
+ Video title
+
+
+ Channel title
+ http://www.youtube.com/channel/CHANNEL_ID
+
+ 2015-03-06T21:40:57+00:00
+ 2015-03-09T19:05:24.552394234+00:00
+
+
+ """;
+ given(this.spec)
+ .filter(document("listen",
+ preprocessRequest(modifyUris()
+ .scheme("https")
+ .host("youtube-listener.javagrunt.com")
+ .removePort())))
+ .contentType("application/atom+xml")
+ .body(exampleEvent)
+ .when()
+ .port(getPort())
+ .post("/api/")
+ .then()
+ .assertThat()
+ .statusCode(is(200));
+ }
+
+ @Test
+ public void actuatorInfo() {
+ given(this.spec)
+ .filter(document("info",
+ preprocessRequest(modifyUris()
+ .scheme("https")
+ .host("youtube-listener.javagrunt.com")
+ .removePort())))
+ .when()
+ .port(getPort())
+ .get("/actuator/info")
+ .then()
+ .assertThat().statusCode(is(200));
+ }
+
+ @Test
+ public void actuatorHealth() {
+ given(this.spec)
+ .filter(document("health",
+ preprocessRequest(modifyUris()
+ .scheme("https")
+ .host("youtube-listener.javagrunt.com")
+ .removePort())))
+ .when()
+ .port(getPort())
+ .get("/actuator/health")
+ .then()
+ .assertThat().statusCode(is(200));
+ }
+
+ @Test
+ void redisShouldBeRunning() {
+ Assertions.assertTrue(redis.isRunning());
+ }
+
+ @AfterAll
+ static void tearDown() {
+ redis.stop();
+ }
+
+}
diff --git a/src/test/java/com/javagrunt/listener/youtube/JavaVirtualMachineTest.java b/src/test/java/com/javagrunt/listener/youtube/JavaVirtualMachineTest.java
new file mode 100644
index 0000000..9eaabbf
--- /dev/null
+++ b/src/test/java/com/javagrunt/listener/youtube/JavaVirtualMachineTest.java
@@ -0,0 +1,16 @@
+package com.javagrunt.listener.youtube;
+
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.web.server.LocalServerPort;
+
+@SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT)
+class JavaVirtualMachineTest extends AbstractAppTests {
+
+ @LocalServerPort
+ private int port;
+
+ @Override
+ int getPort() {
+ return this.port;
+ }
+}
diff --git a/src/test/java/com/javagrunt/listener/youtube/NativeImageIT.java b/src/test/java/com/javagrunt/listener/youtube/NativeImageIT.java
new file mode 100644
index 0000000..d206273
--- /dev/null
+++ b/src/test/java/com/javagrunt/listener/youtube/NativeImageIT.java
@@ -0,0 +1,93 @@
+package com.javagrunt.listener.youtube;
+
+import fr.brouillard.oss.jgitver.GitVersionCalculator;
+import org.apache.maven.shared.invoker.DefaultInvocationRequest;
+import org.apache.maven.shared.invoker.DefaultInvoker;
+import org.apache.maven.shared.invoker.InvocationResult;
+import org.apache.maven.shared.invoker.MavenInvocationException;
+import org.junit.jupiter.api.BeforeAll;
+import org.testcontainers.DockerClientFactory;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.wait.strategy.Wait;
+import org.testcontainers.junit.jupiter.Container;
+import org.testcontainers.utility.LazyFuture;
+
+import java.io.File;
+import java.time.Duration;
+import java.time.temporal.ChronoUnit;
+import java.util.List;
+import java.util.concurrent.Future;
+
+public class NativeImageIT extends AbstractAppTests {
+
+ private static final Future IMAGE_FUTURE = new LazyFuture<>() {
+ @Override
+ protected String resolve() {
+ // Find project's root dir
+ File cwd;
+ cwd = new File(".");
+ while (!new File(cwd, "mvnw").isFile()) {
+ cwd = cwd.getParentFile();
+ }
+
+ var request = new DefaultInvocationRequest()
+ .addShellEnvironment("DOCKER_HOST", DockerClientFactory.instance().getTransportConfig().getDockerHost().toString())
+ .setPomFile(new File(cwd, "pom.xml"))
+ .setGoals(List.of("spring-boot:build-image"))
+ .setMavenExecutable(new File(cwd, "mvnw"))
+ .setProfiles(List.of("native"));
+
+ InvocationResult invocationResult;
+ try {
+ invocationResult = new DefaultInvoker().execute(request);
+ } catch (MavenInvocationException e) {
+ throw new RuntimeException(e);
+ }
+
+ if (invocationResult.getExitCode() != 0) {
+ throw new RuntimeException(invocationResult.getExecutionException());
+ }
+
+ String semanticVersion = null;
+ File workDir = new File(System.getProperty("user.dir"));
+
+ try (GitVersionCalculator jgitver = GitVersionCalculator.location(workDir)) {
+ semanticVersion = jgitver.getVersion().split("-")[0];
+ } catch (Exception e) {
+ logger.error("Error getting semantic version", e);
+ }
+
+ if (System.getProperty("os.arch").contains("aarch")) {
+ return String.format("dashaun/com.javagrunt.listener.youtube:v%s-aarch_64", semanticVersion);
+ } else {
+ return String.format("dashaun/com.javagrunt.listener.youtube:v%s-amd_64", semanticVersion);
+ }
+ }
+ };
+
+ private static int port;
+
+ @Override
+ int getPort() {
+ return port;
+ }
+
+
+ @Container
+ static final GenericContainer> APP = new GenericContainer<>(IMAGE_FUTURE)
+ .withExposedPorts(8080)
+ .withNetworkAliases("app")
+ .withNetwork(getNetwork())
+ .withEnv("REDIS_PORT", "6379")
+ .withEnv("REDIS_HOST", "redis")
+ .waitingFor(Wait.forHttp("/actuator/health"))
+ .withStartupTimeout(Duration.of(600, ChronoUnit.SECONDS))
+ .dependsOn(redis);
+
+ @BeforeAll
+ static void setUp() {
+ APP.start();
+ port = APP.getFirstMappedPort();
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/com/javagrunt/listener/youtube/WebLayerTest.java b/src/test/java/com/javagrunt/listener/youtube/WebLayerTest.java
deleted file mode 100644
index ec78935..0000000
--- a/src/test/java/com/javagrunt/listener/youtube/WebLayerTest.java
+++ /dev/null
@@ -1,146 +0,0 @@
-package com.javagrunt.listener.youtube;
-
-import com.redis.testcontainers.RedisContainer;
-import io.restassured.builder.RequestSpecBuilder;
-import io.restassured.specification.RequestSpecification;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.boot.test.context.TestConfiguration;
-import org.springframework.boot.test.web.server.LocalServerPort;
-import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
-import org.springframework.context.annotation.Bean;
-import org.springframework.restdocs.RestDocumentationContextProvider;
-import org.springframework.restdocs.RestDocumentationExtension;
-import org.testcontainers.containers.GenericContainer;
-import org.testcontainers.junit.jupiter.Container;
-import org.testcontainers.junit.jupiter.Testcontainers;
-import org.testcontainers.utility.DockerImageName;
-
-
-import static io.restassured.RestAssured.given;
-import static org.hamcrest.core.Is.is;
-import static org.junit.Assert.assertTrue;
-import static org.springframework.restdocs.operation.preprocess.Preprocessors.modifyUris;
-import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest;
-import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.document;
-import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.documentationConfiguration;
-
-
-@SuppressWarnings("resource")
-@ExtendWith(RestDocumentationExtension.class)
-@SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT)
-@Testcontainers
-class WebLayerTest {
-
- private RequestSpecification spec;
-
- @LocalServerPort
- private int port;
-
- @Container
- @ServiceConnection
- private static final RedisContainer redis = new RedisContainer(
- RedisContainer.DEFAULT_IMAGE_NAME.withTag(RedisContainer.DEFAULT_TAG));
-
- @BeforeEach
- void setUp(RestDocumentationContextProvider restDocumentation) {
- this.spec = new RequestSpecBuilder()
- .addFilter(documentationConfiguration(restDocumentation))
- .build();
- }
-
- @Test
- void redisShouldBeRunning() {
- Assertions.assertTrue(redis.isRunning());
- }
-
- @Test
- public void getShouldEchoHubChallenge() {
- given(this.spec)
- .filter(document("hello",
- preprocessRequest(modifyUris()
- .scheme("https")
- .host("youtube-listener.javagrunt.com")
- .removePort())))
- .when()
- .port(this.port)
- .get("/api/?hub.mode=subscribe&hub.challenge=CHALLENGE_STRING&hub.topic=somthingcool")
- .then()
- .assertThat().statusCode(is(200))
- .assertThat().body(is("CHALLENGE_STRING"));
- }
-
- @Test
- void postShouldReturnSuccess() throws Exception {
- String exampleEvent = """
-
-
-
- YouTube video feed
- 2015-04-01T19:05:24.552394234+00:00
-
- yt:video:VIDEO_ID
- VIDEO_ID
- CHANNEL_ID
- Video title
-
-
- Channel title
- http://www.youtube.com/channel/CHANNEL_ID
-
- 2015-03-06T21:40:57+00:00
- 2015-03-09T19:05:24.552394234+00:00
-
-
- """;
- given(this.spec)
- .filter(document("listen",
- preprocessRequest(modifyUris()
- .scheme("https")
- .host("youtube-listener.javagrunt.com")
- .removePort())))
- .contentType("application/atom+xml")
- .body(exampleEvent)
- .when()
- .port(this.port)
- .post("/api/")
- .then()
- .assertThat()
- .statusCode(is(200));
- }
-
- @Test
- public void actuatorHealth() {
- given(this.spec)
- .filter(document("health",
- preprocessRequest(modifyUris()
- .scheme("https")
- .host("youtube-listener.javagrunt.com")
- .removePort())))
- .when()
- .port(this.port)
- .get("/actuator/health")
- .then()
- .assertThat().statusCode(is(200));
- }
-
- @Test
- public void actuatorInfo() {
- given(this.spec)
- .filter(document("info",
- preprocessRequest(modifyUris()
- .scheme("https")
- .host("youtube-listener.javagrunt.com")
- .removePort())))
- .when()
- .port(this.port)
- .get("/actuator/info")
- .then()
- .assertThat().statusCode(is(200));
- }
-
-}