Skip to content

Commit

Permalink
Add integration test
Browse files Browse the repository at this point in the history
  • Loading branch information
Matias Schilling committed Jun 19, 2024
1 parent 4477c2a commit 4c26e53
Show file tree
Hide file tree
Showing 66 changed files with 2,689 additions and 2,290 deletions.
20 changes: 18 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,30 @@ env:
JAVA_VERSION: 21

jobs:
test:
unit-test:
runs-on: [ ubuntu-latest ]
steps:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.version }}
- uses: actions/setup-java@v4
with:
cache: gradle
distribution: ${{ env.JAVA_DISTRIBUTION }}
java-version: ${{ env.JAVA_VERSION }}
- run: ./gradlew clean test spotlessCheck --exclude-task :chucknorris-integration-test:test

integration-test:
runs-on: [ ubuntu-latest ]
steps:
- uses: actions/checkout@v4
with:
ref: ${{ inputs.version }}
- run: ./gradlew test spotlessCheck
- uses: actions/setup-java@v4
with:
cache: gradle
distribution: ${{ env.JAVA_DISTRIBUTION }}
java-version: ${{ env.JAVA_VERSION }}
- name: Run Integration Test
shell: bash
run: ./chucknorris-integration-test/script/run-integration-test.sh
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ out/
hs_err_pid*

# Gradle
bin/
.gradle
build/
gradle-app.setting
Expand Down
5 changes: 1 addition & 4 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,13 @@ buildscript {
}
}

version = Git.commitHash()

