Skip to content

Commit

Permalink
[Testing][JShellAPI] Setting first integration test for /eval endpoint;
Browse files Browse the repository at this point in the history
Note: this includes changes and improvements in gradle.build files;
  • Loading branch information
firasrg committed Aug 10, 2024
1 parent 9dc586e commit c8747e1
Show file tree
Hide file tree
Showing 9 changed files with 122 additions and 63 deletions.
63 changes: 21 additions & 42 deletions JShellAPI/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,18 @@ dependencies {
implementation 'com.github.docker-java:docker-java-core:3.3.6'

testImplementation('org.springframework.boot:spring-boot-starter-test') {
configurations {
all {
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging'
exclude group: 'ch.qos.logback', module: 'logback-classic'
exclude group: 'org.apache.logging.log4j', module: 'log4j-to-slf4j'
}
}
exclude group: 'ch.qos.logback', module: 'logback-classic'
}
testImplementation gradleTestKit()
testImplementation 'org.springframework.boot:spring-boot-starter-webflux'

annotationProcessor "org.springframework.boot:spring-boot-configuration-processor"

}

def imageName = 'togetherjava.org:5001/togetherjava/jshellbackend:master' ?: 'latest';

jib {
from.image = 'eclipse-temurin:21'
to {
image = imageName
image = 'togetherjava.org:5001/togetherjava/jshellbackend:master' ?: 'latest'
auth {
username = System.getenv('ORG_REGISTRY_USER') ?: ''
password = System.getenv('ORG_REGISTRY_PASSWORD') ?: ''
Expand All @@ -51,41 +43,28 @@ shadowJar {
archiveVersion.set('')
}

tasks.register('buildDockerImage') {
group = 'Docker'
description = 'builds jshellwrapper as docker image'
dependsOn jibDockerBuild
doFirst{
println('creating docker image...')
}
doLast{
println('docker image is ready for use')
def jshellWrapperImageName = rootProject.ext.jShellWrapperImageName;

processResources {
filesMatching('application.yaml') {
expand(jShellWrapperImageName: jshellWrapperImageName)
}
}

tasks.register('removeDockerImage', Exec) {
group = 'Docker'
description = 'removes jshellwrapper image'
commandLine 'docker', 'rmi', '-f', imageName
doLast{
println('docker image has been removed')
}

def taskBuildDockerImage = tasks.register('buildDockerImage') {
group = 'docker'
description = 'builds jshellwrapper as docker image'
dependsOn project(':JShellWrapper').tasks.named('jibDockerBuild')
}

tasks.named('test') {
dependsOn tasks.named('buildDockerImage')
def taskRemoveDockerImage = tasks.register('removeDockerImage', Exec) {
group = 'docker'
description = 'removes jshellwrapper image'
commandLine 'docker', 'rmi', '-f', jshellWrapperImageName
}

doFirst {
try {
println 'Running JShellAPI tests...'
} catch (Exception e) {
println 'JShellAPI tests failed'
tasks.named('removeDockerImage').get().execute()
throw e
}
}
doLast {
println 'JShellAPI tests completed.'
}
finalizedBy tasks.named('removeDockerImage')
test {
dependsOn taskBuildDockerImage
finalizedBy taskRemoveDockerImage
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.togetherjava.jshellapi.rest;

public final class ApiEndpoints {
private ApiEndpoints() {}

public static final String EVALUATE_CODE_SNIPPET = "/jshell/eval";
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
import com.github.dockerjava.core.DefaultDockerClientConfig;
import com.github.dockerjava.core.DockerClientImpl;
import com.github.dockerjava.httpclient5.ApacheDockerHttpClient;
import jakarta.el.PropertyNotFoundException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;

Expand All @@ -29,6 +31,9 @@ public class DockerService implements DisposableBean {

private final DockerClient client;

@Value("${jshell-wrapper.image-name}")
private String jshellWrapperImageName;

public DockerService(Config config) {
DefaultDockerClientConfig clientConfig =
DefaultDockerClientConfig.createDefaultConfigBuilder().build();
Expand Down Expand Up @@ -59,22 +64,33 @@ private void cleanupLeftovers(UUID currentId) {

public String spawnContainer(long maxMemoryMegs, long cpus, @Nullable String cpuSetCpus,
String name, Duration evalTimeout, long sysoutLimit) throws InterruptedException {
String imageName = "togetherjava.org:5001/togetherjava/jshellwrapper";
String imageName = Optional.ofNullable(this.jshellWrapperImageName)
.orElseThrow(() -> new PropertyNotFoundException(
"unable to find jshellWrapper image name property"));

String[] imageNameParts = imageName.split(":master");

if (imageNameParts.length != 1) {
throw new IllegalArgumentException("invalid jshellWrapper image name");
}

String baseImageName = imageNameParts[0];

boolean presentLocally = client.listImagesCmd()
.withFilter("reference", List.of(imageName))
.withFilter("reference", List.of(baseImageName))
.exec()
.stream()
.flatMap(it -> Arrays.stream(it.getRepoTags()))
.anyMatch(it -> it.endsWith(":master"));

if (!presentLocally) {
client.pullImageCmd(imageName)
client.pullImageCmd(baseImageName)
.withTag("master")
.exec(new PullImageResultCallback())
.awaitCompletion(5, TimeUnit.MINUTES);
}

return client.createContainerCmd(imageName + ":master")
return client.createContainerCmd(baseImageName + ":master")
.withHostConfig(HostConfig.newHostConfig()
.withAutoRemove(true)
.withInit(true)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"properties": [
{
"name": "jshell-wrapper.image-name",
"type": "java.lang.String",
"description": "JShellWrapper image name injected from the top-level gradle build file."
}
] }
3 changes: 3 additions & 0 deletions JShellAPI/src/main/resources/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ jshellapi:
dockerResponseTimeout: 60
dockerConnectionTimeout: 60

jshell-wrapper:
image-name: ${jShellWrapperImageName}

server:
error:
include-message: always
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,62 @@
package org.togetherjava.jshellapi;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.web.reactive.server.WebTestClient;

import org.togetherjava.jshellapi.dto.JShellResult;
import org.togetherjava.jshellapi.rest.ApiEndpoints;

import java.time.Duration;

import static org.assertj.core.api.Assertions.assertThat;

// TODO - write some integrations
/**
* This class holds integration tests for JShellAPI. It depends on gradle building image task, fore
* more information check "test" section in gradle.build file.
*
* @author Firas Regaieg
*/
@ContextConfiguration
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class JShellApiTests {

@Autowired
private WebTestClient webTestClient;

private static final String TEST_EVALUATION_ID = "test";
private static final String TEST_CODE_INPUT = "2+2";
private static final String TEST_CODE_EXPECTED_OUTPUT = "4";

@Test
public void test() {
assertThat(true).isTrue();
@DisplayName("When posting code snippet, evaluate it then returns successfully result")
public void evaluateCodeSnippetTest() {

JShellResult result = this.webTestClient.mutate()
.responseTimeout(Duration.ofSeconds(6))
.build()
.post()
.uri(ApiEndpoints.EVALUATE_CODE_SNIPPET + "/" + TEST_EVALUATION_ID)
.bodyValue(TEST_CODE_INPUT)
.exchange()
.expectStatus()
.isOk()
.expectBody(JShellResult.class)
.value(task -> assertThat(task).isNotNull())
.returnResult()
.getResponseBody();

assertThat(result).isNotNull();

boolean isValidResult = result.snippetsResults()
.stream()
.filter(res -> res.result() != null)
.anyMatch(res -> res.result().equals(TEST_CODE_EXPECTED_OUTPUT));

assertThat(isValidResult).isTrue();

}
}
4 changes: 2 additions & 2 deletions JShellWrapper/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ test {
jib {
from.image = 'eclipse-temurin:22-alpine'
to {
image = 'togetherjava.org:5001/togetherjava/jshellwrapper:master' ?: 'latest'
image = rootProject.ext.jShellWrapperImageName
auth {
username = System.getenv('ORG_REGISTRY_USER') ?: ''
password = System.getenv('ORG_REGISTRY_PASSWORD') ?: ''
Expand All @@ -41,4 +41,4 @@ shadowJar {
archiveBaseName.set('JShellWrapper')
archiveClassifier.set('')
archiveVersion.set('')
}
}
20 changes: 8 additions & 12 deletions JShellWrapper/src/test/java/JShellWrapperTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,10 @@ void testHelloWorld() {

@Test
void testExpressionResult() {
evalTest(
"""
eval
1
"Hello world!\"""",
"""
evalTest("""
eval
1
"Hello world!\"""", """
OK
0
OK
Expand All @@ -67,12 +65,10 @@ void testExpressionResult() {
false
""");
evalTest(
"""
eval
1
2+2""",
"""
evalTest("""
eval
1
2+2""", """
OK
0
OK
Expand Down
4 changes: 4 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,7 @@ subprojects {
testImplementation 'org.junit.jupiter:junit-jupiter:5.10.2'
}
}

ext {
jShellWrapperImageName = 'togetherjava.org:5001/togetherjava/jshellwrapper:master' ?: 'latest'
}

0 comments on commit c8747e1

Please sign in to comment.