Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Create E2E test infrastructure #172

Merged
merged 6 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions .github/workflows/e2e-tests.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
##
# Copyright (C) 2024 Hedera Hashgraph, LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
name: "E2E Test Suites"
on:
push:
branches:
- main
- release/*
pull_request:
branches:
- "*"

defaults:
run:
shell: bash

env:
GRADLE_EXEC: ./gradlew

jobs:
e2e-tests:
runs-on: block-node-linux-medium
steps:
- name: Harden Runner
uses: step-security/harden-runner@17d0e2bd7d51742c71671bd19fa12bdc9d40a3d6 # v2.8.1
with:
egress-policy: audit

- name: Checkout code
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
with:
fetch-depth: 0

- name: Expand Shallow Clone for Spotless
run: |
if [ -f .git/shallow ]; then
git fetch --unshallow --no-recurse-submodules
else
echo "Repository is not shallow, no need to unshallow."
fi

- name: Set up JDK 21
uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1
with:
distribution: 'temurin'
java-version: '21'

- name: Build application
run: ${{ env.GRADLE_EXEC }} build

- name: Run Acceptance Tests
id: acceptance-tests
run: ${GRADLE_EXEC} runSuites
2 changes: 1 addition & 1 deletion .github/workflows/smoke-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ jobs:

- name: Run application in background, capture logs in app.log
run: |
${{ env.GRADLE_EXEC }} run 2> server/src/test/resources/app.log < /dev/null &
${{ env.GRADLE_EXEC }} run -x :suites:run 2> server/src/test/resources/app.log < /dev/null &
echo "Application started with PID $APP_PID"
sleep 10

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,14 @@ extraJavaModuleInfo {
module("org.jetbrains.kotlinx:kotlinx-metadata-jvm", "kotlinx.metadata.jvm")

// Test clients only
module("com.github.docker-java:docker-java-api", "com.github.dockerjava.api")
module("com.github.docker-java:docker-java-transport", "com.github.dockerjava.transport")
module(
"com.github.docker-java:docker-java-transport-zerodep",
"com.github.dockerjava.transport.zerodep"
)
module("org.slf4j:slf4j-api", "org.slf4j") { patchRealModule() }
georgi-l95 marked this conversation as resolved.
Show resolved Hide resolved
module("io.github.cdimascio:java-dotenv", "io.github.cdimascio")
module("com.google.protobuf:protobuf-java-util", "com.google.protobuf.util")
module("com.squareup:javapoet", "com.squareup.javapoet") {
exportAllPackages()
Expand Down
25 changes: 25 additions & 0 deletions buildSrc/src/main/kotlin/com.hedera.block.suites.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright (C) 2024 Hedera Hashgraph, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

plugins {
id("application")
id("com.hedera.block.conventions")
id("me.champeau.jmh")
}

val maven = publishing.publications.create<MavenPublication>("maven") { from(components["java"]) }

signing.sign(maven)
7 changes: 7 additions & 0 deletions gradle/modules.properties
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ org.apache.commons.io=commons-io:commons-io
org.apache.commons.lang3=org.apache.commons:commons-lang3
org.apache.commons.compress=org.apache.commons:commons-compress

org.testcontainers=org.testcontainers:testcontainers
org.testcontainers.junit-jupiter=org.testcontainers:junit-jupiter
com.github.dockerjava.api=com.github.docker-java:docker-java-api
com.github.docker-java.transport.zerodep=com.github.docker-java:docker-java-transport-zerodep
com.github.docker-java.transport.httpclient5=com.github.docker-java:docker-java-transport-httpclient5
io.github.cdimascio=io.github.cdimascio:java-dotenv

java.annotation=javax.annotation:javax.annotation-api
org.apache.logging.log4j.slf4j2.impl=org.apache.logging.log4j:log4j-slf4j2-impl

Expand Down
5 changes: 5 additions & 0 deletions server/docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,10 @@ RUN tar -xvf server-${VERSION}.tar
# Copy the logging properties file
COPY logging.properties logging.properties

# HEALTHCHECK for liveness and readiness
HEALTHCHECK --interval=30s --timeout=10s --start-period=3s --retries=3 \
CMD curl -f http://localhost:8080/healthz/livez || exit 1 && \
curl -f http://localhost:8080/healthz/readyz || exit 1

# RUN the bin script for starting the server
ENTRYPOINT ["/bin/bash", "-c", "/app/server-${VERSION}/bin/server"]
7 changes: 6 additions & 1 deletion settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ plugins {
}

// Include the subprojects
include(":suites")
include(":stream")
include(":server")
include(":simulator")
Expand Down Expand Up @@ -95,9 +96,13 @@ dependencyResolutionManagement {
// Testing only versions
version("org.assertj.core", "3.23.1")
version("org.junit.jupiter.api", "5.10.2")
version("org.junit.platform", "1.11.0")
version("org.mockito", "5.8.0")
version("org.mockito.junit.jupiter", "5.8.0")

version("org.testcontainers", "1.20.1")
version("org.testcontainers.junit-jupiter", "1.20.1")
version("com.github.docker-java", "3.4.0")
version("io.github.cdimascio", "5.2.2")
}
}
}
Expand Down
67 changes: 67 additions & 0 deletions suites/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright (C) 2022-2024 Hedera Hashgraph, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

plugins {
id("application")
id("com.hedera.block.suites")
}

description = "Hedera Block Node E2E Suites"

application {
mainModule = "com.hedera.block.suites"
mainClass = "com.hedera.block.suites.BaseSuite"
}

mainModuleInfo {
requires("org.junit.jupiter.api")
requires("org.junit.platform.suite.api")
requires("org.testcontainers")
requires("io.github.cdimascio")
runtimeOnly("org.testcontainers.junit-jupiter")
runtimeOnly("org.junit.jupiter.engine")
}

val updateDockerEnv =
tasks.register<Exec>("updateDockerEnv") {
description =
"Creates the .env file in the docker folder that contains environment variables for Docker"
group = "docker"

workingDir(layout.projectDirectory.dir("../server/docker"))
commandLine("./update-env.sh", project.version)
}

// Task to build the Docker image
tasks.register<Exec>("createDockerImage") {
description = "Creates the Docker image of the Block Node Server based on the current version"
group = "docker"

dependsOn(updateDockerEnv, tasks.assemble)
workingDir(layout.projectDirectory.dir("../server/docker"))
commandLine("./docker-build.sh", project.version, layout.projectDirectory.dir("..").asFile)
}

tasks.register<Test>("runSuites") {
description = "Runs E2E Test Suites"
group = "suites"
dependsOn("createDockerImage")

useJUnitPlatform()
testLogging { events("passed", "skipped", "failed") }
testClassesDirs = sourceSets["main"].output.classesDirs
classpath = sourceSets["main"].runtimeClasspath
}
124 changes: 124 additions & 0 deletions suites/src/main/java/com/hedera/block/suites/BaseSuite.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/*
* Copyright (C) 2022-2024 Hedera Hashgraph, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.hedera.block.suites;

import io.github.cdimascio.dotenv.Dotenv;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.utility.DockerImageName;

/**
* BaseSuite is an abstract class that provides common setup and teardown functionality for test
* suites using Testcontainers to manage a Docker container for the Block Node server.
*
* <p>This class is responsible for:
*
* <ul>
* <li>Starting a Docker container running the Block Node Application with a specified version.
* <li>Stopping the container after tests have been executed.
* </ul>
*
* <p>The Block Node Application version is retrieved dynamically from an environment file (.env).
*/
public abstract class BaseSuite {

/** Container running the Block Node Application */
protected static GenericContainer<?> blockNodeContainer;

/** Port that is used by the Block Node Application */
protected static int blockNodePort;

/**
* Default constructor for the BaseSuite class.
*
* <p>This constructor can be used by subclasses or the testing framework to initialize the
* BaseSuite. It does not perform any additional setup.
*/
public BaseSuite() {
// No additional setup required
}

/**
* Setup method to be executed before all tests.
*
* <p>This method initializes the Block Node server container using Testcontainers.
*/
@BeforeAll
public static void setup() {
blockNodeContainer = getConfiguration();
blockNodeContainer.start();
}

/**
* Teardown method to be executed after all tests.
*
* <p>This method stops the Block Node server container if it is running. It ensures that
* resources are cleaned up after the test suite execution is complete.
*/
@AfterAll
public static void teardown() {
if (blockNodeContainer != null) {
blockNodeContainer.stop();
}
}

/**
* Retrieves the configuration for the Block Node server container.
*
* <p>This method initializes the Block Node container with the version retrieved from the .env
* file. It configures the container and returns it.
*
* <p>Specific configuration steps include:
*
* <ul>
* <li>Setting the environment variable "VERSION" from the .env file.
* <li>Exposing the default gRPC port (8080).
* <li>Using the Testcontainers health check mechanism to ensure the container is ready.
* </ul>
*
* @return a configured {@link GenericContainer} instance for the Block Node server
*/
public static GenericContainer<?> getConfiguration() {
String blockNodeVersion = BaseSuite.getBlockNodeVersion();
blockNodePort = 8080;
blockNodeContainer =
new GenericContainer<>(
DockerImageName.parse("block-node-server:" + blockNodeVersion))
.withExposedPorts(blockNodePort)
.withEnv("VERSION", blockNodeVersion)
.waitingFor(Wait.forListeningPort())
.waitingFor(Wait.forHealthcheck());
return blockNodeContainer;
}

/**
* Retrieves the Block Node server version from the .env file.
*
* <p>This method loads the .env file from the "../server/docker" directory and extracts the
* value of the "VERSION" environment variable, which represents the version of the Block Node
* server to be used in the container.
*
* @return the version of the Block Node server as a string
*/
private static String getBlockNodeVersion() {
Dotenv dotenv = Dotenv.configure().directory("../server/docker").filename(".env").load();

return dotenv.get("VERSION");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright (C) 2022-2024 Hedera Hashgraph, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.hedera.block.suites.grpc;

import com.hedera.block.suites.grpc.negative.NegativeServerAvailabilityTests;
import com.hedera.block.suites.grpc.positive.PositiveServerAvailabilityTests;
import org.junit.platform.suite.api.SelectClasses;
import org.junit.platform.suite.api.Suite;

/**
* Test suite for running gRPC server availability tests, including both positive and negative test
* scenarios.
*
* <p>This suite aggregates the tests from {@link PositiveServerAvailabilityTests} and {@link
* NegativeServerAvailabilityTests}. The {@code @Suite} annotation allows running all selected
* classes in a single test run.
*/
@Suite
@SelectClasses({PositiveServerAvailabilityTests.class, NegativeServerAvailabilityTests.class})
public class GrpcTestSuites {

/**
* Default constructor for the {@link GrpcTestSuites} class. This constructor is empty as it
* does not need to perform any initialization.
*/
public GrpcTestSuites() {}
}
Loading
Loading