kotest change environment variables - kotlin

I am writing tests for Ktor app using Kotests, but stumbled into the problem how can I change env variables for tests preferably globally. I have tried adding withEnvironment but it throw quite strange error into me
Unable to make field private final java.util.Map java.util.Collections$UnmodifiableMap.m accessible: module java.base does not "opens java.util" to unnamed module #3daa422a
java.lang.reflect.InaccessibleObjectException: Unable to make field private final java.util.Map java.util.Collections$UnmodifiableMap.m accessible: module java.base does not "opens java.util" to unnamed module #3daa422a
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354)
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297)
my test file looks like this
class VisitorSpec : FreeSpec({
val ds = createDataSourceTest()
val visitor = RegisterVisitorDTO(
email = TestConstants.VISITOR_EMAIL,
username = TestConstants.VISITOR_USERNAME,
password = TestConstants.PASSWORD,
firstName = TestConstants.VISITOR_FIRST_NAME,
lastName = TestConstants.VISITOR_LAST_NAME,
gender = TestConstants.VISITOR_GENDER,
birthday = TestConstants.VISITOR_BIRTHDAY,
)
"check visitor routes" - {
val loginData = LoginDTO(TestConstants.VISITOR_EMAIL + 0, TestConstants.PASSWORD)
"can get list of visitors with correct query" {
withEnvironment(
mapOf(
"POSTGRES_URL" to "jdbc:postgresql://localhost:5432/test",
"POSTGRES_USERNAME" to "test_user",
"POSTGRES_PASSWORD" to "test_pass"
)
) {
testApplication {
val client = getClient(ds)
repeat(6) {
registerConfirmedUser(
client, visitor.copy(
email = "${TestConstants.VISITOR_EMAIL}$it",
username = "${TestConstants.VISITOR_USERNAME}$it",
)
)
}
val accessToken = loginUser(client, loginData).run { this.body<LoginResponseDTO>().accessToken }
client.get("/api/v1/visitors?page=1&count=5") {
header("Authorization", "Bearer $accessToken")
}.apply {
val response = this.body<VisitorPaginatedResponseDTO>()
response.data.size.shouldBe(5)
response.totalCount.shouldBe(6)
response.currentPage.shouldBe(1)
}
}
}
}
...
if I remove
withEnvironment(
mapOf(
"POSTGRES_URL" to "jdbc:postgresql://localhost:5432/test",
"POSTGRES_USERNAME" to "test_user",
"POSTGRES_PASSWORD" to "test_pass"
)
)
it will just work but with default db, any advice on this?
In some places, it was advised to use
override fun listeners() = listOf(
SystemEnvironmentTestListener("fooKeyEnv", "barValueEnv"),
SystemPropertyTestListener("fooKeyProp", "barValueProp")
)
but ide tells me that this method is deprecated.
Thanks in advance for any advice.

Recent Java versions prohibit modifying the environment variables with the default access settings (JEP 403: Strongly Encapsulate JDK Internals). Kotest and some other testing frameworks that manipulate the environment variables got affected by this, you can find the related issues:
https://github.com/kotest/kotest/issues/2849
https://github.com/stefanbirkner/system-lambda/issues/23
https://github.com/junit-pioneer/junit-pioneer/issues/509
One solution would be to add the arguments to the JVM running the tests that would make the Java Platform Module System allow the access to the API used by the test framework. Here's an answer that explains the arguments: How to set environment variable in Java without 'illegal reflective access'? How to use add-opens?
The simplest form of the argument, if you are not using Java modules in your code, would be:
--add-opens java.base/java.util=ALL-UNNAMED
If you are running the tests using Gradle, then you can pass this argument to the jvmArgs of the test task:
tasks.withType<Test>().named("jvmTest") {
jvmArgs("--add-opens", "java.base/java.util=ALL-UNNAMED")
}
Note: modifying the module access in this way could make the tests pass even if some of your code needs illegal access to the JDK internals. Make sure that your code doesn't do that or that you have other tests that check for this without modifying module access rights.
It seems that some other libraries, like system-stubs, provide a way to modify the environment variables in tests without illegal reflective access to the JDK internals.

Related

Since ripple-lib-java is unmaintained, how do you manually sign a transaction?

Looking for a lightweight way to manually sign an OfferCreate ... it would be clumsy to start some JavaScript engine in order to get it done.
Hmm ... even though it hasn't been maintained for 4 years it looks like it still works.
Clone ripple-lib-java and do mvn install in ripple-bouncycastle & ripple-core
Then copy the built JARs into <YourProject>/libs
Add local dependencies in Gradle Kotlin DSL:
repositories {
flatDir {
dirs("libs")
}
}
dependencies {
implementation(":ripple-bouncycastle-0.0.1-SNAPSHOT")
implementation(":ripple-core-0.0.1-SNAPSHOT")
runtime("org.json:json:20190722") // No newskool kotlinx-serialization here :(
}
Create & sign OfferCreate:
val offerCreate = OfferCreate()
offerCreate.account(AccountID.fromString("r3jpWpUysF4SAkUNbG4WhDZ5mAJ7rGUDx6"))
offerCreate.expiration(UInt32(get2MinExpiration()))
offerCreate.fee(Amount(BigDecimal("0.00001")))
offerCreate.flags(UInt32(0))
offerCreate.sequence(UInt32(1))
val amountXRP = BigDecimal(200)
val amountBTC = convert(amountXRP, RIPPLE_XRP, BITCOIN)
offerCreate.takerGets(Amount(amountXRP))
offerCreate.takerPays(Amount(amountBTC, Currency.fromString(BITCOIN), AccountID.fromString(BITCOIN_TRUSTED_ISSUER)))
val signedTransaction = offerCreate.sign("[***Secret***]")
println(offerCreate.toJSON())
println(signedTransaction.tx_blob)
A bit annoying that you can't do a toJSON() on a SignedTransaction. It can
t be a security thing, since sign() only adds public key fields: https://xrpl.org/transaction-common-fields.html#signers-field

Micronaut declarative client with base url per environment

I'd like to be able to use Micronaut's declarative client to hit an a different endpoint based on whether I'm in a local development environment vs a production environment.
I'm setting my client's base uri in application.dev.yml:
myserviceclient:
baseUri: http://localhost:1080/endpoint
Reading the docs from Micronaut, they have the developer jumping through quite a few hoops to get a dynamic value piped into the actual client. They're actually quite confusing. So I've created a configuration like this:
#ConfigurationProperties(PREFIX)
class MyServiceClientConfig {
companion object {
const val PREFIX = "myserviceclient"
const val BASE_URL = "http://localhost:1080/endpoint"
}
var baseUri: String? = null
fun toMap(): MutableMap<String, Any> {
val m = HashMap<String, Any>()
if (baseUri != null) {
m["baseUri"] = baseUri!!
}
return m
}
}
But as you can see, that's not actually reading any values from application.yml, it's simply setting a const value as a static on the class. I'd like that BASE_URL value to be dynamic based on which environment I'm in.
To use this class, I've created a declarative client like this:
#Client(MyServiceClientConfig.BASE_URL)
interface MyServiceClient {
#Post("/user/kfc")
#Produces("application/json")
fun sendUserKfc(transactionDto: TransactionDto)
}
The docs show an example where they're interpolating values from the config map that's built like this:
#Get("/api/\${bintray.apiversion}/repos/\${bintray.organization}/\${bintray.repository}/packages")
But how would I make this work in the #Client() annotation?
Nowhere in that example do they show how bintray is getting defined/injected/etc. This appears to be the same syntax that's used with the #Value() annotation. I've tried using that as well, but every value I try to use ends up being null.
This is very frustrating, but I'm sure I'm missing a key piece that will make this all work.
I'm setting my client's base uri in application.dev.yml
You probably want application-dev.yml.
But how would I make this work in the #Client() annotation?
You can put a config key in the #Client value using something like #Client("${myserviceclient.baseUri}").
If you want the url somewhere in your code use this:
#Value("${micronaut.http.services.occupancy.urls}")
private String occupancyUrl;

Integration testing with event sourcing systems

I'm working on a PoC where we use CQRS in combination with Event Sourcing. We use the Axon framework and Axon server as toolset.
We have some microservices (Maven packages) with some business logic.
A simple overview of the application flow:
We post a xml message (with REST) to service 1 that will result in an event (with Aggregate).
Service 2 handles the event "fired" by service 1 and starts a saga flow. Part of the sage flow is for example to send a mail message.
I can do some tests with Axon Test to test the aggregate from service 1 or the saga from service 2. But is there a good option to do a real integration test where we start with posting a message to the REST interface and check all the operations in the aggregate and saga (inclusive sending mail and so on)
Maybe this kind of integration test is overdone and it's better to test each component on it's own. I doubt what's needed / the best solution to test this type of system.
I suggest to have a look at testcontainers (https://www.testcontainers.org/)
It provides a very convenient way to start up and cleanly tear down docker containers in JUnit tests. This feature is very useful for integration testing of applications against real databases and any other resource (for example Axon Server) for which a docker image is available (https://hub.docker.com/r/axoniq/axonserver/).
I'm sharing some code snippets from JUnit 4 test class (Kotlin). Hopefully this can help you to get started and evolve your specific test strategy (integration should cover smaller scope then end-to-end tests). My opinion is that integration test should focus on Axon messaging API components and REST API components separately/independently. End-to-end should cover all components in your microservice/s.
#RunWith(SpringRunner::class)
#SpringBootTest
#ContextConfiguration(initializers = [DrestaurantCourierCommandMicroServiceIT.Initializer::class])
internal class DrestaurantCourierCommandMicroServiceIT {
#Autowired
lateinit var eventStore: EventStore
#Autowired
lateinit var commandGateway: CommandGateway
companion object {
// An Axon Server container
#ClassRule
#JvmField
var axonServerTestContainer = KGenericContainer(
"axoniq/axonserver")
.withExposedPorts(8024, 8124)
.waitingFor(Wait.forHttp("/actuator/info").forPort(8024))
.withStartupTimeout(Duration.of(60L, ChronoUnit.SECONDS))
// A PostgreSQL container is being started up using a JUnit Class Rule which gets triggered before any of the tests are run:
#ClassRule
#JvmField
var postgreSQLContainer = KPostgreSQLContainer(
"postgres:latest")
.withDatabaseName("drestaurant")
.withUsername("demouser")
.withPassword("thepassword")
.withStartupTimeout(Duration.of(60L, ChronoUnit.SECONDS))
}
// Pass details on the application as properties BEFORE Spring starts creating a test context for the test to run in:
class Initializer : ApplicationContextInitializer<ConfigurableApplicationContext> {
override fun initialize(configurableApplicationContext: ConfigurableApplicationContext) {
val values = TestPropertyValues.of(
"spring.datasource.url=" + postgreSQLContainer.jdbcUrl,
"spring.datasource.username=" + postgreSQLContainer.username,
"spring.datasource.password=" + postgreSQLContainer.password,
"spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect",
"axon.axonserver.servers=" + axonServerTestContainer.containerIpAddress + ":" + axonServerTestContainer.getMappedPort(8124)
)
values.applyTo(configurableApplicationContext)
}
}
#Test
fun `restaurant command microservice integration test - happy scenario`() {
val who = "johndoe"
val auditEntry = AuditEntry(who, Calendar.getInstance().time)
val maxNumberOfActiveOrders = 5
val name = PersonName("Ivan", "Dugalic")
val orderId = CourierOrderId("orderId")
// ******* Sending the `createCourierCommand` ***********
val createCourierCommand = CreateCourierCommand(name, maxNumberOfActiveOrders, auditEntry)
commandGateway.sendAndWait<Any>(createCourierCommand)
await withPollInterval org.awaitility.Duration.ONE_SECOND atMost org.awaitility.Duration.FIVE_SECONDS untilAsserted {
val latestCourierCreatedEvent = eventStore.readEvents(createCourierCommand.targetAggregateIdentifier.identifier).asStream().toList().last().payload as CourierCreatedEvent
assertThat(latestCourierCreatedEvent.name).isEqualTo(createCourierCommand.name)
assertThat(latestCourierCreatedEvent.auditEntry.who).isEqualTo(createCourierCommand.auditEntry.who)
assertThat(latestCourierCreatedEvent.maxNumberOfActiveOrders).isEqualTo(createCourierCommand.maxNumberOfActiveOrders)
}
// ******* Sending the `createCourierOrderCommand` **********
val createCourierOrderCommand = CreateCourierOrderCommand(orderId, auditEntry)
commandGateway.sendAndWait<Any>(createCourierOrderCommand)
await withPollInterval org.awaitility.Duration.ONE_SECOND atMost org.awaitility.Duration.FIVE_SECONDS untilAsserted {
val latestCourierOrderCreatedEvent = eventStore.readEvents(createCourierOrderCommand.targetAggregateIdentifier.identifier).asStream().toList().last().payload as CourierOrderCreatedEvent
assertThat(latestCourierOrderCreatedEvent.aggregateIdentifier.identifier).isEqualTo(createCourierOrderCommand.targetAggregateIdentifier.identifier)
assertThat(latestCourierOrderCreatedEvent.auditEntry.who).isEqualTo(createCourierOrderCommand.auditEntry.who)
}
// ******* Assign the courier order to courier **********
val assignCourierOrderToCourierCommand = AssignCourierOrderToCourierCommand(orderId, createCourierCommand.targetAggregateIdentifier, auditEntry)
commandGateway.sendAndWait<Any>(assignCourierOrderToCourierCommand)
await withPollInterval org.awaitility.Duration.ONE_SECOND atMost org.awaitility.Duration.FIVE_SECONDS untilAsserted {
val latestCourierOrderAssignedEvent = eventStore.readEvents(assignCourierOrderToCourierCommand.targetAggregateIdentifier.identifier).asStream().toList().last().payload as CourierOrderAssignedEvent
assertThat(latestCourierOrderAssignedEvent.aggregateIdentifier.identifier).isEqualTo(assignCourierOrderToCourierCommand.targetAggregateIdentifier.identifier)
assertThat(latestCourierOrderAssignedEvent.auditEntry.who).isEqualTo(assignCourierOrderToCourierCommand.auditEntry.who)
assertThat(latestCourierOrderAssignedEvent.courierId.identifier).isEqualTo(assignCourierOrderToCourierCommand.courierId.identifier)
}
}
}
class KGenericContainer(imageName: String) : GenericContainer<KGenericContainer>(imageName)
class KPostgreSQLContainer(imageName: String) : PostgreSQLContainer<KPostgreSQLContainer>(imageName)
Axon developers suggest to go with docker solution as mentioned here.
Testcontainers seems to be the best here.
My java snippet:
#ActiveProfiles("test")
public class TestContainers {
private static final int AXON_HTTP_PORT = 8024;
private static final int AXON_GRPC_PORT = 8124;
public static void startAxonServer() {
GenericContainer axonServer = new GenericContainer("axoniq/axonserver:latest")
.withExposedPorts(AXON_HTTP_PORT, AXON_GRPC_PORT)
.waitingFor(
Wait.forLogMessage(".*Started AxonServer.*", 1)
);
axonServer.start();
System.setProperty("ENV_AXON_GRPC_PORT", String.valueOf(axonServer.getMappedPort(AXON_GRPC_PORT)));
}
Call startAxonServer method in your #BeforeClass. Now you have to obtain external docker ports (these indicated in withExposedPorts are docker-internal).
You can do it in runtime via getMappedPort as shown in my snippet. Remember to provide connection configuration to your test suite. My example on spring boot as follows:
axon:
axonserver:
servers: localhost:${ENV_AXON_GRPC_PORT}
Complete working solution can be found on my github project.

KotlinPoet - Generate Koin module

I'm new to KotlinPoet and I cannot find how to create the following Koin module statement:
internal val apiModules = module {
single<Name1> { get<Retrofit>().create(Name1::class.java) }
single<Name2> { get<Retrofit>().create(Name2::class.java) }
}
directly into a Kotlin file (no wrapper class)
I have been playing around with PropertySpec and CodeBlock but I don't know how to import Koin DSL or how to reference those imported classes in the code generation. I was also unable to generate the code by pure string generation.
You need to generate the file using FileSpec and add a PropertySpec for the module
It shold look similar to this
val moduleClassName = ClassName("org.koin.core.module.Module", "Module") //This will take care of the import
val moduleMemberName = MemberName("org.koin.dsl.module", "module") //This will take care of the import
val moduleInitilizerCodeBlock =
CodeBlock.Builder()
.beginControlFlow("%M", moduleMemberName) //This will take care of the {} and indentations
.addStatment(ADD ANOTHER CODE BLOCK SIMNILAR TO THIS FOR THE SINGLE/FACTORY)
.endControlFlow()
.build()
val module = PropertySpec.builder("YOUR MODULE NAME", moduleClassName)
.initializer(moduleInitilizerCodeBlock)
.build()
FileSpec.Builder("FILE PACKAGE", "FILE NAME")
.addProperty(module)
.build()
This is not full code but it should point you in the right direction.
Side note: I might me wrong about specific namings but again it should be enough

How to unit or integration test use of injected messageSource for i18n in Grails 2.0 service

I make use of a message bundle in one of my services in a Grails 2.0 project for internationalized text. The use case is an email subject that is sent via the mail plugin in an asynchronous way, so it really doesn't make sense to have this in a controller or TagLib (given the usual argument of not accessing your text or views in a service). This code works fine in my running Grails app, but I'm not sure how to test it.
I tried a PluginAwareResourceBundleMessageSource in my defineBeans as that is what my running application injects, but it led to nullpointers as it appears it needs a bunch of setup around plugin managers and such that my test environment is not giving (even integration).
I then tried a ReloadableResourceBundleMessageSource as it was pure Spring, but it can't seem to see my .properties files, and fails with a No message found under code 'my.email.subject' for locale 'en'.
I feel like I'm going down a wormhole a bit as accessing Grails i18n in a service is not documented in the grails docs, so if there is a preferred way to do this, let me know.
Note my .properties file is in the standard grails-app/i18n location.
The test
#TestFor(EmailHelperService)
class EmailHelperServiceTests {
void testSubjectsDefaultLocale() {
defineBeans {
//messageSource(PluginAwareResourceBundleMessageSource); Leads to nullpointers
messageSource(ReloadableResourceBundleMessageSource);
}
String expected = "My Expected subject Passed1 Passed2";
String actual = service.getEmailSubjectForStandardMustGiveGiftFromBusiness(Locale.ENGLISH, Passed1 Passed2);
assertEquals("email subject", expected, actual);
}
Service:
class EmailHelperService {
def messageSource;
public String getEmailSubject(Locale locale, String param1, String param2) {
Object[] params = [param1, param2].toArray();
return messageSource.getMessage("my.email.subject", params, locale );
}
There is already a messageSource in unit tests in Grails, it is a StaticMessageSource (see http://static.springsource.org/spring/docs/2.5.4/api/org/springframework/context/support/StaticMessageSource.html), you can add mock messages with the addMessage method:
messageSource.addMessage("foo.bar", request.locale, "My Message")
In unit tests and the local side of functional tests, sometimes you want the real properties that are in the 18n directory.
This works for me:
MessageSource getI18n() {
// assuming the test cwd is the project dir (where application.properties is)
URL url = new File('grails-app/i18n').toURI().toURL()
def messageSource = new ResourceBundleMessageSource()
messageSource.bundleClassLoader = new URLClassLoader(url)
messageSource.basename = 'messages'
messageSource
}
i18n.getMessage(key, params, locale)
In a unit test you could ensure that you're wired up correctly by doing something like this:
void testSubjectsDefaultLocale() {
def messageSource = new Object()
messageSource.metaClass.getMessage = {subject, params, locale ->
assert "my.email.subject" == subject
assert ["Passed1", "Passed2"] == params
assert Locale.ENGLISH == locale
"It Worked!!!"
}
service.messageSource = messageSource
String actual = service.getEmailSubjectForStandardMustGiveGiftFromBusiness(Locale.ENGLISH, Passed1 Passed2)
assert "It Worked!!!" == actual
}
This will help ensure that you're wired up correctly but it will not ensure that what you're doing actually works. If you're comfortable with that then this would work for you. If you're trying to test that when you give "XYZ" to your .properties file it returns "Hello" then this will not work for you.