feat(ingress): 实现 JDBC 信箱轮询、入站持久化与对拍测试 (U05)
This commit is contained in:
+15
-15
@@ -1,15 +1,15 @@
|
||||
package com.gzzn.omms.msgexchange.nextgen
|
||||
package com.gzzn.omms.msgexchange
|
||||
|
||||
import com.gzzn.omms.msgexchange.nextgen.delivery.Dispatcher
|
||||
import com.gzzn.omms.msgexchange.nextgen.domain.ErrorClass
|
||||
import com.gzzn.omms.msgexchange.nextgen.domain.MsgEvent
|
||||
import com.gzzn.omms.msgexchange.nextgen.domain.ProcStatus
|
||||
import com.gzzn.omms.msgexchange.nextgen.domain.Targets
|
||||
import com.gzzn.omms.msgexchange.nextgen.infra.stub.StubDeliveryPort
|
||||
import com.gzzn.omms.msgexchange.nextgen.infra.stub.StubMsgEvents
|
||||
import com.gzzn.omms.msgexchange.nextgen.infra.stub.StubProcState
|
||||
import com.gzzn.omms.msgexchange.nextgen.ingress.InboxController
|
||||
import com.gzzn.omms.msgexchange.nextgen.processing.Pump
|
||||
import com.gzzn.omms.msgexchange.delivery.Dispatcher
|
||||
import com.gzzn.omms.msgexchange.domain.ErrorClass
|
||||
import com.gzzn.omms.msgexchange.domain.MsgEvent
|
||||
import com.gzzn.omms.msgexchange.domain.ProcStatus
|
||||
import com.gzzn.omms.msgexchange.domain.Targets
|
||||
import com.gzzn.omms.msgexchange.infra.stub.StubDeliveryPort
|
||||
import com.gzzn.omms.msgexchange.infra.stub.StubMsgEvents
|
||||
import com.gzzn.omms.msgexchange.infra.stub.StubProcState
|
||||
import com.gzzn.omms.msgexchange.ingress.InboxController
|
||||
import com.gzzn.omms.msgexchange.processing.Pump
|
||||
import io.micronaut.context.ApplicationContext
|
||||
import io.micronaut.test.extensions.junit5.annotation.MicronautTest
|
||||
import jakarta.inject.Inject
|
||||
@@ -22,7 +22,7 @@ import org.junit.jupiter.api.Test
|
||||
* U07 端到端(stub 装配,等价 /beans 核验):无 MySQL/Redis/Kafka 下,
|
||||
* ①核心 bean 装配齐全;②compat HTTP 写→PG 入队→主泵领取→解码未实装→FAILED(CODEC_ERROR)+退避;
|
||||
* ③U11 重放把 FAILED 拉回 PENDING;④Dispatcher flushSchd 聚合发出 schd。
|
||||
* (生产主路径 JDBC 轮询 InboxPoller 属 U05,本测未覆盖。)
|
||||
* (生产主路径 JDBC 轮询 InboxPoller;compat HTTP 写路径见 InboxService)。
|
||||
* 后台循环关闭(autostart=false),按需手动 tick,避免测试泄漏线程。
|
||||
*/
|
||||
@MicronautTest
|
||||
@@ -44,14 +44,14 @@ class PipelineSmokeTest {
|
||||
@org.junit.jupiter.api.BeforeEach
|
||||
fun cleanStubs() {
|
||||
ctx.getBean(StubProcState::class.java).clear()
|
||||
ctx.getBean(com.gzzn.omms.msgexchange.nextgen.infra.stub.StubInbox::class.java).clear()
|
||||
ctx.getBean(com.gzzn.omms.msgexchange.infra.stub.StubInbox::class.java).clear()
|
||||
ctx.getBean(StubMsgEvents::class.java).clear()
|
||||
ctx.getBean(StubDeliveryPort::class.java).clear()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `core pipeline beans are wired under stubs`() {
|
||||
assertNotNull(ctx.getBean(com.gzzn.omms.msgexchange.nextgen.config.PipelineProps::class.java))
|
||||
assertNotNull(ctx.getBean(com.gzzn.omms.msgexchange.config.PipelineProps::class.java))
|
||||
assertTrue(ctx.getBeansOfType(Pump::class.java).isNotEmpty())
|
||||
assertTrue(ctx.getBeansOfType(Dispatcher::class.java).isNotEmpty())
|
||||
assertNotNull(controller)
|
||||
@@ -82,7 +82,7 @@ class PipelineSmokeTest {
|
||||
val stub = ctx.getBean(StubProcState::class.java)
|
||||
assertEquals(ProcStatus.FAILED, stub.snapshotOf(id)!!.state)
|
||||
|
||||
val n = ctx.getBean(com.gzzn.omms.msgexchange.nextgen.infra.retry.ReplayService::class.java)
|
||||
val n = ctx.getBean(com.gzzn.omms.msgexchange.infra.retry.ReplayService::class.java)
|
||||
.replay(listOf(ErrorClass.CODEC_ERROR))
|
||||
|
||||
assertEquals(1, n)
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.gzzn.omms.msgexchange.nextgen
|
||||
package com.gzzn.omms.msgexchange
|
||||
|
||||
import java.time.Clock
|
||||
import java.time.Instant
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.gzzn.omms.msgexchange.nextgen.config
|
||||
package com.gzzn.omms.msgexchange.config
|
||||
|
||||
import io.micronaut.test.extensions.junit5.annotation.MicronautTest
|
||||
import jakarta.inject.Inject
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.gzzn.omms.msgexchange.nextgen.config
|
||||
package com.gzzn.omms.msgexchange.config
|
||||
|
||||
import io.micronaut.test.extensions.junit5.annotation.MicronautTest
|
||||
import io.micronaut.test.support.TestPropertyProvider
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.gzzn.omms.msgexchange.nextgen.config
|
||||
package com.gzzn.omms.msgexchange.config
|
||||
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
+9
-9
@@ -1,13 +1,13 @@
|
||||
package com.gzzn.omms.msgexchange.nextgen.delivery
|
||||
package com.gzzn.omms.msgexchange.delivery
|
||||
|
||||
import com.gzzn.omms.msgexchange.nextgen.MutableClock
|
||||
import com.gzzn.omms.msgexchange.nextgen.config.PipelineProps
|
||||
import com.gzzn.omms.msgexchange.nextgen.domain.ErrorClass
|
||||
import com.gzzn.omms.msgexchange.nextgen.domain.EventStatus
|
||||
import com.gzzn.omms.msgexchange.nextgen.domain.MsgEvent
|
||||
import com.gzzn.omms.msgexchange.nextgen.domain.Targets
|
||||
import com.gzzn.omms.msgexchange.nextgen.infra.persistence.MsgEventRepository
|
||||
import com.gzzn.omms.msgexchange.nextgen.infra.retry.FailureScheduler
|
||||
import com.gzzn.omms.msgexchange.MutableClock
|
||||
import com.gzzn.omms.msgexchange.config.PipelineProps
|
||||
import com.gzzn.omms.msgexchange.domain.ErrorClass
|
||||
import com.gzzn.omms.msgexchange.domain.EventStatus
|
||||
import com.gzzn.omms.msgexchange.domain.MsgEvent
|
||||
import com.gzzn.omms.msgexchange.domain.Targets
|
||||
import com.gzzn.omms.msgexchange.infra.persistence.MsgEventRepository
|
||||
import com.gzzn.omms.msgexchange.infra.retry.FailureScheduler
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertNotNull
|
||||
import org.junit.jupiter.api.Assertions.assertNull
|
||||
+4
-4
@@ -1,8 +1,8 @@
|
||||
package com.gzzn.omms.msgexchange.nextgen.delivery
|
||||
package com.gzzn.omms.msgexchange.delivery
|
||||
|
||||
import com.gzzn.omms.msgexchange.nextgen.domain.EventStatus
|
||||
import com.gzzn.omms.msgexchange.nextgen.domain.MsgEvent
|
||||
import com.gzzn.omms.msgexchange.nextgen.domain.Targets
|
||||
import com.gzzn.omms.msgexchange.domain.EventStatus
|
||||
import com.gzzn.omms.msgexchange.domain.MsgEvent
|
||||
import com.gzzn.omms.msgexchange.domain.Targets
|
||||
import org.junit.jupiter.api.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
+4
-4
@@ -1,8 +1,8 @@
|
||||
package com.gzzn.omms.msgexchange.nextgen.infra.health
|
||||
package com.gzzn.omms.msgexchange.infra.health
|
||||
|
||||
import com.gzzn.omms.msgexchange.nextgen.delivery.DeliveryPort
|
||||
import com.gzzn.omms.msgexchange.nextgen.infra.redis.FlightRedisClient
|
||||
import com.gzzn.omms.msgexchange.nextgen.infra.redis.RedisScript
|
||||
import com.gzzn.omms.msgexchange.delivery.DeliveryPort
|
||||
import com.gzzn.omms.msgexchange.infra.redis.FlightRedisClient
|
||||
import com.gzzn.omms.msgexchange.infra.redis.RedisScript
|
||||
import io.micronaut.health.HealthStatus
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
+6
-5
@@ -1,9 +1,9 @@
|
||||
package com.gzzn.omms.msgexchange.nextgen.infra.retry
|
||||
package com.gzzn.omms.msgexchange.infra.retry
|
||||
|
||||
import com.gzzn.omms.msgexchange.nextgen.domain.ErrorClass
|
||||
import com.gzzn.omms.msgexchange.nextgen.domain.ProcState
|
||||
import com.gzzn.omms.msgexchange.nextgen.domain.ProcStatus
|
||||
import com.gzzn.omms.msgexchange.nextgen.infra.persistence.ProcStateRepository
|
||||
import com.gzzn.omms.msgexchange.domain.ErrorClass
|
||||
import com.gzzn.omms.msgexchange.domain.ProcState
|
||||
import com.gzzn.omms.msgexchange.domain.ProcStatus
|
||||
import com.gzzn.omms.msgexchange.infra.persistence.ProcStateRepository
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertNull
|
||||
import org.junit.jupiter.api.Test
|
||||
@@ -24,6 +24,7 @@ class ReplayServiceTest {
|
||||
}
|
||||
|
||||
override fun insert(cminmsgsId: Long, state: ProcStatus) = Unit
|
||||
override fun exists(cminmsgsId: Long): Boolean = rows.containsKey(cminmsgsId)
|
||||
override fun headUnfinished(): ProcState? = null
|
||||
override fun tryBindIdentity(cminmsgsId: Long, identityKey: String): Boolean = true
|
||||
override fun ownerOfIdentity(identityKey: String): Long? = null
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.gzzn.omms.msgexchange.ingress
|
||||
|
||||
import com.gzzn.omms.msgexchange.domain.ProcStatus
|
||||
import com.gzzn.omms.msgexchange.infra.stub.StubInbox
|
||||
import com.gzzn.omms.msgexchange.infra.stub.StubProcState
|
||||
import com.gzzn.omms.msgexchange.processing.Pump
|
||||
import io.micronaut.test.extensions.junit5.annotation.MicronautTest
|
||||
import jakarta.inject.Inject
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertNotNull
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
/** 主路径:JDBC 轮询语义(stub 下 InboxPoller + 外部写信箱模拟)。 */
|
||||
@MicronautTest
|
||||
class InboxPollerTest {
|
||||
|
||||
@Inject lateinit var poller: InboxPoller
|
||||
@Inject lateinit var pump: Pump
|
||||
@Inject lateinit var stubInbox: StubInbox
|
||||
@Inject lateinit var stubProc: StubProcState
|
||||
|
||||
@BeforeEach
|
||||
fun clean() {
|
||||
stubInbox.clear()
|
||||
stubProc.clear()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `poller enqueues externally written mailbox rows`() {
|
||||
val id = stubInbox.simulateExternalWrite("<MSG/>")
|
||||
assertEquals(1, poller.pollOnce())
|
||||
assertNotNull(stubProc.snapshotOf(id))
|
||||
assertEquals(ProcStatus.PENDING, stubProc.snapshotOf(id)!!.state)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `poller is idempotent for already enqueued rows`() {
|
||||
val id = stubInbox.simulateExternalWrite("<MSG/>")
|
||||
poller.pollOnce()
|
||||
assertEquals(0, poller.pollOnce())
|
||||
pump.tick()
|
||||
assertEquals(ProcStatus.FAILED, stubProc.snapshotOf(id)!!.state)
|
||||
}
|
||||
}
|
||||
+5
-5
@@ -1,9 +1,9 @@
|
||||
package com.gzzn.omms.msgexchange.nextgen.processing
|
||||
package com.gzzn.omms.msgexchange.processing
|
||||
|
||||
import com.gzzn.omms.msgexchange.nextgen.config.PipelineProps
|
||||
import com.gzzn.omms.msgexchange.nextgen.domain.DecodedMessage
|
||||
import com.gzzn.omms.msgexchange.nextgen.domain.MetaFields
|
||||
import com.gzzn.omms.msgexchange.nextgen.domain.MsgKind
|
||||
import com.gzzn.omms.msgexchange.config.PipelineProps
|
||||
import com.gzzn.omms.msgexchange.domain.DecodedMessage
|
||||
import com.gzzn.omms.msgexchange.domain.MetaFields
|
||||
import com.gzzn.omms.msgexchange.domain.MsgKind
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.time.LocalDate
|
||||
import kotlin.test.assertEquals
|
||||
+33
-29
@@ -1,33 +1,33 @@
|
||||
package com.gzzn.omms.msgexchange.nextgen.processing
|
||||
package com.gzzn.omms.msgexchange.processing
|
||||
|
||||
import com.gzzn.omms.msgexchange.nextgen.MutableClock
|
||||
import com.gzzn.omms.msgexchange.nextgen.codec.DecodeFailure
|
||||
import com.gzzn.omms.msgexchange.nextgen.codec.DecodeResult
|
||||
import com.gzzn.omms.msgexchange.nextgen.codec.XmlCodec
|
||||
import com.gzzn.omms.msgexchange.nextgen.config.PipelineProps
|
||||
import com.gzzn.omms.msgexchange.nextgen.domain.DecodedMessage
|
||||
import com.gzzn.omms.msgexchange.nextgen.domain.Decision
|
||||
import com.gzzn.omms.msgexchange.nextgen.domain.ErrorClass
|
||||
import com.gzzn.omms.msgexchange.nextgen.domain.EventStatus
|
||||
import com.gzzn.omms.msgexchange.nextgen.domain.MsgEvent
|
||||
import com.gzzn.omms.msgexchange.nextgen.domain.MsgKind
|
||||
import com.gzzn.omms.msgexchange.nextgen.domain.MetaFields
|
||||
import com.gzzn.omms.msgexchange.nextgen.domain.NotifyPayload
|
||||
import com.gzzn.omms.msgexchange.nextgen.domain.ProcState
|
||||
import com.gzzn.omms.msgexchange.nextgen.domain.ProcStatus
|
||||
import com.gzzn.omms.msgexchange.nextgen.domain.SchdPush
|
||||
import com.gzzn.omms.msgexchange.nextgen.domain.Targets
|
||||
import com.gzzn.omms.msgexchange.nextgen.infra.persistence.CminmsgInboxRepository
|
||||
import com.gzzn.omms.msgexchange.nextgen.infra.persistence.MsgEventRepository
|
||||
import com.gzzn.omms.msgexchange.nextgen.infra.persistence.ProcStateRepository
|
||||
import com.gzzn.omms.msgexchange.nextgen.infra.persistence.PumpJobRepository
|
||||
import com.gzzn.omms.msgexchange.nextgen.infra.persistence.RefDataRepository
|
||||
import com.gzzn.omms.msgexchange.nextgen.infra.persistence.ReqTrackRepository
|
||||
import com.gzzn.omms.msgexchange.nextgen.infra.persistence.FlightStateRepository
|
||||
import com.gzzn.omms.msgexchange.nextgen.infra.redis.FlightRedisClient
|
||||
import com.gzzn.omms.msgexchange.nextgen.infra.redis.RedisScript
|
||||
import com.gzzn.omms.msgexchange.nextgen.infra.retry.FailureScheduler
|
||||
import com.gzzn.omms.msgexchange.nextgen.infra.retry.ProcFailure
|
||||
import com.gzzn.omms.msgexchange.MutableClock
|
||||
import com.gzzn.omms.msgexchange.codec.DecodeFailure
|
||||
import com.gzzn.omms.msgexchange.codec.DecodeResult
|
||||
import com.gzzn.omms.msgexchange.codec.XmlCodec
|
||||
import com.gzzn.omms.msgexchange.config.PipelineProps
|
||||
import com.gzzn.omms.msgexchange.domain.DecodedMessage
|
||||
import com.gzzn.omms.msgexchange.domain.Decision
|
||||
import com.gzzn.omms.msgexchange.domain.ErrorClass
|
||||
import com.gzzn.omms.msgexchange.domain.EventStatus
|
||||
import com.gzzn.omms.msgexchange.domain.MsgEvent
|
||||
import com.gzzn.omms.msgexchange.domain.MsgKind
|
||||
import com.gzzn.omms.msgexchange.domain.MetaFields
|
||||
import com.gzzn.omms.msgexchange.domain.NotifyPayload
|
||||
import com.gzzn.omms.msgexchange.domain.ProcState
|
||||
import com.gzzn.omms.msgexchange.domain.ProcStatus
|
||||
import com.gzzn.omms.msgexchange.domain.SchdPush
|
||||
import com.gzzn.omms.msgexchange.domain.Targets
|
||||
import com.gzzn.omms.msgexchange.infra.persistence.CminmsgInboxRepository
|
||||
import com.gzzn.omms.msgexchange.infra.persistence.MsgEventRepository
|
||||
import com.gzzn.omms.msgexchange.infra.persistence.ProcStateRepository
|
||||
import com.gzzn.omms.msgexchange.infra.persistence.PumpJobRepository
|
||||
import com.gzzn.omms.msgexchange.infra.persistence.RefDataRepository
|
||||
import com.gzzn.omms.msgexchange.infra.persistence.ReqTrackRepository
|
||||
import com.gzzn.omms.msgexchange.infra.persistence.FlightStateRepository
|
||||
import com.gzzn.omms.msgexchange.infra.redis.FlightRedisClient
|
||||
import com.gzzn.omms.msgexchange.infra.redis.RedisScript
|
||||
import com.gzzn.omms.msgexchange.infra.retry.FailureScheduler
|
||||
import com.gzzn.omms.msgexchange.infra.retry.ProcFailure
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertNotNull
|
||||
import org.junit.jupiter.api.Assertions.assertNull
|
||||
@@ -50,6 +50,8 @@ class MessageProcessorTest {
|
||||
|
||||
override fun insert(cminmsgsId: Long, state: ProcStatus) { record[cminmsgsId] = ProcState(cminmsgsId, state) }
|
||||
|
||||
override fun exists(cminmsgsId: Long): Boolean = record.containsKey(cminmsgsId)
|
||||
|
||||
override fun headUnfinished(): ProcState? = null
|
||||
|
||||
override fun tryBindIdentity(cminmsgsId: Long, identityKey: String): Boolean {
|
||||
@@ -99,6 +101,8 @@ class MessageProcessorTest {
|
||||
|
||||
override fun insertRaw(rawXml: String): Long = 0
|
||||
|
||||
override fun pollUnprocessed(afterId: Long, limit: Int): List<Long> = emptyList()
|
||||
|
||||
override fun rawOf(cminmsgsId: Long): String? {
|
||||
throwOnRawOf?.let { throw it }
|
||||
return raws[cminmsgsId]
|
||||
Reference in New Issue
Block a user