feat(persistence)!: V1.4.0 退场 legacy 槽位存储,persistNextStates 成为唯一写路径 (ACM2-29 P4)
- V1.4.0 迁移:DROP FLIGHT_SCHD 全部 77 个槽位/里程碑/紧凑航路列(V1.3.0 起 零写入、P2-2 起零读取)+ 三个与 PK 前缀冗余的 (flid) 单列索引;回退窗口关闭 (对拍留档:P2-1 FS7 v2 双读差异记录) - 接口收敛:FlightSchdRepository 删除 upsertSnapshotBatch/upsertIncremental, 删除废弃 FlightStateRepository 及 Jdbc/Stub 适配器;仓储与装配器删除全部 legacy flatten/slot/milestone/route 视图代码,ALL_COLUMNS 收敛为无损承载列 - FS7 对拍工具移除 diffDualRead(legacy 槽位基准退场);diff() 对 legacy FLTR JSON 比对保留;FlightStoreDualReadTest 随工具移除 - 测试种子统一走 v2 写路径(support/SeedHelpers),GuardedFlightSchd 简化 - docs/decision-flight-state.md:头部复核入口更新 + 新增 §10 v2 落地终态索引, §1–§9 保留为历史记录 验证:MSGX_PG_PORT=5433 真实 PG 全量 100 用例 0 失败 0 跳过;无环境变量 Testcontainers 全新容器 V1.0.0→V1.4.0 迁移链 18/18 全绿
This commit is contained in:
-15
@@ -231,7 +231,6 @@ class FlightSchdJdbcPgTest {
|
||||
)
|
||||
assertNull(repo.findByFlid(flid)!!["GTDT"])
|
||||
assertEquals(0, ds.query("SELECT COUNT(*) FROM flight_gate WHERE flid = ?", { ps -> ps.setString(1, flid) }) { rs -> rs.getInt(1) }.first())
|
||||
assertLegacyCollectionColumnsAllNull(flid)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -533,7 +532,6 @@ class FlightSchdJdbcPgTest {
|
||||
)
|
||||
|
||||
repo.persistNextStates(day, listOf(state), snapshotReplace = true)
|
||||
assertLegacyCollectionColumnsAllNull(flid)
|
||||
|
||||
val roundtrip = repo.findNextStateByFlid(flid)!!
|
||||
assertEquals(1L, roundtrip.stateVersion)
|
||||
@@ -585,7 +583,6 @@ class FlightSchdJdbcPgTest {
|
||||
)
|
||||
|
||||
repo.persistNextStates(day, listOf(state), snapshotReplace = true)
|
||||
assertLegacyCollectionColumnsAllNull(flid)
|
||||
assertEquals("MU123", repo.findByFlid(flid)!!["FLNO"])
|
||||
assertEquals(1, ds.query("SELECT COUNT(*) FROM flight_gate WHERE flid = ?", { ps -> ps.setString(1, flid) }) { rs -> rs.getInt(1) }.first())
|
||||
assertEquals(1, ds.query("SELECT COUNT(*) FROM flight_delay WHERE flid = ?", { ps -> ps.setString(1, flid) }) { rs -> rs.getInt(1) }.first())
|
||||
@@ -794,16 +791,4 @@ class FlightSchdJdbcPgTest {
|
||||
if (e != null && a != null) assertEquals(e, a, path) else assertEquals(expected, actual, path)
|
||||
}
|
||||
|
||||
private fun assertLegacyCollectionColumnsAllNull(flid: String) {
|
||||
val columnList = com.gzzn.omms.msgexchange.support.LEGACY_COLLECTION_STORAGE_COLUMNS.joinToString(", ")
|
||||
ds.queryOne(
|
||||
"SELECT $columnList FROM flight_schd WHERE flid = ?",
|
||||
{ ps -> ps.setString(1, flid) },
|
||||
) { rs ->
|
||||
com.gzzn.omms.msgexchange.support.LEGACY_COLLECTION_STORAGE_COLUMNS.forEach { column ->
|
||||
assertNull(rs.getString(column), "legacy column $column must be null for v2 persistNextStates (flid=$flid)")
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.gzzn.omms.msgexchange.jobs
|
||||
|
||||
import com.gzzn.omms.msgexchange.infra.persistence.FlightSchdRepository
|
||||
import com.gzzn.omms.msgexchange.infra.stub.StubFlightSchd
|
||||
import com.gzzn.omms.msgexchange.support.seedSnapshot
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertNotNull
|
||||
@@ -44,7 +45,7 @@ class HistorySweepJobTest {
|
||||
"F2" to mapOf("FLID" to "F2"),
|
||||
"F3" to mapOf("FLID" to "F3"),
|
||||
)
|
||||
flightSchd.upsertSnapshotBatch("2026-09-01", flights)
|
||||
seedSnapshot(flightSchd, "2026-09-01", flights)
|
||||
flightSchd.putGenIfVersion("2026-09-01", 0L, FlightSchdRepository.GenMeta("2026-09-01", 1L, setOf("F1", "F2", "F3")))
|
||||
|
||||
// 模拟判史:F1, F2, F3 全部为历史候选
|
||||
@@ -72,7 +73,7 @@ class HistorySweepJobTest {
|
||||
"F2" to mapOf("FLID" to "F2"),
|
||||
"F3" to mapOf("FLID" to "F3"),
|
||||
)
|
||||
flightSchd.upsertSnapshotBatch("2026-09-01", flights)
|
||||
seedSnapshot(flightSchd, "2026-09-01", flights)
|
||||
|
||||
HistorySweepJob.historyPicker = { it }
|
||||
// 模拟 ES 写入:F1 和 F2 成功,F3 失败
|
||||
@@ -95,7 +96,7 @@ class HistorySweepJobTest {
|
||||
"F1" to mapOf("FLID" to "F1"),
|
||||
"F2" to mapOf("FLID" to "F2"),
|
||||
)
|
||||
flightSchd.upsertSnapshotBatch("2026-09-01", flights)
|
||||
seedSnapshot(flightSchd, "2026-09-01", flights)
|
||||
|
||||
HistorySweepJob.historyPicker = { it }
|
||||
// 模拟 ES 全部写入失败
|
||||
@@ -114,7 +115,7 @@ class HistorySweepJobTest {
|
||||
val flights = listOf(
|
||||
"F1" to mapOf("FLID" to "F1"),
|
||||
)
|
||||
flightSchd.upsertSnapshotBatch("2026-09-01", flights)
|
||||
seedSnapshot(flightSchd, "2026-09-01", flights)
|
||||
|
||||
// 第一次运行模拟:ES 写入成功,但在调用 deleteByFlids 前崩溃(PG 依然保留 F1)
|
||||
val esUpserted = mutableSetOf<String>()
|
||||
@@ -151,7 +152,7 @@ class HistorySweepJobTest {
|
||||
@Test
|
||||
fun `Scenario 5 - Replaying deleteByFlids is completely idempotent with no side effects`() {
|
||||
val flights = listOf("F1" to mapOf("FLID" to "F1"))
|
||||
flightSchd.upsertSnapshotBatch("2026-09-01", flights)
|
||||
seedSnapshot(flightSchd, "2026-09-01", flights)
|
||||
|
||||
// 首次删除
|
||||
val deletedCount1 = flightSchd.deleteByFlids(setOf("F1"))
|
||||
|
||||
@@ -29,6 +29,8 @@ import com.gzzn.omms.msgexchange.infra.stub.StubMsgEvents
|
||||
import com.gzzn.omms.msgexchange.infra.stub.StubPipelineTransactionManager
|
||||
import com.gzzn.omms.msgexchange.infra.stub.StubProcState
|
||||
import com.gzzn.omms.msgexchange.infra.stub.StubReqTrack
|
||||
import com.gzzn.omms.msgexchange.support.seedIncremental
|
||||
import com.gzzn.omms.msgexchange.support.seedSnapshot
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertFalse
|
||||
@@ -269,10 +271,10 @@ class FlightSchdInvariantTest {
|
||||
|
||||
// 1. 增量更新写入一条临时加飞航班 ADFT(FDAY 为 NULL)
|
||||
val adftChange = FlightChange(flid = "ADFT_888", fields = mapOf("FLID" to "ADFT_888", "FLTY" to "ADFT"))
|
||||
flightSchd.upsertIncremental(listOf(adftChange))
|
||||
seedIncremental(flightSchd, listOf(adftChange))
|
||||
|
||||
// 2. 写入旧代的一条定期计划航班 REG_OLD(FDAY 为 2026-09-07)
|
||||
flightSchd.upsertSnapshotBatch(day, listOf("REG_OLD" to mapOf("FLID" to "REG_OLD", "FLTY" to "REG")))
|
||||
seedSnapshot(flightSchd, day, listOf("REG_OLD" to mapOf("FLID" to "REG_OLD", "FLTY" to "REG")))
|
||||
flightSchd.putGenIfVersion(day, 0L, FlightSchdRepository.GenMeta(day, 1L, setOf("REG_OLD")))
|
||||
|
||||
// 3. 执行下一轮快照 DNLD,新代仅包含 REG_NEW(REG_OLD 不在新代中,属于待删差集;ADFT 也不在新代中)
|
||||
@@ -308,11 +310,11 @@ class FlightSchdInvariantTest {
|
||||
val dayNew = "2026-09-08"
|
||||
|
||||
// 初始属旧代
|
||||
flightSchd.upsertSnapshotBatch(dayOld, listOf("FL_MIG" to mapOf("FLID" to "FL_MIG", "REMC" to "07")))
|
||||
seedSnapshot(flightSchd, dayOld, listOf("FL_MIG" to mapOf("FLID" to "FL_MIG", "REMC" to "07")))
|
||||
flightSchd.putGenIfVersion(dayOld, 0L, FlightSchdRepository.GenMeta(dayOld, 1L, setOf("FL_MIG")))
|
||||
|
||||
// 随后 09-08 快照写入将 FDAY 更新为 2026-09-08
|
||||
flightSchd.upsertSnapshotBatch(dayNew, listOf("FL_MIG" to mapOf("FLID" to "FL_MIG", "REMC" to "08")))
|
||||
seedSnapshot(flightSchd, dayNew, listOf("FL_MIG" to mapOf("FLID" to "FL_MIG", "REMC" to "08")))
|
||||
|
||||
// 此时 09-07 再次执行差删(差集中包含 FL_MIG)
|
||||
val deleted = flightSchd.deleteDiffByDay(dayOld, listOf("FL_MIG"))
|
||||
@@ -334,7 +336,7 @@ class FlightSchdInvariantTest {
|
||||
TimeZone.setDefault(TimeZone.getTimeZone(ZoneId.of("Asia/Tokyo")))
|
||||
|
||||
val now = Instant.parse("2026-09-07T08:00:00.123456Z")
|
||||
flightSchd.upsertSnapshotBatch("2026-09-07", listOf("TZ_01" to mapOf("FLID" to "TZ_01")), now)
|
||||
seedSnapshot(flightSchd, "2026-09-07", listOf("TZ_01" to mapOf("FLID" to "TZ_01")), now)
|
||||
flightSchd.putGenIfVersion("2026-09-07", 0L, FlightSchdRepository.GenMeta("2026-09-07", 1L, setOf("TZ_01")), now)
|
||||
|
||||
val gen = flightSchd.getGen("2026-09-07")
|
||||
|
||||
@@ -156,13 +156,6 @@ class MessageProcessorTest {
|
||||
val gens = mutableMapOf<String, FlightSchdRepository.GenMeta>()
|
||||
val incrementalChanges = mutableListOf<com.gzzn.omms.msgexchange.domain.FlightChange>()
|
||||
|
||||
override fun upsertSnapshotBatch(day: String, flights: List<Pair<String, FlightFields>>, now: Instant) {
|
||||
this.flights.putAll(flights)
|
||||
}
|
||||
override fun upsertIncremental(changes: List<com.gzzn.omms.msgexchange.domain.FlightChange>, now: Instant) {
|
||||
incrementalChanges.addAll(changes)
|
||||
changes.forEach { flights[it.flid] = it.fields }
|
||||
}
|
||||
override fun deleteDiffByDay(day: String, delFlids: Collection<String>): Int = 0
|
||||
override fun findByFlid(flid: String): FlightFields? = flights[flid]
|
||||
override fun findByFlids(flids: Collection<String>): Map<String, FlightFields> =
|
||||
|
||||
-4
@@ -70,8 +70,6 @@ class V2PipelineWritePathContractTest {
|
||||
|
||||
assertEquals(ProcStatus.SUCCEEDED, procState.snapshotOf(headId)!!.state)
|
||||
assertEquals(1, flightSchd.persistNextStatesCalls)
|
||||
assertEquals(0, flightSchd.legacySnapshotCalls)
|
||||
assertEquals(0, flightSchd.legacyIncrementalCalls)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -109,7 +107,5 @@ class V2PipelineWritePathContractTest {
|
||||
|
||||
assertEquals(ProcStatus.SUCCEEDED, procState.snapshotOf(headId)!!.state)
|
||||
assertEquals(1, flightSchd.persistNextStatesCalls)
|
||||
assertEquals(0, flightSchd.legacySnapshotCalls)
|
||||
assertEquals(0, flightSchd.legacyIncrementalCalls)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.gzzn.omms.msgexchange.support
|
||||
|
||||
import com.gzzn.omms.msgexchange.domain.FlightChange
|
||||
import com.gzzn.omms.msgexchange.domain.flight.FlightStateEngine
|
||||
import com.gzzn.omms.msgexchange.infra.persistence.FlightFields
|
||||
import com.gzzn.omms.msgexchange.infra.persistence.FlightSchdRepository
|
||||
import java.time.Instant
|
||||
|
||||
/** 测试种子助手:v2 写路径(persistNextStates)等价替代已退场的 legacy upsert(P4)。 */
|
||||
fun seedSnapshot(
|
||||
repo: FlightSchdRepository,
|
||||
day: String,
|
||||
flights: List<Pair<String, FlightFields>>,
|
||||
now: Instant = Instant.now(),
|
||||
) {
|
||||
repo.persistNextStates(
|
||||
day,
|
||||
flights.map { (flid, fields) ->
|
||||
FlightStateEngine.apply(
|
||||
null,
|
||||
FlightStateEngine.commandsFromFields(flid, fields, snapshotReplace = true),
|
||||
"seed-$day",
|
||||
bumpVersion = false,
|
||||
).copy(stateVersion = 1L)
|
||||
},
|
||||
snapshotReplace = true,
|
||||
now = now,
|
||||
)
|
||||
}
|
||||
|
||||
fun seedIncremental(repo: FlightSchdRepository, changes: List<FlightChange>, messageId: String = "seed-inc") {
|
||||
repo.persistNextStates(
|
||||
null,
|
||||
changes.map { change ->
|
||||
FlightStateEngine.apply(
|
||||
repo.findNextStateByFlid(change.flid),
|
||||
FlightStateEngine.commandsFromFields(change.flid, change.fields, snapshotReplace = false),
|
||||
messageId,
|
||||
bumpVersion = true,
|
||||
)
|
||||
},
|
||||
snapshotReplace = false,
|
||||
)
|
||||
}
|
||||
@@ -1,46 +1,17 @@
|
||||
package com.gzzn.omms.msgexchange.support
|
||||
|
||||
import com.gzzn.omms.msgexchange.domain.FlightChange
|
||||
import com.gzzn.omms.msgexchange.domain.flight.FlightNextState
|
||||
import com.gzzn.omms.msgexchange.infra.persistence.FlightFields
|
||||
import com.gzzn.omms.msgexchange.infra.persistence.FlightSchdRepository
|
||||
import com.gzzn.omms.msgexchange.infra.stub.StubFlightSchd
|
||||
import java.time.Instant
|
||||
|
||||
/** V1.2.0 宽表集合存储列;v2 persistNextStates 不得写入。 */
|
||||
val LEGACY_COLLECTION_STORAGE_COLUMNS: List<String> = listOf(
|
||||
"gate1", "pgot1", "pgct1", "gotm1", "gctm1", "gtyp1",
|
||||
"gate2", "pgot2", "pgct2", "gotm2", "gctm2", "gtyp2",
|
||||
"chkc1", "ccls1", "pcot1", "pcct1", "cotm1", "cctm1", "ctyp1",
|
||||
"chkc2", "ccls2", "pcot2", "pcct2", "cotm2", "cctm2", "ctyp2",
|
||||
"chkc3", "ccls3", "pcot3", "pcct3", "cotm3", "cctm3", "ctyp3",
|
||||
"belt1", "bcls1", "bpcot1", "bpcct1", "fbag1", "lbag1", "btyp1",
|
||||
"belt2", "bcls2", "bpcot2", "bpcct2", "fbag2", "lbag2", "btyp2",
|
||||
"psst1", "stst1", "stet1", "psst2", "stst2", "stet2",
|
||||
"chut1", "chcls1", "pcbt1", "pcet1", "cbtm1", "cetm1", "chtyp1",
|
||||
"chut2", "chcls2", "pcbt2", "pcet2", "cbtm2", "cetm2", "chtyp2",
|
||||
"dely_code", "dely_strt", "dely_dura", "dely_remc",
|
||||
"abtm_a", "abtm_d", "chot_on", "chot_off",
|
||||
"rout_path", "erut_path",
|
||||
)
|
||||
|
||||
/** v2 写路径守卫(P4 退场后):persistNextStates 是唯一写路径,legacy upsert 入口已从接口删除。 */
|
||||
class GuardedFlightSchd(
|
||||
private val inner: FlightSchdRepository = StubFlightSchd(),
|
||||
) : FlightSchdRepository {
|
||||
var legacySnapshotCalls = 0
|
||||
var legacyIncrementalCalls = 0
|
||||
var persistNextStatesCalls = 0
|
||||
|
||||
override fun upsertSnapshotBatch(day: String, flights: List<Pair<String, FlightFields>>, now: Instant) {
|
||||
legacySnapshotCalls++
|
||||
throw AssertionError("v2 path must not call upsertSnapshotBatch")
|
||||
}
|
||||
|
||||
override fun upsertIncremental(changes: List<FlightChange>, now: Instant) {
|
||||
legacyIncrementalCalls++
|
||||
throw AssertionError("v2 path must not call upsertIncremental")
|
||||
}
|
||||
|
||||
override fun persistNextStates(day: String?, states: List<FlightNextState>, snapshotReplace: Boolean, now: Instant) {
|
||||
persistNextStatesCalls++
|
||||
inner.persistNextStates(day, states, snapshotReplace, now)
|
||||
|
||||
@@ -1,156 +0,0 @@
|
||||
package com.gzzn.omms.msgexchange.tools
|
||||
|
||||
import com.gzzn.omms.msgexchange.infra.persistence.jdbc.FlightSchdReadAssembler
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertFalse
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
/**
|
||||
* FS7 v2 双读对拍:明细表组装 vs legacy 槽位列组装。
|
||||
* 反例来自 review-flight-state-2026-09-08 §12.1 / SIS §3.34.3(G28/G33/G23)。
|
||||
*/
|
||||
class FlightStoreDualReadTest {
|
||||
|
||||
private val tool = FlightStoreDiffTool()
|
||||
private val flid = "TEST_DUAL_READ"
|
||||
|
||||
private fun detailView(vararg collections: Pair<String, String>) = mapOf(
|
||||
flid to linkedMapOf(
|
||||
"FLID" to flid,
|
||||
*collections,
|
||||
),
|
||||
)
|
||||
|
||||
private fun legacyView(vararg collections: Pair<String, String>) = mapOf(
|
||||
flid to linkedMapOf(
|
||||
"FLID" to flid,
|
||||
*collections,
|
||||
),
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `Dual read GREEN when detail and legacy slot views agree within slot capacity`() {
|
||||
val gtdt = """[{"GTNO":"1","GATE":"G28","PGOT":"07SEP260800"},{"GTNO":"2","GATE":"G33"}]"""
|
||||
val detail = detailView("GTDT" to gtdt)
|
||||
val legacy = legacyView("GTDT" to gtdt)
|
||||
|
||||
val report = tool.diffDualRead(detail, legacy)
|
||||
assertTrue(report.isGreen)
|
||||
assertEquals(1, report.matchedCount)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Dual read RED for SIS three-gate sample G28 G33 G23 - third gate must not be ignored`() {
|
||||
val detailGtdt = """[
|
||||
{"GTNO":"1","GATE":"G28"},
|
||||
{"GTNO":"2","GATE":"G33"},
|
||||
{"GTNO":"3","GATE":"G23"}
|
||||
]""".replace("\n", "").replace(" ", "")
|
||||
val legacyGtdt = """[{"GTNO":"1","GATE":"G28"},{"GTNO":"2","GATE":"G33"}]"""
|
||||
|
||||
val report = tool.diffDualRead(
|
||||
detailView("GTDT" to detailGtdt),
|
||||
legacyView("GTDT" to legacyGtdt),
|
||||
)
|
||||
assertFalse(report.isGreen)
|
||||
assertTrue(report.unexpectedDeviations.any { it.path == "GTDT" })
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Dual read RED for non-contiguous GTNO=3 - must not rewrite to GTNO=1`() {
|
||||
val detailGtdt = """[{"GTNO":"3","GATE":"G28"}]"""
|
||||
val legacyGtdt = """[{"GTNO":"1","GATE":"G28"}]"""
|
||||
|
||||
val report = tool.diffDualRead(
|
||||
detailView("GTDT" to detailGtdt),
|
||||
legacyView("GTDT" to legacyGtdt),
|
||||
)
|
||||
assertFalse(report.isGreen)
|
||||
assertTrue(report.unexpectedDeviations.any { it.path?.startsWith("GTDT") == true })
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Dual read RED for duplicate CHKC different cabin classes - legacy dedupe loses row`() {
|
||||
val detailCkdt = """[
|
||||
{"CKNO":"1","CHKC":"01","CCLS":"F"},
|
||||
{"CKNO":"2","CHKC":"01","CCLS":"Y"}
|
||||
]""".replace("\n", "").replace(" ", "")
|
||||
val legacyCkdt = """[{"CKNO":"1","CHKC":"01","CCLS":"F"}]"""
|
||||
|
||||
val report = tool.diffDualRead(
|
||||
detailView("CKDT" to detailCkdt),
|
||||
legacyView("CKDT" to legacyCkdt),
|
||||
)
|
||||
assertFalse(report.isGreen)
|
||||
assertTrue(report.unexpectedDeviations.any { it.path == "CKDT" })
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Dual read RED for two bridge ops with ABDG - legacy milestone view drops bridge id`() {
|
||||
val detailAbtm = """[
|
||||
{"ASNO":"1","ABDG":"B12","ABOP":"A","AOTM":"07SEP260730"},
|
||||
{"ASNO":"2","ABDG":"B15","ABOP":"D","AOTM":"07SEP261200"}
|
||||
]""".replace("\n", "").replace(" ", "")
|
||||
val legacyAbtm = """[
|
||||
{"ASNO":"1","ABOP":"A","AOTM":"07SEP260730"},
|
||||
{"ASNO":"2","ABOP":"D","AOTM":"07SEP261200"}
|
||||
]""".replace("\n", "").replace(" ", "")
|
||||
|
||||
val report = tool.diffDualRead(
|
||||
detailView("ABTM" to detailAbtm),
|
||||
legacyView("ABTM" to legacyAbtm),
|
||||
)
|
||||
assertFalse(report.isGreen)
|
||||
assertTrue(report.unexpectedDeviations.any { it.path?.contains("ABDG") == true || it.path?.startsWith("ABTM") == true })
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Dual read RED for two DELY reasons - legacy keeps only first`() {
|
||||
val detailDely = """[
|
||||
{"DLNO":"1","CODE":"01","STRT":"07SEP260800","DURA":"30"},
|
||||
{"DLNO":"2","CODE":"02","STRT":"07SEP260900","DURA":"15"}
|
||||
]""".replace("\n", "").replace(" ", "")
|
||||
val legacyDely = """[{"CODE":"01","STRT":"07SEP260800","DURA":"30"}]"""
|
||||
|
||||
val report = tool.diffDualRead(
|
||||
detailView("DELY" to detailDely),
|
||||
legacyView("DELY" to legacyDely),
|
||||
)
|
||||
assertFalse(report.isGreen)
|
||||
assertTrue(report.unexpectedDeviations.any { it.path == "DELY" })
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `FlightSchdReadAssembler exposes lossy legacy slot view for three-gate SIS sample`() {
|
||||
val row = mapOf(
|
||||
"GATE1" to "G28",
|
||||
"GATE2" to "G33",
|
||||
)
|
||||
val detailCollections = mapOf(
|
||||
"GTDT" to """[
|
||||
{"GTNO":"1","GATE":"G28"},
|
||||
{"GTNO":"2","GATE":"G33"},
|
||||
{"GTNO":"3","GATE":"G23"}
|
||||
]""".replace("\n", "").replace(" ", ""),
|
||||
)
|
||||
|
||||
val detailView = FlightSchdReadAssembler.assembleDetailOnly(flid, row, detailCollections)
|
||||
val legacyView = FlightSchdReadAssembler.assembleLegacySlotsOnly(flid, row)
|
||||
|
||||
val report = tool.diffDualRead(mapOf(flid to detailView), mapOf(flid to legacyView))
|
||||
assertFalse(report.isGreen)
|
||||
assertTrue(legacyView["GTDT"]!!.contains("G23").not())
|
||||
assertTrue(detailView["GTDT"]!!.contains("G23"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `FlightSchdReadAssembler merged view follows detail table when present`() {
|
||||
val row = mapOf("GATE1" to "OLD")
|
||||
val detailCollections = mapOf(
|
||||
"GTDT" to """[{"GTNO":"1","GATE":"G28"}]""",
|
||||
)
|
||||
val merged = FlightSchdReadAssembler.assembleMerged(flid, row, detailCollections)
|
||||
assertEquals(detailCollections["GTDT"], merged["GTDT"])
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user