subprojects { Project project ->
apply plugin: 'com.diffplug.spotless'
apply plugin: 'io.spring.dependency-management'
apply plugin: 'java'

sourceCompatibility = 17
targetCompatibility = 17
version Git.commitHash()

dependencyManagement {
imports { mavenBom("org.springframework.boot:spring-boot-dependencies:${Version.SPRING_BOOT}") }
Expand All @@ -39,7 +36,7 @@ subprojects { Project project ->

spotless {
java {
googleJavaFormat()
eclipse().configFile('../format.xml')
}
}
}
5 changes: 4 additions & 1 deletion buildSrc/src/main/groovy/Version.groovy
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
interface Version {
public static final String SPRING_BOOT = '2.7.18'
public static final String LOMBOK = '1.18.30'
public static final String JUNIT = '5.10.2'
public static final String REST_ASSURED = '5.4.0'
public static final String SPOTLESS = '7.0.0.BETA1'
public static final String SPRING_BOOT = '2.7.18'
}
12 changes: 12 additions & 0 deletions chucknorris-integration-test/build.gradle
Original file line number Diff line number Diff line change
@@ -1,2 +1,14 @@
dependencies {
testImplementation 'commons-logging:commons-logging:1.3.2'

testImplementation "org.junit.jupiter:junit-jupiter-api:${Version.JUNIT}"
testImplementation "org.junit.jupiter:junit-jupiter:${Version.JUNIT}"
testImplementation("io.rest-assured:json-path:${Version.REST_ASSURED}") {
exclude group: "org.apache.groovy", module: "groovy"
exclude group: "org.apache.groovy", module: "groovy-xml"
}
testImplementation("io.rest-assured:rest-assured:${Version.REST_ASSURED}") {
exclude group: "org.apache.groovy", module: "groovy"
exclude group: "org.apache.groovy", module: "groovy-xml"
}
}
14 changes: 12 additions & 2 deletions chucknorris-integration-test/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ services:
image: chucknorrisio/postgres
container_name: chucknorris-database
healthcheck:
interval: 30s
interval: 5s
retries: 5
start_period: 80s
test: [ "CMD-SHELL", "pg_isready", "-d", "db_prod" ]
timeout: 60s
ports:
- '5432:5432'

service:
subject:
environment:
- APPLICATION_EVENT_SNS_TOPIC_ARN=my-application-event-sns-topic-arn
- AWS_ACCESS_KEY_ID=my-aws-access-key-id
Expand All @@ -36,3 +36,13 @@ services:
condition: service_healthy
ports:
- '8080:8080'

integration-test:
command: "./gradlew :chucknorris-integration-test:test --stacktrace"
container_name: "integration-test"
depends_on:
- subject
image: "azul/zulu-openjdk-alpine:21-jre-headless-latest"
volumes:
- "../:/home/gradle/project"
working_dir: /home/gradle/project
14 changes: 5 additions & 9 deletions chucknorris-integration-test/script/run-integration-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,9 @@ readonly SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
readonly MODULE_DIR=$(cd "${SCRIPT_DIR}/.." && pwd)
readonly PROJECT_DIR=$(cd "${SCRIPT_DIR}/../.." && pwd)

./gradlew --build-file "$PROJECT_DIR/build.gradle" clean dockerTagCurrent
./gradlew --build-file "$PROJECT_DIR/chucknorris-web/build.gradle" clean dockerTagLatest

# docker-compose --file "$MODULE_DIR/docker-compose.yml" up \
# --exit-code-from "integration-test" \
# --quiet-pull \
# --remove-orphans

docker-compose --file "$MODULE_DIR/docker-compose.yml" up \
--quiet-pull \
--remove-orphans
docker-compose --file "$MODULE_DIR/docker-compose.yml" up \
--exit-code-from "integration-test" \
--quiet-pull \
--remove-orphans
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package io.chucknorris.integration_test.joke;

import static io.restassured.RestAssured.given;

import io.restassured.RestAssured;
import java.util.List;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

class JokeTest {

@BeforeAll
public static void beforeAll() {
RestAssured.baseURI = "http://subject";
RestAssured.port = 8080;
}

@DisplayName("Should return joke")
@Test
void shouldReturnJoke() {
var specification = given()
.log().all()
.accept("application/json");

var response = specification.when().get("/jokes/random");

response.then()
.log().all()
.statusCode(200)
.assertThat()
.header("Content-Type", Matchers.equalTo("application/json"))
.body("categories", Matchers.isA(List.class))
.body("created_at", Matchers.isA(String.class))
.body("icon_url", Matchers.isA(String.class))
.body("id", Matchers.isA(String.class))
.body("updated_at", Matchers.isA(String.class))
.body("url", Matchers.isA(String.class))
.body("value", Matchers.isA(String.class));
}

@DisplayName("Should include CORS headers")
@Test
void shouldIncludeCORSHeaders() {
var specification = given()
.log().all()
.accept("application/json")
.header("Origin", "http://localhost:3000");

var response = specification.when().get("/jokes/random");

response.then()
.log().all()
.statusCode(200)
.assertThat()
.header("Access-Control-Allow-Origin", Matchers.equalTo("*"))
.header("Access-Control-Expose-Headers", Matchers.equalTo("Access-Control-Allow-Origin Access-Control-Allow-Credentials"))
.header("Content-Type", Matchers.equalTo("application/json"));
}
}
23 changes: 2 additions & 21 deletions chucknorris-web/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,8 @@ plugins {
id 'org.springframework.boot'
}

repositories {
mavenCentral()
}

// versions
def lombokVersion = "1.18.30"

dependencies {
annotationProcessor "org.projectlombok:lombok:${lombokVersion}"
annotationProcessor "org.projectlombok:lombok:${Version.LOMBOK}"

implementation "com.amazonaws:aws-java-sdk:1.11.561"
implementation "com.fasterxml.jackson.core:jackson-core:2.17.1"
Expand All @@ -20,7 +13,7 @@ dependencies {
implementation "nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:2.3.0"
implementation "org.hibernate:hibernate-validator:6.0.16.Final"
implementation "org.postgresql:postgresql:42.2.9"
implementation "org.projectlombok:lombok:${lombokVersion}"
implementation "org.projectlombok:lombok:${Version.LOMBOK}"
implementation "org.springframework.boot:spring-boot-starter-data-jpa"
implementation "org.springframework.boot:spring-boot-starter-thymeleaf"
implementation "org.springframework.boot:spring-boot-starter-web"
Expand All @@ -35,7 +28,6 @@ tasks {
manifest.attributes("Multi-Release": "true")

archiveBaseName.set(project.name)
archiveVersion.set(project.version)

if (project.hasProperty("archiveName")) {
archiveFileName.set(project.properties["archiveName"] as String)
Expand All @@ -48,22 +40,11 @@ tasks {
def imageName = "${project.properties.group}/${project.name}"
name = "${imageName}:latest"

tag("current", "${imageName}:${project.version}")
tag("latest", "${imageName}:latest")
tag("herokuProduction", "registry.heroku.com/chucky/web")

dockerfile file("${projectDir}/src/main/docker/Dockerfile")
files tasks.bootJar.outputs
buildArgs([JAR_FILE: bootJar.getArchiveFileName().get()])
}

springBoot {
buildInfo {
properties {
artifact = "${project.name}-${project.version}.jar"
version = project.version
name = project.name
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@ComponentScan(basePackages = {"io.chucknorris"})
@ComponentScan(basePackages = { "io.chucknorris" })
@EnableJpaAuditing
@SpringBootApplication
public class Application {

public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,38 +16,38 @@
@ControllerAdvice
public class ApplicationExceptionHandler extends ResponseEntityExceptionHandler {

@ExceptionHandler(value = {ConstraintViolationException.class})
protected ResponseEntity<Object> handleConstraintViolationException(
ConstraintViolationException exception, ServletWebRequest request) {
switch (request.getHeader(HttpHeaders.ACCEPT)) {
case MediaType.TEXT_PLAIN_VALUE:
StringBuilder stringBuilder = new StringBuilder();
for (ConstraintViolation violation : exception.getConstraintViolations()) {
stringBuilder.append(
violation.getPropertyPath().toString() + ": " + violation.getMessage() + '\n');
}
@ExceptionHandler(value = { ConstraintViolationException.class })
protected ResponseEntity<Object> handleConstraintViolationException(
ConstraintViolationException exception, ServletWebRequest request) {
switch (request.getHeader(HttpHeaders.ACCEPT)) {
case MediaType.TEXT_PLAIN_VALUE:
StringBuilder stringBuilder = new StringBuilder();
for (ConstraintViolation violation : exception.getConstraintViolations()) {
stringBuilder.append(
violation.getPropertyPath().toString() + ": " + violation.getMessage() + '\n');
}

return handleExceptionInternal(
exception,
stringBuilder.toString(),
new HttpHeaders(),
HttpStatus.BAD_REQUEST,
request);
default:
LinkedHashMap<String, Object> constraintViolations = new LinkedHashMap<>();
for (ConstraintViolation violation : exception.getConstraintViolations()) {
constraintViolations.put(violation.getPropertyPath().toString(), violation.getMessage());
}
return handleExceptionInternal(
exception,
stringBuilder.toString(),
new HttpHeaders(),
HttpStatus.BAD_REQUEST,
request);
default:
LinkedHashMap<String, Object> constraintViolations = new LinkedHashMap<>();
for (ConstraintViolation violation : exception.getConstraintViolations()) {
constraintViolations.put(violation.getPropertyPath().toString(), violation.getMessage());
}

LinkedHashMap<String, Object> body = new LinkedHashMap<>();
body.put("timestamp", new Date());
body.put("status", HttpStatus.BAD_REQUEST.value());
body.put("error", HttpStatus.BAD_REQUEST.getReasonPhrase());
body.put("message", exception.getMessage());
body.put("violations", constraintViolations);
LinkedHashMap<String, Object> body = new LinkedHashMap<>();
body.put("timestamp", new Date());
body.put("status", HttpStatus.BAD_REQUEST.value());
body.put("error", HttpStatus.BAD_REQUEST.getReasonPhrase());
body.put("message", exception.getMessage());
body.put("violations", constraintViolations);

return handleExceptionInternal(
exception, body, new HttpHeaders(), HttpStatus.BAD_REQUEST, request);
return handleExceptionInternal(
exception, body, new HttpHeaders(), HttpStatus.BAD_REQUEST, request);
}
}
}
}
Loading

0 comments on commit 4c26e53

Please sign in to comment.