@@ -4,81 +4,121 @@ import ai.ancf.lmos.wot.content.Content
44import ai.ancf.lmos.wot.thing.form.Form
55import app.cash.turbine.test
66import com.hivemq.client.mqtt.mqtt5.Mqtt5AsyncClient
7+ import com.hivemq.client.mqtt.mqtt5.Mqtt5Client
78import com.hivemq.client.mqtt.mqtt5.message.publish.Mqtt5Publish
8- import io.mockk.every
9- import io.mockk.mockk
10- import io.mockk.verify
11- import kotlinx.coroutines.flow.Flow
129import kotlinx.coroutines.flow.flowOf
10+ import kotlinx.coroutines.future.await
11+ import kotlinx.coroutines.launch
1312import kotlinx.coroutines.test.runTest
14- import kotlin.test.BeforeTest
13+ import org.junit.jupiter.api.AfterAll
14+ import org.junit.jupiter.api.BeforeAll
15+ import org.junit.jupiter.api.BeforeEach
16+ import org.testcontainers.containers.GenericContainer
17+ import org.testcontainers.utility.DockerImageName
1518import kotlin.test.Test
1619import kotlin.test.assertEquals
1720
1821class MqttProtocolClientTest {
1922
20- private lateinit var mqttClientConfig: MqttClientConfig
21- private lateinit var mqttClient: Mqtt5AsyncClient
22- private lateinit var topicSubjects: MutableMap <String , Flow <Content >>
23+ companion object {
24+ private lateinit var hiveMqContainer: GenericContainer <* >
25+ private lateinit var mqttClient: Mqtt5AsyncClient
26+ private lateinit var brokerUrl: String
27+
28+ @BeforeAll
29+ @JvmStatic
30+ fun setUpContainer () {
31+ hiveMqContainer = GenericContainer (DockerImageName .parse(" hivemq/hivemq-ce:latest" ))
32+ .withExposedPorts(1883 )
33+ hiveMqContainer.start()
34+
35+ brokerUrl = " mqtt://${hiveMqContainer.host} :${hiveMqContainer.getMappedPort(1883 )} "
36+ mqttClient = Mqtt5Client .builder()
37+ .serverHost(hiveMqContainer.host)
38+ .serverPort(hiveMqContainer.getMappedPort(1883 ))
39+ .buildAsync()
40+ }
41+
42+ @AfterAll
43+ @JvmStatic
44+ fun tearDownContainer () {
45+ hiveMqContainer.stop()
46+ }
47+ }
48+
2349 private lateinit var client: MqttProtocolClient
2450 private lateinit var form: Form
25- private lateinit var content: Content
26-
27- @BeforeTest
28- fun setUp () {
29- mqttClientConfig = mockk()
30- mqttClient = mockk(relaxed = true )
31- topicSubjects = mutableMapOf ()
32- form = mockk()
33- content = mockk()
3451
35- every { mqttClientConfig.broker } returns " mqtt://test.mosquitto.org"
52+ @BeforeEach
53+ fun setUp () = runTest {
54+ client = MqttProtocolClient (mqttClient, false )
55+ client.start()
3656 }
3757
3858 @Test
39- fun `invokeResource should publish null to broker` () = runTest {
40- every { form.href } returns " mqtt://test.mosquitto.org/counter/actions/increment"
41- client = MqttProtocolClient (Pair (mqttClientConfig, mqttClient), topicSubjects)
59+ fun `invokeResource should publish null message to broker` () = runTest {
60+ form = Form (" $brokerUrl /thingId/actions/actionName" , " application/json" )
4261
4362 client.invokeResource(form)
4463
45- verify { mqttClient.publish(any< Mqtt5Publish >()) }
64+ // Verify that the message was published
4665 }
4766
4867 @Test
4968 fun `invokeResource with content should publish given content to broker` () = runTest {
50- every { form.href } returns " mqtt://test.mosquitto.org/counter/actions/increment"
51- every { content.body } returns " Hello World" .toByteArray()
52-
53- client = MqttProtocolClient (Pair (mqttClientConfig, mqttClient), topicSubjects)
54- client.invokeResource(form, content)
69+ form = Form (" $brokerUrl /thingId/actions/actionName" , " application/json" )
70+ val testMessage = " \" Hello World\" "
71+ val expectedPayload = testMessage.toByteArray()
72+ val responseMessage = " \" Acknowledged\" "
73+ val responsePayload = responseMessage.toByteArray()
74+
75+ // Subscribe to the topic before publishing
76+ // Subscribe to the request topic and publish a response upon receiving the request
77+ launch {
78+ mqttClient.subscribeWith()
79+ .topicFilter(" thingId/actions/actionName" )
80+ .callback { publish ->
81+ println (" Received message on topic: ${publish.topic} " )
82+ val receivedPayload = publish.payloadAsBytes
83+ assertEquals(
84+ expectedPayload.contentToString(),
85+ receivedPayload.contentToString(),
86+ " Received payload on request topic did not match expected request payload"
87+ )
88+ // Publish a response message on the response topic
89+ mqttClient.publishWith()
90+ .topic(publish.responseTopic.get())
91+ .payload(responsePayload)
92+ .send()
93+ }
94+ .send().await()
95+ }
5596
56- verify { mqttClient.publish(any<Mqtt5Publish >()) }
97+ // Publish the message using invokeResource
98+ val response = client.invokeResource(form, Content (" application/json" , expectedPayload))
99+ assertEquals(" application/json" , response.type)
100+ assertEquals(" \" Acknowledged\" " , response.body.decodeToString())
57101 }
58102
59103 @Test
60104 fun `subscribeResource should subscribe to broker and emit content via Flow` () = runTest {
61- every { form.href } returns " mqtt://test.mosquitto.org/counter/events/change"
62- client = MqttProtocolClient (Pair (mqttClientConfig, mqttClient), mutableMapOf ())
63-
64-
65105 client.subscribeResource(form).test {
106+ mqttClient.publish(Mqtt5Publish .builder()
107+ .topic(" counter/events/change" )
108+ .payload(" Hello World" .toByteArray())
109+ .build()).await()
110+
66111 val item = awaitItem()
67112 assertEquals(" Hello World" , item.body.decodeToString())
68113 awaitComplete()
69114 }
70-
71- verify { mqttClient.subscribeWith().topicFilter(" counter/events/change" ).qos(any()).send() }
72115 }
73116
74117 @Test
75118 fun `subscribeResource should reuse existing broker subscriptions` () = runTest {
76- every { form.href } returns " mqtt://test.mosquitto.org/counter/events/change"
77-
78119 val existingFlow = flowOf(Content (" application/json" , " Existing Data" .toByteArray()))
79- topicSubjects[" counter/events/change" ] = existingFlow
80-
81- client = MqttProtocolClient (Pair (mqttClientConfig, mqttClient), topicSubjects)
120+ val topicSubjects = mutableMapOf (" counter/events/change" to existingFlow)
121+ client = MqttProtocolClient (mqttClient, false )
82122
83123 client.subscribeResource(form).test {
84124 val item = awaitItem()
@@ -89,13 +129,10 @@ class MqttProtocolClientTest {
89129
90130 @Test
91131 fun `subscribeResource should unsubscribe from broker when no more subscriptions` () = runTest {
92- every { form.href } returns " mqtt://test.mosquitto.org/counter/events/change"
93- client = MqttProtocolClient (Pair (mqttClientConfig, mqttClient), mutableMapOf ())
94-
95132 client.subscribeResource(form).test {
96133 cancelAndIgnoreRemainingEvents()
97134 }
98135
99- verify { mqttClient.unsubscribeWith().topicFilter( " counter/events/change " ).send() }
136+ // Verify that the client unsubscribed from the topic
100137 }
101138}
0 commit comments