Skip to content

Commit 7b3b86b

Browse files
authored
support for bridging app to the validation context (#176)
1 parent 32bfb4b commit 7b3b86b

File tree

3 files changed

+116
-0
lines changed

3 files changed

+116
-0
lines changed

examples/spring-example/src/test/kotlin/com/stove/spring/example/e2e/ExampleTest.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,27 @@ import com.trendyol.stove.testing.e2e.kafka.KafkaSystem.Companion.shouldBeFailed
1111
import com.trendyol.stove.testing.e2e.kafka.KafkaSystem.Companion.shouldBePublishedOnCondition
1212
import com.trendyol.stove.testing.e2e.kafka.kafka
1313
import com.trendyol.stove.testing.e2e.system.TestSystem
14+
import com.trendyol.stove.testing.e2e.using
1415
import com.trendyol.stove.testing.e2e.wiremock.wiremock
1516
import io.kotest.core.spec.style.FunSpec
1617
import io.kotest.matchers.shouldBe
1718
import io.kotest.matchers.string.shouldContain
1819
import stove.spring.example.application.handlers.ProductCreateRequest
1920
import stove.spring.example.application.handlers.ProductCreatedEvent
2021
import stove.spring.example.application.services.SupplierPermission
22+
import stove.spring.example.infrastructure.couchbase.CouchbaseProperties
2123
import stove.spring.example.infrastructure.messaging.kafka.consumers.BusinessException
2224
import stove.spring.example.infrastructure.messaging.kafka.consumers.ProductCreateEvent
2325

2426
class ExampleTest : FunSpec({
27+
test("bridge should work") {
28+
TestSystem.validate {
29+
using<CouchbaseProperties> {
30+
this.bucketName shouldBe "Stove"
31+
}
32+
}
33+
}
34+
2535
test("index should be reachable") {
2636
TestSystem.validate {
2737
http {

examples/spring-example/src/test/kotlin/com/stove/spring/example/e2e/TestSystemConfig.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.stove.spring.example.e2e
22

3+
import com.trendyol.stove.testing.e2e.bridge
34
import com.trendyol.stove.testing.e2e.couchbase.CouchbaseSystemOptions
45
import com.trendyol.stove.testing.e2e.couchbase.couchbase
56
import com.trendyol.stove.testing.e2e.http.httpClient
@@ -27,6 +28,7 @@ class TestSystemConfig : AbstractProjectConfig() {
2728
kafka {
2829
KafkaSystemOptions(containerOptions = KafkaContainerOptions(tag = "latest"))
2930
}
31+
bridge()
3032
wiremock {
3133
WireMockSystemOptions(
3234
port = 9099,
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package com.trendyol.stove.testing.e2e
2+
3+
import arrow.core.getOrElse
4+
import com.trendyol.stove.testing.e2e.system.TestSystem
5+
import com.trendyol.stove.testing.e2e.system.ValidationDsl
6+
import com.trendyol.stove.testing.e2e.system.WithDsl
7+
import com.trendyol.stove.testing.e2e.system.abstractions.AfterRunAwareWithContext
8+
import com.trendyol.stove.testing.e2e.system.abstractions.ExperimentalStoveDsl
9+
import com.trendyol.stove.testing.e2e.system.abstractions.PluggedSystem
10+
import com.trendyol.stove.testing.e2e.system.abstractions.SystemNotRegisteredException
11+
import org.springframework.beans.factory.getBean
12+
import org.springframework.context.ApplicationContext
13+
14+
/**
15+
* A system that provides a bridge between the test system and the application context.
16+
*
17+
* @property testSystem the test system to bridge.
18+
*/
19+
class BridgeSystem(override val testSystem: TestSystem) : PluggedSystem, AfterRunAwareWithContext<ApplicationContext> {
20+
21+
/**
22+
* The application context used to resolve dependencies.
23+
*/
24+
lateinit var ctx: ApplicationContext
25+
26+
/**
27+
* Closes the bridge system.
28+
*/
29+
override fun close(): Unit = Unit
30+
31+
/**
32+
* Initializes the bridge system after the test run.
33+
*
34+
* @param context the application context.
35+
*/
36+
override suspend fun afterRun(context: ApplicationContext) {
37+
ctx = context
38+
}
39+
40+
/**
41+
* Resolves a bean of the specified type from the application context.
42+
*
43+
* @param T the type of bean to resolve.
44+
* @return the resolved bean.
45+
*/
46+
@PublishedApi
47+
internal inline fun <reified T : Any> resolve(): T = ctx.getBean()
48+
49+
/**
50+
* Executes the specified validation function using the resolved bean.
51+
*
52+
* @param T the type of object being validated.
53+
* @param validation the validation function to apply to the object.
54+
*/
55+
inline fun <reified T : Any> using(validation: T.() -> Unit): Unit = validation(resolve())
56+
}
57+
58+
/**
59+
* Adds a bridge system to the test system and returns the modified test system.
60+
*
61+
* @receiver the test system to modify.
62+
* @return the modified test system.
63+
*/
64+
fun TestSystem.withBridgeSystem(): TestSystem = getOrRegister(BridgeSystem(this)).let { this }
65+
66+
/**
67+
* Returns the bridge system associated with the test system.
68+
*
69+
* @receiver the test system.
70+
* @return the bridge system.
71+
* @throws SystemNotRegisteredException if the bridge system is not registered.
72+
*/
73+
@ExperimentalStoveDsl
74+
fun WithDsl.bridge(): TestSystem = this.testSystem.withBridgeSystem()
75+
76+
/**
77+
* Returns the bridge system associated with the test system.
78+
* This function is only available in the validation DSL.
79+
*
80+
* @receiver the test system.
81+
* @return the bridge system.
82+
* @throws SystemNotRegisteredException if the bridge system is not registered.
83+
*/
84+
fun TestSystem.bridge(): BridgeSystem = getOrNone<BridgeSystem>().getOrElse { throw SystemNotRegisteredException(BridgeSystem::class) }
85+
86+
/**
87+
* Executes the specified validation function using the resolved bean from the bridge system.
88+
* Resolved beans are using physical components of the application.
89+
*
90+
* Suggested usage: validating or preparing the application state without accessing the physical components directly.
91+
* ```kotlin
92+
* TestSystem.validate {
93+
* using<PersonService> {
94+
* this.serviceName shouldBe "personService"
95+
* this.find(userId = 123) shouldBe Person(id = 123, name = "John Doe")
96+
* }
97+
* }
98+
* ```
99+
*
100+
* @receiver the validation DSL.
101+
* @param T the type of object being validated.
102+
* @param validation the validation function to apply to the object.
103+
*/
104+
inline fun <reified T : Any> ValidationDsl.using(validation: T.() -> Unit): Unit = this.testSystem.bridge().using(validation)

0 commit comments

Comments
 (0)