From d00633e16da5023da25249bcf093d1a4aca334e2 Mon Sep 17 00:00:00 2001 From: Alfredo Gutierrez Date: Tue, 27 Aug 2024 08:58:59 -0600 Subject: [PATCH] feat: Dagger refactor (#134) Signed-off-by: Alfredo Gutierrez --- .../com/hedera/block/server/BlockNodeApp.java | 67 +++---------- .../BlockNodeAppInjectionComponent.java | 25 +++++ .../server/BlockNodeAppInjectionModule.java | 40 ++++++-- .../block/server/BlockStreamService.java | 2 + .../com/hedera/block/server/Constants.java | 3 + .../java/com/hedera/block/server/Server.java | 24 ++++- .../config/BlockNodeConfigExtension.java | 2 - .../block/server/config/BlockNodeContext.java | 6 +- .../config/BlockNodeContextFactory.java | 91 ------------------ .../server/config/ConfigInjectionModule.java | 82 ++++++++++++++++ .../server/health/HealthInjectionModule.java | 3 +- .../mediator/MediatorInjectionModule.java | 51 ++++++++++ .../metrics/MetricsInjectionModule.java | 56 +++++++++++ .../PersistenceInjectionModule.java | 63 +++++++++++++ server/src/main/java/module-info.java | 2 + .../BlockNodeAppInjectionModuleTest.java | 50 +++++++++- .../hedera/block/server/BlockNodeAppTest.java | 76 +++++++++++++++ .../config/BlockNodeContextFactoryTest.java | 33 ------- .../server/config/BlockNodeContextTest.java | 5 +- .../config/ConfigInjectionModuleTest.java | 94 +++++++++++++++++++ .../mediator/LiveStreamMediatorImplTest.java | 23 ++--- .../mediator/MediatorInjectionModuleTest.java | 57 +++++++++++ .../metrics/MetricsInjectionModuleTest.java | 55 +++++++++++ .../PersistenceInjectionModuleTest.java | 90 ++++++++++++++++++ .../ProducerBlockItemObserverTest.java | 3 +- .../block/server/util/TestConfigUtil.java | 24 +++-- 26 files changed, 799 insertions(+), 228 deletions(-) delete mode 100644 server/src/main/java/com/hedera/block/server/config/BlockNodeContextFactory.java create mode 100644 server/src/main/java/com/hedera/block/server/config/ConfigInjectionModule.java create mode 100644 server/src/main/java/com/hedera/block/server/mediator/MediatorInjectionModule.java create mode 100644 server/src/main/java/com/hedera/block/server/metrics/MetricsInjectionModule.java create mode 100644 server/src/main/java/com/hedera/block/server/persistence/PersistenceInjectionModule.java create mode 100644 server/src/test/java/com/hedera/block/server/BlockNodeAppTest.java delete mode 100644 server/src/test/java/com/hedera/block/server/config/BlockNodeContextFactoryTest.java create mode 100644 server/src/test/java/com/hedera/block/server/config/ConfigInjectionModuleTest.java create mode 100644 server/src/test/java/com/hedera/block/server/mediator/MediatorInjectionModuleTest.java create mode 100644 server/src/test/java/com/hedera/block/server/metrics/MetricsInjectionModuleTest.java create mode 100644 server/src/test/java/com/hedera/block/server/persistence/PersistenceInjectionModuleTest.java diff --git a/server/src/main/java/com/hedera/block/server/BlockNodeApp.java b/server/src/main/java/com/hedera/block/server/BlockNodeApp.java index 58b668ed..53b0d7e0 100644 --- a/server/src/main/java/com/hedera/block/server/BlockNodeApp.java +++ b/server/src/main/java/com/hedera/block/server/BlockNodeApp.java @@ -19,22 +19,12 @@ import static java.lang.System.Logger; import static java.lang.System.Logger.Level.INFO; -import com.hedera.block.server.config.BlockNodeContext; -import com.hedera.block.server.data.ObjectEvent; import com.hedera.block.server.health.HealthService; -import com.hedera.block.server.mediator.LiveStreamMediatorBuilder; -import com.hedera.block.server.mediator.StreamMediator; -import com.hedera.block.server.persistence.storage.PersistenceStorageConfig; -import com.hedera.block.server.persistence.storage.read.BlockAsDirReaderBuilder; -import com.hedera.block.server.persistence.storage.read.BlockReader; -import com.hedera.block.server.persistence.storage.write.BlockAsDirWriterBuilder; -import com.hedera.block.server.persistence.storage.write.BlockWriter; -import com.hedera.hapi.block.SubscribeStreamResponse; -import com.hedera.hapi.block.stream.Block; -import com.hedera.hapi.block.stream.BlockItem; import edu.umd.cs.findbugs.annotations.NonNull; import io.helidon.webserver.WebServer; +import io.helidon.webserver.WebServerConfig; import io.helidon.webserver.grpc.GrpcRouting; +import io.helidon.webserver.grpc.GrpcService; import io.helidon.webserver.http.HttpRouting; import java.io.IOException; import javax.inject.Inject; @@ -51,23 +41,27 @@ public class BlockNodeApp { private final ServiceStatus serviceStatus; private final HealthService healthService; - private final BlockNodeContext blockNodeContext; + private final GrpcService blockStreamService; + private final WebServerConfig.Builder webServerBuilder; /** - * Has all needed dependencies to start the server and initialize the context. + * Constructs a new BlockNodeApp with the specified dependencies. * - * @param serviceStatus the status of the service - * @param healthService the health service - * @param blockNodeContext the context of the block node + * @param serviceStatus has the status of the service + * @param healthService handles the health API requests + * @param blockStreamService handles the GRPC API requests + * @param webServerBuilder used to build the web server and start it */ @Inject public BlockNodeApp( @NonNull ServiceStatus serviceStatus, @NonNull HealthService healthService, - @NonNull BlockNodeContext blockNodeContext) { + @NonNull GrpcService blockStreamService, + @NonNull WebServerConfig.Builder webServerBuilder) { this.serviceStatus = serviceStatus; this.healthService = healthService; - this.blockNodeContext = blockNodeContext; + this.blockStreamService = blockStreamService; + this.webServerBuilder = webServerBuilder; } /** @@ -77,23 +71,6 @@ public BlockNodeApp( */ public void start() throws IOException { - final BlockWriter blockWriter = - BlockAsDirWriterBuilder.newBuilder(blockNodeContext).build(); - final StreamMediator> streamMediator = - LiveStreamMediatorBuilder.newBuilder(blockWriter, blockNodeContext, serviceStatus) - .build(); - - final BlockReader blockReader = - BlockAsDirReaderBuilder.newBuilder( - blockNodeContext - .configuration() - .getConfigData(PersistenceStorageConfig.class)) - .build(); - - final BlockStreamService blockStreamService = - buildBlockStreamService( - streamMediator, blockReader, serviceStatus, blockNodeContext); - final GrpcRouting.Builder grpcRouting = GrpcRouting.builder().service(blockStreamService); final HttpRouting.Builder httpRouting = @@ -102,11 +79,7 @@ public void start() throws IOException { // Build the web server // TODO: make port server a configurable value. final WebServer webServer = - WebServer.builder() - .port(8080) - .addRouting(grpcRouting) - .addRouting(httpRouting) - .build(); + webServerBuilder.port(8080).addRouting(grpcRouting).addRouting(httpRouting).build(); // Update the serviceStatus with the web server serviceStatus.setWebServer(webServer); @@ -117,16 +90,4 @@ public void start() throws IOException { // Log the server status LOGGER.log(INFO, String.format("Block Node Server started at port: %d", webServer.port())); } - - @NonNull - private static BlockStreamService buildBlockStreamService( - @NonNull - final StreamMediator> - streamMediator, - @NonNull final BlockReader blockReader, - @NonNull final ServiceStatus serviceStatus, - @NonNull final BlockNodeContext blockNodeContext) { - - return new BlockStreamService(streamMediator, blockReader, serviceStatus, blockNodeContext); - } } diff --git a/server/src/main/java/com/hedera/block/server/BlockNodeAppInjectionComponent.java b/server/src/main/java/com/hedera/block/server/BlockNodeAppInjectionComponent.java index b429b4e8..114bedc0 100644 --- a/server/src/main/java/com/hedera/block/server/BlockNodeAppInjectionComponent.java +++ b/server/src/main/java/com/hedera/block/server/BlockNodeAppInjectionComponent.java @@ -16,7 +16,13 @@ package com.hedera.block.server; +import com.hedera.block.server.config.ConfigInjectionModule; import com.hedera.block.server.health.HealthInjectionModule; +import com.hedera.block.server.mediator.MediatorInjectionModule; +import com.hedera.block.server.metrics.MetricsInjectionModule; +import com.hedera.block.server.persistence.PersistenceInjectionModule; +import com.swirlds.config.api.Configuration; +import dagger.BindsInstance; import dagger.Component; import javax.inject.Singleton; @@ -26,6 +32,10 @@ modules = { BlockNodeAppInjectionModule.class, HealthInjectionModule.class, + PersistenceInjectionModule.class, + MediatorInjectionModule.class, + ConfigInjectionModule.class, + MetricsInjectionModule.class, }) public interface BlockNodeAppInjectionComponent { /** @@ -34,4 +44,19 @@ public interface BlockNodeAppInjectionComponent { * @return the block node app server */ BlockNodeApp getBlockNodeApp(); + + /** + * Factory for the block node app injection component, needs a configuration to create the + * component and the block node app with all the wired dependencies. + */ + @Component.Factory + interface Factory { + /** + * Create the block node app injection component. + * + * @param configuration the configuration + * @return the block node app injection component + */ + BlockNodeAppInjectionComponent create(@BindsInstance Configuration configuration); + } } diff --git a/server/src/main/java/com/hedera/block/server/BlockNodeAppInjectionModule.java b/server/src/main/java/com/hedera/block/server/BlockNodeAppInjectionModule.java index 5a5b076e..023293f7 100644 --- a/server/src/main/java/com/hedera/block/server/BlockNodeAppInjectionModule.java +++ b/server/src/main/java/com/hedera/block/server/BlockNodeAppInjectionModule.java @@ -17,11 +17,13 @@ package com.hedera.block.server; import com.hedera.block.server.config.BlockNodeContext; -import com.hedera.block.server.config.BlockNodeContextFactory; +import com.hedera.block.server.metrics.MetricsService; +import com.swirlds.config.api.Configuration; import dagger.Binds; import dagger.Module; import dagger.Provides; -import java.io.IOException; +import io.helidon.webserver.WebServerConfig; +import io.helidon.webserver.grpc.GrpcService; import javax.inject.Singleton; /** @@ -42,17 +44,37 @@ public interface BlockNodeAppInjectionModule { ServiceStatus bindServiceStatus(ServiceStatusImpl serviceStatus); /** - * Provides a block node context singleton using the factory. + * Provides a block node context singleton. * + * @param config should come from DI + * @param metricsService should come from DI * @return a block node context singleton */ + @Singleton @Provides + static BlockNodeContext provideBlockNodeContext( + Configuration config, MetricsService metricsService) { + return new BlockNodeContext(metricsService, config); + } + + /** + * Provides a block stream service singleton using DI. + * + * @param blockStreamService should come from DI + * @return a block stream service singleton + */ @Singleton - static BlockNodeContext provideBlockNodeContext() { - try { - return BlockNodeContextFactory.create(); - } catch (IOException e) { - throw new RuntimeException(e); - } + @Binds + GrpcService bindBlockStreamService(BlockStreamService blockStreamService); + + /** + * Provides a web server config builder singleton using DI. + * + * @return a web server config builder singleton + */ + @Singleton + @Provides + static WebServerConfig.Builder provideWebServerConfigBuilder() { + return WebServerConfig.builder(); } } diff --git a/server/src/main/java/com/hedera/block/server/BlockStreamService.java b/server/src/main/java/com/hedera/block/server/BlockStreamService.java index 3b820b09..4ab2654e 100644 --- a/server/src/main/java/com/hedera/block/server/BlockStreamService.java +++ b/server/src/main/java/com/hedera/block/server/BlockStreamService.java @@ -50,6 +50,7 @@ import java.io.IOException; import java.time.Clock; import java.util.Optional; +import javax.inject.Inject; /** * The BlockStreamService class defines the gRPC service for the block stream service. It provides @@ -76,6 +77,7 @@ public class BlockStreamService implements GrpcService { * @param serviceStatus the service status provides methods to check service availability and to * stop the service and web server in the event of an unrecoverable exception */ + @Inject BlockStreamService( @NonNull final StreamMediator> diff --git a/server/src/main/java/com/hedera/block/server/Constants.java b/server/src/main/java/com/hedera/block/server/Constants.java index 5b9850af..8fbd6900 100644 --- a/server/src/main/java/com/hedera/block/server/Constants.java +++ b/server/src/main/java/com/hedera/block/server/Constants.java @@ -22,6 +22,9 @@ public final class Constants { private Constants() {} + /** Constant mapped to the application.properties file in resources with default values */ + @NonNull public static final String APPLICATION_PROPERTIES = "app.properties"; + /** Constant mapped to the name of the service in the .proto file */ @NonNull public static final String SERVICE_NAME = "BlockStreamService"; diff --git a/server/src/main/java/com/hedera/block/server/Server.java b/server/src/main/java/com/hedera/block/server/Server.java index 77d5e68c..48b39490 100644 --- a/server/src/main/java/com/hedera/block/server/Server.java +++ b/server/src/main/java/com/hedera/block/server/Server.java @@ -19,7 +19,13 @@ import static java.lang.System.Logger; import static java.lang.System.Logger.Level.INFO; +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 java.io.IOException; +import java.nio.file.Path; /** Main class for the block node server */ public class Server { @@ -36,8 +42,24 @@ private Server() {} */ public static void main(final String[] args) throws IOException { LOGGER.log(INFO, "Starting BlockNode Server"); + + // Init BlockNode Configuration + Configuration configuration = + ConfigurationBuilder.create() + .withSource(SystemEnvironmentConfigSource.getInstance()) + .withSource(SystemPropertiesConfigSource.getInstance()) + .withSource( + new ClasspathFileConfigSource( + Path.of(Constants.APPLICATION_PROPERTIES))) + .autoDiscoverExtensions() + .build(); + + // Init Dagger DI Component, passing in the configuration. + // this is where all the dependencies are wired up (magic happens) final BlockNodeAppInjectionComponent daggerComponent = - DaggerBlockNodeAppInjectionComponent.create(); + DaggerBlockNodeAppInjectionComponent.factory().create(configuration); + + // Use Dagger DI Component to start the BlockNodeApp with all wired dependencies final BlockNodeApp blockNodeApp = daggerComponent.getBlockNodeApp(); blockNodeApp.start(); } diff --git a/server/src/main/java/com/hedera/block/server/config/BlockNodeConfigExtension.java b/server/src/main/java/com/hedera/block/server/config/BlockNodeConfigExtension.java index d99c08b9..62065e39 100644 --- a/server/src/main/java/com/hedera/block/server/config/BlockNodeConfigExtension.java +++ b/server/src/main/java/com/hedera/block/server/config/BlockNodeConfigExtension.java @@ -19,7 +19,6 @@ import com.google.auto.service.AutoService; import com.hedera.block.server.consumer.ConsumerConfig; import com.hedera.block.server.persistence.storage.PersistenceStorageConfig; -import com.swirlds.common.config.BasicCommonConfig; import com.swirlds.common.metrics.config.MetricsConfig; import com.swirlds.common.metrics.platform.prometheus.PrometheusConfig; import com.swirlds.config.api.ConfigurationExtension; @@ -44,7 +43,6 @@ public BlockNodeConfigExtension() { @Override public Set> getConfigDataTypes() { return Set.of( - BasicCommonConfig.class, MetricsConfig.class, PrometheusConfig.class, ConsumerConfig.class, diff --git a/server/src/main/java/com/hedera/block/server/config/BlockNodeContext.java b/server/src/main/java/com/hedera/block/server/config/BlockNodeContext.java index 92fc36d3..3d3fa452 100644 --- a/server/src/main/java/com/hedera/block/server/config/BlockNodeContext.java +++ b/server/src/main/java/com/hedera/block/server/config/BlockNodeContext.java @@ -18,18 +18,14 @@ import com.hedera.block.server.metrics.MetricsService; import com.swirlds.config.api.Configuration; -import com.swirlds.metrics.api.Metrics; import edu.umd.cs.findbugs.annotations.NonNull; /** * Context for the block node. This record is returned by the BlockNodeContextFactory when a new * configuration is created. * - * @param metrics the metrics used for monitoring and reporting * @param metricsService the service responsible for handling metrics * @param configuration the configuration settings for the block node */ public record BlockNodeContext( - @NonNull Metrics metrics, - @NonNull MetricsService metricsService, - @NonNull Configuration configuration) {} + @NonNull MetricsService metricsService, @NonNull Configuration configuration) {} diff --git a/server/src/main/java/com/hedera/block/server/config/BlockNodeContextFactory.java b/server/src/main/java/com/hedera/block/server/config/BlockNodeContextFactory.java deleted file mode 100644 index 9081b770..00000000 --- a/server/src/main/java/com/hedera/block/server/config/BlockNodeContextFactory.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * 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.server.config; - -import com.hedera.block.server.metrics.MetricsService; -import com.swirlds.common.metrics.platform.DefaultMetricsProvider; -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 com.swirlds.metrics.api.Metrics; -import edu.umd.cs.findbugs.annotations.NonNull; -import java.io.IOException; -import java.nio.file.Path; - -/** - * Use the static method create() to obtain a new {@link BlockNodeContext} when initializing the - * application. - * - *

When a context is created all enabled sources of configuration information will be read and - * applicable values made available through the context created.
- * The application should use the context to obtain configuration and metrics support. - */ -public class BlockNodeContextFactory { - - /** - * A resource file name from which application configuration is read. The properties in this - * file are available as configuration from the {@link BlockNodeContext}. - */ - private static final String APPLICATION_PROPERTIES = "app.properties"; - - private BlockNodeContextFactory() {} - - /** - * Create a new application context for use in the system. The context is needed at - * initialization. - * - * @return a context with configuration and metrics. - * @throws IOException when the java libraries fail to read information from a configuration - * source. - */ - @NonNull - public static BlockNodeContext create() throws IOException { - final Configuration configuration = getConfiguration(); - final Metrics metrics = getMetrics(configuration); - final MetricsService metricsService = new MetricsService(metrics); - return new BlockNodeContext(metrics, metricsService, configuration); - } - - /** - * Read configuration for this system. The configuration sources will include environment - * variables, system properties, and the properties file named {@value APPLICATION_PROPERTIES}. - * - * @return the configuration as read from environment, properties, and files. - * @throws IOException when the java libraries fail to read information from a configuration - * source. - */ - @NonNull - private static Configuration getConfiguration() throws IOException { - - return ConfigurationBuilder.create() - .withSource(SystemEnvironmentConfigSource.getInstance()) - .withSource(SystemPropertiesConfigSource.getInstance()) - .withSource(new ClasspathFileConfigSource(Path.of(APPLICATION_PROPERTIES))) - .autoDiscoverExtensions() - .build(); - } - - @NonNull - private static Metrics getMetrics(@NonNull final Configuration configuration) { - final DefaultMetricsProvider metricsProvider = new DefaultMetricsProvider(configuration); - final Metrics metrics = metricsProvider.createGlobalMetrics(); - metricsProvider.start(); - return metrics; - } -} diff --git a/server/src/main/java/com/hedera/block/server/config/ConfigInjectionModule.java b/server/src/main/java/com/hedera/block/server/config/ConfigInjectionModule.java new file mode 100644 index 00000000..94365187 --- /dev/null +++ b/server/src/main/java/com/hedera/block/server/config/ConfigInjectionModule.java @@ -0,0 +1,82 @@ +/* + * 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.server.config; + +import com.hedera.block.server.consumer.ConsumerConfig; +import com.hedera.block.server.persistence.storage.PersistenceStorageConfig; +import com.swirlds.common.metrics.config.MetricsConfig; +import com.swirlds.common.metrics.platform.prometheus.PrometheusConfig; +import com.swirlds.config.api.Configuration; +import dagger.Module; +import dagger.Provides; +import javax.inject.Singleton; + +/** + * A Dagger module for providing configuration dependencies, any specific configuration should be + * part of this module. + */ +@Module +public interface ConfigInjectionModule { + + /** + * Provides a persistence storage configuration singleton using the configuration. + * + * @param configuration is the configuration singleton + * @return a persistence storage configuration singleton + */ + @Singleton + @Provides + static PersistenceStorageConfig providePersistenceStorageConfig(Configuration configuration) { + return configuration.getConfigData(PersistenceStorageConfig.class); + } + + /** + * Provides a metrics configuration singleton using the configuration. + * + * @param configuration is the configuration singleton + * @return a metrics configuration singleton + */ + @Singleton + @Provides + static MetricsConfig provideMetricsConfig(Configuration configuration) { + return configuration.getConfigData(MetricsConfig.class); + } + + /** + * Provides a Prometheus configuration singleton using the configuration. + * + * @param configuration is the configuration singleton + * @return a Prometheus configuration singleton + */ + @Singleton + @Provides + static PrometheusConfig providePrometheusConfig(Configuration configuration) { + return configuration.getConfigData(PrometheusConfig.class); + } + + /** + * Provides a consumer configuration singleton using the configuration. + * + * @param configuration is the configuration singleton + * @return a consumer configuration singleton + */ + @Singleton + @Provides + static ConsumerConfig provideConsumerConfig(Configuration configuration) { + return configuration.getConfigData(ConsumerConfig.class); + } +} diff --git a/server/src/main/java/com/hedera/block/server/health/HealthInjectionModule.java b/server/src/main/java/com/hedera/block/server/health/HealthInjectionModule.java index db882c9b..377cbc53 100644 --- a/server/src/main/java/com/hedera/block/server/health/HealthInjectionModule.java +++ b/server/src/main/java/com/hedera/block/server/health/HealthInjectionModule.java @@ -18,7 +18,6 @@ import dagger.Binds; import dagger.Module; -import edu.umd.cs.findbugs.annotations.NonNull; import javax.inject.Singleton; /** @@ -36,5 +35,5 @@ public interface HealthInjectionModule { */ @Singleton @Binds - HealthService bindHealthService(@NonNull HealthServiceImpl healthService); + HealthService bindHealthService(HealthServiceImpl healthService); } diff --git a/server/src/main/java/com/hedera/block/server/mediator/MediatorInjectionModule.java b/server/src/main/java/com/hedera/block/server/mediator/MediatorInjectionModule.java new file mode 100644 index 00000000..f50e107b --- /dev/null +++ b/server/src/main/java/com/hedera/block/server/mediator/MediatorInjectionModule.java @@ -0,0 +1,51 @@ +/* + * 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.server.mediator; + +import com.hedera.block.server.ServiceStatus; +import com.hedera.block.server.config.BlockNodeContext; +import com.hedera.block.server.data.ObjectEvent; +import com.hedera.block.server.persistence.storage.write.BlockWriter; +import com.hedera.hapi.block.SubscribeStreamResponse; +import com.hedera.hapi.block.stream.BlockItem; +import dagger.Module; +import dagger.Provides; +import edu.umd.cs.findbugs.annotations.NonNull; +import javax.inject.Singleton; + +/** A Dagger module for providing dependencies for Mediator Module.` */ +@Module +public interface MediatorInjectionModule { + + /** + * Provides the stream mediator. + * + * @param blockWriter the block writer + * @param blockNodeContext the block node context + * @param serviceStatus the service status + * @return the stream mediator + */ + @Provides + @Singleton + static StreamMediator> providesStreamMediator( + @NonNull BlockWriter blockWriter, + @NonNull BlockNodeContext blockNodeContext, + @NonNull ServiceStatus serviceStatus) { + return LiveStreamMediatorBuilder.newBuilder(blockWriter, blockNodeContext, serviceStatus) + .build(); + } +} diff --git a/server/src/main/java/com/hedera/block/server/metrics/MetricsInjectionModule.java b/server/src/main/java/com/hedera/block/server/metrics/MetricsInjectionModule.java new file mode 100644 index 00000000..f65d3e8e --- /dev/null +++ b/server/src/main/java/com/hedera/block/server/metrics/MetricsInjectionModule.java @@ -0,0 +1,56 @@ +/* + * 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.server.metrics; + +import com.swirlds.common.metrics.platform.DefaultMetricsProvider; +import com.swirlds.config.api.Configuration; +import com.swirlds.metrics.api.Metrics; +import dagger.Module; +import dagger.Provides; +import javax.inject.Singleton; + +/** The module used to inject the metrics service and metrics into the application. */ +@Module +public interface MetricsInjectionModule { + + /** + * Provides the metrics service. + * + * @param metrics the metrics to be used by the service + * @return the metrics service + */ + @Singleton + @Provides + static MetricsService provideMetricsService(Metrics metrics) { + return new MetricsService(metrics); + } + + /** + * Provides the metrics. + * + * @param configuration the configuration to be used by the metrics + * @return the metrics + */ + @Singleton + @Provides + static Metrics provideMetrics(Configuration configuration) { + final DefaultMetricsProvider metricsProvider = new DefaultMetricsProvider(configuration); + final Metrics metrics = metricsProvider.createGlobalMetrics(); + metricsProvider.start(); + return metrics; + } +} diff --git a/server/src/main/java/com/hedera/block/server/persistence/PersistenceInjectionModule.java b/server/src/main/java/com/hedera/block/server/persistence/PersistenceInjectionModule.java new file mode 100644 index 00000000..2cddc12b --- /dev/null +++ b/server/src/main/java/com/hedera/block/server/persistence/PersistenceInjectionModule.java @@ -0,0 +1,63 @@ +/* + * 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.server.persistence; + +import com.hedera.block.server.config.BlockNodeContext; +import com.hedera.block.server.persistence.storage.PersistenceStorageConfig; +import com.hedera.block.server.persistence.storage.read.BlockAsDirReaderBuilder; +import com.hedera.block.server.persistence.storage.read.BlockReader; +import com.hedera.block.server.persistence.storage.write.BlockAsDirWriterBuilder; +import com.hedera.block.server.persistence.storage.write.BlockWriter; +import com.hedera.hapi.block.stream.Block; +import com.hedera.hapi.block.stream.BlockItem; +import dagger.Module; +import dagger.Provides; +import java.io.IOException; +import javax.inject.Singleton; + +/** A Dagger module for providing dependencies for Persistence Module. */ +@Module +public interface PersistenceInjectionModule { + + /** + * Provides a block writer singleton using the block node context. + * + * @param blockNodeContext the application context + * @return a block writer singleton + */ + @Provides + @Singleton + static BlockWriter providesBlockWriter(BlockNodeContext blockNodeContext) { + try { + return BlockAsDirWriterBuilder.newBuilder(blockNodeContext).build(); + } catch (IOException e) { + throw new RuntimeException("Failed to create block writer", e); + } + } + + /** + * Provides a block reader singleton using the persistence storage config. + * + * @param config the persistence storage configuration needed to build the block reader + * @return a block reader singleton + */ + @Provides + @Singleton + static BlockReader providesBlockReader(PersistenceStorageConfig config) { + return BlockAsDirReaderBuilder.newBuilder(config).build(); + } +} diff --git a/server/src/main/java/module-info.java b/server/src/main/java/module-info.java index 4ad339a6..4a938f30 100644 --- a/server/src/main/java/module-info.java +++ b/server/src/main/java/module-info.java @@ -10,8 +10,10 @@ exports com.hedera.block.server.persistence.storage.remove; exports com.hedera.block.server.config; exports com.hedera.block.server.mediator; + exports com.hedera.block.server.metrics; exports com.hedera.block.server.data; exports com.hedera.block.server.health; + exports com.hedera.block.server.persistence; requires com.hedera.block.stream; requires com.google.protobuf; diff --git a/server/src/test/java/com/hedera/block/server/BlockNodeAppInjectionModuleTest.java b/server/src/test/java/com/hedera/block/server/BlockNodeAppInjectionModuleTest.java index 704409c6..cfafca95 100644 --- a/server/src/test/java/com/hedera/block/server/BlockNodeAppInjectionModuleTest.java +++ b/server/src/test/java/com/hedera/block/server/BlockNodeAppInjectionModuleTest.java @@ -17,17 +17,57 @@ package com.hedera.block.server; import com.hedera.block.server.config.BlockNodeContext; +import com.hedera.block.server.data.ObjectEvent; +import com.hedera.block.server.mediator.StreamMediator; +import com.hedera.block.server.metrics.MetricsService; +import com.hedera.block.server.persistence.storage.read.BlockReader; +import com.hedera.block.server.util.TestConfigUtil; +import com.hedera.hapi.block.SubscribeStreamResponse; +import com.hedera.hapi.block.stream.Block; +import com.hedera.hapi.block.stream.BlockItem; +import com.swirlds.config.api.Configuration; +import io.helidon.webserver.WebServerConfig; +import java.io.IOException; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.Mock; class BlockNodeAppInjectionModuleTest { + + @Mock private StreamMediator> streamMediator; + + @Mock private BlockReader blockReader; + + @Mock private ServiceStatus serviceStatus; + + private BlockNodeContext blockNodeContext; + + @BeforeEach + void setUp() throws IOException { + blockNodeContext = TestConfigUtil.getTestBlockNodeContext(); + } + @Test void testProvideBlockNodeContext() { - BlockNodeContext blockNodeContext = BlockNodeAppInjectionModule.provideBlockNodeContext(); + Configuration configuration = blockNodeContext.configuration(); + MetricsService metricsService = blockNodeContext.metricsService(); + + BlockNodeContext providedBlockNodeContext = + BlockNodeAppInjectionModule.provideBlockNodeContext(configuration, metricsService); + + Assertions.assertEquals(blockNodeContext, providedBlockNodeContext); + Assertions.assertEquals( + blockNodeContext.configuration(), providedBlockNodeContext.configuration()); + Assertions.assertEquals( + blockNodeContext.metricsService(), providedBlockNodeContext.metricsService()); + } + + @Test + void testProvideWebServerConfigBuilder() { + WebServerConfig.Builder webServerConfigBuilder = + BlockNodeAppInjectionModule.provideWebServerConfigBuilder(); - Assertions.assertNotNull(blockNodeContext); - Assertions.assertNotNull(blockNodeContext.configuration()); - Assertions.assertNotNull(blockNodeContext.metrics()); - Assertions.assertNotNull(blockNodeContext.metricsService()); + Assertions.assertNotNull(webServerConfigBuilder); } } diff --git a/server/src/test/java/com/hedera/block/server/BlockNodeAppTest.java b/server/src/test/java/com/hedera/block/server/BlockNodeAppTest.java new file mode 100644 index 00000000..92f7f480 --- /dev/null +++ b/server/src/test/java/com/hedera/block/server/BlockNodeAppTest.java @@ -0,0 +1,76 @@ +/* + * 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.server; + +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.hedera.block.server.health.HealthService; +import io.helidon.webserver.WebServer; +import io.helidon.webserver.WebServerConfig; +import io.helidon.webserver.grpc.GrpcRouting; +import io.helidon.webserver.http.HttpRouting; +import java.io.IOException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class BlockNodeAppTest { + + @Mock private ServiceStatus serviceStatus; + + @Mock private HealthService healthService; + + @Mock private BlockStreamService blockStreamService; + + @Mock private WebServerConfig.Builder webServerBuilder; + + @Mock private WebServer webServer; + + @InjectMocks private BlockNodeApp blockNodeApp; + + @BeforeEach + void setup() { + when(webServerBuilder.port(8080)).thenReturn(webServerBuilder); + when(webServerBuilder.addRouting(any(GrpcRouting.Builder.class))) + .thenReturn(webServerBuilder); + when(webServerBuilder.addRouting(any(HttpRouting.Builder.class))) + .thenReturn(webServerBuilder); + when(webServerBuilder.build()).thenReturn(webServer); + when(healthService.getHealthRootPath()).thenReturn("/health"); + } + + @Test + void testStartServer() throws IOException { + // Act + blockNodeApp.start(); + + // Assert + verify(serviceStatus).setWebServer(webServer); + verify(webServer).start(); + verify(healthService).getHealthRootPath(); + verify(webServerBuilder).port(8080); + verify(webServerBuilder).addRouting(any(GrpcRouting.Builder.class)); + verify(webServerBuilder).addRouting(any(HttpRouting.Builder.class)); + verify(webServerBuilder).build(); + } +} diff --git a/server/src/test/java/com/hedera/block/server/config/BlockNodeContextFactoryTest.java b/server/src/test/java/com/hedera/block/server/config/BlockNodeContextFactoryTest.java deleted file mode 100644 index cd02cd8b..00000000 --- a/server/src/test/java/com/hedera/block/server/config/BlockNodeContextFactoryTest.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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.server.config; - -import static org.junit.jupiter.api.Assertions.*; - -import java.io.IOException; -import org.junit.jupiter.api.Test; - -class BlockNodeContextFactoryTest { - - @Test - void create_returnsBlockNodeContext() throws IOException { - BlockNodeContext context = BlockNodeContextFactory.create(); - - assertNotNull(context.metrics()); - assertNotNull(context.configuration()); - } -} diff --git a/server/src/test/java/com/hedera/block/server/config/BlockNodeContextTest.java b/server/src/test/java/com/hedera/block/server/config/BlockNodeContextTest.java index 690fc450..ef257300 100644 --- a/server/src/test/java/com/hedera/block/server/config/BlockNodeContextTest.java +++ b/server/src/test/java/com/hedera/block/server/config/BlockNodeContextTest.java @@ -21,20 +21,17 @@ import com.hedera.block.server.metrics.MetricsService; import com.swirlds.config.api.Configuration; -import com.swirlds.metrics.api.Metrics; import org.junit.jupiter.api.Test; class BlockNodeContextTest { @Test void BlockNodeContext_initializesWithMetricsAndConfiguration() { - Metrics metrics = mock(Metrics.class); Configuration configuration = mock(Configuration.class); MetricsService metricsService = mock(MetricsService.class); - BlockNodeContext context = new BlockNodeContext(metrics, metricsService, configuration); + BlockNodeContext context = new BlockNodeContext(metricsService, configuration); - assertEquals(metrics, context.metrics()); assertEquals(metricsService, context.metricsService()); assertEquals(configuration, context.configuration()); } diff --git a/server/src/test/java/com/hedera/block/server/config/ConfigInjectionModuleTest.java b/server/src/test/java/com/hedera/block/server/config/ConfigInjectionModuleTest.java new file mode 100644 index 00000000..6c9d7505 --- /dev/null +++ b/server/src/test/java/com/hedera/block/server/config/ConfigInjectionModuleTest.java @@ -0,0 +1,94 @@ +/* + * 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.server.config; + +import static org.junit.jupiter.api.Assertions.*; + +import com.hedera.block.server.consumer.ConsumerConfig; +import com.hedera.block.server.persistence.storage.PersistenceStorageConfig; +import com.hedera.block.server.util.TestConfigUtil; +import com.swirlds.common.metrics.config.MetricsConfig; +import com.swirlds.common.metrics.platform.prometheus.PrometheusConfig; +import com.swirlds.config.api.Configuration; +import java.io.IOException; +import org.junit.jupiter.api.Test; + +class ConfigInjectionModuleTest { + + @Test + void testProvidePersistenceStorageConfig() throws IOException { + + BlockNodeContext context = TestConfigUtil.getTestBlockNodeContext(); + Configuration configuration = context.configuration(); + PersistenceStorageConfig persistenceStorageConfig = + configuration.getConfigData(PersistenceStorageConfig.class); + + // Call the method under test + PersistenceStorageConfig providedConfig = + ConfigInjectionModule.providePersistenceStorageConfig(configuration); + + // Verify that the correct config data is returned + assertNotNull(providedConfig); + assertSame(persistenceStorageConfig, providedConfig); + } + + @Test + void testProvideMetricsConfig() throws IOException { + + BlockNodeContext context = TestConfigUtil.getTestBlockNodeContext(); + Configuration configuration = context.configuration(); + MetricsConfig metricsConfig = configuration.getConfigData(MetricsConfig.class); + + // Call the method under test + MetricsConfig providedConfig = ConfigInjectionModule.provideMetricsConfig(configuration); + + // Verify that the correct config data is returned + assertNotNull(providedConfig); + assertSame(metricsConfig, providedConfig); + } + + @Test + void testProvidePrometheusConfig() throws IOException { + + BlockNodeContext context = TestConfigUtil.getTestBlockNodeContext(); + Configuration configuration = context.configuration(); + PrometheusConfig prometheusConfig = configuration.getConfigData(PrometheusConfig.class); + + // Call the method under test + PrometheusConfig providedConfig = + ConfigInjectionModule.providePrometheusConfig(configuration); + + // Verify that the correct config data is returned + assertNotNull(providedConfig); + assertSame(prometheusConfig, providedConfig); + } + + @Test + void testProvideConsumerConfig() throws IOException { + + BlockNodeContext context = TestConfigUtil.getTestBlockNodeContext(); + Configuration configuration = context.configuration(); + ConsumerConfig consumerConfig = configuration.getConfigData(ConsumerConfig.class); + + // Call the method under test + ConsumerConfig providedConfig = ConfigInjectionModule.provideConsumerConfig(configuration); + + // Verify that the correct config data is returned + assertNotNull(providedConfig); + assertSame(consumerConfig, providedConfig); + } +} diff --git a/server/src/test/java/com/hedera/block/server/mediator/LiveStreamMediatorImplTest.java b/server/src/test/java/com/hedera/block/server/mediator/LiveStreamMediatorImplTest.java index 6fcffe6b..f0f6ab21 100644 --- a/server/src/test/java/com/hedera/block/server/mediator/LiveStreamMediatorImplTest.java +++ b/server/src/test/java/com/hedera/block/server/mediator/LiveStreamMediatorImplTest.java @@ -30,8 +30,6 @@ import com.hedera.block.server.ServiceStatusImpl; import com.hedera.block.server.config.BlockNodeContext; -import com.hedera.block.server.config.BlockNodeContextFactory; -import com.hedera.block.server.consumer.ConsumerConfig; import com.hedera.block.server.consumer.ConsumerStreamResponseObserver; import com.hedera.block.server.data.ObjectEvent; import com.hedera.block.server.persistence.storage.write.BlockWriter; @@ -81,7 +79,6 @@ public class LiveStreamMediatorImplTest { private static final int testTimeout = 200; - private final ConsumerConfig consumerConfig = new ConsumerConfig(TIMEOUT_THRESHOLD_MILLIS); private final BlockNodeContext testContext; public LiveStreamMediatorImplTest() throws IOException { @@ -95,7 +92,7 @@ public LiveStreamMediatorImplTest() throws IOException { @Test public void testUnsubscribeEach() throws InterruptedException, IOException { - final BlockNodeContext blockNodeContext = BlockNodeContextFactory.create(); + final BlockNodeContext blockNodeContext = TestConfigUtil.getTestBlockNodeContext(); final var streamMediatorBuilder = LiveStreamMediatorBuilder.newBuilder( blockWriter, blockNodeContext, new ServiceStatusImpl()); @@ -140,7 +137,7 @@ public void testUnsubscribeEach() throws InterruptedException, IOException { @Test public void testMediatorPersistenceWithoutSubscribers() throws IOException { - final BlockNodeContext blockNodeContext = BlockNodeContextFactory.create(); + final BlockNodeContext blockNodeContext = TestConfigUtil.getTestBlockNodeContext(); final var streamMediator = LiveStreamMediatorBuilder.newBuilder( blockWriter, blockNodeContext, new ServiceStatusImpl()) @@ -159,9 +156,9 @@ blockWriter, blockNodeContext, new ServiceStatusImpl()) } @Test - public void testMediatorPublishEventToSubscribers() throws IOException, InterruptedException { + public void testMediatorPublishEventToSubscribers() throws IOException { - final BlockNodeContext blockNodeContext = BlockNodeContextFactory.create(); + final BlockNodeContext blockNodeContext = TestConfigUtil.getTestBlockNodeContext(); final var streamMediator = LiveStreamMediatorBuilder.newBuilder( blockWriter, blockNodeContext, new ServiceStatusImpl()) @@ -221,7 +218,7 @@ blockWriter, blockNodeContext, new ServiceStatusImpl()) @Test public void testSubAndUnsubHandling() throws IOException { - final BlockNodeContext blockNodeContext = BlockNodeContextFactory.create(); + final BlockNodeContext blockNodeContext = TestConfigUtil.getTestBlockNodeContext(); final var streamMediator = LiveStreamMediatorBuilder.newBuilder( blockWriter, blockNodeContext, new ServiceStatusImpl()) @@ -257,7 +254,7 @@ blockWriter, blockNodeContext, new ServiceStatusImpl()) @Test public void testOnCancelSubscriptionHandling() throws IOException { - final BlockNodeContext blockNodeContext = BlockNodeContextFactory.create(); + final BlockNodeContext blockNodeContext = TestConfigUtil.getTestBlockNodeContext(); final var streamMediator = LiveStreamMediatorBuilder.newBuilder( blockWriter, blockNodeContext, new ServiceStatusImpl()) @@ -292,7 +289,7 @@ blockWriter, blockNodeContext, new ServiceStatusImpl()) @Test public void testOnCloseSubscriptionHandling() throws IOException { - final BlockNodeContext blockNodeContext = BlockNodeContextFactory.create(); + final BlockNodeContext blockNodeContext = TestConfigUtil.getTestBlockNodeContext(); final var streamMediator = LiveStreamMediatorBuilder.newBuilder( blockWriter, blockNodeContext, new ServiceStatusImpl()) @@ -326,8 +323,8 @@ blockWriter, blockNodeContext, new ServiceStatusImpl()) } @Test - public void testMediatorBlocksPublishAfterException() throws IOException, InterruptedException { - final BlockNodeContext blockNodeContext = BlockNodeContextFactory.create(); + public void testMediatorBlocksPublishAfterException() throws IOException { + final BlockNodeContext blockNodeContext = TestConfigUtil.getTestBlockNodeContext(); final var streamMediator = LiveStreamMediatorBuilder.newBuilder( blockWriter, blockNodeContext, new ServiceStatusImpl()) @@ -362,7 +359,7 @@ blockWriter, blockNodeContext, new ServiceStatusImpl()) @Test public void testUnsubscribeWhenNotSubscribed() throws IOException { - final BlockNodeContext blockNodeContext = BlockNodeContextFactory.create(); + final BlockNodeContext blockNodeContext = TestConfigUtil.getTestBlockNodeContext(); final var streamMediator = LiveStreamMediatorBuilder.newBuilder( blockWriter, blockNodeContext, new ServiceStatusImpl()) diff --git a/server/src/test/java/com/hedera/block/server/mediator/MediatorInjectionModuleTest.java b/server/src/test/java/com/hedera/block/server/mediator/MediatorInjectionModuleTest.java new file mode 100644 index 00000000..2a05d499 --- /dev/null +++ b/server/src/test/java/com/hedera/block/server/mediator/MediatorInjectionModuleTest.java @@ -0,0 +1,57 @@ +/* + * 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.server.mediator; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import com.hedera.block.server.ServiceStatus; +import com.hedera.block.server.config.BlockNodeContext; +import com.hedera.block.server.data.ObjectEvent; +import com.hedera.block.server.persistence.storage.write.BlockWriter; +import com.hedera.hapi.block.SubscribeStreamResponse; +import com.hedera.hapi.block.stream.BlockItem; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class MediatorInjectionModuleTest { + + @Mock private BlockWriter blockWriter; + + @Mock private BlockNodeContext blockNodeContext; + + @Mock private ServiceStatus serviceStatus; + + @BeforeEach + void setup() { + // Any setup before each test can be done here + } + + @Test + void testProvidesStreamMediator() { + // Call the method under test + StreamMediator> streamMediator = + MediatorInjectionModule.providesStreamMediator( + blockWriter, blockNodeContext, serviceStatus); + + // Verify that the streamMediator is correctly instantiated + assertNotNull(streamMediator); + } +} diff --git a/server/src/test/java/com/hedera/block/server/metrics/MetricsInjectionModuleTest.java b/server/src/test/java/com/hedera/block/server/metrics/MetricsInjectionModuleTest.java new file mode 100644 index 00000000..525ef747 --- /dev/null +++ b/server/src/test/java/com/hedera/block/server/metrics/MetricsInjectionModuleTest.java @@ -0,0 +1,55 @@ +/* + * 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.server.metrics; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import com.hedera.block.server.config.BlockNodeContext; +import com.hedera.block.server.util.TestConfigUtil; +import com.swirlds.config.api.Configuration; +import com.swirlds.metrics.api.Metrics; +import java.io.IOException; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class MetricsInjectionModuleTest { + + @Mock private Metrics metrics; + + @Test + void testProvideMetricsService() { + // Call the method under test + MetricsService metricsService = MetricsInjectionModule.provideMetricsService(metrics); + + // Verify that the metricsService is correctly instantiated + assertNotNull(metricsService); + } + + @Test + void testProvideMetrics() throws IOException { + BlockNodeContext context = TestConfigUtil.getTestBlockNodeContext(); + Configuration configuration = context.configuration(); + + // Call the method under test + Metrics providedMetrics = MetricsInjectionModule.provideMetrics(configuration); + + assertNotNull(providedMetrics); + } +} diff --git a/server/src/test/java/com/hedera/block/server/persistence/PersistenceInjectionModuleTest.java b/server/src/test/java/com/hedera/block/server/persistence/PersistenceInjectionModuleTest.java new file mode 100644 index 00000000..70ed73e6 --- /dev/null +++ b/server/src/test/java/com/hedera/block/server/persistence/PersistenceInjectionModuleTest.java @@ -0,0 +1,90 @@ +/* + * 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.server.persistence; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.hedera.block.server.config.BlockNodeContext; +import com.hedera.block.server.persistence.storage.PersistenceStorageConfig; +import com.hedera.block.server.persistence.storage.read.BlockReader; +import com.hedera.block.server.persistence.storage.write.BlockWriter; +import com.hedera.block.server.util.TestConfigUtil; +import com.hedera.hapi.block.stream.Block; +import com.hedera.hapi.block.stream.BlockItem; +import com.swirlds.config.api.Configuration; +import java.io.IOException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class PersistenceInjectionModuleTest { + + @Mock private BlockNodeContext blockNodeContext; + + @Mock private PersistenceStorageConfig persistenceStorageConfig; + + @BeforeEach + void setup() throws IOException { + // Setup any necessary mocks before each test + blockNodeContext = TestConfigUtil.getTestBlockNodeContext(); + persistenceStorageConfig = + blockNodeContext.configuration().getConfigData(PersistenceStorageConfig.class); + } + + @Test + void testProvidesBlockWriter() { + + BlockWriter blockWriter = + PersistenceInjectionModule.providesBlockWriter(blockNodeContext); + + assertNotNull(blockWriter); + } + + @Test + void testProvidesBlockWriter_IOException() { + BlockNodeContext blockNodeContext = mock(BlockNodeContext.class); + PersistenceStorageConfig persistenceStorageConfig = mock(PersistenceStorageConfig.class); + when(persistenceStorageConfig.rootPath()).thenReturn("invalid-path*9/////+>"); + Configuration configuration = mock(Configuration.class); + when(blockNodeContext.configuration()).thenReturn(configuration); + when(configuration.getConfigData(PersistenceStorageConfig.class)) + .thenReturn(persistenceStorageConfig); + + // Expect a RuntimeException due to the IOException + RuntimeException exception = + assertThrows( + RuntimeException.class, + () -> PersistenceInjectionModule.providesBlockWriter(blockNodeContext)); + + // Verify the exception message + assertTrue(exception.getMessage().contains("Failed to create block writer")); + } + + @Test + void testProvidesBlockReader() { + + BlockReader blockReader = + PersistenceInjectionModule.providesBlockReader(persistenceStorageConfig); + + assertNotNull(blockReader); + } +} diff --git a/server/src/test/java/com/hedera/block/server/producer/ProducerBlockItemObserverTest.java b/server/src/test/java/com/hedera/block/server/producer/ProducerBlockItemObserverTest.java index 774a3c6b..15afd780 100644 --- a/server/src/test/java/com/hedera/block/server/producer/ProducerBlockItemObserverTest.java +++ b/server/src/test/java/com/hedera/block/server/producer/ProducerBlockItemObserverTest.java @@ -32,7 +32,6 @@ import com.hedera.block.server.ServiceStatus; import com.hedera.block.server.ServiceStatusImpl; import com.hedera.block.server.config.BlockNodeContext; -import com.hedera.block.server.config.BlockNodeContextFactory; import com.hedera.block.server.consumer.ConsumerConfig; import com.hedera.block.server.consumer.ConsumerStreamResponseObserver; import com.hedera.block.server.data.ObjectEvent; @@ -118,7 +117,7 @@ public void testProducerOnNext() throws IOException, NoSuchAlgorithmException { @Test public void testProducerWithManyConsumers() throws IOException { - final BlockNodeContext blockNodeContext = BlockNodeContextFactory.create(); + final BlockNodeContext blockNodeContext = TestConfigUtil.getTestBlockNodeContext(); final var streamMediator = LiveStreamMediatorBuilder.newBuilder( blockWriter, blockNodeContext, new ServiceStatusImpl()) diff --git a/server/src/test/java/com/hedera/block/server/util/TestConfigUtil.java b/server/src/test/java/com/hedera/block/server/util/TestConfigUtil.java index 30f3e121..ef3789dc 100644 --- a/server/src/test/java/com/hedera/block/server/util/TestConfigUtil.java +++ b/server/src/test/java/com/hedera/block/server/util/TestConfigUtil.java @@ -17,11 +17,13 @@ package com.hedera.block.server.util; import com.hedera.block.server.config.BlockNodeContext; -import com.hedera.block.server.config.BlockNodeContextFactory; import com.hedera.block.server.config.TestConfigBuilder; import com.hedera.block.server.consumer.ConsumerConfig; +import com.hedera.block.server.metrics.MetricsService; +import com.swirlds.common.metrics.platform.DefaultMetricsProvider; import com.swirlds.config.api.Configuration; import com.swirlds.config.extensions.sources.ClasspathFileConfigSource; +import com.swirlds.metrics.api.Metrics; import edu.umd.cs.findbugs.annotations.NonNull; import java.io.IOException; import java.nio.file.Path; @@ -40,11 +42,6 @@ private TestConfigUtil() {} public static BlockNodeContext getTestBlockNodeContext( @NonNull Map customProperties) throws IOException { - // we still use the BlockNodeContextFactory to create the BlockNodeContext temporally, - // but we will replace the configuration with a test configuration - // sooner we will need to create a metrics mock, and never use the BlockNodeContextFactory. - BlockNodeContext blockNodeContext = BlockNodeContextFactory.create(); - // create test configuration TestConfigBuilder testConfigBuilder = new TestConfigBuilder(true) @@ -61,11 +58,22 @@ public static BlockNodeContext getTestBlockNodeContext( Configuration testConfiguration = testConfigBuilder.getOrCreateConfig(); - return new BlockNodeContext( - blockNodeContext.metrics(), blockNodeContext.metricsService(), testConfiguration); + Metrics metrics = getTestMetrics(testConfiguration); + + MetricsService metricsService = new MetricsService(metrics); + + return new BlockNodeContext(metricsService, testConfiguration); } public static BlockNodeContext getTestBlockNodeContext() throws IOException { return getTestBlockNodeContext(Collections.emptyMap()); } + + // TODO: we need to create a Mock metrics, and avoid using the real metrics for tests + public static Metrics getTestMetrics(@NonNull Configuration configuration) { + final DefaultMetricsProvider metricsProvider = new DefaultMetricsProvider(configuration); + final Metrics metrics = metricsProvider.createGlobalMetrics(); + metricsProvider.start(); + return metrics; + } }