fix(processing): 接通 Redis 投影并修正验证缺口

This commit is contained in:
windyboy
2026-09-21 17:03:26 +08:00
parent 6980926a7c
commit 43bb561f47
17 changed files with 162 additions and 43 deletions
@@ -11,7 +11,7 @@ import java.sql.DriverManager
/**
* 在真实 PostgreSQL 上跑一遍迁移链,确认结果符合预期:`V1__flight_state_baseline.sql`
* 基线加 `V2__flight_chute_class_type_rename.sql` 更名、`V3__msg_event_hold.sql` 事件归属列
* 基线加 V2V6 增量迁移
* 依序执行成功,该建的表和 PIPELINE_LOCK 单行种子都在,回填事实落在基线里,`INBOX_CURSOR`、
* `BACKFILL_TODO`、`idx_evt_flid`、`PROC_STATE` 的处理开始时间列都不复存在;FLIGHT_CHUTE
* 的类字段列已由 V2 更名为 CCLS/CTYPSIS 口径)。
@@ -51,13 +51,17 @@ class FlywayMigrationTest {
while (rs.next()) {
records.add(Triple(rs.getString("version"), rs.getString("script"), rs.getBoolean("success")))
}
assertEquals(3, records.size, "迁移链应为 V1 基线 + V2 更名 + V3 事件归属三条")
assertEquals("1", records[0].first)
assertEquals("V1__flight_state_baseline.sql", records[0].second)
assertEquals("2", records[1].first)
assertEquals("V2__flight_chute_class_type_rename.sql", records[1].second)
assertEquals("3", records[2].first)
assertEquals("V3__msg_event_hold.sql", records[2].second)
assertEquals(
listOf(
"1" to "V1__flight_state_baseline.sql",
"2" to "V2__flight_chute_class_type_rename.sql",
"3" to "V3__msg_event_hold.sql",
"4" to "V4__req_track_outbound_seqn.sql",
"5" to "V5__unmapped_field_srvt_vipf.sql",
"6" to "V6__basicdata_ref_data.sql",
),
records.map { it.first to it.second },
)
assertTrue(records.all { it.third })
}
@@ -79,6 +79,26 @@ class JdbcFlightStateRoutePgTest {
assertEquals(4, loaded.collections["ERUT"]!!.size)
}
@Test
fun `CHDT persists and loads SIS class fields`() {
assumeTrue(PgTestSupport.canConnect(), PgTestSupport.skipMessage())
val repo = JdbcFlightStateRepository(dataSource(), Clock.fixed(t0, ZoneOffset.UTC))
val flid = "CH-" + UUID.randomUUID().toString().take(8)
val chdt = mapOf("CHNO" to "1", "CHUT" to "C01", "CCLS" to "A", "CTYP" to "IN")
repo.persistFullState(
FlightSnapshot(
flid, LocalDate.of(2026, 9, 12), FlightState.ACTIVE, 1,
mapOf("SODT" to "12Sep261200"),
mapOf("CHDT" to listOf(chdt)),
),
msgId = 2,
now = t0,
)
assertEquals(listOf(chdt), repo.loadFullSnapshot(flid)!!.collections["CHDT"])
}
private fun dataSource(): javax.sql.DataSource {
Flyway.configure()
.dataSource(PgTestSupport.jdbcUrl, PgTestSupport.user, PgTestSupport.password)
@@ -0,0 +1,40 @@
package com.gzzn.omms.msgexchange.infra.projection
import com.gzzn.omms.msgexchange.config.RedisProps
import com.gzzn.omms.msgexchange.processing.FlightProjectionWrite
import com.gzzn.omms.msgexchange.support.RedisTestSupport
import io.lettuce.core.RedisClient
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Assumptions.assumeTrue
import org.junit.jupiter.api.Test
import java.util.UUID
class RedisFlightProjectionPortTest {
@Test
fun `writes reads and deletes flight hash in real Valkey`() {
assumeTrue(RedisTestSupport.canConnect(), RedisTestSupport.skipMessage())
val client = RedisClient.create(RedisTestSupport.uri)
client.connect().use { connection ->
val key = "flightInfo:test:${UUID.randomUUID()}"
val port = RedisFlightProjectionPort(RedisProps().apply { flightKey = key }, connection)
try {
port.write(
listOf(
FlightProjectionWrite.Upsert("F1", 1, """{"flid":"F1","stateVersion":1}"""),
FlightProjectionWrite.Upsert("F2", 1, """{"flid":"F2","stateVersion":1}"""),
),
)
assertEquals(setOf("F1", "F2"), port.readAll().map { Regex("F\\d").find(it)!!.value }.toSet())
port.write(listOf(FlightProjectionWrite.Delete("F1", 2)))
assertEquals(listOf("""{"flid":"F2","stateVersion":1}"""), port.readAll())
assertTrue(port.ping())
} finally {
connection.sync().del(key)
}
}
client.shutdown()
}
}
@@ -0,0 +1,31 @@
package com.gzzn.omms.msgexchange.support
import org.testcontainers.containers.GenericContainer
private class ValkeyContainer(image: String) : GenericContainer<ValkeyContainer>(image)
/** Real Valkey endpoint for projection adapter tests; skips when neither env nor Docker is available. */
object RedisTestSupport {
private val envUri = System.getenv("MSGX_REDIS_URI")
private val container: ValkeyContainer? by lazy {
if (envUri != null) null else runCatching {
ValkeyContainer("valkey/valkey:8-alpine").withExposedPorts(6379).also { it.start() }
}.getOrNull()
}
val uri: String
get() = envUri ?: container?.let { "redis://${it.host}:${it.getMappedPort(6379)}" }
?: "redis://127.0.0.1:6379"
fun canConnect(): Boolean = runCatching {
val client = io.lettuce.core.RedisClient.create(uri)
try {
client.connect().use { it.sync().ping() }
} finally {
client.shutdown()
}
}.isSuccess
fun skipMessage(): String = "Valkey not accessible (set MSGX_REDIS_URI or enable Docker for Testcontainers)"
}