From 3a565f3f06526b22f6fed3e46b4ce6848bd6d05c Mon Sep 17 00:00:00 2001 From: Georgi Lazarov Date: Fri, 11 Oct 2024 00:59:10 +0300 Subject: [PATCH] feat: Add simulator as dependency in Suites (#242) Signed-off-by: georgi-l95 --- .../com.hedera.block.jpms-modules.gradle.kts | 1 - .../block/simulator/BlockStreamSimulator.java | 6 +-- .../simulator/BlockStreamSimulatorApp.java | 15 +++--- .../BlockSimulatorParsingException.java | 29 ++++++++++++ .../generator/BlockAsFileLargeDataSets.java | 15 ++++-- .../generator/BlockStreamManager.java | 14 +++--- simulator/src/main/java/module-info.java | 2 + .../simulator/BlockStreamSimulatorTest.java | 8 ++-- .../BlockAsDirBlockStreamManagerTest.java | 6 +-- .../BlockAsFileBlockStreamManagerTest.java | 8 ++-- .../BlockAsFileLargeDataSetsTest.java | 44 ++++++++++++++++-- suites/build.gradle.kts | 12 +++-- .../com/hedera/block/suites/BaseSuite.java | 39 +++++++++++++++- .../DataPersistenceTestSuites.java | 39 ++++++++++++++++ .../PositiveDataPersistenceTests.java | 26 +++++++++++ suites/src/main/java/module-info.java | 15 ++++++ ...00000000000000000000000000000000001.blk.gz | Bin 0 -> 6394 bytes ...00000000000000000000000000000000002.blk.gz | Bin 0 -> 8242 bytes ...00000000000000000000000000000000003.blk.gz | Bin 0 -> 9754 bytes ...00000000000000000000000000000000004.blk.gz | Bin 0 -> 8897 bytes ...00000000000000000000000000000000005.blk.gz | Bin 0 -> 10775 bytes 21 files changed, 238 insertions(+), 41 deletions(-) create mode 100644 simulator/src/main/java/com/hedera/block/simulator/exception/BlockSimulatorParsingException.java create mode 100644 suites/src/main/java/com/hedera/block/suites/persistence/DataPersistenceTestSuites.java create mode 100644 suites/src/main/java/com/hedera/block/suites/persistence/positive/PositiveDataPersistenceTests.java create mode 100644 suites/src/main/java/module-info.java create mode 100644 suites/src/main/resources/block-0.0.3/000000000000000000000000000000000001.blk.gz create mode 100644 suites/src/main/resources/block-0.0.3/000000000000000000000000000000000002.blk.gz create mode 100644 suites/src/main/resources/block-0.0.3/000000000000000000000000000000000003.blk.gz create mode 100644 suites/src/main/resources/block-0.0.3/000000000000000000000000000000000004.blk.gz create mode 100644 suites/src/main/resources/block-0.0.3/000000000000000000000000000000000005.blk.gz diff --git a/buildSrc/src/main/kotlin/com.hedera.block.jpms-modules.gradle.kts b/buildSrc/src/main/kotlin/com.hedera.block.jpms-modules.gradle.kts index b6374db6f..14ae2e6c5 100644 --- a/buildSrc/src/main/kotlin/com.hedera.block.jpms-modules.gradle.kts +++ b/buildSrc/src/main/kotlin/com.hedera.block.jpms-modules.gradle.kts @@ -191,7 +191,6 @@ extraJavaModuleInfo { "com.github.docker-java:docker-java-transport-zerodep", "com.github.dockerjava.transport.zerodep" ) - module("org.slf4j:slf4j-api", "org.slf4j") { patchRealModule() } 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") { diff --git a/simulator/src/main/java/com/hedera/block/simulator/BlockStreamSimulator.java b/simulator/src/main/java/com/hedera/block/simulator/BlockStreamSimulator.java index f9b21a4c2..4c5d2861e 100644 --- a/simulator/src/main/java/com/hedera/block/simulator/BlockStreamSimulator.java +++ b/simulator/src/main/java/com/hedera/block/simulator/BlockStreamSimulator.java @@ -18,7 +18,7 @@ import static java.lang.System.Logger.Level.INFO; -import com.hedera.pbj.runtime.ParseException; +import com.hedera.block.simulator.exception.BlockSimulatorParsingException; import com.swirlds.config.api.Configuration; import com.swirlds.config.api.ConfigurationBuilder; import com.swirlds.config.extensions.sources.ClasspathFileConfigSource; @@ -41,10 +41,10 @@ private BlockStreamSimulator() {} * @param args the arguments to be passed to the block stream simulator * @throws IOException if an I/O error occurs * @throws InterruptedException if the thread is interrupted - * @throws ParseException if a parse error occurs + * @throws BlockSimulatorParsingException if a parse error occurs */ public static void main(String[] args) - throws IOException, InterruptedException, ParseException { + throws IOException, InterruptedException, BlockSimulatorParsingException { LOGGER.log(INFO, "Starting Block Stream Simulator"); diff --git a/simulator/src/main/java/com/hedera/block/simulator/BlockStreamSimulatorApp.java b/simulator/src/main/java/com/hedera/block/simulator/BlockStreamSimulatorApp.java index 391380fc3..7e7859d37 100644 --- a/simulator/src/main/java/com/hedera/block/simulator/BlockStreamSimulatorApp.java +++ b/simulator/src/main/java/com/hedera/block/simulator/BlockStreamSimulatorApp.java @@ -17,13 +17,14 @@ package com.hedera.block.simulator; import com.hedera.block.simulator.config.data.BlockStreamConfig; +import com.hedera.block.simulator.exception.BlockSimulatorParsingException; import com.hedera.block.simulator.generator.BlockStreamManager; import com.hedera.block.simulator.grpc.PublishStreamGrpcClient; import com.hedera.hapi.block.stream.BlockItem; -import com.hedera.pbj.runtime.ParseException; import com.swirlds.config.api.Configuration; import edu.umd.cs.findbugs.annotations.NonNull; import java.io.IOException; +import java.util.concurrent.atomic.AtomicBoolean; import javax.inject.Inject; /** BlockStream Simulator App */ @@ -39,7 +40,7 @@ public class BlockStreamSimulatorApp { private final int delayBetweenBlockItems; - boolean isRunning = false; + private final AtomicBoolean isRunning = new AtomicBoolean(false); /** * Creates a new BlockStreamSimulatorApp instance. @@ -66,14 +67,14 @@ public BlockStreamSimulatorApp( * Starts the block stream simulator. * * @throws InterruptedException if the thread is interrupted - * @throws ParseException if a parse error occurs + * @throws BlockSimulatorParsingException if a parse error occurs * @throws IOException if an I/O error occurs */ - public void start() throws InterruptedException, ParseException, IOException { + public void start() throws InterruptedException, BlockSimulatorParsingException, IOException { int delayMSBetweenBlockItems = delayBetweenBlockItems / 1_000_000; int delayNSBetweenBlockItems = delayBetweenBlockItems % 1_000_000; - isRunning = true; + isRunning.set(true); LOGGER.log(System.Logger.Level.INFO, "Block Stream Simulator has started"); boolean streamBlockItem = true; @@ -113,12 +114,12 @@ public void start() throws InterruptedException, ParseException, IOException { * @return true if the block stream simulator is running, false otherwise */ public boolean isRunning() { - return isRunning; + return isRunning.get(); } /** Stops the block stream simulator. */ public void stop() { - isRunning = false; + isRunning.set(false); LOGGER.log(System.Logger.Level.INFO, "Block Stream Simulator has stopped"); } } diff --git a/simulator/src/main/java/com/hedera/block/simulator/exception/BlockSimulatorParsingException.java b/simulator/src/main/java/com/hedera/block/simulator/exception/BlockSimulatorParsingException.java new file mode 100644 index 000000000..7f755b27d --- /dev/null +++ b/simulator/src/main/java/com/hedera/block/simulator/exception/BlockSimulatorParsingException.java @@ -0,0 +1,29 @@ +/* + * 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. + */ + +package com.hedera.block.simulator.exception; + +/** Use this checked exception to represent a Block Simulator parsing exception. */ +public class BlockSimulatorParsingException extends Exception { + /** + * Constructs a new parsing exception with the specified detail message. + * + * @param message the detail message + */ + public BlockSimulatorParsingException(String message) { + super(message); + } +} diff --git a/simulator/src/main/java/com/hedera/block/simulator/generator/BlockAsFileLargeDataSets.java b/simulator/src/main/java/com/hedera/block/simulator/generator/BlockAsFileLargeDataSets.java index c0c02a6b2..0e7c493c8 100644 --- a/simulator/src/main/java/com/hedera/block/simulator/generator/BlockAsFileLargeDataSets.java +++ b/simulator/src/main/java/com/hedera/block/simulator/generator/BlockAsFileLargeDataSets.java @@ -21,6 +21,7 @@ import com.hedera.block.simulator.config.data.BlockStreamConfig; import com.hedera.block.simulator.config.types.GenerationMode; +import com.hedera.block.simulator.exception.BlockSimulatorParsingException; import com.hedera.hapi.block.stream.Block; import com.hedera.hapi.block.stream.BlockItem; import com.hedera.pbj.runtime.ParseException; @@ -59,7 +60,7 @@ public GenerationMode getGenerationMode() { } @Override - public BlockItem getNextBlockItem() throws IOException, ParseException { + public BlockItem getNextBlockItem() throws IOException, BlockSimulatorParsingException { if (currentBlock != null && currentBlock.items().size() > currentBlockItemIndex) { return currentBlock.items().get(currentBlockItemIndex++); } else { @@ -74,13 +75,17 @@ public BlockItem getNextBlockItem() throws IOException, ParseException { } @Override - public Block getNextBlock() throws IOException, ParseException { + public Block getNextBlock() throws IOException, BlockSimulatorParsingException { currentBlockIndex++; String nextBlockFileName = String.format(formatString, currentBlockIndex); File blockFile = new File(blockstreamPath, nextBlockFileName); - if (blockFile.exists()) { + if (!blockFile.exists()) { + return null; + } + + try { byte[] blockBytes = readFileBytes(blockFile.toPath()); LOGGER.log(INFO, "Loading block: " + blockFile.getName()); @@ -88,8 +93,8 @@ public Block getNextBlock() throws IOException, ParseException { Block block = Block.PROTOBUF.parse(Bytes.wrap(blockBytes)); LOGGER.log(INFO, "block loaded with items size= " + block.items().size()); return block; + } catch (ParseException e) { + throw new BlockSimulatorParsingException(e.getMessage()); } - - return null; // No more blocks found } } diff --git a/simulator/src/main/java/com/hedera/block/simulator/generator/BlockStreamManager.java b/simulator/src/main/java/com/hedera/block/simulator/generator/BlockStreamManager.java index b9d7cdba2..550f83421 100644 --- a/simulator/src/main/java/com/hedera/block/simulator/generator/BlockStreamManager.java +++ b/simulator/src/main/java/com/hedera/block/simulator/generator/BlockStreamManager.java @@ -17,9 +17,9 @@ package com.hedera.block.simulator.generator; import com.hedera.block.simulator.config.types.GenerationMode; +import com.hedera.block.simulator.exception.BlockSimulatorParsingException; import com.hedera.hapi.block.stream.Block; import com.hedera.hapi.block.stream.BlockItem; -import com.hedera.pbj.runtime.ParseException; import java.io.IOException; /** The block stream manager interface. */ @@ -36,17 +36,17 @@ public interface BlockStreamManager { * Get the next block item. * * @return the next block item - * @throws IOException if an I/O error occurs - * @throws ParseException if a parse error occurs + * @throws IOException if a I/O error occurs + * @throws BlockSimulatorParsingException if a parse error occurs */ - BlockItem getNextBlockItem() throws IOException, ParseException; + BlockItem getNextBlockItem() throws IOException, BlockSimulatorParsingException; /** * Get the next block. * * @return the next block - * @throws IOException if an I/O error occurs - * @throws ParseException if a parse error occurs + * @throws IOException if a I/O error occurs + * @throws BlockSimulatorParsingException if a parse error occurs */ - Block getNextBlock() throws IOException, ParseException; + Block getNextBlock() throws IOException, BlockSimulatorParsingException; } diff --git a/simulator/src/main/java/module-info.java b/simulator/src/main/java/module-info.java index 9750258d5..ddca45813 100644 --- a/simulator/src/main/java/module-info.java +++ b/simulator/src/main/java/module-info.java @@ -3,6 +3,8 @@ /** Runtime module of the simulator. */ module com.hedera.block.simulator { exports com.hedera.block.simulator.config.data; + exports com.hedera.block.simulator.exception; + exports com.hedera.block.simulator; requires static com.github.spotbugs.annotations; requires static com.google.auto.service; diff --git a/simulator/src/test/java/com/hedera/block/simulator/BlockStreamSimulatorTest.java b/simulator/src/test/java/com/hedera/block/simulator/BlockStreamSimulatorTest.java index 37de86ff8..24ba07053 100644 --- a/simulator/src/test/java/com/hedera/block/simulator/BlockStreamSimulatorTest.java +++ b/simulator/src/test/java/com/hedera/block/simulator/BlockStreamSimulatorTest.java @@ -19,10 +19,10 @@ import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.when; +import com.hedera.block.simulator.exception.BlockSimulatorParsingException; import com.hedera.block.simulator.generator.BlockStreamManager; import com.hedera.block.simulator.grpc.PublishStreamGrpcClient; import com.hedera.hapi.block.stream.BlockItem; -import com.hedera.pbj.runtime.ParseException; import com.swirlds.config.api.Configuration; import java.io.IOException; import java.nio.file.Paths; @@ -63,13 +63,15 @@ void tearDown() { } @Test - void start_logsStartedMessage() throws InterruptedException, ParseException, IOException { + void start_logsStartedMessage() + throws InterruptedException, BlockSimulatorParsingException, IOException { blockStreamSimulator.start(); assertTrue(blockStreamSimulator.isRunning()); } @Test - void start_exitByBlockNull() throws InterruptedException, ParseException, IOException { + void start_exitByBlockNull() + throws InterruptedException, BlockSimulatorParsingException, IOException { BlockStreamManager blockStreamManager = Mockito.mock(BlockStreamManager.class); when(blockStreamManager.getNextBlockItem()).thenReturn(BlockItem.newBuilder().build()); diff --git a/simulator/src/test/java/com/hedera/block/simulator/generator/BlockAsDirBlockStreamManagerTest.java b/simulator/src/test/java/com/hedera/block/simulator/generator/BlockAsDirBlockStreamManagerTest.java index 1c391fea7..6ea25a542 100644 --- a/simulator/src/test/java/com/hedera/block/simulator/generator/BlockAsDirBlockStreamManagerTest.java +++ b/simulator/src/test/java/com/hedera/block/simulator/generator/BlockAsDirBlockStreamManagerTest.java @@ -22,7 +22,7 @@ import com.hedera.block.simulator.config.data.BlockStreamConfig; import com.hedera.block.simulator.config.types.GenerationMode; -import com.hedera.pbj.runtime.ParseException; +import com.hedera.block.simulator.exception.BlockSimulatorParsingException; import java.io.IOException; import java.nio.file.Paths; import org.junit.jupiter.api.Test; @@ -45,7 +45,7 @@ void getGenerationMode() { } @Test - void getNextBlockItem() throws IOException, ParseException { + void getNextBlockItem() throws IOException, BlockSimulatorParsingException { BlockStreamManager blockStreamManager = getBlockAsDirBlockStreamManager(getAbsoluteFolder(rootFolder)); @@ -55,7 +55,7 @@ void getNextBlockItem() throws IOException, ParseException { } @Test - void getNextBlock() throws IOException, ParseException { + void getNextBlock() throws IOException, BlockSimulatorParsingException { BlockStreamManager blockStreamManager = getBlockAsDirBlockStreamManager(getAbsoluteFolder(rootFolder)); diff --git a/simulator/src/test/java/com/hedera/block/simulator/generator/BlockAsFileBlockStreamManagerTest.java b/simulator/src/test/java/com/hedera/block/simulator/generator/BlockAsFileBlockStreamManagerTest.java index 9c207f117..b833a57d9 100644 --- a/simulator/src/test/java/com/hedera/block/simulator/generator/BlockAsFileBlockStreamManagerTest.java +++ b/simulator/src/test/java/com/hedera/block/simulator/generator/BlockAsFileBlockStreamManagerTest.java @@ -20,7 +20,7 @@ import com.hedera.block.simulator.config.data.BlockStreamConfig; import com.hedera.block.simulator.config.types.GenerationMode; -import com.hedera.pbj.runtime.ParseException; +import com.hedera.block.simulator.exception.BlockSimulatorParsingException; import java.io.IOException; import java.nio.file.Paths; import org.junit.jupiter.api.Test; @@ -41,7 +41,7 @@ void getGenerationMode() { } @Test - void getNextBlock() throws IOException, ParseException { + void getNextBlock() throws IOException, BlockSimulatorParsingException { BlockStreamManager blockStreamManager = getBlockAsFileBlockStreamManager(getAbsoluteFolder(gzRootFolder)); for (int i = 0; i < 3000; i++) { @@ -50,7 +50,7 @@ void getNextBlock() throws IOException, ParseException { } @Test - void getNextBlockItem() throws IOException, ParseException { + void getNextBlockItem() throws IOException, BlockSimulatorParsingException { BlockStreamManager blockStreamManager = getBlockAsFileBlockStreamManager(getAbsoluteFolder(gzRootFolder)); for (int i = 0; i < 35000; i++) { @@ -59,7 +59,7 @@ void getNextBlockItem() throws IOException, ParseException { } @Test - void loadBlockBlk() throws IOException, ParseException { + void loadBlockBlk() throws IOException, BlockSimulatorParsingException { String blkRootFolder = "src/test/resources/block-0.0.3-blk/"; BlockStreamManager blockStreamManager = getBlockAsFileBlockStreamManager(getAbsoluteFolder(blkRootFolder)); diff --git a/simulator/src/test/java/com/hedera/block/simulator/generator/BlockAsFileLargeDataSetsTest.java b/simulator/src/test/java/com/hedera/block/simulator/generator/BlockAsFileLargeDataSetsTest.java index 87639a0e4..01b3006b8 100644 --- a/simulator/src/test/java/com/hedera/block/simulator/generator/BlockAsFileLargeDataSetsTest.java +++ b/simulator/src/test/java/com/hedera/block/simulator/generator/BlockAsFileLargeDataSetsTest.java @@ -20,14 +20,17 @@ import com.hedera.block.simulator.config.data.BlockStreamConfig; import com.hedera.block.simulator.config.types.GenerationMode; +import com.hedera.block.simulator.exception.BlockSimulatorParsingException; import com.hedera.hapi.block.stream.BlockItem; -import com.hedera.pbj.runtime.ParseException; import java.io.File; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; class BlockAsFileLargeDataSetsTest { @@ -51,7 +54,7 @@ void getGenerationMode() { } @Test - void getNextBlock() throws IOException, ParseException { + void getNextBlock() throws IOException, BlockSimulatorParsingException { BlockStreamManager blockStreamManager = getBlockAsFileLargeDatasetsBlockStreamManager(getAbsoluteFolder(rootFolder)); for (int i = 0; i < filesInFolder; i++) { @@ -62,7 +65,7 @@ void getNextBlock() throws IOException, ParseException { } @Test - void getNextBlockItem() throws IOException, ParseException { + void getNextBlockItem() throws IOException, BlockSimulatorParsingException { BlockStreamManager blockStreamManager = getBlockAsFileLargeDatasetsBlockStreamManager(getAbsoluteFolder(rootFolder)); @@ -75,6 +78,41 @@ void getNextBlockItem() throws IOException, ParseException { } } + @Test + void gettingNextBlockItemThrowsParsingException(@TempDir Path tempDir) throws IOException { + String blockFolderName = "block-0.0.3-blk"; + Path blockDirPath = tempDir.resolve(blockFolderName); + Files.createDirectories(blockDirPath); + + int nextBlockIndex = 1; + int paddedLength = 36; + String fileExtension = ".blk"; + String formatString = "%0" + paddedLength + "d" + fileExtension; + + String currentBlockFileName = String.format(formatString, nextBlockIndex); + Path currentBlockFilePath = blockDirPath.resolve(currentBlockFileName); + + byte[] invalidData = "invalid block data".getBytes(); + Files.write(currentBlockFilePath, invalidData); + + BlockStreamConfig blockStreamConfig = + new BlockStreamConfig( + GenerationMode.DIR, + blockDirPath.toString(), + 1_500_000, + "BlockAsFileBlockStreamManager", + 10_000, + 36, + ".blk"); + BlockAsFileLargeDataSets blockStreamManager = + new BlockAsFileLargeDataSets(blockStreamConfig); + + assertThrows( + BlockSimulatorParsingException.class, + blockStreamManager::getNextBlock, + "Expected getNextBlock() to throw BlockSimulatorParsingException"); + } + private BlockAsFileLargeDataSets getBlockAsFileLargeDatasetsBlockStreamManager( String rootFolder) { BlockStreamConfig blockStreamConfig = diff --git a/suites/build.gradle.kts b/suites/build.gradle.kts index 4ed80e300..d5d5534ee 100644 --- a/suites/build.gradle.kts +++ b/suites/build.gradle.kts @@ -21,20 +21,23 @@ plugins { description = "Hedera Block Node E2E Suites" +dependencies { implementation(project(":simulator")) } + 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") + runtimeOnly("org.testcontainers") + runtimeOnly("com.swirlds.config.impl") } +// workaround until https://github.com/hashgraph/hedera-block-node/pull/216 is integrated +dependencies.constraints { implementation("org.slf4j:slf4j-api:2.0.6") } + val updateDockerEnv = tasks.register("updateDockerEnv") { description = @@ -58,6 +61,7 @@ tasks.register("createDockerImage") { tasks.register("runSuites") { description = "Runs E2E Test Suites" group = "suites" + modularity.inferModulePath = false dependsOn("createDockerImage") useJUnitPlatform() diff --git a/suites/src/main/java/com/hedera/block/suites/BaseSuite.java b/suites/src/main/java/com/hedera/block/suites/BaseSuite.java index 9e976be6e..5c226bc0f 100644 --- a/suites/src/main/java/com/hedera/block/suites/BaseSuite.java +++ b/suites/src/main/java/com/hedera/block/suites/BaseSuite.java @@ -16,7 +16,17 @@ package com.hedera.block.suites; +import com.hedera.block.simulator.BlockStreamSimulatorApp; +import com.hedera.block.simulator.BlockStreamSimulatorInjectionComponent; +import com.hedera.block.simulator.DaggerBlockStreamSimulatorInjectionComponent; +import com.swirlds.config.api.Configuration; +import com.swirlds.config.api.ConfigurationBuilder; +import com.swirlds.config.extensions.sources.ClasspathFileConfigSource; +import com.swirlds.config.extensions.sources.SystemEnvironmentConfigSource; +import com.swirlds.config.extensions.sources.SystemPropertiesConfigSource; import io.github.cdimascio.dotenv.Dotenv; +import java.io.IOException; +import java.nio.file.Path; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.testcontainers.containers.GenericContainer; @@ -44,6 +54,9 @@ public abstract class BaseSuite { /** Port that is used by the Block Node Application */ protected static int blockNodePort; + /** Block Simulator Application instance */ + protected static BlockStreamSimulatorApp blockStreamSimulatorApp; + /** * Default constructor for the BaseSuite class. * @@ -60,9 +73,16 @@ public BaseSuite() { *

This method initializes the Block Node server container using Testcontainers. */ @BeforeAll - public static void setup() { + public static void setup() throws IOException { blockNodeContainer = getConfiguration(); blockNodeContainer.start(); + + // TODO remove in the next PR which adds tests + BlockStreamSimulatorInjectionComponent DIComponent = + DaggerBlockStreamSimulatorInjectionComponent.factory() + .create(loadDefaultConfiguration()); + + BlockStreamSimulatorApp blockStreamSimulatorApp = DIComponent.getBlockStreamSimulatorApp(); } /** @@ -107,6 +127,23 @@ public static GenericContainer getConfiguration() { return blockNodeContainer; } + /** + * Builds the default block simulator configuration + * + * @return default block simulator configuration + * @throws IOException if an I/O error occurs + */ + protected static Configuration loadDefaultConfiguration() throws IOException { + ConfigurationBuilder configurationBuilder = + ConfigurationBuilder.create() + .withSource(SystemEnvironmentConfigSource.getInstance()) + .withSource(SystemPropertiesConfigSource.getInstance()) + .withSource(new ClasspathFileConfigSource(Path.of("app.properties"))) + .autoDiscoverExtensions(); + + return configurationBuilder.build(); + } + /** * Retrieves the Block Node server version from the .env file. * diff --git a/suites/src/main/java/com/hedera/block/suites/persistence/DataPersistenceTestSuites.java b/suites/src/main/java/com/hedera/block/suites/persistence/DataPersistenceTestSuites.java new file mode 100644 index 000000000..283d98549 --- /dev/null +++ b/suites/src/main/java/com/hedera/block/suites/persistence/DataPersistenceTestSuites.java @@ -0,0 +1,39 @@ +/* + * 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.persistence; + +import com.hedera.block.suites.persistence.positive.PositiveDataPersistenceTests; +import org.junit.platform.suite.api.SelectClasses; +import org.junit.platform.suite.api.Suite; + +/** + * Test suite for running data persistence tests, including both positive and negative test + * scenarios. + * + *

This suite aggregates the tests from {@link PositiveDataPersistenceTests}. The {@code @Suite} + * annotation allows running all selected classes in a single test run. + */ +@Suite +@SelectClasses({PositiveDataPersistenceTests.class}) +public class DataPersistenceTestSuites { + + /** + * Default constructor for the {@link DataPersistenceTestSuites} class. This constructor is + * empty as it does not need to perform any initialization. + */ + public DataPersistenceTestSuites() {} +} diff --git a/suites/src/main/java/com/hedera/block/suites/persistence/positive/PositiveDataPersistenceTests.java b/suites/src/main/java/com/hedera/block/suites/persistence/positive/PositiveDataPersistenceTests.java new file mode 100644 index 000000000..1e4ffba16 --- /dev/null +++ b/suites/src/main/java/com/hedera/block/suites/persistence/positive/PositiveDataPersistenceTests.java @@ -0,0 +1,26 @@ +/* + * 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.persistence.positive; + +import com.hedera.block.suites.BaseSuite; +import org.junit.jupiter.api.DisplayName; + +@DisplayName("Positive Data Persistence Tests") +public class PositiveDataPersistenceTests extends BaseSuite { + /** Default constructor for the {@link PositiveDataPersistenceTests} class. */ + public PositiveDataPersistenceTests() {} +} diff --git a/suites/src/main/java/module-info.java b/suites/src/main/java/module-info.java new file mode 100644 index 000000000..513183446 --- /dev/null +++ b/suites/src/main/java/module-info.java @@ -0,0 +1,15 @@ +/** Runtime module of the suites. */ +module com.hedera.block.node.suites { + requires com.hedera.block.simulator; + requires com.swirlds.config.api; + requires com.swirlds.config.extensions; + + // Require testing libraries + requires io.github.cdimascio; + requires org.junit.jupiter.api; + requires org.junit.platform.suite.api; + requires org.testcontainers; + + exports com.hedera.block.suites to + org.junit.platform.commons; +} diff --git a/suites/src/main/resources/block-0.0.3/000000000000000000000000000000000001.blk.gz b/suites/src/main/resources/block-0.0.3/000000000000000000000000000000000001.blk.gz new file mode 100644 index 0000000000000000000000000000000000000000..06b71e83799342968a108317c03ae8bd07d14352 GIT binary patch literal 6394 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 I{5&22090Fgxc~qF literal 0 HcmV?d00001 diff --git a/suites/src/main/resources/block-0.0.3/000000000000000000000000000000000002.blk.gz b/suites/src/main/resources/block-0.0.3/000000000000000000000000000000000002.blk.gz new file mode 100644 index 0000000000000000000000000000000000000000..2a999fd88f7a73a41f5f5ac2095ad137d2e4dd68 GIT binary patch literal 8242 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 literal 0 HcmV?d00001 diff --git a/suites/src/main/resources/block-0.0.3/000000000000000000000000000000000003.blk.gz b/suites/src/main/resources/block-0.0.3/000000000000000000000000000000000003.blk.gz new file mode 100644 index 0000000000000000000000000000000000000000..547b9675338f1c6b856661db5e7e0507ceadac7f GIT binary patch literal 9754 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