Merge pull request #38 from p2p-org/ganache_in_tests

Integration tests with ganache and SpringBootTest
This commit is contained in:
a10zn8
2022-11-23 18:06:49 +04:00
committed by GitHub
5 changed files with 162 additions and 3 deletions

View File

@@ -58,6 +58,7 @@ configurations {
compile.exclude group: "org.slf4j", module: "log4j-over-slf4j" compile.exclude group: "org.slf4j", module: "log4j-over-slf4j"
// should be used only for generation of the stubs, the lib contains grpc classes // should be used only for generation of the stubs, the lib contains grpc classes
compile.exclude group: "com.salesforce.servicelibs", module: "reactor-grpc" compile.exclude group: "com.salesforce.servicelibs", module: "reactor-grpc"
} }
dependencies { dependencies {
@@ -101,6 +102,13 @@ dependencies {
testImplementation libs.java.websocket testImplementation libs.java.websocket
testImplementation libs.equals.verifier testImplementation libs.equals.verifier
testImplementation libs.groovy testImplementation libs.groovy
testImplementation libs.bundles.testcontainers
testImplementation libs.bundles.junit
testImplementation(libs.spring.boot.starter.test) {
exclude module: 'spring-boot-starter-logging'
}
testImplementation libs.grpc.testing
detektPlugins libs.detekt.formatting detektPlugins libs.detekt.formatting
} }

View File

@@ -111,6 +111,14 @@ spring-security-core = { module = "org.springframework.security:spring-security-
spring-security-web = { module = "org.springframework.security:spring-security-web", version.ref = "spring-security" } spring-security-web = { module = "org.springframework.security:spring-security-web", version.ref = "spring-security" }
spring-security-config = { module = "org.springframework.security:spring-security-config", version.ref = "spring-security" } spring-security-config = { module = "org.springframework.security:spring-security-config", version.ref = "spring-security" }
spring-boot-starter-test = { module = "org.springframework.boot:spring-boot-starter-test", version.ref = "spring-boot" }
testcontainers = "org.testcontainers:testcontainers:1.17.5"
testcontainers-ganache = "io.github.ganchix:testcontainers-java-module-ganache:0.0.4"
junit-jupiter = "org.junit.jupiter:junit-jupiter:5.9.1"
assertj = "org.assertj:assertj-core:3.23.1"
[bundles] [bundles]
apache-commons = ["commons-io", "apache-commons-lang3", "apache-commons-collections4"] apache-commons = ["commons-io", "apache-commons-lang3", "apache-commons-collections4"]
etherjar = ["etherjar-domain", "etherjar-hex", "etherjar-rpc-api", "etherjar-rpc-http", "etherjar-rpc-ws", "etherjar-tx", "etherjar-contract", "etherjar-erc20"] etherjar = ["etherjar-domain", "etherjar-hex", "etherjar-rpc-api", "etherjar-rpc-http", "etherjar-rpc-ws", "etherjar-tx", "etherjar-contract", "etherjar-erc20"]
@@ -122,6 +130,9 @@ netty = ["netty-common", "netty-transport", "netty-handler-core", "netty-handler
reactor = ["reactor-core", "reactor-netty", "reactor-extra", "reactor-kotlin"] reactor = ["reactor-core", "reactor-netty", "reactor-extra", "reactor-kotlin"]
slf4j = ["slf4j-api", "slf4j-jul", "slf4j-jcl", "log4j-slf4j"] slf4j = ["slf4j-api", "slf4j-jul", "slf4j-jcl", "log4j-slf4j"]
spring-framework = ["spring-boot-starter", "spring-security-core", "spring-security-web", "spring-security-config"] spring-framework = ["spring-boot-starter", "spring-security-core", "spring-security-web", "spring-security-config"]
testcontainers = ["testcontainers", "testcontainers-ganache"]
junit = ["junit-jupiter", "assertj"]
[plugins] [plugins]
kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }

View File

@@ -33,9 +33,11 @@ import org.springframework.boot.SpringApplication
import org.springframework.context.ApplicationContext import org.springframework.context.ApplicationContext
import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Profile
import org.springframework.core.env.Environment import org.springframework.core.env.Environment
import org.springframework.scheduling.annotation.EnableAsync import org.springframework.scheduling.annotation.EnableAsync
import org.springframework.scheduling.annotation.EnableScheduling import org.springframework.scheduling.annotation.EnableScheduling
import org.springframework.util.ResourceUtils
import reactor.core.scheduler.Scheduler import reactor.core.scheduler.Scheduler
import reactor.core.scheduler.Schedulers import reactor.core.scheduler.Schedulers
import java.io.File import java.io.File
@@ -47,8 +49,8 @@ import kotlin.system.exitProcess
@EnableScheduling @EnableScheduling
@EnableAsync @EnableAsync
open class Config( open class Config(
@Autowired private val env: Environment, private val env: Environment,
@Autowired private val ctx: ApplicationContext private val ctx: ApplicationContext
) { ) {
companion object { companion object {
@@ -61,7 +63,12 @@ open class Config(
private var configFilePath: File? = null private var configFilePath: File? = null
init { init {
configFilePath = getConfigPath() configFilePath = if (env.activeProfiles.contains("integration-test")) {
ResourceUtils.getFile("classpath:integration/dshackle.yaml")
} else {
getConfigPath()
}
Global.version = env.getProperty("version.app", Global.version).let { Global.version = env.getProperty("version.app", Global.version).let {
if (it.contains("SNAPSHOT")) { if (it.contains("SNAPSHOT")) {
listOfNotNull(it, env.getProperty("version.commit")).joinToString("-") listOfNotNull(it, env.getProperty("version.commit")).joinToString("-")
@@ -95,6 +102,7 @@ open class Config(
} }
@Bean @Bean
@Profile("!integration-test")
open fun mainConfig(@Autowired fileResolver: FileResolver): MainConfig { open fun mainConfig(@Autowired fileResolver: FileResolver): MainConfig {
val f = configFilePath ?: throw IllegalStateException("Config path is not set") val f = configFilePath ?: throw IllegalStateException("Config path is not set")
log.info("Using config: ${f.absolutePath}") log.info("Using config: ${f.absolutePath}")

View File

@@ -0,0 +1,111 @@
package io.emeraldpay.dshackle
import io.emeraldpay.api.proto.BlockchainGrpc
import io.emeraldpay.api.proto.BlockchainOuterClass
import io.emeraldpay.api.proto.Common.ChainRef
import io.emeraldpay.dshackle.config.MainConfig
import io.emeraldpay.dshackle.config.MainConfigReader
import io.emeraldpay.dshackle.config.UpstreamsConfig
import io.github.ganchix.ganache.Account
import io.github.ganchix.ganache.GanacheContainer
import io.grpc.BindableService
import io.grpc.inprocess.InProcessChannelBuilder
import io.grpc.inprocess.InProcessServerBuilder
import org.assertj.core.api.Assertions
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.context.TestConfiguration
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Import
import org.springframework.context.annotation.Profile
import org.springframework.test.context.ActiveProfiles
import org.springframework.util.ResourceUtils
import java.math.BigInteger
import java.net.URI
@SpringBootTest(properties = ["spring.main.allow-bean-definition-overriding=true"])
@Import(Config::class)
@ActiveProfiles("integration-test")
class IntegrationTest {
@Autowired
lateinit var services: List<BindableService>
lateinit var stub: BlockchainGrpc.BlockchainBlockingStub
companion object {
val PRIVATE_KEY_0 = "ae020c8ddb6fbc24e167b011666639d2ce3d4aa0d9c13d02d726d6865618a781"
val PRIVATE_KEY_1 = "81ad1ba5c4da47feb0f0163c0c61a66c4d0e6a66bd839827444b1e3362016140"
var ganacheContainer: GanacheContainer<*> = GanacheContainer<Nothing>().apply {
withAccounts(
listOf(
Account.builder().privateKey(PRIVATE_KEY_0).balance(BigInteger.valueOf(2000000000000000000)).build(),
Account.builder().privateKey(PRIVATE_KEY_1).balance(BigInteger.valueOf(2000000000000000000)).build()
)
)
}
init {
ganacheContainer.start()
}
}
@BeforeEach
fun prepare() {
val serverName = InProcessServerBuilder.generateName()
val builder = InProcessServerBuilder.forName(serverName)
.directExecutor()
services.forEach { builder.addService(it) }
val managedChannel = InProcessChannelBuilder.forName(serverName).directExecutor().build()
val server = builder.build()
server.start()
stub = BlockchainGrpc.newBlockingStub(managedChannel)
}
@Test
fun test() {
val result = stub.describe(BlockchainOuterClass.DescribeRequest.newBuilder().build())
Assertions.assertThat(result.chainsCount).isEqualTo(1)
Assertions.assertThat(result.chainsList[0].chain).isEqualTo(ChainRef.CHAIN_ETHEREUM)
Assertions.assertThat(result.chainsList[0].nodesCount).isEqualTo(1)
}
@TestConfiguration
open class Config {
@Bean
@Profile("integration-test")
open fun mainConfig(@Autowired fileResolver: FileResolver): MainConfig {
val reader = MainConfigReader(fileResolver)
val config = reader.read(
ResourceUtils.getFile("classpath:integration/dshackle.yaml")
.inputStream()
)!!
patch(config)
return config
}
private fun patch(config: MainConfig) {
config.upstreams?.upstreams?.add(
UpstreamsConfig.Upstream<UpstreamsConfig.EthereumPosConnection>().apply {
id = "ganache"
nodeId = 1
chain = "ethereum"
connection = UpstreamsConfig.EthereumPosConnection().apply {
execution = UpstreamsConfig.EthereumConnection().apply {
rpc = UpstreamsConfig.HttpEndpoint(
URI.create(
"http://" + ganacheContainer.getHost() + ":" + ganacheContainer.getMappedPort(8545) + "/"
)
)
}
}
}
)
}
}
}

View File

@@ -0,0 +1,21 @@
version: v1
host: 0.0.0.0
port: 2450
tls:
enabled: false
proxy:
host: 0.0.0.0
port: 8550
routes:
- id: eth
blockchain: ethereum
monitoring:
enabled: false
extended: true
signed-response:
enabled: false
cluster:
upstreams: