diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..c8b241f
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1 @@
+target/*
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 524f096..626464d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,14 +1,9 @@
# Compiled class file
*.class
-# Log file
-*.log
-
-# BlueJ files
-*.ctxt
-
-# Mobile Tools for Java (J2ME)
-.mtj.tmp/
+# Maven
+target/
+.m2/
# Package Files #
*.jar
@@ -19,6 +14,23 @@
*.tar.gz
*.rar
-# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
-hs_err_pid*
-replay_pid*
+# IntelliJ Idea
+.idea/*
+!.idea/runConfigurations
+*.iws
+*.ipr
+*.iml
+*.releaseBackup
+atlassian-ide-plugin.xml
+
+# Netbeans
+nbactions.xml
+nb-configuration.xml
+
+# Eclipse
+.settings
+.settings/
+.project
+.classpath
+.factorypath
+
diff --git a/.helidon b/.helidon
new file mode 100644
index 0000000..b8dbc7e
--- /dev/null
+++ b/.helidon
@@ -0,0 +1,6 @@
+#Helidon Project Configuration
+#Mon Nov 11 14:29:25 UTC 2024
+schema.version=1.1.0
+helidon.version=4.1.3
+project.flavor=mp
+project.archetype=quickstart
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..51f5303
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,39 @@
+
+# 1st stage, build the app
+FROM container-registry.oracle.com/java/jdk-no-fee-term:21 as build
+
+# Install maven
+WORKDIR /usr/share
+RUN set -x && \
+ curl -O https://archive.apache.org/dist/maven/maven-3/3.8.4/binaries/apache-maven-3.8.4-bin.tar.gz && \
+ tar -xvf apache-maven-*-bin.tar.gz && \
+ rm apache-maven-*-bin.tar.gz && \
+ mv apache-maven-* maven && \
+ ln -s /usr/share/maven/bin/mvn /bin/
+
+WORKDIR /helidon
+
+# Create a first layer to cache the "Maven World" in the local repository.
+# Incremental docker builds will always resume after that, unless you update
+# the pom
+ADD pom.xml .
+RUN mvn package -Dmaven.test.skip -Declipselink.weave.skip -Declipselink.weave.skip -DskipOpenApiGenerate
+
+# Do the Maven build!
+# Incremental docker builds will resume here when you change sources
+ADD src src
+RUN mvn package -DskipTests
+
+RUN echo "done!"
+
+# 2nd stage, build the runtime image
+FROM container-registry.oracle.com/java/jdk-no-fee-term:21
+WORKDIR /helidon
+
+# Copy the binary built in the 1st stage
+COPY --from=build /helidon/target/movies.jar ./
+COPY --from=build /helidon/target/libs ./libs
+
+CMD ["java", "-jar", "movies.jar"]
+
+EXPOSE 8080
diff --git a/Dockerfile.jlink b/Dockerfile.jlink
new file mode 100644
index 0000000..6b0c5e7
--- /dev/null
+++ b/Dockerfile.jlink
@@ -0,0 +1,35 @@
+
+# 1st stage, build the app
+FROM container-registry.oracle.com/java/jdk-no-fee-term:21 as build
+
+WORKDIR /usr/share
+
+# Install maven
+RUN set -x && \
+ curl -O https://archive.apache.org/dist/maven/maven-3/3.8.4/binaries/apache-maven-3.8.4-bin.tar.gz && \
+ tar -xvf apache-maven-*-bin.tar.gz && \
+ rm apache-maven-*-bin.tar.gz && \
+ mv apache-maven-* maven && \
+ ln -s /usr/share/maven/bin/mvn /bin/
+
+WORKDIR /helidon
+
+# Create a first layer to cache the "Maven World" in the local repository.
+# Incremental docker builds will always resume after that, unless you update
+# the pom
+ADD pom.xml .
+RUN mvn package -Dmaven.test.skip -Declipselink.weave.skip
+
+# Do the Maven build to create the custom Java Runtime Image
+# Incremental docker builds will resume here when you change sources
+ADD src src
+RUN mvn package -Pjlink-image -DskipTests
+RUN echo "done!"
+
+# 2nd stage, build the final image with the JRI built in the 1st stage
+
+FROM debian:stretch-slim
+WORKDIR /helidon
+COPY --from=build /helidon/target/movies-jri ./
+ENTRYPOINT ["/bin/bash", "/helidon/bin/start"]
+EXPOSE 8080
diff --git a/Dockerfile.native b/Dockerfile.native
new file mode 100644
index 0000000..f124eb7
--- /dev/null
+++ b/Dockerfile.native
@@ -0,0 +1,39 @@
+
+# 1st stage, build the app
+FROM ghcr.io/graalvm/graalvm-community:21.0.0-ol9 as build
+
+WORKDIR /usr/share
+
+# Install maven
+RUN set -x && \
+ curl -O https://archive.apache.org/dist/maven/maven-3/3.8.4/binaries/apache-maven-3.8.4-bin.tar.gz && \
+ tar -xvf apache-maven-*-bin.tar.gz && \
+ rm apache-maven-*-bin.tar.gz && \
+ mv apache-maven-* maven && \
+ ln -s /usr/share/maven/bin/mvn /bin/
+
+WORKDIR /helidon
+
+# Create a first layer to cache the "Maven World" in the local repository.
+# Incremental docker builds will always resume after that, unless you update
+# the pom
+ADD pom.xml .
+RUN mvn package -Pnative-image -Dnative.image.skip -Dmaven.test.skip -Declipselink.weave.skip
+
+# Do the Maven build!
+# Incremental docker builds will resume here when you change sources
+ADD src src
+RUN mvn package -Pnative-image -Dnative.image.buildStatic -DskipTests
+
+RUN echo "done!"
+
+# 2nd stage, build the runtime image
+FROM scratch
+WORKDIR /helidon
+
+# Copy the binary built in the 1st stage
+COPY --from=build /helidon/target/movies .
+
+ENTRYPOINT ["./movies"]
+
+EXPOSE 8080
diff --git a/README.md b/README.md
index 9d6e0e2..9ba653a 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,158 @@
-# movies-management
-Sample of REST API using Helidon and Oracle NoSQL
+# movies
+
+Sample Helidon MP project that includes multiple REST operations.
+
+## Build and run
+
+
+With JDK21
+```bash
+mvn package
+java -jar target/movies.jar
+```
+
+## Exercise the application
+
+Basic:
+```
+curl -X GET http://localhost:8080/simple-greet
+Hello World!
+```
+
+
+JSON:
+```
+curl -X GET http://localhost:8080/greet
+{"message":"Hello World!"}
+
+curl -X GET http://localhost:8080/greet/Joe
+{"message":"Hello Joe!"}
+
+curl -X PUT -H "Content-Type: application/json" -d '{"greeting" : "Hola"}' http://localhost:8080/greet/greeting
+
+curl -X GET http://localhost:8080/greet/Jose
+{"message":"Hola Jose!"}
+```
+
+
+
+## Try health
+
+```
+curl -s -X GET http://localhost:8080/health
+{"outcome":"UP",...
+
+```
+
+
+## Building a Native Image
+
+The generation of native binaries requires an installation of GraalVM 22.1.0+.
+
+You can build a native binary using Maven as follows:
+
+```
+mvn -Pnative-image install -DskipTests
+```
+
+The generation of the executable binary may take a few minutes to complete depending on
+your hardware and operating system. When completed, the executable file will be available
+under the `target` directory and be named after the artifact ID you have chosen during the
+project generation phase.
+
+
+
+## Try metrics
+
+```
+# Prometheus Format
+curl -s -X GET http://localhost:8080/metrics
+# TYPE base:gc_g1_young_generation_count gauge
+. . .
+
+# JSON Format
+curl -H 'Accept: application/json' -X GET http://localhost:8080/metrics
+{"base":...
+. . .
+```
+
+
+
+## Building the Docker Image
+
+```
+docker build -t movies .
+```
+
+## Running the Docker Image
+
+```
+docker run --rm -p 8080:8080 movies:latest
+```
+
+Exercise the application as described above.
+
+
+## Run the application in Kubernetes
+
+If you don’t have access to a Kubernetes cluster, you can [install one](https://helidon.io/docs/latest/#/about/kubernetes) on your desktop.
+
+### Verify connectivity to cluster
+
+```
+kubectl cluster-info # Verify which cluster
+kubectl get pods # Verify connectivity to cluster
+```
+
+### Deploy the application to Kubernetes
+
+```
+kubectl create -f app.yaml # Deploy application
+kubectl get pods # Wait for quickstart pod to be RUNNING
+kubectl get service movies # Get service info
+kubectl port-forward service/movies 8081:8080 # Forward service port to 8081
+```
+
+You can now exercise the application as you did before but use the port number 8081.
+
+After you’re done, cleanup.
+
+```
+kubectl delete -f app.yaml
+```
+
+
+## Building a Custom Runtime Image
+
+Build the custom runtime image using the jlink image profile:
+
+```
+mvn package -Pjlink-image
+```
+
+This uses the helidon-maven-plugin to perform the custom image generation.
+After the build completes it will report some statistics about the build including the reduction in image size.
+
+The target/movies-jri directory is a self contained custom image of your application. It contains your application,
+its runtime dependencies and the JDK modules it depends on. You can start your application using the provide start script:
+
+```
+./target/movies-jri/bin/start
+```
+
+Class Data Sharing (CDS) Archive
+Also included in the custom image is a Class Data Sharing (CDS) archive that improves your application’s startup
+performance and in-memory footprint. You can learn more about Class Data Sharing in the JDK documentation.
+
+The CDS archive increases your image size to get these performance optimizations. It can be of significant size (tens of MB).
+The size of the CDS archive is reported at the end of the build output.
+
+If you’d rather have a smaller image size (with a slightly increased startup time) you can skip the creation of the CDS
+archive by executing your build like this:
+
+```
+mvn package -Pjlink-image -Djlink.image.addClassDataSharingArchive=false
+```
+
+For more information on available configuration options see the helidon-maven-plugin documentation.
+
diff --git a/app.yaml b/app.yaml
new file mode 100644
index 0000000..205399b
--- /dev/null
+++ b/app.yaml
@@ -0,0 +1,37 @@
+kind: Service
+apiVersion: v1
+metadata:
+ name: movies
+ labels:
+ app: movies
+spec:
+ type: ClusterIP
+ selector:
+ app: movies
+ ports:
+ - name: tcp
+ port: 8080
+ protocol: TCP
+ targetPort: 8080
+---
+kind: Deployment
+apiVersion: apps/v1
+metadata:
+ name: movies
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ app: movies
+ template:
+ metadata:
+ labels:
+ app: movies
+ version: v1
+ spec:
+ containers:
+ - name: movies
+ image: movies
+ imagePullPolicy: IfNotPresent
+ ports:
+ - containerPort: 8080
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..45d1928
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,96 @@
+
+
+ 4.0.0
+
+ io.helidon.applications
+ helidon-mp
+ 4.1.3
+
+
+ expert.os.demos
+ movies
+ 1.0-SNAPSHOT
+
+
+
+
+ io.helidon.microprofile.bundles
+ helidon-microprofile-core
+
+
+ io.helidon.microprofile.openapi
+ helidon-microprofile-openapi
+
+
+ io.helidon.microprofile.health
+ helidon-microprofile-health
+
+
+ jakarta.json.bind
+ jakarta.json.bind-api
+
+
+ org.glassfish.jersey.media
+ jersey-media-json-binding
+ runtime
+
+
+ io.helidon.logging
+ helidon-logging-jul
+ runtime
+
+
+ io.smallrye
+ jandex
+ runtime
+
+
+ org.eclipse.microprofile.metrics
+ microprofile-metrics-api
+
+
+ io.helidon.microprofile.metrics
+ helidon-microprofile-metrics
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ test
+
+
+ io.helidon.microprofile.testing
+ helidon-microprofile-testing-junit5
+ test
+
+
+ org.hamcrest
+ hamcrest-all
+ test
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+ copy-libs
+
+
+
+
+ io.smallrye
+ jandex-maven-plugin
+
+
+ make-index
+
+
+
+
+
+
diff --git a/src/main/java/expert/os/demos/movies/GreetResource.java b/src/main/java/expert/os/demos/movies/GreetResource.java
new file mode 100644
index 0000000..3ac3aa5
--- /dev/null
+++ b/src/main/java/expert/os/demos/movies/GreetResource.java
@@ -0,0 +1,115 @@
+
+package expert.os.demos.movies;
+
+import jakarta.enterprise.context.RequestScoped;
+import jakarta.inject.Inject;
+import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.PUT;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.PathParam;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+
+import org.eclipse.microprofile.openapi.annotations.enums.SchemaType;
+import org.eclipse.microprofile.openapi.annotations.media.Content;
+import org.eclipse.microprofile.openapi.annotations.media.Schema;
+import org.eclipse.microprofile.openapi.annotations.parameters.RequestBody;
+import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
+import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
+
+/**
+ * A simple JAX-RS resource to greet you. Examples:
+ *
+ * Get default greeting message:
+ * curl -X GET http://localhost:8080/greet
+ *
+ * Get greeting message for Joe:
+ * curl -X GET http://localhost:8080/greet/Joe
+ *
+ * Change greeting
+ * curl -X PUT -H "Content-Type: application/json" -d '{"greeting" : "Howdy"}' http://localhost:8080/greet/greeting
+ *
+ * The message is returned as a JSON object.
+ */
+@Path("/greet")
+@RequestScoped
+public class GreetResource {
+
+ /**
+ * The greeting message provider.
+ */
+ private final GreetingProvider greetingProvider;
+
+ /**
+ * Using constructor injection to get a configuration property.
+ * By default this gets the value from META-INF/microprofile-config
+ *
+ * @param greetingConfig the configured greeting message
+ */
+ @Inject
+ public GreetResource(GreetingProvider greetingConfig) {
+ this.greetingProvider = greetingConfig;
+ }
+
+ /**
+ * Return a worldly greeting message.
+ *
+ * @return {@link Message}
+ */
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ public Message getDefaultMessage() {
+ return createResponse("World");
+ }
+
+ /**
+ * Return a greeting message using the name that was provided.
+ *
+ * @param name the name to greet
+ * @return {@link Message}
+ */
+ @Path("/{name}")
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ public Message getMessage(@PathParam("name") String name) {
+ return createResponse(name);
+ }
+
+ /**
+ * Set the greeting to use in future messages.
+ *
+ * @param message Message containing the new greeting
+ * @return {@link Response}
+ */
+ @Path("/greeting")
+ @PUT
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @RequestBody(name = "greeting",
+ required = true,
+ content = @Content(mediaType = "application/json",
+ schema = @Schema(type = SchemaType.OBJECT, requiredProperties = { "greeting" })))
+ @APIResponses({
+ @APIResponse(name = "normal", responseCode = "204", description = "Greeting updated"),
+ @APIResponse(name = "missing 'greeting'", responseCode = "400",
+ description = "JSON did not contain setting for 'greeting'")})
+ public Response updateGreeting(Message message) {
+
+ if (message.getGreeting() == null || message.getGreeting().isEmpty()) {
+ Message error = new Message();
+ error.setMessage("No greeting provided");
+ return Response.status(Response.Status.BAD_REQUEST).entity(error).build();
+ }
+
+ greetingProvider.setMessage(message.getGreeting());
+ return Response.status(Response.Status.NO_CONTENT).build();
+ }
+
+ private Message createResponse(String who) {
+ String msg = String.format("%s %s!", greetingProvider.getMessage(), who);
+
+ return new Message(msg);
+ }
+}
diff --git a/src/main/java/expert/os/demos/movies/GreetingProvider.java b/src/main/java/expert/os/demos/movies/GreetingProvider.java
new file mode 100644
index 0000000..2312116
--- /dev/null
+++ b/src/main/java/expert/os/demos/movies/GreetingProvider.java
@@ -0,0 +1,35 @@
+
+package expert.os.demos.movies;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
+
+import org.eclipse.microprofile.config.inject.ConfigProperty;
+
+/**
+ * Provider for greeting message.
+ */
+@ApplicationScoped
+public class GreetingProvider {
+ private final AtomicReference message = new AtomicReference<>();
+
+ /**
+ * Create a new greeting provider, reading the message from configuration.
+ *
+ * @param message greeting to use
+ */
+ @Inject
+ public GreetingProvider(@ConfigProperty(name = "app.greeting") String message) {
+ this.message.set(message);
+ }
+
+ String getMessage() {
+ return message.get();
+ }
+
+ void setMessage(String message) {
+ this.message.set(message);
+ }
+}
diff --git a/src/main/java/expert/os/demos/movies/Message.java b/src/main/java/expert/os/demos/movies/Message.java
new file mode 100644
index 0000000..4033810
--- /dev/null
+++ b/src/main/java/expert/os/demos/movies/Message.java
@@ -0,0 +1,31 @@
+package expert.os.demos.movies;
+
+public class Message {
+
+ private String message;
+
+ private String greeting;
+
+ public Message() {
+ }
+
+ public Message(String message) {
+ this.message = message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+ public String getMessage() {
+ return this.message;
+ }
+
+ public void setGreeting(String greeting) {
+ this.greeting = greeting;
+ }
+
+ public String getGreeting() {
+ return this.greeting;
+ }
+}
diff --git a/src/main/java/expert/os/demos/movies/SimpleGreetResource.java b/src/main/java/expert/os/demos/movies/SimpleGreetResource.java
new file mode 100644
index 0000000..9ca6c6a
--- /dev/null
+++ b/src/main/java/expert/os/demos/movies/SimpleGreetResource.java
@@ -0,0 +1,69 @@
+
+package expert.os.demos.movies;
+
+import org.eclipse.microprofile.metrics.MetricUnits;
+import org.eclipse.microprofile.metrics.annotation.Counted;
+import org.eclipse.microprofile.metrics.annotation.Timed;
+import jakarta.ws.rs.PathParam;
+
+import jakarta.inject.Inject;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.core.MediaType;
+
+import org.eclipse.microprofile.config.inject.ConfigProperty;
+
+/**
+ * A simple JAX-RS resource to greet you. Examples:
+ *
+ * Get default greeting message:
+ * curl -X GET http://localhost:8080/simple-greet
+ *
+ * The message is returned as a JSON object.
+ */
+@Path("/simple-greet")
+public class SimpleGreetResource {
+
+ private static final String PERSONALIZED_GETS_COUNTER_NAME = "personalizedGets";
+ private static final String PERSONALIZED_GETS_COUNTER_DESCRIPTION = "Counts personalized GET operations";
+ private static final String GETS_TIMER_NAME = "allGets";
+ private static final String GETS_TIMER_DESCRIPTION = "Tracks all GET operations";
+ private final String message;
+
+ @Inject
+ public SimpleGreetResource(@ConfigProperty(name = "app.greeting") String message) {
+ this.message = message;
+ }
+
+ /**
+ * Return a worldly greeting message.
+ *
+ * @return {@link Message}
+ */
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ public Message getDefaultMessage() {
+ String msg = String.format("%s %s!", message, "World");
+ Message message = new Message();
+ message.setMessage(msg);
+ return message;
+ }
+
+
+ @Path("/{name}")
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @Counted(name = PERSONALIZED_GETS_COUNTER_NAME,
+ absolute = true,
+ description = PERSONALIZED_GETS_COUNTER_DESCRIPTION)
+ @Timed(name = GETS_TIMER_NAME,
+ description = GETS_TIMER_DESCRIPTION,
+ unit = MetricUnits.SECONDS,
+ absolute = true)
+ public Message getMessage(@PathParam("name") String name) {
+ String message = String.format("Hello %s", name);
+ return new Message(message);
+ }
+
+}
diff --git a/src/main/java/expert/os/demos/movies/package-info.java b/src/main/java/expert/os/demos/movies/package-info.java
new file mode 100644
index 0000000..6a35e6d
--- /dev/null
+++ b/src/main/java/expert/os/demos/movies/package-info.java
@@ -0,0 +1,2 @@
+
+package expert.os.demos.movies;
diff --git a/src/main/resources/META-INF/beans.xml b/src/main/resources/META-INF/beans.xml
new file mode 100644
index 0000000..b099340
--- /dev/null
+++ b/src/main/resources/META-INF/beans.xml
@@ -0,0 +1,8 @@
+
+
+
diff --git a/src/main/resources/META-INF/microprofile-config.properties b/src/main/resources/META-INF/microprofile-config.properties
new file mode 100644
index 0000000..e3b22ac
--- /dev/null
+++ b/src/main/resources/META-INF/microprofile-config.properties
@@ -0,0 +1,11 @@
+# Microprofile server properties
+server.port=8080
+server.host=0.0.0.0
+
+# Change the following to true to enable the optional MicroProfile Metrics REST.request metrics
+metrics.rest-request.enabled=false
+
+# Application properties. This is the default greeting
+app.greeting=Hello
+
+
diff --git a/src/main/resources/META-INF/native-image/expert/os/demos/movies/native-image.properties b/src/main/resources/META-INF/native-image/expert/os/demos/movies/native-image.properties
new file mode 100644
index 0000000..5eef9a3
--- /dev/null
+++ b/src/main/resources/META-INF/native-image/expert/os/demos/movies/native-image.properties
@@ -0,0 +1 @@
+Args=--initialize-at-build-time=expert.os.demos.movies
\ No newline at end of file
diff --git a/src/main/resources/logging.properties b/src/main/resources/logging.properties
new file mode 100644
index 0000000..ffad43d
--- /dev/null
+++ b/src/main/resources/logging.properties
@@ -0,0 +1,20 @@
+
+# Example Logging Configuration File
+# For more information see $JAVA_HOME/jre/lib/logging.properties
+
+# Send messages to the console
+handlers=io.helidon.logging.jul.HelidonConsoleHandler
+
+# HelidonConsoleHandler uses a SimpleFormatter subclass that replaces "!thread!" with the current thread
+java.util.logging.SimpleFormatter.format=%1$tY.%1$tm.%1$td %1$tH:%1$tM:%1$tS %4$s %3$s !thread!: %5$s%6$s%n
+
+# Global logging level. Can be overridden by specific loggers
+.level=INFO
+
+# Quiet Weld
+org.jboss.level=WARNING
+
+# Component specific log levels
+#io.helidon.config.level=INFO
+#io.helidon.security.level=INFO
+#io.helidon.common.level=INFO
diff --git a/src/test/java/expert/os/demos/movies/MainTest.java b/src/test/java/expert/os/demos/movies/MainTest.java
new file mode 100644
index 0000000..5be4d03
--- /dev/null
+++ b/src/test/java/expert/os/demos/movies/MainTest.java
@@ -0,0 +1,97 @@
+
+package expert.os.demos.movies;
+
+import jakarta.inject.Inject;
+import jakarta.ws.rs.client.WebTarget;
+import jakarta.ws.rs.core.Response;
+import org.eclipse.microprofile.metrics.Counter;
+import org.eclipse.microprofile.metrics.MetricRegistry;
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.core.MediaType;
+
+import io.helidon.microprofile.testing.junit5.HelidonTest;
+import io.helidon.metrics.api.MetricsFactory;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.AfterAll;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+@HelidonTest
+class MainTest {
+
+ @Inject
+ private MetricRegistry registry;
+
+ @Inject
+ private WebTarget target;
+
+
+ @Test
+ void testHealth() {
+ Response response = target
+ .path("health")
+ .request()
+ .get();
+ assertThat(response.getStatus(), is(200));
+ }
+
+ @Test
+ void testMicroprofileMetrics() {
+ Message message = target.path("simple-greet/Joe")
+ .request()
+ .get(Message.class);
+
+ assertThat(message.getMessage(), is("Hello Joe"));
+ Counter counter = registry.counter("personalizedGets");
+ double before = counter.getCount();
+
+ message = target.path("simple-greet/Eric")
+ .request()
+ .get(Message.class);
+
+ assertThat(message.getMessage(), is("Hello Eric"));
+ double after = counter.getCount();
+ assertEquals(1d, after - before, "Difference in personalized greeting counter between successive calls");
+ }
+
+ @AfterAll
+ static void clear() {
+ MetricsFactory.closeAll();
+ }
+
+
+ @Test
+ void testGreet() {
+ Message message = target
+ .path("simple-greet")
+ .request()
+ .get(Message.class);
+ assertThat(message.getMessage(), is("Hello World!"));
+ }
+
+ @Test
+ void testGreetings() {
+ Message jsonMessage = target
+ .path("greet/Joe")
+ .request()
+ .get(Message.class);
+ assertThat(jsonMessage.getMessage(), is("Hello Joe!"));
+
+ try (Response r = target
+ .path("greet/greeting")
+ .request()
+ .put(Entity.entity("{\"greeting\" : \"Hola\"}", MediaType.APPLICATION_JSON))) {
+ assertThat(r.getStatus(), is(204));
+ }
+
+ jsonMessage = target
+ .path("greet/Jose")
+ .request()
+ .get(Message.class);
+ assertThat(jsonMessage.getMessage(), is("Hola Jose!"));
+ }
+
+}
diff --git a/src/test/resources/META-INF/microprofile-config.properties b/src/test/resources/META-INF/microprofile-config.properties
new file mode 100644
index 0000000..e69de29
diff --git a/src/test/resources/application-test.yaml b/src/test/resources/application-test.yaml
new file mode 100644
index 0000000..94f3451
--- /dev/null
+++ b/src/test/resources/application-test.yaml
@@ -0,0 +1,2 @@
+security:
+ enabled: false
\ No newline at end of file